1use std::fmt::{self, Debug, Display, Formatter};
27use std::str::FromStr;
28
29use bc::{
30 InvalidPubkey, OutputPk, PubkeyHash, ScriptHash, ScriptPubkey, WPubkeyHash, WScriptHash,
31 WitnessVer,
32};
33use bech32::u5;
34
35use crate::base58;
36
37pub const PUBKEY_ADDRESS_PREFIX_MAIN: u8 = 0; pub const SCRIPT_ADDRESS_PREFIX_MAIN: u8 = 5; pub const PUBKEY_ADDRESS_PREFIX_TEST: u8 = 111; pub const SCRIPT_ADDRESS_PREFIX_TEST: u8 = 196; #[derive(Clone, Eq, PartialEq, Debug, Display, Error)]
48#[display(doc_comments)]
49pub enum AddressError {
50 InvalidTaprootKey,
52 UnsupportedScriptPubkey,
54}
55
56#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
58#[display(doc_comments)]
59pub enum AddressParseError {
60 #[from]
62 Base58(base58::Error),
63
64 #[from]
66 Bech32(bech32::Error),
67
68 InvalidAddressVersion(u8),
70
71 InvalidWitnessVersion(u8),
73
74 FutureTaprootVersion(usize, String),
76
77 FutureWitnessVersion(WitnessVer),
79
80 InvalidBech32Variant(bech32::Variant),
82
83 UnrecognizableFormat(String),
85
86 #[from(InvalidPubkey<32>)]
88 WrongPublicKeyData,
89
90 UnrecognizedAddressType,
93}
94
95#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
96pub struct Address {
97 pub payload: AddressPayload,
99
100 pub network: AddressNetwork,
102}
103
104impl Address {
105 pub fn new(payload: AddressPayload, network: AddressNetwork) -> Self {
106 Address { payload, network }
107 }
108
109 pub fn with(
113 script: &ScriptPubkey,
114 network: impl Into<AddressNetwork>,
115 ) -> Result<Self, AddressError> {
116 let payload = AddressPayload::from_script(script)?;
117 Ok(Address {
118 payload,
119 network: network.into(),
120 })
121 }
122
123 pub fn script_pubkey(self) -> ScriptPubkey { self.payload.script_pubkey() }
125
126 pub fn is_testnet(self) -> bool { self.network != AddressNetwork::Mainnet }
128
129 pub fn address_type(self) -> AddressType { self.payload.address_type() }
131}
132
133impl Display for Address {
134 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
135 let (version, variant, prog) = match self.payload {
136 AddressPayload::Pkh(PubkeyHash(hash)) | AddressPayload::Sh(ScriptHash(hash)) => {
137 let mut prefixed = [0; 21];
138 prefixed[0] = match (self.payload, self.network) {
139 (AddressPayload::Pkh(_), AddressNetwork::Mainnet) => PUBKEY_ADDRESS_PREFIX_MAIN,
140 (AddressPayload::Sh(_), AddressNetwork::Mainnet) => SCRIPT_ADDRESS_PREFIX_MAIN,
141 (AddressPayload::Pkh(_), _) => PUBKEY_ADDRESS_PREFIX_TEST,
142 (AddressPayload::Sh(_), _) => SCRIPT_ADDRESS_PREFIX_TEST,
143 _ => unreachable!(),
144 };
145 prefixed[1..].copy_from_slice(hash.as_ref());
146 return base58::encode_check_to_fmt(f, &prefixed[..]);
147 }
148 AddressPayload::Wpkh(hash) => {
149 (WitnessVer::V0, bech32::Variant::Bech32, Box::new(hash) as Box<dyn AsRef<[u8]>>)
150 }
151 AddressPayload::Wsh(hash) => {
152 (WitnessVer::V0, bech32::Variant::Bech32, Box::new(hash) as Box<dyn AsRef<[u8]>>)
153 }
154 AddressPayload::Tr(pk) => (
155 WitnessVer::V1,
156 bech32::Variant::Bech32m,
157 Box::new(pk.to_byte_array()) as Box<dyn AsRef<[u8]>>,
158 ),
159 };
160
161 struct UpperWriter<W: fmt::Write>(W);
162 impl<W: fmt::Write> fmt::Write for UpperWriter<W> {
163 fn write_str(&mut self, s: &str) -> fmt::Result {
164 for c in s.chars() {
165 self.0.write_char(c.to_ascii_uppercase())?;
166 }
167 Ok(())
168 }
169 }
170
171 let mut upper_writer;
172 let writer = if f.alternate() {
173 upper_writer = UpperWriter(f);
174 &mut upper_writer as &mut dyn fmt::Write
175 } else {
176 f as &mut dyn fmt::Write
177 };
178 let mut bech32_writer =
179 bech32::Bech32Writer::new(self.network.bech32_hrp(), variant, writer)?;
180 let ver_u5 = u5::try_from_u8(version.version_no()).expect("witness version <= 16");
181 bech32::WriteBase32::write_u5(&mut bech32_writer, ver_u5)?;
182 bech32::ToBase32::write_base32(&prog.as_ref(), &mut bech32_writer)
183 }
184}
185
186impl FromStr for Address {
187 type Err = AddressParseError;
188
189 fn from_str(s: &str) -> Result<Self, Self::Err> {
190 let parse_base58 = || -> Result<Self, Self::Err> {
191 if s.len() > 50 {
192 return Err(AddressParseError::Base58(base58::Error::InvalidLength(
193 s.len() * 11 / 15,
194 )));
195 }
196 let data = base58::decode_check(s)?;
197 if data.len() != 21 {
198 return Err(AddressParseError::Base58(base58::Error::InvalidLength(data.len())));
199 }
200
201 let network = match data[0] {
202 PUBKEY_ADDRESS_PREFIX_MAIN | SCRIPT_ADDRESS_PREFIX_MAIN => AddressNetwork::Mainnet,
203 PUBKEY_ADDRESS_PREFIX_TEST | SCRIPT_ADDRESS_PREFIX_TEST => AddressNetwork::Testnet,
204 x => return Err(AddressParseError::InvalidAddressVersion(x)),
205 };
206
207 let mut hash = [0u8; 20];
208 hash.copy_from_slice(&data[1..]);
209 let payload = match data[0] {
210 PUBKEY_ADDRESS_PREFIX_MAIN | PUBKEY_ADDRESS_PREFIX_TEST => {
211 AddressPayload::Pkh(PubkeyHash::from(hash))
212 }
213 SCRIPT_ADDRESS_PREFIX_MAIN | SCRIPT_ADDRESS_PREFIX_TEST => {
214 AddressPayload::Sh(ScriptHash::from(hash))
215 }
216 _ => unreachable!(),
217 };
218
219 Ok(Address::new(payload, network))
220 };
221
222 let parse_bech32 = |hri: String,
223 payload: Vec<bech32::u5>,
224 variant: bech32::Variant|
225 -> Result<Self, Self::Err> {
226 let network = match hri.as_str() {
227 "bc" | "BC" => AddressNetwork::Mainnet,
228 "tb" | "TB" => AddressNetwork::Testnet,
229 "bcrt" | "BCRT" => AddressNetwork::Regtest,
230 _ => return parse_base58(),
231 };
232 let (v, p5) = payload.split_at(1);
233 let wv = v[0].to_u8();
234 let version = WitnessVer::from_version_no(wv).map_err(|err| {
235 eprintln!("{err}");
236 AddressParseError::InvalidWitnessVersion(wv)
237 })?;
238 let program: Vec<u8> = bech32::FromBase32::from_base32(p5)?;
239 let payload = match (version, variant) {
240 (WitnessVer::V0, bech32::Variant::Bech32) if program.len() == 20 => {
241 let mut hash = [0u8; 20];
242 hash.copy_from_slice(&program);
243 AddressPayload::Wpkh(hash.into())
244 }
245 (WitnessVer::V0, bech32::Variant::Bech32) if program.len() == 32 => {
246 let mut hash = [0u8; 32];
247 hash.copy_from_slice(&program);
248 AddressPayload::Wsh(hash.into())
249 }
250 (WitnessVer::V1, bech32::Variant::Bech32m) if program.len() == 32 => {
251 let mut key = [0u8; 32];
252 key.copy_from_slice(&program);
253 let pk = OutputPk::from_byte_array(key)?;
254 AddressPayload::Tr(pk)
255 }
256
257 (WitnessVer::V1, bech32::Variant::Bech32m) => {
258 return Err(AddressParseError::FutureTaprootVersion(
259 program.len(),
260 s.to_owned(),
261 ));
262 }
263
264 (WitnessVer::V0 | WitnessVer::V1, wrong) => {
265 return Err(AddressParseError::InvalidBech32Variant(wrong));
266 }
267
268 (future, _) => return Err(AddressParseError::FutureWitnessVersion(future)),
269 };
270 Ok(Address::new(payload, network))
271 };
272
273 match bech32::decode(s) {
274 Ok((hri, payload, variant)) => parse_bech32(hri, payload, variant),
275 Err(_) => {
276 parse_base58().map_err(|_| AddressParseError::UnrecognizableFormat(s.to_owned()))
277 }
278 }
279 }
280}
281
282#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
284pub enum AddressPayload {
285 #[from]
287 Pkh(PubkeyHash),
288
289 #[from]
291 Sh(ScriptHash),
292
293 #[from]
295 Wpkh(WPubkeyHash),
296
297 #[from]
299 Wsh(WScriptHash),
300
301 #[from]
303 Tr(OutputPk),
304}
305
306impl AddressPayload {
307 pub fn into_address(self, network: AddressNetwork) -> Address {
309 Address {
310 payload: self,
311 network,
312 }
313 }
314
315 pub fn from_script(script: &ScriptPubkey) -> Result<Self, AddressError> {
318 Ok(if script.is_p2pkh() {
319 let mut bytes = [0u8; 20];
320 bytes.copy_from_slice(&script[3..23]);
321 AddressPayload::Pkh(PubkeyHash::from(bytes))
322 } else if script.is_p2sh() {
323 let mut bytes = [0u8; 20];
324 bytes.copy_from_slice(&script[2..22]);
325 AddressPayload::Sh(ScriptHash::from(bytes))
326 } else if script.is_p2wpkh() {
327 let mut bytes = [0u8; 20];
328 bytes.copy_from_slice(&script[2..]);
329 AddressPayload::Wpkh(WPubkeyHash::from(bytes))
330 } else if script.is_p2wsh() {
331 let mut bytes = [0u8; 32];
332 bytes.copy_from_slice(&script[2..]);
333 AddressPayload::Wsh(WScriptHash::from(bytes))
334 } else if script.is_p2tr() {
335 let mut bytes = [0u8; 32];
336 bytes.copy_from_slice(&script[2..]);
337 AddressPayload::Tr(
338 OutputPk::from_byte_array(bytes).map_err(|_| AddressError::InvalidTaprootKey)?,
339 )
340 } else {
341 return Err(AddressError::UnsupportedScriptPubkey);
342 })
343 }
344
345 pub fn script_pubkey(self) -> ScriptPubkey {
347 match self {
348 AddressPayload::Pkh(hash) => ScriptPubkey::p2pkh(hash),
349 AddressPayload::Sh(hash) => ScriptPubkey::p2sh(hash),
350 AddressPayload::Wpkh(hash) => ScriptPubkey::p2wpkh(hash),
351 AddressPayload::Wsh(hash) => ScriptPubkey::p2wsh(hash),
352 AddressPayload::Tr(output_key) => ScriptPubkey::p2tr_tweaked(output_key),
353 }
354 }
355
356 pub fn address_type(self) -> AddressType {
358 match self {
359 AddressPayload::Pkh(_) => AddressType::P2pkh,
360 AddressPayload::Sh(_) => AddressType::P2sh,
361 AddressPayload::Wpkh(_) => AddressType::P2wpkh,
362 AddressPayload::Wsh(_) => AddressType::P2wsh,
363 AddressPayload::Tr(_) => AddressType::P2tr,
364 }
365 }
366}
367
368impl From<AddressPayload> for ScriptPubkey {
369 fn from(ap: AddressPayload) -> Self { ap.script_pubkey() }
370}
371
372#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
374pub enum AddressType {
375 #[display("P2PKH")]
377 P2pkh,
378
379 #[display("P2SH")]
381 P2sh,
382
383 #[display("P2WPKH")]
385 P2wpkh,
386
387 #[display("P2WSH")]
389 P2wsh,
390
391 #[display("P2TR")]
393 P2tr,
394}
395
396impl AddressType {
397 pub fn witness_version(self) -> Option<WitnessVer> {
400 match self {
401 AddressType::P2pkh => None,
402 AddressType::P2sh => None,
403 AddressType::P2wpkh | AddressType::P2wsh => Some(WitnessVer::V0),
404 AddressType::P2tr => Some(WitnessVer::V1),
405 }
406 }
407}
408
409impl FromStr for AddressType {
410 type Err = AddressParseError;
411
412 fn from_str(s: &str) -> Result<Self, Self::Err> {
413 #[allow(clippy::match_str_case_mismatch)]
414 Ok(match s.to_uppercase().as_str() {
415 "P2PKH" => AddressType::P2pkh,
416 "P2SH" => AddressType::P2sh,
417 "P2WPKH" => AddressType::P2wpkh,
418 "P2WSH" => AddressType::P2wsh,
419 "P2TR" => AddressType::P2tr,
420 _ => return Err(AddressParseError::UnrecognizedAddressType),
421 })
422 }
423}
424
425#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
427pub enum AddressNetwork {
428 Mainnet,
430
431 Testnet,
433
434 Regtest,
436}
437
438impl AddressNetwork {
439 pub fn is_testnet(self) -> bool { self != Self::Mainnet }
442
443 pub fn bech32_hrp(self) -> &'static str {
444 match self {
445 AddressNetwork::Mainnet => "bc",
446 AddressNetwork::Testnet => "tb",
447 AddressNetwork::Regtest => "bcrt",
448 }
449 }
450}
451
452#[cfg(feature = "serde")]
453mod _serde {
454 use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
455
456 use super::*;
457
458 impl Serialize for Address {
459 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
460 where S: Serializer {
461 serializer.serialize_str(&self.to_string())
462 }
463 }
464
465 impl<'de> Deserialize<'de> for Address {
466 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
467 where D: Deserializer<'de> {
468 let s = String::deserialize(deserializer)?;
469 Address::from_str(&s).map_err(|err| {
470 de::Error::custom(format!(
471 "invalid xpub specification string representation; {err}"
472 ))
473 })
474 }
475 }
476}
477
478#[cfg(test)]
479mod test {
480 use super::*;
481
482 #[test]
483 fn display_from_str() {
484 let b32 = "tb1p5kgdjdf99vfa2xwufd2cx2qru468z79s2arn3jf5feg95d9m62gqzpnjjk";
485 assert_eq!(Address::from_str(b32).unwrap().to_string(), b32);
486 }
487}