1use std::convert::TryFrom as _;
19use std::error;
20use std::fmt;
21use std::fmt::Write as _;
22use std::str::FromStr;
23
24use bech32::{Bech32, Bech32m, ByteIterExt, Fe32, Fe32IterExt, Hrp};
25use crate::blech32::{Blech32, Blech32m};
26use crate::hashes::Hash;
27use bitcoin::base58;
28use bitcoin::PublicKey;
29use crate::internals::array::ArrayExt as _;
30use crate::internals::slice::SliceExt;
31use secp256k1_zkp;
32use secp256k1_zkp::Secp256k1;
33use secp256k1_zkp::Verification;
34#[cfg(feature = "serde")]
35use serde;
36
37use crate::schnorr::{TapTweak, TweakedPublicKey, UntweakedPublicKey};
38use crate::taproot::TapNodeHash;
39
40use crate::{opcodes, script};
41use crate::{PubkeyHash, ScriptHash, WPubkeyHash, WScriptHash};
42
43#[derive(Debug, PartialEq)]
45pub enum AddressError {
46 Base58(base58::Error),
48 Bech32(bech32::primitives::decode::SegwitHrpstringError),
50 Blech32(crate::blech32::decode::SegwitHrpstringError),
52 InvalidAddress(String),
54 InvalidWitnessVersion(u8),
56 InvalidWitnessProgramLength(usize),
58 InvalidSegwitV0ProgramLength(usize),
60 InvalidWitnessEncoding,
62 InvalidSegwitV0Encoding,
64
65 InvalidBlindingPubKey(secp256k1_zkp::UpstreamError),
67
68 InvalidLength(usize),
70
71 InvalidAddressVersion(u8),
73}
74
75impl From<bech32::primitives::decode::SegwitHrpstringError> for AddressError {
76 fn from(e: bech32::primitives::decode::SegwitHrpstringError) -> Self {
77 AddressError::Bech32(e)
78 }
79}
80
81impl From<crate::blech32::decode::SegwitHrpstringError> for AddressError {
82 fn from(e: crate::blech32::decode::SegwitHrpstringError) -> Self {
83 AddressError::Blech32(e)
84 }
85}
86
87impl fmt::Display for AddressError {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 match *self {
90 AddressError::Base58(ref e) => write!(f, "base58 error: {}", e),
91 AddressError::Bech32(ref e) => write!(f, "bech32 error: {}", e),
92 AddressError::Blech32(ref e) => write!(f, "blech32 error: {}", e),
93 AddressError::InvalidAddress(ref a) => {
94 write!(f, "was unable to parse the address: {}", a)
95 }
96 AddressError::InvalidWitnessVersion(ref wver) => {
97 write!(f, "invalid witness script version: {}", wver)
98 }
99 AddressError::InvalidWitnessProgramLength(ref len) => {
100 write!(
101 f,
102 "the witness program must be between 2 and 40 bytes in length, not {}",
103 len
104 )
105 }
106 AddressError::InvalidSegwitV0ProgramLength(ref len) => {
107 write!(
108 f,
109 "a v0 witness program must be length 20 or 32, not {}",
110 len
111 )
112 }
113 AddressError::InvalidBlindingPubKey(ref e) => {
114 write!(f, "an invalid blinding pubkey was encountered: {}", e)
115 }
116 AddressError::InvalidWitnessEncoding => {
117 write!(f, "v1+ witness program must use b(l)ech32m not b(l)ech32")
118 }
119 AddressError::InvalidSegwitV0Encoding => {
120 write!(f, "v0 witness program must use b(l)ech32 not b(l)ech32m")
121 }
122 AddressError::InvalidLength(len) => {
123 write!(f, "Address data has invalid length {}", len)
124 }
125 AddressError::InvalidAddressVersion(v) => {
126 write!(f, "address version {} is invalid for this type", v)
127 }
128 }
129 }
130}
131
132impl error::Error for AddressError {
133 fn cause(&self) -> Option<&dyn error::Error> {
134 match *self {
135 AddressError::Base58(ref e) => Some(e),
136 AddressError::Bech32(ref e) => Some(e),
137 AddressError::Blech32(ref e) => Some(e),
138 AddressError::InvalidBlindingPubKey(ref e) => Some(e),
139 _ => None,
140 }
141 }
142}
143
144#[doc(hidden)]
145impl From<base58::Error> for AddressError {
146 fn from(e: base58::Error) -> AddressError {
147 AddressError::Base58(e)
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
153pub struct AddressParams {
154 pub p2pkh_prefix: u8,
156 pub p2sh_prefix: u8,
158 pub blinded_prefix: u8,
160 pub bech_hrp: Hrp,
162 pub blech_hrp: Hrp,
164}
165
166impl AddressParams {
167 pub const LIQUID: AddressParams = AddressParams {
169 p2pkh_prefix: 57,
170 p2sh_prefix: 39,
171 blinded_prefix: 12,
172 bech_hrp: Hrp::parse_unchecked("ex"),
173 blech_hrp: Hrp::parse_unchecked("lq"),
174 };
175
176 pub const ELEMENTS: AddressParams = AddressParams {
178 p2pkh_prefix: 235,
179 p2sh_prefix: 75,
180 blinded_prefix: 4,
181 bech_hrp: Hrp::parse_unchecked("ert"),
182 blech_hrp: Hrp::parse_unchecked("el"),
183 };
184
185 pub const LIQUID_TESTNET: AddressParams = AddressParams {
187 p2pkh_prefix: 36,
188 p2sh_prefix: 19,
189 blinded_prefix: 23,
190 bech_hrp: Hrp::parse_unchecked("tex"),
191 blech_hrp: Hrp::parse_unchecked("tlq"),
192 };
193}
194
195#[derive(Debug, Clone, PartialEq, Eq, Hash)]
197pub enum Payload {
198 PubkeyHash(PubkeyHash),
200 ScriptHash(ScriptHash),
202 WitnessProgram {
204 version: Fe32,
206 program: Vec<u8>,
208 },
209}
210
211#[derive(Clone, PartialEq, Eq, Hash)]
213pub struct Address {
214 pub params: &'static AddressParams,
216 pub payload: Payload,
218 pub blinding_pubkey: Option<secp256k1_zkp::PublicKey>,
220}
221
222impl Address {
223 pub fn is_blinded(&self) -> bool {
225 self.blinding_pubkey.is_some()
226 }
227
228 pub fn is_liquid(&self) -> bool {
230 self.params == &AddressParams::LIQUID
231 }
232
233 #[inline]
236 pub fn p2pkh(
237 pk: &PublicKey,
238 blinder: Option<secp256k1_zkp::PublicKey>,
239 params: &'static AddressParams,
240 ) -> Address {
241 let mut hash_engine = PubkeyHash::engine();
242 pk.write_into(&mut hash_engine)
243 .expect("engines don't error");
244
245 Address {
246 params,
247 payload: Payload::PubkeyHash(PubkeyHash::from_engine(hash_engine)),
248 blinding_pubkey: blinder,
249 }
250 }
251
252 #[inline]
255 pub fn p2sh(
256 script: &script::Script,
257 blinder: Option<secp256k1_zkp::PublicKey>,
258 params: &'static AddressParams,
259 ) -> Address {
260 Address {
261 params,
262 payload: Payload::ScriptHash(ScriptHash::hash(&script[..])),
263 blinding_pubkey: blinder,
264 }
265 }
266
267 pub fn p2wpkh(
270 pk: &PublicKey,
271 blinder: Option<secp256k1_zkp::PublicKey>,
272 params: &'static AddressParams,
273 ) -> Address {
274 let mut hash_engine = WPubkeyHash::engine();
275 pk.write_into(&mut hash_engine)
276 .expect("engines don't error");
277
278 Address {
279 params,
280 payload: Payload::WitnessProgram {
281 version: Fe32::Q,
282 program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
283 },
284 blinding_pubkey: blinder,
285 }
286 }
287
288 pub fn p2shwpkh(
291 pk: &PublicKey,
292 blinder: Option<secp256k1_zkp::PublicKey>,
293 params: &'static AddressParams,
294 ) -> Address {
295 let mut hash_engine = ScriptHash::engine();
296 pk.write_into(&mut hash_engine)
297 .expect("engines don't error");
298
299 let builder = script::Builder::new()
300 .push_int(0)
301 .push_slice(&ScriptHash::from_engine(hash_engine)[..]);
302
303 Address {
304 params,
305 payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())),
306 blinding_pubkey: blinder,
307 }
308 }
309
310 pub fn p2wsh(
312 script: &script::Script,
313 blinder: Option<secp256k1_zkp::PublicKey>,
314 params: &'static AddressParams,
315 ) -> Address {
316 Address {
317 params,
318 payload: Payload::WitnessProgram {
319 version: Fe32::Q,
320 program: WScriptHash::hash(&script[..])[..].to_vec(),
321 },
322 blinding_pubkey: blinder,
323 }
324 }
325
326 pub fn p2shwsh(
329 script: &script::Script,
330 blinder: Option<secp256k1_zkp::PublicKey>,
331 params: &'static AddressParams,
332 ) -> Address {
333 let ws = script::Builder::new()
334 .push_int(0)
335 .push_slice(&WScriptHash::hash(&script[..])[..])
336 .into_script();
337
338 Address {
339 params,
340 payload: Payload::ScriptHash(ScriptHash::hash(&ws[..])),
341 blinding_pubkey: blinder,
342 }
343 }
344
345 pub fn p2tr<C: Verification>(
347 secp: &Secp256k1<C>,
348 internal_key: UntweakedPublicKey,
349 merkle_root: Option<TapNodeHash>,
350 blinder: Option<secp256k1_zkp::PublicKey>,
351 params: &'static AddressParams,
352 ) -> Address {
353 Address {
354 params,
355 payload: {
356 let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
357 Payload::WitnessProgram {
358 version: Fe32::P,
359 program: output_key.into_inner().serialize().to_vec(),
360 }
361 },
362 blinding_pubkey: blinder,
363 }
364 }
365
366 pub fn p2tr_tweaked(
370 output_key: TweakedPublicKey,
371 blinder: Option<secp256k1_zkp::PublicKey>,
372 params: &'static AddressParams,
373 ) -> Address {
374 Address {
375 params,
376 payload: Payload::WitnessProgram {
377 version: Fe32::P,
378 program: output_key.into_inner().serialize().to_vec(),
379 },
380 blinding_pubkey: blinder,
381 }
382 }
383
384 pub fn from_script(
386 script: &script::Script,
387 blinder: Option<secp256k1_zkp::PublicKey>,
388 params: &'static AddressParams,
389 ) -> Option<Address> {
390 Some(Address {
391 payload: if script.is_p2pkh() {
392 Payload::PubkeyHash(Hash::from_slice(&script.as_bytes()[3..23]).unwrap())
393 } else if script.is_p2sh() {
394 Payload::ScriptHash(Hash::from_slice(&script.as_bytes()[2..22]).unwrap())
395 } else if script.is_v0_p2wpkh() {
396 Payload::WitnessProgram {
397 version: Fe32::Q,
398 program: script.as_bytes()[2..22].to_vec(),
399 }
400 } else if script.is_v0_p2wsh() {
401 Payload::WitnessProgram {
402 version: Fe32::Q,
403 program: script.as_bytes()[2..34].to_vec(),
404 }
405 } else if script.is_v1plus_p2witprog() {
406 Payload::WitnessProgram {
407 version: Fe32::try_from(script.as_bytes()[0] - 0x50).expect("0<32"),
408 program: script.as_bytes()[2..].to_vec(),
409 }
410 } else {
411 return None;
412 },
413 blinding_pubkey: blinder,
414 params,
415 })
416 }
417
418 pub fn script_pubkey(&self) -> script::Script {
420 match self.payload {
421 Payload::PubkeyHash(ref hash) => script::Builder::new()
422 .push_opcode(opcodes::all::OP_DUP)
423 .push_opcode(opcodes::all::OP_HASH160)
424 .push_slice(&hash[..])
425 .push_opcode(opcodes::all::OP_EQUALVERIFY)
426 .push_opcode(opcodes::all::OP_CHECKSIG),
427 Payload::ScriptHash(ref hash) => script::Builder::new()
428 .push_opcode(opcodes::all::OP_HASH160)
429 .push_slice(&hash[..])
430 .push_opcode(opcodes::all::OP_EQUAL),
431 Payload::WitnessProgram {
432 version: witver,
433 program: ref witprog,
434 } => script::Builder::new()
435 .push_int(i64::from(witver.to_u8()))
436 .push_slice(witprog),
437 }
438 .into_script()
439 }
440
441 #[must_use]
443 pub fn to_unconfidential(&self) -> Address {
444 Address {
445 params: self.params,
446 payload: self.payload.clone(),
447 blinding_pubkey: None,
448 }
449 }
450
451 #[must_use]
453 pub fn to_confidential(&self, blinding_pubkey: secp256k1_zkp::PublicKey) -> Address {
454 Address {
455 params: self.params,
456 payload: self.payload.clone(),
457 blinding_pubkey: Some(blinding_pubkey),
458 }
459 }
460
461 fn from_bech32(
462 s: &str,
463 blinded: bool,
464 params: &'static AddressParams,
465 ) -> Result<Address, AddressError> {
466 let (version, data): (Fe32, Vec<u8>) = if blinded {
467 let hs = crate::blech32::decode::SegwitHrpstring::new(s)?;
468 (hs.witness_version(), hs.byte_iter().collect())
469 } else {
470 let hs = bech32::primitives::decode::SegwitHrpstring::new(s)?;
471 (hs.witness_version(), hs.byte_iter().collect())
472 };
473
474 let (blinding_pubkey, program) = match blinded {
475 true => {
476 let (pk, rest) = SliceExt::split_first_chunk::<33>(data.as_slice())
477 .ok_or(AddressError::InvalidSegwitV0Encoding)?;
478 (
479 Some(
480 secp256k1_zkp::PublicKey::from_slice(pk)
481 .map_err(AddressError::InvalidBlindingPubKey)?,
482 ),
483 rest.to_vec(),
484 )
485 },
486 false => (None, data),
487 };
488
489 Ok(Address {
490 params,
491 payload: Payload::WitnessProgram { version, program },
492 blinding_pubkey,
493 })
494 }
495
496 fn from_base58(data: &[u8], params: &'static AddressParams) -> Result<Address, AddressError> {
498 let len_error = AddressError::InvalidLength(data.len());
499 let (blinding_prefix, blinded_data) = match data.split_first() {
505 Some(v) => v,
506 None => return Err(len_error),
507 };
508
509 let (prefix, blinding_pubkey, hash) = if *blinding_prefix == params.blinded_prefix {
510 let (prefix, pubkey_and_hash) = match blinded_data.split_first() {
511 Some(v) => v,
512 None => return Err(len_error),
513 };
514
515 let pubkey_and_hash = <&[u8; 53]>::try_from(pubkey_and_hash).map_err(|_| len_error)?;
516 let (pubkey, hash) = pubkey_and_hash.split_array::<33, 20>();
517
518 let blinding_pubkey = secp256k1_zkp::PublicKey::from_slice(pubkey)
519 .map_err(AddressError::InvalidBlindingPubKey)?;
520
521 (prefix, Some(blinding_pubkey), hash)
522 } else {
523 let hash = <&[u8; 20]>::try_from(blinded_data).map_err(|_| len_error)?;
524 (blinding_prefix, None, hash)
525 };
526
527 let payload = if *prefix == params.p2pkh_prefix {
528 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash))
529 } else if *prefix == params.p2sh_prefix {
530 Payload::ScriptHash(ScriptHash::from_byte_array(*hash))
531 } else {
532 return Err(AddressError::InvalidAddressVersion(*prefix));
533 };
534
535 Ok(Address {
536 params,
537 payload,
538 blinding_pubkey,
539 })
540 }
541
542 pub fn parse_with_params(
545 s: &str,
546 params: &'static AddressParams,
547 ) -> Result<Address, AddressError> {
548 let prefix = find_prefix(s);
550 let b32_ex = match_prefix(prefix, params.bech_hrp);
551 let b32_bl = match_prefix(prefix, params.blech_hrp);
552 if b32_ex || b32_bl {
553 return Address::from_bech32(s, b32_bl, params);
554 }
555
556 if s.len() > 150 {
558 return Err(AddressError::InvalidLength(s.len() * 11 / 15));
559 }
560 let data = base58::decode_check(s)?;
561 Address::from_base58(&data, params)
562 }
563}
564
565impl fmt::Display for Address {
566 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
567 match self.payload {
568 Payload::PubkeyHash(ref hash) => {
569 if let Some(ref blinder) = self.blinding_pubkey {
570 let mut prefixed = [0; 55]; prefixed[0] = self.params.blinded_prefix;
572 prefixed[1] = self.params.p2pkh_prefix;
573 prefixed[2..35].copy_from_slice(&blinder.serialize());
574 prefixed[35..].copy_from_slice(&hash[..]);
575 base58::encode_check_to_fmt(fmt, &prefixed[..])
576 } else {
577 let mut prefixed = [0; 21];
578 prefixed[0] = self.params.p2pkh_prefix;
579 prefixed[1..].copy_from_slice(&hash[..]);
580 base58::encode_check_to_fmt(fmt, &prefixed[..])
581 }
582 }
583 Payload::ScriptHash(ref hash) => {
584 if let Some(ref blinder) = self.blinding_pubkey {
585 let mut prefixed = [0; 55]; prefixed[0] = self.params.blinded_prefix;
587 prefixed[1] = self.params.p2sh_prefix;
588 prefixed[2..35].copy_from_slice(&blinder.serialize());
589 prefixed[35..].copy_from_slice(&hash[..]);
590 base58::encode_check_to_fmt(fmt, &prefixed[..])
591 } else {
592 let mut prefixed = [0; 21];
593 prefixed[0] = self.params.p2sh_prefix;
594 prefixed[1..].copy_from_slice(&hash[..]);
595 base58::encode_check_to_fmt(fmt, &prefixed[..])
596 }
597 }
598 Payload::WitnessProgram {
599 version: witver,
600 program: ref witprog,
601 } => {
602 let hrp = match self.blinding_pubkey.is_some() {
603 true => self.params.blech_hrp,
604 false => self.params.bech_hrp,
605 };
606
607 if self.is_blinded() {
609 if let Some(ref blinder) = self.blinding_pubkey {
610 let byte_iter = IntoIterator::into_iter(blinder.serialize())
611 .chain(witprog.iter().copied());
612 let fe_iter = byte_iter.bytes_to_fes();
613 if witver.to_u8() == 0 {
614 for c in fe_iter
615 .with_checksum::<Blech32>(&hrp)
616 .with_witness_version(witver)
617 .chars()
618 {
619 fmt.write_char(c)?;
620 }
621 } else {
622 for c in fe_iter
623 .with_checksum::<Blech32m>(&hrp)
624 .with_witness_version(witver)
625 .chars()
626 {
627 fmt.write_char(c)?;
628 }
629 }
630 return Ok(());
631 }
632 }
633
634 let byte_iter = witprog.iter().copied();
635 let fe_iter = byte_iter.bytes_to_fes();
636 if witver.to_u8() == 0 {
637 for c in fe_iter
638 .with_checksum::<Bech32>(&hrp)
639 .with_witness_version(witver)
640 .chars()
641 {
642 fmt.write_char(c)?;
643 }
644 } else {
645 for c in fe_iter
646 .with_checksum::<Bech32m>(&hrp)
647 .with_witness_version(witver)
648 .chars()
649 {
650 fmt.write_char(c)?;
651 }
652 }
653 Ok(())
654 }
655 }
656 }
657}
658
659impl fmt::Debug for Address {
660 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
661 fmt::Display::fmt(self, fmt)
662 }
663}
664
665fn find_prefix(bech32: &str) -> &str {
668 match bech32.rfind('1') {
670 None => bech32,
671 Some(sep) => bech32.split_at(sep).0,
672 }
673}
674
675fn match_prefix(prefix_mixed: &str, target: Hrp) -> bool {
679 target.len() == prefix_mixed.len() && target
680 .lowercase_char_iter()
681 .zip(prefix_mixed.chars())
682 .all(|(char_lower, char_mixed)| char_lower == char_mixed.to_ascii_lowercase())
683}
684
685impl FromStr for Address {
686 type Err = AddressError;
687
688 fn from_str(s: &str) -> Result<Address, AddressError> {
689 let liq = &AddressParams::LIQUID;
691 let ele = &AddressParams::ELEMENTS;
692 let liq_test = &AddressParams::LIQUID_TESTNET;
693
694 let net_arr = [liq, ele, liq_test];
695
696 let prefix = find_prefix(s);
697 for net in &net_arr {
698 if match_prefix(prefix, net.bech_hrp) {
700 return Address::from_bech32(s, false, net);
701 }
702 if match_prefix(prefix, net.blech_hrp) {
703 return Address::from_bech32(s, true, net);
704 }
705 }
706
707 if s.len() > 150 {
709 return Err(AddressError::InvalidLength(s.len() * 11 / 15));
710 }
711 let data = base58::decode_check(s)?;
712 if data.is_empty() {
713 return Err(AddressError::InvalidLength(data.len()));
714 }
715
716 let p = data[0];
717 for net in &net_arr {
718 if p == net.p2pkh_prefix || p == net.p2sh_prefix || p == net.blinded_prefix {
719 return Address::from_base58(&data, net);
720 }
721 }
722
723 Err(AddressError::InvalidAddress(s.to_owned()))
724 }
725}
726
727#[cfg(feature = "serde")]
728impl<'de> serde::Deserialize<'de> for Address {
729 #[inline]
730 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
731 where
732 D: serde::Deserializer<'de>,
733 {
734 use std::fmt::Formatter;
735
736 struct Visitor;
737 impl<'de> serde::de::Visitor<'de> for Visitor {
738 type Value = Address;
739
740 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
741 formatter.write_str("a Bitcoin address")
742 }
743
744 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
745 where
746 E: serde::de::Error,
747 {
748 Address::from_str(v).map_err(E::custom)
749 }
750
751 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
752 where
753 E: serde::de::Error,
754 {
755 self.visit_str(v)
756 }
757
758 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
759 where
760 E: serde::de::Error,
761 {
762 self.visit_str(&v)
763 }
764 }
765
766 deserializer.deserialize_str(Visitor)
767 }
768}
769
770#[cfg(feature = "serde")]
771impl serde::Serialize for Address {
772 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
773 where
774 S: serde::Serializer,
775 {
776 serializer.serialize_str(&self.to_string())
777 }
778}
779
780#[cfg(test)]
781mod test {
782 use super::*;
783 use crate::Script;
784 use bitcoin::key;
785 use secp256k1_zkp::{PublicKey, Secp256k1};
786 #[cfg(feature = "serde")]
787 use serde_json;
788
789 fn roundtrips(addr: &Address) {
790 assert_eq!(
791 Address::from_str(&addr.to_string()).ok().as_ref(),
792 Some(addr),
793 "string round-trip failed for {}",
794 addr,
795 );
796 assert_eq!(
797 Address::from_script(&addr.script_pubkey(), addr.blinding_pubkey, addr.params).as_ref(),
798 Some(addr),
799 "script round-trip failed for {}",
800 addr,
801 );
802 #[cfg(feature = "serde")]
803 assert_eq!(
804 serde_json::from_value::<Address>(serde_json::to_value(addr).unwrap())
805 .ok()
806 .as_ref(),
807 Some(addr)
808 );
809 }
810
811 #[test]
812 fn regression_188() {
813 let addr = Address::from_str("tlq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f3mmz5l7uw5pqmx6xf5xy50hsn6vhkm5euwt72x878eq6zxx2z58hd7zrsg9qn").unwrap();
815 roundtrips(&addr);
816 }
817
818 #[test]
819 fn exhaustive() {
820 let blinder_hex = "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
821 let blinder = PublicKey::from_str(blinder_hex).unwrap();
822 let sk_wif = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy";
823 let sk = key::PrivateKey::from_wif(sk_wif).unwrap();
824 let pk = sk.public_key(&Secp256k1::new());
825 let script: Script = vec![1u8, 2, 42, 255, 196].into();
826
827 let vectors = [
828 Address::p2pkh(&pk, None, &AddressParams::LIQUID),
829 Address::p2pkh(&pk, None, &AddressParams::ELEMENTS),
830 Address::p2pkh(&pk, Some(blinder), &AddressParams::LIQUID),
831 Address::p2pkh(&pk, Some(blinder), &AddressParams::ELEMENTS),
832 Address::p2sh(&script, None, &AddressParams::LIQUID),
833 Address::p2sh(&script, None, &AddressParams::ELEMENTS),
834 Address::p2sh(&script, Some(blinder), &AddressParams::LIQUID),
835 Address::p2sh(&script, Some(blinder), &AddressParams::ELEMENTS),
837 Address::p2wpkh(&pk, None, &AddressParams::LIQUID),
838 Address::p2wpkh(&pk, None, &AddressParams::ELEMENTS),
839 Address::p2wpkh(&pk, Some(blinder), &AddressParams::LIQUID),
840 Address::p2wpkh(&pk, Some(blinder), &AddressParams::ELEMENTS),
841 Address::p2shwpkh(&pk, None, &AddressParams::LIQUID),
842 Address::p2shwpkh(&pk, None, &AddressParams::ELEMENTS),
843 Address::p2shwpkh(&pk, Some(blinder), &AddressParams::LIQUID),
844 Address::p2shwpkh(&pk, Some(blinder), &AddressParams::ELEMENTS),
846 Address::p2wsh(&script, None, &AddressParams::LIQUID),
847 Address::p2wsh(&script, None, &AddressParams::ELEMENTS),
848 Address::p2wsh(&script, Some(blinder), &AddressParams::LIQUID),
849 Address::p2wsh(&script, Some(blinder), &AddressParams::ELEMENTS),
851 Address::p2shwsh(&script, None, &AddressParams::LIQUID),
852 Address::p2shwsh(&script, None, &AddressParams::ELEMENTS),
853 Address::p2shwsh(&script, Some(blinder), &AddressParams::LIQUID),
855 Address::p2shwsh(&script, Some(blinder), &AddressParams::ELEMENTS),
857 ];
858
859 for addr in &vectors {
860 roundtrips(addr);
861 }
862 }
863
864 #[test]
865 fn test_actuals() {
866 let addresses = [
868 ("2dxmEBXc2qMYcLSKiDBxdEePY3Ytixmnh4E", false, AddressParams::ELEMENTS),
870 ("CTEo6VKG8xbe7HnfVW9mQoWTgtgeRSPktwTLbELzGw5tV8Ngzu53EBiasFMQKVbWmKWWTAdN5AUf4M6Y", true, AddressParams::ELEMENTS),
871 ("ert1qwhh2n5qypypm0eufahm2pvj8raj9zq5c27cysu", false, AddressParams::ELEMENTS),
872 ("el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse", true, AddressParams::ELEMENTS),
873 ("GqiQRsPEyJLAsEBFB5R34KHuqxDNkG3zur", false, AddressParams::LIQUID),
875 ("VJLDwMVWXg8RKq4mRe3YFNTAEykVN6V8x5MRUKKoC3nfRnbpnZeiG3jygMC6A4Gw967GY5EotJ4Rau2F", true, AddressParams::LIQUID),
876 ("ex1q7gkeyjut0mrxc3j0kjlt7rmcnvsh0gt45d3fud", false, AddressParams::LIQUID),
877 ("lq1qqf8er278e6nyvuwtgf39e6ewvdcnjupn9a86rzpx655y5lhkt0walu3djf9cklkxd3ryld97hu8h3xepw7sh2rlu7q45dcew5", true, AddressParams::LIQUID),
878 ];
879
880 for &(a, blinded, ref params) in &addresses {
881 let result = a.parse();
882 assert!(
883 result.is_ok(),
884 "vector: {}, err: \"{}\"",
885 a,
886 result.unwrap_err()
887 );
888 let addr: Address = result.unwrap();
889 assert_eq!(a, &addr.to_string(), "vector: {}", a);
890 assert_eq!(blinded, addr.is_blinded());
891 assert_eq!(params, addr.params);
892 roundtrips(&addr);
893 }
894 }
895
896 #[test]
897 fn test_blech32_vectors() {
898 let address: Result<Address, _> = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse".parse();
900 assert!(address.is_ok());
901
902 let address: Result<Address, _> = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsxguu9nrdg2pc".parse();
903 assert_eq!(
904 address.err().unwrap().to_string(),
905 "blech32 error: invalid checksum", );
907
908 let address: Result<Address, _> = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsnnmzrstzt7de".parse();
909 assert_eq!(
910 address.err().unwrap().to_string(),
911 "blech32 error: invalid checksum", );
913
914 let address: Result<Address, _> =
915 "ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqqu2tys".parse();
916 assert_eq!(
917 address.err().unwrap().to_string(),
918 "bech32 error: invalid segwit witness version: 17 (bech32 character: '3')",
919 );
920
921 let address: Result<Address, _> = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpe9jfn0gypaj".parse();
922 assert_eq!(
923 address.err().unwrap().to_string(),
924 "blech32 error: invalid witness length",
925 );
926
927 let address: Result<Address, _> = "rrr1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfs2d9rp7meq4kg".parse();
930 assert_eq!(address.err().unwrap().to_string(), "base58 error: decode",);
931 }
932
933 #[test]
934 fn test_fixed_addresses() {
935 let pk = bitcoin::PublicKey::from_str(
936 "0212bf0ea45b733dfde8ecb5e896306c4165c666c99fc5d1ab887f71393a975cea",
937 )
938 .unwrap();
939 let script = Script::default();
940 let secp = Secp256k1::verification_only();
941 let internal_key = UntweakedPublicKey::from_str(
942 "93c7378d96518a75448821c4f7c8f4bae7ce60f804d03d1f0628dd5dd0f5de51",
943 )
944 .unwrap();
945 let tap_node_hash = TapNodeHash::all_zeros();
946
947 let mut expected = IntoIterator::into_iter([
948 "2dszRCFv8Ub4ytKo1Q1vXXGgSx7mekNDwSJ",
949 "XToMocNywBYNSiXUe5xvoa2naAps9Ek1hq",
950 "ert1qew0l0emv7449u7hqgc8utzdzryhse79yhq2sxv",
951 "XZF6k8S6eoVxXMB4NpWjh2s7LjQUP7pw2R",
952 "ert1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2szaqlpq",
953 "ert1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02q2cqvj9",
954 "ert1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390sqkjp06",
955 "CTEkC79sYAvWNcxd8iTYnYo226FqRBbzBcMppq7L2dA8jVXJWoo1kKWB3UBLY6gBjiXf87ibs8c6mQyZ",
956 "AzpjUhKMLJi9y2oLt3ZdM3BP9nHdLPJfGMVxRBaRc2gDpeNqPMVpShTszJW7bX42vT2KoejYy8GtbcxH",
957 "el1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gmz9ej9kyq5yc",
958 "AzpjUhKMLJi9y2oLt3ZdM3BP9nHdLPJfGMVxRBaRc2gDpeNvq6SLVpBVwtakF6nmUFundyW7YjUdVkpr",
959 "el1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4h79u87cnxdzq",
960 "el1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75cj24fq2u2ls5",
961 "el1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2lhjd0k0q7qn4h",
962 "QFq3vvrr6Ub2KAyb3LdoCxEQvKukB6nN9i",
963 "GydeMhecNgrq17WMkyyTM4ETv1YubMVtLN",
964 "ex1qew0l0emv7449u7hqgc8utzdzryhse79ydjqgek",
965 "H55PJDhj6JpR5k9wViXGEX4nga8WmhXtnD",
966 "ex1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2s4sla8h",
967 "ex1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02qa4lw5j",
968 "ex1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390shmdrfd",
969 "VTptY6cqJbusNpL5xvo8VL38nLX9PGDjfYQfqhu9EaA7FtuidkWyQzMHY9jzZrpBcCXT437vM6V4N8kh",
970 "VJL64Ep3rcngP4cScRme15q9i8MCNiuqWeiG3YbtduUidVyorg7nRsgmmF714QtH3sNpWB2CqsVVciQh",
971 "lq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gs2923tg58xcz",
972 "VJL64Ep3rcngP4cScRme15q9i8MCNiuqWeiG3YbtduUidVyuJR4JUzQPiqBdhzd1bgGHLVnmRUjfHc68",
973 "lq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz47jmkmgmr9a4s",
974 "lq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75375l4kfvf08y",
975 "lq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2l77n92erwrrz8",
976 "FojPFeboBgrd953mXXe72KWthjVwHWozqN",
977 "8vsafXgrB5bJeSidGbK5eYnjKvQ3RiB4BB",
978 "tex1qew0l0emv7449u7hqgc8utzdzryhse79yh5jp9a",
979 "92KKc3jxthYtj5ND1KrtY1d46UyeWV6XbP",
980 "tex1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2s5fd6kc",
981 "tex1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02quvdf9a",
982 "tex1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390skzlycz",
983 "vtS71VhcpFt978sha5d1L2gCzp3UL5kXacRpb3N4GTW5MwvBzz5HwxYyB8Pns4yM2dd2osmQkHSkp88u",
984 "vjTuLJ76nGi8PUopBVmGK8bLKPfBpaBWf6wKfn8z9Vdz6ubVhpvmMr6TK2RcqAYiujN1g1uwg8kejrM3",
985 "tlq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gq8g2kuxfj8ft",
986 "vjTuLJ76nGi8PUopBVmGK8bLKPfBpaBWf6wKfn8z9Vdz6ubb9ZsHQxp5GcWFUkHTTYFUWLgWFk1DN5Fe",
987 "tlq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4e6vcdfcyp5m8",
988 "tlq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75kkr3rh2tdxfn",
989 "tlq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2lekytucqf82vs",
990 ]);
991
992 for params in [
993 &AddressParams::ELEMENTS,
994 &AddressParams::LIQUID,
995 &AddressParams::LIQUID_TESTNET,
996 ] {
997 for blinder in [None, Some(pk.inner)] {
998 let addr = Address::p2pkh(&pk, blinder, params);
999 assert_eq!(&addr.to_string(), expected.next().unwrap());
1000
1001 let addr = Address::p2sh(&script, blinder, params);
1002 assert_eq!(&addr.to_string(), expected.next().unwrap());
1003
1004 let addr = Address::p2wpkh(&pk, blinder, params);
1005 assert_eq!(&addr.to_string(), expected.next().unwrap());
1006
1007 let addr = Address::p2shwpkh(&pk, blinder, params);
1008 assert_eq!(&addr.to_string(), expected.next().unwrap());
1009
1010 let addr = Address::p2wsh(&script, blinder, params);
1011 assert_eq!(&addr.to_string(), expected.next().unwrap());
1012
1013 let addr = Address::p2tr(&secp, internal_key, None, blinder, params);
1014 assert_eq!(&addr.to_string(), expected.next().unwrap());
1015
1016 let addr = Address::p2tr(&secp, internal_key, Some(tap_node_hash), blinder, params);
1017 assert_eq!(&addr.to_string(), expected.next().unwrap());
1018 }
1019 }
1020 }
1021}