hal_simplicity/actions/
address.rs1use elements::bitcoin::{secp256k1, PublicKey};
2use elements::{Address, Script};
3
4use crate::address::{AddressInfo, Addresses};
5use crate::Network;
6
7#[derive(Debug, thiserror::Error)]
8pub enum AddressError {
9 #[error("invalid blinder hex: {0}")]
10 BlinderHex(hex::FromHexError),
11
12 #[error("invalid blinder: {0}")]
13 BlinderInvalid(secp256k1::Error),
14
15 #[error("invalid pubkey: {0}")]
16 PubkeyInvalid(elements::bitcoin::key::ParsePublicKeyError),
17
18 #[error("invalid script hex: {0}")]
19 ScriptHex(hex::FromHexError),
20
21 #[error("can't create addresses without a pubkey")]
22 MissingInput,
23
24 #[error("invalid address format: {0}")]
25 AddressParse(elements::address::AddressError),
26
27 #[error("no address provided")]
28 NoAddressProvided,
29
30 #[error("addresses always have params")]
31 AddressesAlwaysHaveParams,
32}
33
34pub fn address_create(
36 pubkey_hex: Option<&str>,
37 script_hex: Option<&str>,
38 blinder_hex: Option<&str>,
39 network: Network,
40) -> Result<Addresses, AddressError> {
41 let blinder = blinder_hex
42 .map(|b| {
43 let bytes = hex::decode(b).map_err(AddressError::BlinderHex)?;
44 secp256k1::PublicKey::from_slice(&bytes).map_err(AddressError::BlinderInvalid)
45 })
46 .transpose()?;
47
48 let created = if let Some(pubkey_hex) = pubkey_hex {
49 let pubkey: PublicKey = pubkey_hex.parse().map_err(AddressError::PubkeyInvalid)?;
50 Addresses::from_pubkey(&pubkey, blinder, network)
51 } else if let Some(script_hex) = script_hex {
52 let script_bytes = hex::decode(script_hex).map_err(AddressError::ScriptHex)?;
53 let script: Script = script_bytes.into();
54 Addresses::from_script(&script, blinder, network)
55 } else {
56 return Err(AddressError::MissingInput);
57 };
58
59 Ok(created)
60}
61
62pub fn address_inspect(address_str: &str) -> Result<AddressInfo, AddressError> {
64 let address: Address = address_str.parse().map_err(AddressError::AddressParse)?;
65 let script_pk = address.script_pubkey();
66
67 let mut info = AddressInfo {
68 network: Network::from_params(address.params)
69 .ok_or(AddressError::AddressesAlwaysHaveParams)?,
70 script_pub_key: hal::tx::OutputScriptInfo {
71 hex: Some(script_pk.to_bytes().into()),
72 asm: Some(script_pk.asm()),
73 address: None,
74 type_: None,
75 },
76 type_: None,
77 pubkey_hash: None,
78 script_hash: None,
79 witness_pubkey_hash: None,
80 witness_script_hash: None,
81 witness_program_version: None,
82 blinding_pubkey: address.blinding_pubkey,
83 unconfidential: if address.blinding_pubkey.is_some() {
84 Some(Address {
85 params: address.params,
86 payload: address.payload.clone(),
87 blinding_pubkey: None,
88 })
89 } else {
90 None
91 },
92 };
93
94 use elements::address::Payload;
95 use elements::hashes::Hash;
96 use elements::{WPubkeyHash, WScriptHash};
97
98 match address.payload {
99 Payload::PubkeyHash(pkh) => {
100 info.type_ = Some("p2pkh".to_owned());
101 info.pubkey_hash = Some(pkh);
102 }
103 Payload::ScriptHash(sh) => {
104 info.type_ = Some("p2sh".to_owned());
105 info.script_hash = Some(sh);
106 }
107 Payload::WitnessProgram {
108 version,
109 program,
110 } => {
111 let version = version.to_u8() as usize;
112 info.witness_program_version = Some(version);
113
114 if version == 0 {
115 if program.len() == 20 {
116 info.type_ = Some("p2wpkh".to_owned());
117 info.witness_pubkey_hash =
118 Some(WPubkeyHash::from_slice(&program).expect("size 20"));
119 } else if program.len() == 32 {
120 info.type_ = Some("p2wsh".to_owned());
121 info.witness_script_hash =
122 Some(WScriptHash::from_slice(&program).expect("size 32"));
123 } else {
124 info.type_ = Some("invalid-witness-program".to_owned());
125 }
126 } else {
127 info.type_ = Some("unknown-witness-program-version".to_owned());
128 }
129 }
130 }
131
132 Ok(info)
133}