1use crate::address_funds::AddressWitness;
2use crate::address_funds::AddressWitnessVerificationOperations;
3use crate::prelude::AddressNonce;
4use crate::ProtocolError;
5use bech32::{Bech32m, Hrp};
6use bincode::{Decode, Encode};
7use dashcore::address::Payload;
8use dashcore::blockdata::script::ScriptBuf;
9use dashcore::hashes::{sha256d, Hash};
10use dashcore::key::Secp256k1;
11use dashcore::secp256k1::ecdsa::RecoverableSignature;
12use dashcore::secp256k1::Message;
13use dashcore::signer::CompactSignature;
14use dashcore::{Address, Network, PrivateKey, PubkeyHash, PublicKey, ScriptHash};
15use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
16#[cfg(feature = "serde-conversion")]
17use serde::{Deserialize, Serialize};
18use std::convert::TryFrom;
19use std::str::FromStr;
20
21pub const ADDRESS_HASH_SIZE: usize = 20;
23
24#[derive(
25 Debug,
26 PartialEq,
27 Eq,
28 Clone,
29 Copy,
30 Hash,
31 Ord,
32 PartialOrd,
33 Encode,
34 Decode,
35 PlatformSerialize,
36 PlatformDeserialize,
37)]
38#[platform_serialize(unversioned)]
39pub enum PlatformAddress {
40 P2pkh([u8; 20]),
44 P2sh([u8; 20]),
48}
49
50#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
51impl crate::serialization::JsonConvertible for PlatformAddress {}
52
53#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
54impl crate::serialization::ValueConvertible for PlatformAddress {}
55
56#[cfg(all(
57 test,
58 feature = "json-conversion",
59 feature = "value-conversion",
60 feature = "serde-conversion"
61))]
62mod json_convertible_tests {
63 use super::*;
64 use platform_value::Value;
65 use serde_json::json;
66
67 #[test]
73 fn json_round_trip_p2pkh() {
74 use crate::serialization::JsonConvertible;
75 let original = PlatformAddress::P2pkh([0xab; 20]);
76 let json = original.to_json().expect("to_json");
77 assert_eq!(json, json!("00abababababababababababababababababababab"));
79 let recovered = PlatformAddress::from_json(json).expect("from_json");
80 assert_eq!(original, recovered);
81 }
82
83 #[test]
84 fn json_round_trip_p2sh() {
85 use crate::serialization::JsonConvertible;
86 let original = PlatformAddress::P2sh([0xcd; 20]);
87 let json = original.to_json().expect("to_json");
88 assert_eq!(json, json!("01cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"));
90 let recovered = PlatformAddress::from_json(json).expect("from_json");
91 assert_eq!(original, recovered);
92 }
93
94 #[test]
95 fn value_round_trip_p2pkh() {
96 use crate::serialization::ValueConvertible;
97 let original = PlatformAddress::P2pkh([0xab; 20]);
98 let value = original.to_object().expect("to_object");
99 let mut expected = vec![0x00];
102 expected.extend_from_slice(&[0xab; 20]);
103 assert_eq!(value, Value::Bytes(expected));
104 let recovered = PlatformAddress::from_object(value).expect("from_object");
105 assert_eq!(original, recovered);
106 }
107
108 #[test]
109 fn value_round_trip_p2sh() {
110 use crate::serialization::ValueConvertible;
111 let original = PlatformAddress::P2sh([0xcd; 20]);
112 let value = original.to_object().expect("to_object");
113 let mut expected = vec![0x01];
114 expected.extend_from_slice(&[0xcd; 20]);
115 assert_eq!(value, Value::Bytes(expected));
116 let recovered = PlatformAddress::from_object(value).expect("from_object");
117 assert_eq!(original, recovered);
118 }
119}
120
121#[cfg(feature = "serde-conversion")]
127impl Serialize for PlatformAddress {
128 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129 where
130 S: serde::Serializer,
131 {
132 let bytes = self.to_bytes();
133 if serializer.is_human_readable() {
134 serializer.serialize_str(&hex::encode(&bytes))
135 } else {
136 serializer.serialize_bytes(&bytes)
137 }
138 }
139}
140
141#[cfg(feature = "serde-conversion")]
142impl<'de> Deserialize<'de> for PlatformAddress {
143 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144 where
145 D: serde::Deserializer<'de>,
146 {
147 use serde::de::{self, Visitor};
148 use std::fmt;
149
150 const PLATFORM_ADDRESS_BYTE_LEN: usize = 21;
152
153 struct PlatformAddressVisitor;
154
155 impl<'de> Visitor<'de> for PlatformAddressVisitor {
156 type Value = PlatformAddress;
157
158 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
159 formatter.write_str("PlatformAddress as 21 bytes or hex string")
160 }
161
162 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
163 let bytes =
164 hex::decode(value).map_err(|err| E::custom(format!("invalid hex: {}", err)))?;
165 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
166 return Err(E::invalid_length(bytes.len(), &self));
167 }
168 PlatformAddress::from_bytes(&bytes).map_err(|err| E::custom(err.to_string()))
169 }
170
171 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
172 self.visit_str(&value)
173 }
174
175 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
176 if value.len() != PLATFORM_ADDRESS_BYTE_LEN {
177 return Err(E::invalid_length(value.len(), &self));
178 }
179 PlatformAddress::from_bytes(value).map_err(|err| E::custom(err.to_string()))
180 }
181
182 fn visit_byte_buf<E: de::Error>(self, value: Vec<u8>) -> Result<Self::Value, E> {
183 self.visit_bytes(&value)
184 }
185
186 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
187 where
188 A: de::SeqAccess<'de>,
189 {
190 let mut bytes = Vec::with_capacity(PLATFORM_ADDRESS_BYTE_LEN);
193 while let Some(byte) = seq.next_element::<u8>()? {
194 if bytes.len() >= PLATFORM_ADDRESS_BYTE_LEN {
195 return Err(de::Error::invalid_length(
196 bytes.len() + 1,
197 &"at most 21 bytes",
198 ));
199 }
200 bytes.push(byte);
201 }
202 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
203 return Err(de::Error::invalid_length(bytes.len(), &self));
204 }
205 PlatformAddress::from_bytes(&bytes)
206 .map_err(|err| de::Error::custom(err.to_string()))
207 }
208 }
209
210 if deserializer.is_human_readable() {
214 deserializer.deserialize_str(PlatformAddressVisitor)
215 } else {
216 deserializer.deserialize_bytes(PlatformAddressVisitor)
217 }
218 }
219}
220
221impl TryFrom<Address> for PlatformAddress {
222 type Error = ProtocolError;
223
224 fn try_from(address: Address) -> Result<Self, Self::Error> {
225 match address.payload() {
226 Payload::PubkeyHash(hash) => Ok(PlatformAddress::P2pkh(*hash.as_ref())),
227 Payload::ScriptHash(hash) => Ok(PlatformAddress::P2sh(*hash.as_ref())),
228 _ => Err(ProtocolError::DecodingError(
229 "unsupported address type for PlatformAddress: only P2PKH and P2SH are supported"
230 .to_string(),
231 )),
232 }
233 }
234}
235
236impl From<&PrivateKey> for PlatformAddress {
237 fn from(private_key: &PrivateKey) -> Self {
242 let secp = Secp256k1::new();
243 let pubkey_hash = private_key.public_key(&secp).pubkey_hash();
244 PlatformAddress::P2pkh(*pubkey_hash.as_byte_array())
245 }
246}
247
248impl Default for PlatformAddress {
249 fn default() -> Self {
250 PlatformAddress::P2pkh([0u8; 20])
251 }
252}
253
254pub const PLATFORM_HRP_MAINNET: &str = "dash";
256pub const PLATFORM_HRP_TESTNET: &str = "tdash";
258
259pub(crate) fn classify_platform_hrp(hrp: &str) -> Result<bool, ProtocolError> {
264 match hrp {
265 PLATFORM_HRP_MAINNET => Ok(true),
266 PLATFORM_HRP_TESTNET => Ok(false),
267 other => Err(ProtocolError::DecodingError(format!(
268 "not a platform address: HRP '{other}' is neither \
269 '{PLATFORM_HRP_MAINNET}' nor '{PLATFORM_HRP_TESTNET}'"
270 ))),
271 }
272}
273
274impl PlatformAddress {
275 pub const P2PKH_TYPE: u8 = 0xb0;
277 pub const P2SH_TYPE: u8 = 0x80;
279
280 pub fn hrp_for_network(network: Network) -> &'static str {
286 match network {
287 Network::Mainnet => PLATFORM_HRP_MAINNET,
288 Network::Testnet | Network::Devnet | Network::Regtest => PLATFORM_HRP_TESTNET,
289 }
290 }
291
292 pub fn to_bech32m_string(&self, network: Network) -> String {
309 let hrp_str = Self::hrp_for_network(network);
310 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
311
312 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
315 match self {
316 PlatformAddress::P2pkh(hash) => {
317 payload.push(Self::P2PKH_TYPE);
318 payload.extend_from_slice(hash);
319 }
320 PlatformAddress::P2sh(hash) => {
321 payload.push(Self::P2SH_TYPE);
322 payload.extend_from_slice(hash);
323 }
324 }
325
326 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
328 }
329
330 pub fn from_bech32m_string(s: &str) -> Result<Self, ProtocolError> {
341 let (hrp, data) =
342 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
343
344 classify_platform_hrp(&hrp.as_str().to_ascii_lowercase())?;
345
346 if data.len() != 1 + ADDRESS_HASH_SIZE {
348 return Err(ProtocolError::DecodingError(format!(
349 "invalid Platform address length: expected {} bytes, got {}",
350 1 + ADDRESS_HASH_SIZE,
351 data.len()
352 )));
353 }
354
355 let address_type = data[0];
357 let hash: [u8; 20] = data[1..21]
358 .try_into()
359 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
360
361 let address = match address_type {
362 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
363 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
364 _ => Err(ProtocolError::DecodingError(format!(
365 "invalid address type: 0x{:02x}",
366 address_type
367 ))),
368 }?;
369
370 Ok(address)
371 }
372
373 pub fn is_mainnet_bech32m(s: &str) -> Result<bool, ProtocolError> {
384 let (hrp, _) =
385 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{e}")))?;
386 classify_platform_hrp(&hrp.to_lowercase())
387 }
388
389 pub fn to_address_with_network(&self, network: Network) -> Address {
391 match self {
392 PlatformAddress::P2pkh(hash) => Address::new(
393 network,
394 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
395 ),
396 PlatformAddress::P2sh(hash) => Address::new(
397 network,
398 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
399 ),
400 }
401 }
402
403 pub fn to_bytes(&self) -> Vec<u8> {
409 bincode::encode_to_vec(self, bincode::config::standard())
410 .expect("PlatformAddress serialization cannot fail")
411 }
412
413 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
416 use base64::engine::general_purpose::STANDARD;
417 use base64::Engine;
418
419 let mut bytes = self.to_bytes();
420 bytes.extend_from_slice(&nonce.to_be_bytes());
421
422 STANDARD.encode(bytes)
423 }
424
425 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
430 let (address, _): (Self, usize) =
431 bincode::decode_from_slice(bytes, bincode::config::standard()).map_err(|e| {
432 ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e))
433 })?;
434 Ok(address)
435 }
436
437 pub fn hash(&self) -> &[u8; 20] {
439 match self {
440 PlatformAddress::P2pkh(hash) => hash,
441 PlatformAddress::P2sh(hash) => hash,
442 }
443 }
444
445 pub fn is_p2pkh(&self) -> bool {
447 matches!(self, PlatformAddress::P2pkh(_))
448 }
449
450 pub fn is_p2sh(&self) -> bool {
452 matches!(self, PlatformAddress::P2sh(_))
453 }
454
455 pub fn verify_bytes_against_witness(
475 &self,
476 witness: &AddressWitness,
477 signable_bytes: &[u8],
478 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
479 match (self, witness) {
480 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
481 let data_hash = dashcore::signer::double_sha(signable_bytes);
489 dashcore::signer::verify_hash_signature(
490 &data_hash,
491 signature.as_slice(),
492 pubkey_hash,
493 )
494 .map_err(|e| {
495 ProtocolError::AddressWitnessError(format!(
496 "P2PKH signature verification failed: {}",
497 e
498 ))
499 })?;
500
501 Ok(AddressWitnessVerificationOperations::for_p2pkh(
502 signable_bytes.len(),
503 ))
504 }
505 (
506 PlatformAddress::P2sh(script_hash),
507 AddressWitness::P2sh {
508 signatures,
509 redeem_script,
510 },
511 ) => {
512 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
514 let computed_hash = script.script_hash();
515 if computed_hash.as_byte_array() != script_hash {
516 return Err(ProtocolError::AddressWitnessError(format!(
517 "Script hash {} does not match address hash {}",
518 hex::encode(computed_hash.as_byte_array()),
519 hex::encode(script_hash)
520 )));
521 }
522
523 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
526
527 let valid_signatures: Vec<_> = signatures
529 .iter()
530 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
531 .collect();
532
533 if valid_signatures.len() < threshold {
534 return Err(ProtocolError::AddressWitnessError(format!(
535 "Not enough signatures: got {}, need {}",
536 valid_signatures.len(),
537 threshold
538 )));
539 }
540
541 let mut sig_idx = 0;
544 let mut pubkey_idx = 0;
545 let mut matched = 0;
546 let mut signature_verifications: u16 = 0;
547
548 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
549 let msg = Message::from_digest(signable_bytes_hash);
550 let secp = Secp256k1::new();
551
552 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
553 signature_verifications += 1;
554
555 let sig = RecoverableSignature::from_compact_signature(
556 valid_signatures[sig_idx].as_slice(),
557 )
558 .map_err(|e| {
559 ProtocolError::AddressWitnessError(format!(
560 "Invalid signature format: {}",
561 e
562 ))
563 })?;
564
565 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
566 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
567 })?;
568
569 if secp
570 .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner)
571 .is_ok()
572 {
573 matched += 1;
574 sig_idx += 1;
575 }
576 pubkey_idx += 1;
577 }
578
579 if matched >= threshold {
580 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
581 signature_verifications,
582 signable_bytes.len(),
583 ))
584 } else {
585 Err(ProtocolError::AddressWitnessError(format!(
586 "Not enough valid signatures: verified {}, need {}",
587 matched, threshold
588 )))
589 }
590 }
591 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
592 Err(ProtocolError::AddressWitnessError(
593 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
594 ))
595 }
596 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
597 Err(ProtocolError::AddressWitnessError(
598 "P2SH address requires P2sh witness, got P2pkh".to_string(),
599 ))
600 }
601 }
602 }
603
604 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
619 use dashcore::blockdata::opcodes::all::*;
620
621 let mut instructions = script.instructions();
622 let mut pubkeys = Vec::new();
623
624 let threshold = match instructions.next() {
626 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
627 let byte = op.to_u8();
628 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
629 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
630 } else {
631 return Err(ProtocolError::AddressWitnessError(format!(
632 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
633 First opcode was 0x{:02x}, expected OP_1 through OP_16",
634 byte
635 )));
636 }
637 }
638 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
639 return Err(ProtocolError::AddressWitnessError(
640 "Unsupported P2SH script type: only standard multisig is supported. \
641 Script starts with a data push instead of OP_M threshold."
642 .to_string(),
643 ))
644 }
645 Some(Err(e)) => {
646 return Err(ProtocolError::AddressWitnessError(format!(
647 "Error parsing P2SH script: {:?}",
648 e
649 )))
650 }
651 None => {
652 return Err(ProtocolError::AddressWitnessError(
653 "Empty P2SH redeem script".to_string(),
654 ))
655 }
656 };
657
658 loop {
660 match instructions.next() {
661 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
662 let len = bytes.len();
664 if len != 33 {
665 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
666 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
667 len,
668 ),
669 ));
670 }
671 pubkeys.push(bytes.as_bytes().to_vec());
672 }
673 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
674 let byte = op.to_u8();
675 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
676 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
678 if pubkeys.len() != n {
679 return Err(ProtocolError::AddressWitnessError(format!(
680 "Multisig script declares {} keys but contains {}",
681 n,
682 pubkeys.len()
683 )));
684 }
685 break;
686 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
687 return Err(ProtocolError::AddressWitnessError(
689 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
690 ));
691 } else {
692 return Err(ProtocolError::AddressWitnessError(format!(
693 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
694 byte
695 )));
696 }
697 }
698 Some(Err(e)) => {
699 return Err(ProtocolError::AddressWitnessError(format!(
700 "Error parsing multisig script: {:?}",
701 e
702 )))
703 }
704 None => {
705 return Err(ProtocolError::AddressWitnessError(
706 "Incomplete multisig script: unexpected end before OP_N".to_string(),
707 ))
708 }
709 }
710 }
711
712 if threshold > pubkeys.len() {
714 return Err(ProtocolError::AddressWitnessError(format!(
715 "Invalid multisig: threshold {} exceeds number of keys {}",
716 threshold,
717 pubkeys.len()
718 )));
719 }
720
721 match instructions.next() {
723 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
724 if op == OP_CHECKMULTISIG {
725 if instructions.next().is_some() {
727 return Err(ProtocolError::AddressWitnessError(
728 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
729 ));
730 }
731 Ok((threshold, pubkeys))
732 } else if op == OP_CHECKMULTISIGVERIFY {
733 Err(ProtocolError::AddressWitnessError(
734 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
735 .to_string(),
736 ))
737 } else {
738 Err(ProtocolError::AddressWitnessError(format!(
739 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
740 op.to_u8()
741 )))
742 }
743 }
744 _ => Err(ProtocolError::AddressWitnessError(
745 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
746 )),
747 }
748 }
749}
750
751impl std::fmt::Display for PlatformAddress {
752 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
753 match self {
754 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
755 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
756 }
757 }
758}
759
760#[derive(Debug, Clone, PartialEq, Eq)]
762pub struct PlatformAddressParseError(pub String);
763
764impl std::fmt::Display for PlatformAddressParseError {
765 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
766 write!(f, "{}", self.0)
767 }
768}
769
770impl std::error::Error for PlatformAddressParseError {}
771
772impl FromStr for PlatformAddress {
773 type Err = PlatformAddressParseError;
774
775 fn from_str(s: &str) -> Result<Self, Self::Err> {
784 Self::from_bech32m_string(s).map_err(|e| PlatformAddressParseError(e.to_string()))
785 }
786}
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791 use dashcore::blockdata::opcodes::all::*;
792 use dashcore::hashes::Hash;
793 use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey};
794 use dashcore::PublicKey;
795 use platform_value::BinaryData;
796
797 #[test]
803 fn hrp_is_shared_across_all_non_mainnet_networks() {
804 let testnet = PlatformAddress::hrp_for_network(Network::Testnet);
805 assert_eq!(testnet, PLATFORM_HRP_TESTNET);
806 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), testnet);
807 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), testnet);
808 assert_ne!(
809 PlatformAddress::hrp_for_network(Network::Mainnet),
810 testnet,
811 "mainnet must use a distinct HRP"
812 );
813 }
814
815 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
817 let secp = Secp256k1::new();
818 let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key");
819 let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key);
820 let public_key = PublicKey::new(raw_public_key);
821 (secret_key, public_key)
822 }
823
824 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
826 dashcore::signer::sign(data, secret_key.as_ref())
827 .expect("signing should succeed")
828 .to_vec()
829 }
830
831 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
833 let mut script = Vec::new();
834
835 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
837
838 for pubkey in pubkeys {
840 let bytes = pubkey.to_bytes();
841 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
843 }
844
845 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
847
848 script.push(OP_CHECKMULTISIG.to_u8());
850
851 script
852 }
853
854 #[test]
855 fn test_platform_address_from_private_key() {
856 let seed = [1u8; 32];
858 let (secret_key, public_key) = create_keypair(seed);
859
860 let private_key = PrivateKey::new(secret_key, Network::Testnet);
862
863 let address_from_private = PlatformAddress::from(&private_key);
865
866 let pubkey_hash = public_key.pubkey_hash();
869 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
870
871 assert_eq!(
873 address_from_private, address_from_pubkey,
874 "Address derived from private key should match Hash160(compressed_pubkey)"
875 );
876
877 assert!(address_from_private.is_p2pkh());
879 }
880
881 #[test]
882 fn test_p2pkh_verify_signature_success() {
883 let seed = [1u8; 32];
885 let (secret_key, public_key) = create_keypair(seed);
886
887 let pubkey_hash = public_key.pubkey_hash();
889 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
890
891 let signable_bytes = b"test message for P2PKH verification";
893
894 let signature = sign_data(signable_bytes, &secret_key);
896
897 let witness = AddressWitness::P2pkh {
899 signature: BinaryData::new(signature),
900 };
901
902 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
904 assert!(
905 result.is_ok(),
906 "P2PKH verification should succeed: {:?}",
907 result
908 );
909 }
910
911 #[test]
912 fn test_p2pkh_verify_wrong_signature_fails() {
913 let seed = [1u8; 32];
915 let (secret_key, public_key) = create_keypair(seed);
916
917 let pubkey_hash = public_key.pubkey_hash();
919 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
920
921 let sign_bytes = b"original message";
923 let verify_bytes = b"different message";
924 let signature = sign_data(sign_bytes, &secret_key);
925
926 let witness = AddressWitness::P2pkh {
928 signature: BinaryData::new(signature),
929 };
930
931 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
933 assert!(
934 result.is_err(),
935 "P2PKH verification should fail with wrong data"
936 );
937 }
938
939 #[test]
940 fn test_p2pkh_verify_wrong_key_fails() {
941 let seed1 = [1u8; 32];
943 let seed2 = [2u8; 32];
944 let (_secret_key1, public_key1) = create_keypair(seed1);
945 let (secret_key2, _public_key2) = create_keypair(seed2);
946
947 let pubkey_hash = public_key1.pubkey_hash();
949 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
950
951 let signable_bytes = b"test message";
953 let signature = sign_data(signable_bytes, &secret_key2);
954
955 let witness = AddressWitness::P2pkh {
957 signature: BinaryData::new(signature),
958 };
959
960 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
962 assert!(
963 result.is_err(),
964 "P2PKH verification should fail when signed with wrong key"
965 );
966 }
967
968 #[test]
973 fn test_p2sh_2_of_3_multisig_verify_success() {
974 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
976 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
977 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
978
979 let redeem_script = create_multisig_script(2, &pubkeys);
981
982 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
984 let script_hash = script_buf.script_hash();
985 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
986
987 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
989
990 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
992 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
993
994 let witness = AddressWitness::P2sh {
997 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
998 redeem_script: BinaryData::new(redeem_script),
999 };
1000
1001 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1003 assert!(
1004 result.is_ok(),
1005 "P2SH 2-of-3 multisig verification should succeed: {:?}",
1006 result
1007 );
1008 }
1009
1010 #[test]
1011 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
1012 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1014 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1015 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1016
1017 let redeem_script = create_multisig_script(2, &pubkeys);
1019
1020 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1022 let script_hash = script_buf.script_hash();
1023 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1024
1025 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
1027
1028 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1030 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
1031
1032 let witness = AddressWitness::P2sh {
1034 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
1035 redeem_script: BinaryData::new(redeem_script),
1036 };
1037
1038 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1040 assert!(
1041 result.is_ok(),
1042 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
1043 result
1044 );
1045 }
1046
1047 #[test]
1048 fn test_p2sh_not_enough_signatures_fails() {
1049 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1051 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1052 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1053
1054 let redeem_script = create_multisig_script(2, &pubkeys);
1056
1057 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1059 let script_hash = script_buf.script_hash();
1060 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1061
1062 let signable_bytes = b"test message";
1064
1065 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1067
1068 let witness = AddressWitness::P2sh {
1070 signatures: vec![BinaryData::new(sig0)],
1071 redeem_script: BinaryData::new(redeem_script),
1072 };
1073
1074 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1076 assert!(
1077 result.is_err(),
1078 "P2SH should fail with only 1 signature when 2 required"
1079 );
1080 assert!(
1081 result.unwrap_err().to_string().contains("Not enough"),
1082 "Error should mention not enough signatures"
1083 );
1084 }
1085
1086 #[test]
1087 fn test_p2sh_wrong_script_hash_fails() {
1088 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1090 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1091 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1092
1093 let redeem_script = create_multisig_script(2, &pubkeys);
1095
1096 let wrong_hash = [0xABu8; 20];
1098 let address = PlatformAddress::P2sh(wrong_hash);
1099
1100 let signable_bytes = b"test message";
1102
1103 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1105 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1106
1107 let witness = AddressWitness::P2sh {
1109 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
1110 redeem_script: BinaryData::new(redeem_script),
1111 };
1112
1113 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1115 assert!(
1116 result.is_err(),
1117 "P2SH should fail when script hash doesn't match address"
1118 );
1119 assert!(
1120 result
1121 .unwrap_err()
1122 .to_string()
1123 .contains("does not match address hash"),
1124 "Error should mention hash mismatch"
1125 );
1126 }
1127
1128 #[test]
1129 fn test_p2pkh_and_p2sh_together() {
1130 let p2pkh_seed = [10u8; 32];
1134 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
1135 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
1136 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
1137
1138 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
1140 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
1141 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
1142 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
1143 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1144 let script_hash = script_buf.script_hash();
1145 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1146
1147 let signable_bytes = b"combined transaction data to redeem both outputs";
1149
1150 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
1152 let p2pkh_witness = AddressWitness::P2pkh {
1153 signature: BinaryData::new(p2pkh_sig),
1154 };
1155 let p2pkh_result =
1156 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1157 assert!(
1158 p2pkh_result.is_ok(),
1159 "P2PKH redemption should succeed: {:?}",
1160 p2pkh_result
1161 );
1162
1163 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
1165 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
1166 let p2sh_witness = AddressWitness::P2sh {
1167 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
1168 redeem_script: BinaryData::new(redeem_script),
1169 };
1170 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1171 assert!(
1172 p2sh_result.is_ok(),
1173 "P2SH redemption should succeed: {:?}",
1174 p2sh_result
1175 );
1176
1177 }
1179
1180 #[test]
1181 fn test_witness_type_mismatch() {
1182 let seed = [1u8; 32];
1184 let (_, public_key) = create_keypair(seed);
1185 let pubkey_hash = public_key.pubkey_hash();
1186 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
1187
1188 let p2sh_hash = [0xABu8; 20];
1190 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
1191
1192 let signable_bytes = b"test data";
1193
1194 let p2sh_witness = AddressWitness::P2sh {
1196 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
1197 redeem_script: BinaryData::new(vec![0x52]),
1198 };
1199 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1200 assert!(result.is_err());
1201 assert!(result
1202 .unwrap_err()
1203 .to_string()
1204 .contains("P2PKH address requires P2pkh witness"));
1205
1206 let p2pkh_witness = AddressWitness::P2pkh {
1208 signature: BinaryData::new(vec![0x30, 0x44]),
1209 };
1210 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1211 assert!(result.is_err());
1212 assert!(result
1213 .unwrap_err()
1214 .to_string()
1215 .contains("P2SH address requires P2sh witness"));
1216 }
1217
1218 #[test]
1223 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1224 let hash: [u8; 20] = [
1226 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1227 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1228 ];
1229 let address = PlatformAddress::P2pkh(hash);
1230
1231 let encoded = address.to_bech32m_string(Network::Mainnet);
1233
1234 assert_eq!(
1236 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1237 "P2PKH mainnet encoding mismatch"
1238 );
1239
1240 let decoded =
1242 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1243 assert_eq!(decoded, address);
1244 }
1245
1246 #[test]
1247 fn test_bech32m_p2pkh_testnet_roundtrip() {
1248 let hash: [u8; 20] = [
1250 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1251 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1252 ];
1253 let address = PlatformAddress::P2pkh(hash);
1254
1255 let encoded = address.to_bech32m_string(Network::Testnet);
1257
1258 assert_eq!(
1260 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1261 "P2PKH testnet encoding mismatch"
1262 );
1263
1264 let decoded =
1266 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1267 assert_eq!(decoded, address);
1268 }
1269
1270 #[test]
1271 fn test_bech32m_p2sh_mainnet_roundtrip() {
1272 let hash: [u8; 20] = [
1274 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1275 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1276 ];
1277 let address = PlatformAddress::P2sh(hash);
1278
1279 let encoded = address.to_bech32m_string(Network::Mainnet);
1281
1282 assert_eq!(
1284 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1285 "P2SH mainnet encoding mismatch"
1286 );
1287
1288 let decoded =
1290 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1291 assert_eq!(decoded, address);
1292 }
1293
1294 #[test]
1295 fn test_bech32m_p2sh_testnet_roundtrip() {
1296 let hash: [u8; 20] = [
1298 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1299 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1300 ];
1301 let address = PlatformAddress::P2sh(hash);
1302
1303 let encoded = address.to_bech32m_string(Network::Testnet);
1305
1306 assert_eq!(
1308 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1309 "P2SH testnet encoding mismatch"
1310 );
1311
1312 let decoded =
1314 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1315 assert_eq!(decoded, address);
1316 }
1317
1318 #[test]
1319 fn test_bech32m_devnet_uses_testnet_hrp() {
1320 let hash: [u8; 20] = [0xAB; 20];
1321 let address = PlatformAddress::P2pkh(hash);
1322
1323 let encoded = address.to_bech32m_string(Network::Devnet);
1325 assert!(
1326 encoded.starts_with("tdash1"),
1327 "Devnet address should start with 'tdash1', got: {}",
1328 encoded
1329 );
1330 }
1331
1332 #[test]
1333 fn test_bech32m_regtest_uses_testnet_hrp() {
1334 let hash: [u8; 20] = [0xAB; 20];
1335 let address = PlatformAddress::P2pkh(hash);
1336
1337 let encoded = address.to_bech32m_string(Network::Regtest);
1339 assert!(
1340 encoded.starts_with("tdash1"),
1341 "Regtest address should start with 'tdash1', got: {}",
1342 encoded
1343 );
1344 }
1345
1346 #[test]
1347 fn test_bech32m_invalid_hrp_fails() {
1348 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1349 let payload: [u8; 21] = [0x00; 21];
1350 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1351
1352 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1353 assert!(result.is_err());
1354 let err = result.unwrap_err();
1355 assert!(
1356 err.to_string().contains("not a platform address"),
1357 "Error should mention non-platform HRP: {}",
1358 err
1359 );
1360 }
1361
1362 #[test]
1363 fn test_bech32m_invalid_checksum_fails() {
1364 let hash: [u8; 20] = [0xAB; 20];
1366 let address = PlatformAddress::P2pkh(hash);
1367 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1368
1369 let last_char = encoded.pop().unwrap();
1371 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1372 encoded.push(corrupted_char);
1373
1374 let result = PlatformAddress::from_bech32m_string(&encoded);
1375 assert!(result.is_err(), "Should fail with corrupted checksum");
1376 }
1377
1378 #[test]
1379 fn test_bech32m_invalid_type_byte_fails() {
1380 let hrp = Hrp::parse("dash").unwrap();
1383 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1385
1386 let result = PlatformAddress::from_bech32m_string(&encoded);
1387 assert!(result.is_err());
1388 let err = result.unwrap_err();
1389 assert!(
1390 err.to_string().contains("invalid address type"),
1391 "Error should mention invalid type: {}",
1392 err
1393 );
1394 }
1395
1396 #[test]
1397 fn test_bech32m_too_short_fails() {
1398 let hrp = Hrp::parse("dash").unwrap();
1400 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1402
1403 let result = PlatformAddress::from_bech32m_string(&encoded);
1404 assert!(result.is_err());
1405 let err = result.unwrap_err();
1406 assert!(
1407 err.to_string().contains("invalid Platform address length"),
1408 "Error should mention invalid length: {}",
1409 err
1410 );
1411 }
1412
1413 #[test]
1414 fn test_bech32m_from_str_trait() {
1415 let hash: [u8; 20] = [
1417 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1418 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1419 ];
1420 let original = PlatformAddress::P2pkh(hash);
1421
1422 let encoded = original.to_bech32m_string(Network::Testnet);
1424 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1425
1426 assert_eq!(parsed, original);
1427 }
1428
1429 #[test]
1430 fn test_bech32m_case_insensitive() {
1431 let hash: [u8; 20] = [0xAB; 20];
1434 let address = PlatformAddress::P2pkh(hash);
1435
1436 let lowercase = address.to_bech32m_string(Network::Mainnet);
1437 let uppercase = lowercase.to_uppercase();
1438
1439 let decoded_lower = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1441 let decoded_upper = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1442
1443 assert_eq!(decoded_lower, decoded_upper);
1444 assert_eq!(decoded_lower, address);
1445 }
1446
1447 #[test]
1448 fn test_bech32m_all_zeros_p2pkh() {
1449 let address = PlatformAddress::P2pkh([0u8; 20]);
1451 let encoded = address.to_bech32m_string(Network::Mainnet);
1452 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1453 assert_eq!(decoded, address);
1454 }
1455
1456 #[test]
1457 fn test_bech32m_all_ones_p2sh() {
1458 let address = PlatformAddress::P2sh([0xFF; 20]);
1460 let encoded = address.to_bech32m_string(Network::Mainnet);
1461 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1462 assert_eq!(decoded, address);
1463 }
1464
1465 #[test]
1466 fn test_hrp_for_network() {
1467 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1468 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1469 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1470 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1471 }
1472
1473 #[test]
1474 fn test_storage_bytes_format() {
1475 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1479 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1480
1481 let p2pkh_bytes = p2pkh.to_bytes();
1482 let p2sh_bytes = p2sh.to_bytes();
1483
1484 assert_eq!(p2pkh_bytes.len(), 21);
1486 assert_eq!(p2sh_bytes.len(), 21);
1487 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1488 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1489
1490 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1492 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1493 assert_eq!(p2pkh_decoded, p2pkh);
1494 assert_eq!(p2sh_decoded, p2sh);
1495 }
1496
1497 #[test]
1498 fn test_bech32m_uses_different_type_bytes_than_storage() {
1499 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1502 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1503
1504 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1506 assert_eq!(p2sh.to_bytes()[0], 0x01);
1507
1508 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1510 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1511
1512 let p2pkh_decoded = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1513 let p2sh_decoded = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1514
1515 assert_eq!(p2pkh_decoded, p2pkh);
1516 assert_eq!(p2sh_decoded, p2sh);
1517 }
1518
1519 #[test]
1520 fn test_is_mainnet_bech32m_mainnet_is_true() {
1521 let encoded = PlatformAddress::P2pkh([0x11; 20]).to_bech32m_string(Network::Mainnet);
1522 assert!(encoded.starts_with("dash1"));
1523 assert!(PlatformAddress::is_mainnet_bech32m(&encoded).unwrap());
1524 }
1525
1526 #[test]
1527 fn test_is_mainnet_bech32m_all_non_mainnet_networks_are_false() {
1528 for network in [Network::Testnet, Network::Devnet, Network::Regtest] {
1532 let encoded = PlatformAddress::P2pkh([0x22; 20]).to_bech32m_string(network);
1533 assert!(encoded.starts_with("tdash1"), "network {network:?}");
1534 assert!(
1535 !PlatformAddress::is_mainnet_bech32m(&encoded).unwrap(),
1536 "network {network:?} must classify as non-mainnet"
1537 );
1538 }
1539 }
1540
1541 #[test]
1542 fn test_is_mainnet_bech32m_is_case_insensitive() {
1543 let mainnet = PlatformAddress::P2pkh([0x33; 20])
1544 .to_bech32m_string(Network::Mainnet)
1545 .to_uppercase();
1546 assert!(mainnet.starts_with("DASH1"));
1547 assert!(PlatformAddress::is_mainnet_bech32m(&mainnet).unwrap());
1548
1549 let testnet = PlatformAddress::P2pkh([0x44; 20])
1550 .to_bech32m_string(Network::Testnet)
1551 .to_uppercase();
1552 assert!(testnet.starts_with("TDASH1"));
1553 assert!(!PlatformAddress::is_mainnet_bech32m(&testnet).unwrap());
1554 }
1555
1556 #[test]
1557 fn test_is_mainnet_bech32m_non_platform_hrp_errors() {
1558 let err = PlatformAddress::is_mainnet_bech32m("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
1560 .unwrap_err();
1561 assert!(
1562 err.to_string().contains("not a platform address"),
1563 "unexpected error: {err}"
1564 );
1565 }
1566
1567 #[test]
1568 fn test_is_mainnet_bech32m_malformed_data_part_errors() {
1569 assert!(
1573 PlatformAddress::is_mainnet_bech32m("dash1!").is_err(),
1574 "dash1! must error, not return Ok(true)"
1575 );
1576 }
1577
1578 #[test]
1579 fn test_is_mainnet_bech32m_missing_separator_errors() {
1580 let err = PlatformAddress::is_mainnet_bech32m("nodelimiterhere").unwrap_err();
1581 assert!(
1583 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1584 "unexpected error: {err}"
1585 );
1586 }
1587
1588 #[test]
1589 fn test_is_mainnet_bech32m_empty_errors() {
1590 let err = PlatformAddress::is_mainnet_bech32m("").unwrap_err();
1591 assert!(
1592 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1593 "unexpected error: {err}"
1594 );
1595 }
1596}