hal_simplicity/actions/
address.rs

1use 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
34/// Create addresses from a public key or script.
35pub 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
62/// Inspect an address and return detailed information.
63pub 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}