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(i64::from(witver.to_u8()))
434 .push_slice(witprog),
435 }
436 .into_script()
437 }
438
439 #[must_use]
441 pub fn to_unconfidential(&self) -> Address {
442 Address {
443 params: self.params,
444 payload: self.payload.clone(),
445 blinding_pubkey: None,
446 }
447 }
448
449 #[must_use]
451 pub fn to_confidential(&self, blinding_pubkey: secp256k1_zkp::PublicKey) -> Address {
452 Address {
453 params: self.params,
454 payload: self.payload.clone(),
455 blinding_pubkey: Some(blinding_pubkey),
456 }
457 }
458
459 fn from_bech32(
460 s: &str,
461 blinded: bool,
462 params: &'static AddressParams,
463 ) -> Result<Address, AddressError> {
464 let (version, data): (Fe32, Vec<u8>) = if blinded {
465 let hs = crate::blech32::decode::SegwitHrpstring::new(s)?;
466 (hs.witness_version(), hs.byte_iter().collect())
467 } else {
468 let hs = bech32::primitives::decode::SegwitHrpstring::new(s)?;
469 (hs.witness_version(), hs.byte_iter().collect())
470 };
471
472 let (blinding_pubkey, program) = match blinded {
473 true => (
474 Some(
475 secp256k1_zkp::PublicKey::from_slice(&data[..33])
476 .map_err(AddressError::InvalidBlindingPubKey)?,
477 ),
478 data[33..].to_vec(),
479 ),
480 false => (None, data),
481 };
482
483 Ok(Address {
484 params,
485 payload: Payload::WitnessProgram { version, program },
486 blinding_pubkey,
487 })
488 }
489
490 fn from_base58(data: &[u8], params: &'static AddressParams) -> Result<Address, AddressError> {
492 let blinded = data[0] == params.blinded_prefix;
498 let prefix = match (blinded, data.len()) {
499 (true, 55) => data[1],
500 (false, 21) => data[0],
501 (_, len) => return Err(AddressError::InvalidLength(len)),
502 };
503
504 let (blinding_pubkey, payload_data) = match blinded {
505 true => (
506 Some(
507 secp256k1_zkp::PublicKey::from_slice(&data[2..35])
508 .map_err(AddressError::InvalidBlindingPubKey)?,
509 ),
510 &data[35..],
511 ),
512 false => (None, &data[1..]),
513 };
514
515 let payload = if prefix == params.p2pkh_prefix {
516 Payload::PubkeyHash(PubkeyHash::from_slice(payload_data).unwrap())
517 } else if prefix == params.p2sh_prefix {
518 Payload::ScriptHash(ScriptHash::from_slice(payload_data).unwrap())
519 } else {
520 return Err(AddressError::InvalidAddressVersion(prefix));
521 };
522
523 Ok(Address {
524 params,
525 payload,
526 blinding_pubkey,
527 })
528 }
529
530 pub fn parse_with_params(
533 s: &str,
534 params: &'static AddressParams,
535 ) -> Result<Address, AddressError> {
536 let prefix = find_prefix(s);
538 let b32_ex = match_prefix(prefix, params.bech_hrp);
539 let b32_bl = match_prefix(prefix, params.blech_hrp);
540 if b32_ex || b32_bl {
541 return Address::from_bech32(s, b32_bl, params);
542 }
543
544 if s.len() > 150 {
546 return Err(AddressError::InvalidLength(s.len() * 11 / 15));
547 }
548 let data = base58::decode_check(s)?;
549 Address::from_base58(&data, params)
550 }
551}
552
553impl fmt::Display for Address {
554 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
555 match self.payload {
556 Payload::PubkeyHash(ref hash) => {
557 if let Some(ref blinder) = self.blinding_pubkey {
558 let mut prefixed = [0; 55]; prefixed[0] = self.params.blinded_prefix;
560 prefixed[1] = self.params.p2pkh_prefix;
561 prefixed[2..35].copy_from_slice(&blinder.serialize());
562 prefixed[35..].copy_from_slice(&hash[..]);
563 base58::encode_check_to_fmt(fmt, &prefixed[..])
564 } else {
565 let mut prefixed = [0; 21];
566 prefixed[0] = self.params.p2pkh_prefix;
567 prefixed[1..].copy_from_slice(&hash[..]);
568 base58::encode_check_to_fmt(fmt, &prefixed[..])
569 }
570 }
571 Payload::ScriptHash(ref hash) => {
572 if let Some(ref blinder) = self.blinding_pubkey {
573 let mut prefixed = [0; 55]; prefixed[0] = self.params.blinded_prefix;
575 prefixed[1] = self.params.p2sh_prefix;
576 prefixed[2..35].copy_from_slice(&blinder.serialize());
577 prefixed[35..].copy_from_slice(&hash[..]);
578 base58::encode_check_to_fmt(fmt, &prefixed[..])
579 } else {
580 let mut prefixed = [0; 21];
581 prefixed[0] = self.params.p2sh_prefix;
582 prefixed[1..].copy_from_slice(&hash[..]);
583 base58::encode_check_to_fmt(fmt, &prefixed[..])
584 }
585 }
586 Payload::WitnessProgram {
587 version: witver,
588 program: ref witprog,
589 } => {
590 let hrp = match self.blinding_pubkey.is_some() {
591 true => self.params.blech_hrp,
592 false => self.params.bech_hrp,
593 };
594
595 if self.is_blinded() {
597 if let Some(ref blinder) = self.blinding_pubkey {
598 let byte_iter = IntoIterator::into_iter(blinder.serialize())
599 .chain(witprog.iter().copied());
600 let fe_iter = byte_iter.bytes_to_fes();
601 if witver.to_u8() == 0 {
602 for c in fe_iter
603 .with_checksum::<Blech32>(&hrp)
604 .with_witness_version(witver)
605 .chars()
606 {
607 fmt.write_char(c)?;
608 }
609 } else {
610 for c in fe_iter
611 .with_checksum::<Blech32m>(&hrp)
612 .with_witness_version(witver)
613 .chars()
614 {
615 fmt.write_char(c)?;
616 }
617 }
618 return Ok(());
619 }
620 }
621
622 let byte_iter = witprog.iter().copied();
623 let fe_iter = byte_iter.bytes_to_fes();
624 if witver.to_u8() == 0 {
625 for c in fe_iter
626 .with_checksum::<Bech32>(&hrp)
627 .with_witness_version(witver)
628 .chars()
629 {
630 fmt.write_char(c)?;
631 }
632 } else {
633 for c in fe_iter
634 .with_checksum::<Bech32m>(&hrp)
635 .with_witness_version(witver)
636 .chars()
637 {
638 fmt.write_char(c)?;
639 }
640 }
641 Ok(())
642 }
643 }
644 }
645}
646
647impl fmt::Debug for Address {
648 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
649 fmt::Display::fmt(self, fmt)
650 }
651}
652
653fn find_prefix(bech32: &str) -> &str {
656 match bech32.rfind('1') {
658 None => bech32,
659 Some(sep) => bech32.split_at(sep).0,
660 }
661}
662
663fn match_prefix(prefix_mixed: &str, target: Hrp) -> bool {
667 target.len() == prefix_mixed.len() && target
668 .lowercase_char_iter()
669 .zip(prefix_mixed.chars())
670 .all(|(char_lower, char_mixed)| char_lower == char_mixed.to_ascii_lowercase())
671}
672
673impl FromStr for Address {
674 type Err = AddressError;
675
676 fn from_str(s: &str) -> Result<Address, AddressError> {
677 let liq = &AddressParams::LIQUID;
679 let ele = &AddressParams::ELEMENTS;
680 let liq_test = &AddressParams::LIQUID_TESTNET;
681
682 let net_arr = [liq, ele, liq_test];
683
684 let prefix = find_prefix(s);
685 for net in &net_arr {
686 if match_prefix(prefix, net.bech_hrp) {
688 return Address::from_bech32(s, false, net);
689 }
690 if match_prefix(prefix, net.blech_hrp) {
691 return Address::from_bech32(s, true, net);
692 }
693 }
694
695 if s.len() > 150 {
697 return Err(AddressError::InvalidLength(s.len() * 11 / 15));
698 }
699 let data = base58::decode_check(s)?;
700 if data.is_empty() {
701 return Err(AddressError::InvalidLength(data.len()));
702 }
703
704 let p = data[0];
705 for net in &net_arr {
706 if p == net.p2pkh_prefix || p == net.p2sh_prefix || p == net.blinded_prefix {
707 return Address::from_base58(&data, net);
708 }
709 }
710
711 Err(AddressError::InvalidAddress(s.to_owned()))
712 }
713}
714
715#[cfg(feature = "serde")]
716impl<'de> serde::Deserialize<'de> for Address {
717 #[inline]
718 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
719 where
720 D: serde::Deserializer<'de>,
721 {
722 use std::fmt::Formatter;
723
724 struct Visitor;
725 impl<'de> serde::de::Visitor<'de> for Visitor {
726 type Value = Address;
727
728 fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
729 formatter.write_str("a Bitcoin address")
730 }
731
732 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
733 where
734 E: serde::de::Error,
735 {
736 Address::from_str(v).map_err(E::custom)
737 }
738
739 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
740 where
741 E: serde::de::Error,
742 {
743 self.visit_str(v)
744 }
745
746 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
747 where
748 E: serde::de::Error,
749 {
750 self.visit_str(&v)
751 }
752 }
753
754 deserializer.deserialize_str(Visitor)
755 }
756}
757
758#[cfg(feature = "serde")]
759impl serde::Serialize for Address {
760 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
761 where
762 S: serde::Serializer,
763 {
764 serializer.serialize_str(&self.to_string())
765 }
766}
767
768#[cfg(test)]
769mod test {
770 use super::*;
771 use crate::Script;
772 use bitcoin::key;
773 use secp256k1_zkp::{PublicKey, Secp256k1};
774 #[cfg(feature = "serde")]
775 use serde_json;
776
777 fn roundtrips(addr: &Address) {
778 assert_eq!(
779 Address::from_str(&addr.to_string()).ok().as_ref(),
780 Some(addr),
781 "string round-trip failed for {}",
782 addr,
783 );
784 assert_eq!(
785 Address::from_script(&addr.script_pubkey(), addr.blinding_pubkey, addr.params).as_ref(),
786 Some(addr),
787 "script round-trip failed for {}",
788 addr,
789 );
790 #[cfg(feature = "serde")]
791 assert_eq!(
792 serde_json::from_value::<Address>(serde_json::to_value(addr).unwrap())
793 .ok()
794 .as_ref(),
795 Some(addr)
796 );
797 }
798
799 #[test]
800 fn regression_188() {
801 let addr = Address::from_str("tlq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f3mmz5l7uw5pqmx6xf5xy50hsn6vhkm5euwt72x878eq6zxx2z58hd7zrsg9qn").unwrap();
803 roundtrips(&addr);
804 }
805
806 #[test]
807 fn exhaustive() {
808 let blinder_hex = "0218845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
809 let blinder = PublicKey::from_str(blinder_hex).unwrap();
810 let sk_wif = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy";
811 let sk = key::PrivateKey::from_wif(sk_wif).unwrap();
812 let pk = sk.public_key(&Secp256k1::new());
813 let script: Script = vec![1u8, 2, 42, 255, 196].into();
814
815 let vectors = [
816 Address::p2pkh(&pk, None, &AddressParams::LIQUID),
817 Address::p2pkh(&pk, None, &AddressParams::ELEMENTS),
818 Address::p2pkh(&pk, Some(blinder), &AddressParams::LIQUID),
819 Address::p2pkh(&pk, Some(blinder), &AddressParams::ELEMENTS),
820 Address::p2sh(&script, None, &AddressParams::LIQUID),
821 Address::p2sh(&script, None, &AddressParams::ELEMENTS),
822 Address::p2sh(&script, Some(blinder), &AddressParams::LIQUID),
823 Address::p2sh(&script, Some(blinder), &AddressParams::ELEMENTS),
825 Address::p2wpkh(&pk, None, &AddressParams::LIQUID),
826 Address::p2wpkh(&pk, None, &AddressParams::ELEMENTS),
827 Address::p2wpkh(&pk, Some(blinder), &AddressParams::LIQUID),
828 Address::p2wpkh(&pk, Some(blinder), &AddressParams::ELEMENTS),
829 Address::p2shwpkh(&pk, None, &AddressParams::LIQUID),
830 Address::p2shwpkh(&pk, None, &AddressParams::ELEMENTS),
831 Address::p2shwpkh(&pk, Some(blinder), &AddressParams::LIQUID),
832 Address::p2shwpkh(&pk, Some(blinder), &AddressParams::ELEMENTS),
834 Address::p2wsh(&script, None, &AddressParams::LIQUID),
835 Address::p2wsh(&script, None, &AddressParams::ELEMENTS),
836 Address::p2wsh(&script, Some(blinder), &AddressParams::LIQUID),
837 Address::p2wsh(&script, Some(blinder), &AddressParams::ELEMENTS),
839 Address::p2shwsh(&script, None, &AddressParams::LIQUID),
840 Address::p2shwsh(&script, None, &AddressParams::ELEMENTS),
841 Address::p2shwsh(&script, Some(blinder), &AddressParams::LIQUID),
843 Address::p2shwsh(&script, Some(blinder), &AddressParams::ELEMENTS),
845 ];
846
847 for addr in &vectors {
848 roundtrips(addr);
849 }
850 }
851
852 #[test]
853 fn test_actuals() {
854 let addresses = [
856 ("2dxmEBXc2qMYcLSKiDBxdEePY3Ytixmnh4E", false, AddressParams::ELEMENTS),
858 ("CTEo6VKG8xbe7HnfVW9mQoWTgtgeRSPktwTLbELzGw5tV8Ngzu53EBiasFMQKVbWmKWWTAdN5AUf4M6Y", true, AddressParams::ELEMENTS),
859 ("ert1qwhh2n5qypypm0eufahm2pvj8raj9zq5c27cysu", false, AddressParams::ELEMENTS),
860 ("el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse", true, AddressParams::ELEMENTS),
861 ("GqiQRsPEyJLAsEBFB5R34KHuqxDNkG3zur", false, AddressParams::LIQUID),
863 ("VJLDwMVWXg8RKq4mRe3YFNTAEykVN6V8x5MRUKKoC3nfRnbpnZeiG3jygMC6A4Gw967GY5EotJ4Rau2F", true, AddressParams::LIQUID),
864 ("ex1q7gkeyjut0mrxc3j0kjlt7rmcnvsh0gt45d3fud", false, AddressParams::LIQUID),
865 ("lq1qqf8er278e6nyvuwtgf39e6ewvdcnjupn9a86rzpx655y5lhkt0walu3djf9cklkxd3ryld97hu8h3xepw7sh2rlu7q45dcew5", true, AddressParams::LIQUID),
866 ];
867
868 for &(a, blinded, ref params) in &addresses {
869 let result = a.parse();
870 assert!(
871 result.is_ok(),
872 "vector: {}, err: \"{}\"",
873 a,
874 result.unwrap_err()
875 );
876 let addr: Address = result.unwrap();
877 assert_eq!(a, &addr.to_string(), "vector: {}", a);
878 assert_eq!(blinded, addr.is_blinded());
879 assert_eq!(params, addr.params);
880 roundtrips(&addr);
881 }
882 }
883
884 #[test]
885 fn test_blech32_vectors() {
886 let address: Result<Address, _> = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse".parse();
888 assert!(address.is_ok());
889
890 let address: Result<Address, _> = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsxguu9nrdg2pc".parse();
891 assert_eq!(
892 address.err().unwrap().to_string(),
893 "blech32 error: invalid checksum", );
895
896 let address: Result<Address, _> = "el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsnnmzrstzt7de".parse();
897 assert_eq!(
898 address.err().unwrap().to_string(),
899 "blech32 error: invalid checksum", );
901
902 let address: Result<Address, _> =
903 "ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqqu2tys".parse();
904 assert_eq!(
905 address.err().unwrap().to_string(),
906 "bech32 error: invalid segwit witness version: 17 (bech32 character: '3')",
907 );
908
909 let address: Result<Address, _> = "el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpe9jfn0gypaj".parse();
910 assert_eq!(
911 address.err().unwrap().to_string(),
912 "blech32 error: invalid witness length",
913 );
914
915 let address: Result<Address, _> = "rrr1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfs2d9rp7meq4kg".parse();
918 assert_eq!(address.err().unwrap().to_string(), "base58 error: decode",);
919 }
920
921 #[test]
922 fn test_fixed_addresses() {
923 let pk = bitcoin::PublicKey::from_str(
924 "0212bf0ea45b733dfde8ecb5e896306c4165c666c99fc5d1ab887f71393a975cea",
925 )
926 .unwrap();
927 let script = Script::default();
928 let secp = Secp256k1::verification_only();
929 let internal_key = UntweakedPublicKey::from_str(
930 "93c7378d96518a75448821c4f7c8f4bae7ce60f804d03d1f0628dd5dd0f5de51",
931 )
932 .unwrap();
933 let tap_node_hash = TapNodeHash::all_zeros();
934
935 let mut expected = IntoIterator::into_iter([
936 "2dszRCFv8Ub4ytKo1Q1vXXGgSx7mekNDwSJ",
937 "XToMocNywBYNSiXUe5xvoa2naAps9Ek1hq",
938 "ert1qew0l0emv7449u7hqgc8utzdzryhse79yhq2sxv",
939 "XZF6k8S6eoVxXMB4NpWjh2s7LjQUP7pw2R",
940 "ert1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2szaqlpq",
941 "ert1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02q2cqvj9",
942 "ert1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390sqkjp06",
943 "CTEkC79sYAvWNcxd8iTYnYo226FqRBbzBcMppq7L2dA8jVXJWoo1kKWB3UBLY6gBjiXf87ibs8c6mQyZ",
944 "AzpjUhKMLJi9y2oLt3ZdM3BP9nHdLPJfGMVxRBaRc2gDpeNqPMVpShTszJW7bX42vT2KoejYy8GtbcxH",
945 "el1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gmz9ej9kyq5yc",
946 "AzpjUhKMLJi9y2oLt3ZdM3BP9nHdLPJfGMVxRBaRc2gDpeNvq6SLVpBVwtakF6nmUFundyW7YjUdVkpr",
947 "el1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4h79u87cnxdzq",
948 "el1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75cj24fq2u2ls5",
949 "el1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2lhjd0k0q7qn4h",
950 "QFq3vvrr6Ub2KAyb3LdoCxEQvKukB6nN9i",
951 "GydeMhecNgrq17WMkyyTM4ETv1YubMVtLN",
952 "ex1qew0l0emv7449u7hqgc8utzdzryhse79ydjqgek",
953 "H55PJDhj6JpR5k9wViXGEX4nga8WmhXtnD",
954 "ex1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2s4sla8h",
955 "ex1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02qa4lw5j",
956 "ex1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390shmdrfd",
957 "VTptY6cqJbusNpL5xvo8VL38nLX9PGDjfYQfqhu9EaA7FtuidkWyQzMHY9jzZrpBcCXT437vM6V4N8kh",
958 "VJL64Ep3rcngP4cScRme15q9i8MCNiuqWeiG3YbtduUidVyorg7nRsgmmF714QtH3sNpWB2CqsVVciQh",
959 "lq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gs2923tg58xcz",
960 "VJL64Ep3rcngP4cScRme15q9i8MCNiuqWeiG3YbtduUidVyuJR4JUzQPiqBdhzd1bgGHLVnmRUjfHc68",
961 "lq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz47jmkmgmr9a4s",
962 "lq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75375l4kfvf08y",
963 "lq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2l77n92erwrrz8",
964 "FojPFeboBgrd953mXXe72KWthjVwHWozqN",
965 "8vsafXgrB5bJeSidGbK5eYnjKvQ3RiB4BB",
966 "tex1qew0l0emv7449u7hqgc8utzdzryhse79yh5jp9a",
967 "92KKc3jxthYtj5ND1KrtY1d46UyeWV6XbP",
968 "tex1quwcvgs5clswpfxhm7nyfjmaeysn6us0yvjdexn9yjkv3k7zjhp2s5fd6kc",
969 "tex1p8qs0qcn25l2y6yvtc5t95rr8w9pndcj64c8rkutnvkcvdp6gh02quvdf9a",
970 "tex1pxrrurkg8j8pve97lffvv2y67cf7ux478h077c87qacqzhue7390skzlycz",
971 "vtS71VhcpFt978sha5d1L2gCzp3UL5kXacRpb3N4GTW5MwvBzz5HwxYyB8Pns4yM2dd2osmQkHSkp88u",
972 "vjTuLJ76nGi8PUopBVmGK8bLKPfBpaBWf6wKfn8z9Vdz6ubVhpvmMr6TK2RcqAYiujN1g1uwg8kejrM3",
973 "tlq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4jul7lnkeat2teawq3s0cky6yxf0pnu2gq8g2kuxfj8ft",
974 "vjTuLJ76nGi8PUopBVmGK8bLKPfBpaBWf6wKfn8z9Vdz6ubb9ZsHQxp5GcWFUkHTTYFUWLgWFk1DN5Fe",
975 "tlq1qqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww4casc3pf3lquzjd0haxgn9hmjfp84eq7geymjdx2f9verdu99wz4e6vcdfcyp5m8",
976 "tlq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5wpq7p3x4f75f5gch3gktgxxwu2rxm394tsw8dchxedsc6r53w75kkr3rh2tdxfn",
977 "tlq1pqgft7r4ytdenml0gaj67393sd3qkt3nxex0ut5dt3plhzwf6jaww5vx8c8vs0ywzejta7jjcc5f4asnacdtu0wlaas0upmsq90enaz2lekytucqf82vs",
978 ]);
979
980 for params in [
981 &AddressParams::ELEMENTS,
982 &AddressParams::LIQUID,
983 &AddressParams::LIQUID_TESTNET,
984 ] {
985 for blinder in [None, Some(pk.inner)] {
986 let addr = Address::p2pkh(&pk, blinder, params);
987 assert_eq!(&addr.to_string(), expected.next().unwrap());
988
989 let addr = Address::p2sh(&script, blinder, params);
990 assert_eq!(&addr.to_string(), expected.next().unwrap());
991
992 let addr = Address::p2wpkh(&pk, blinder, params);
993 assert_eq!(&addr.to_string(), expected.next().unwrap());
994
995 let addr = Address::p2shwpkh(&pk, blinder, params);
996 assert_eq!(&addr.to_string(), expected.next().unwrap());
997
998 let addr = Address::p2wsh(&script, blinder, params);
999 assert_eq!(&addr.to_string(), expected.next().unwrap());
1000
1001 let addr = Address::p2tr(&secp, internal_key, None, blinder, params);
1002 assert_eq!(&addr.to_string(), expected.next().unwrap());
1003
1004 let addr = Address::p2tr(&secp, internal_key, Some(tap_node_hash), blinder, params);
1005 assert_eq!(&addr.to_string(), expected.next().unwrap());
1006 }
1007 }
1008 }
1009}