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