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(feature = "serde-conversion")]
56impl Serialize for PlatformAddress {
57 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58 where
59 S: serde::Serializer,
60 {
61 let bytes = self.to_bytes();
62 if serializer.is_human_readable() {
63 serializer.serialize_str(&hex::encode(&bytes))
64 } else {
65 serializer.serialize_bytes(&bytes)
66 }
67 }
68}
69
70#[cfg(feature = "serde-conversion")]
71impl<'de> Deserialize<'de> for PlatformAddress {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: serde::Deserializer<'de>,
75 {
76 use serde::de::{self, Visitor};
77 use std::fmt;
78
79 const PLATFORM_ADDRESS_BYTE_LEN: usize = 21;
81
82 struct PlatformAddressVisitor;
83
84 impl<'de> Visitor<'de> for PlatformAddressVisitor {
85 type Value = PlatformAddress;
86
87 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
88 formatter.write_str("PlatformAddress as 21 bytes or hex string")
89 }
90
91 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
92 let bytes =
93 hex::decode(value).map_err(|err| E::custom(format!("invalid hex: {}", err)))?;
94 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
95 return Err(E::invalid_length(bytes.len(), &self));
96 }
97 PlatformAddress::from_bytes(&bytes).map_err(|err| E::custom(err.to_string()))
98 }
99
100 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
101 self.visit_str(&value)
102 }
103
104 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
105 if value.len() != PLATFORM_ADDRESS_BYTE_LEN {
106 return Err(E::invalid_length(value.len(), &self));
107 }
108 PlatformAddress::from_bytes(value).map_err(|err| E::custom(err.to_string()))
109 }
110
111 fn visit_byte_buf<E: de::Error>(self, value: Vec<u8>) -> Result<Self::Value, E> {
112 self.visit_bytes(&value)
113 }
114
115 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
116 where
117 A: de::SeqAccess<'de>,
118 {
119 let mut bytes = Vec::with_capacity(PLATFORM_ADDRESS_BYTE_LEN);
122 while let Some(byte) = seq.next_element::<u8>()? {
123 if bytes.len() >= PLATFORM_ADDRESS_BYTE_LEN {
124 return Err(de::Error::invalid_length(
125 bytes.len() + 1,
126 &"at most 21 bytes",
127 ));
128 }
129 bytes.push(byte);
130 }
131 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
132 return Err(de::Error::invalid_length(bytes.len(), &self));
133 }
134 PlatformAddress::from_bytes(&bytes)
135 .map_err(|err| de::Error::custom(err.to_string()))
136 }
137 }
138
139 if deserializer.is_human_readable() {
143 deserializer.deserialize_str(PlatformAddressVisitor)
144 } else {
145 deserializer.deserialize_bytes(PlatformAddressVisitor)
146 }
147 }
148}
149
150impl TryFrom<Address> for PlatformAddress {
151 type Error = ProtocolError;
152
153 fn try_from(address: Address) -> Result<Self, Self::Error> {
154 match address.payload() {
155 Payload::PubkeyHash(hash) => Ok(PlatformAddress::P2pkh(*hash.as_ref())),
156 Payload::ScriptHash(hash) => Ok(PlatformAddress::P2sh(*hash.as_ref())),
157 _ => Err(ProtocolError::DecodingError(
158 "unsupported address type for PlatformAddress: only P2PKH and P2SH are supported"
159 .to_string(),
160 )),
161 }
162 }
163}
164
165impl From<&PrivateKey> for PlatformAddress {
166 fn from(private_key: &PrivateKey) -> Self {
171 let secp = Secp256k1::new();
172 let pubkey_hash = private_key.public_key(&secp).pubkey_hash();
173 PlatformAddress::P2pkh(*pubkey_hash.as_byte_array())
174 }
175}
176
177impl Default for PlatformAddress {
178 fn default() -> Self {
179 PlatformAddress::P2pkh([0u8; 20])
180 }
181}
182
183pub const PLATFORM_HRP_MAINNET: &str = "dash";
185pub const PLATFORM_HRP_TESTNET: &str = "tdash";
187
188pub(crate) fn classify_platform_hrp(hrp: &str) -> Result<bool, ProtocolError> {
193 match hrp {
194 PLATFORM_HRP_MAINNET => Ok(true),
195 PLATFORM_HRP_TESTNET => Ok(false),
196 other => Err(ProtocolError::DecodingError(format!(
197 "not a platform address: HRP '{other}' is neither \
198 '{PLATFORM_HRP_MAINNET}' nor '{PLATFORM_HRP_TESTNET}'"
199 ))),
200 }
201}
202
203impl PlatformAddress {
204 pub const P2PKH_TYPE: u8 = 0xb0;
206 pub const P2SH_TYPE: u8 = 0x80;
208
209 pub fn hrp_for_network(network: Network) -> &'static str {
215 match network {
216 Network::Mainnet => PLATFORM_HRP_MAINNET,
217 Network::Testnet | Network::Devnet | Network::Regtest => PLATFORM_HRP_TESTNET,
218 }
219 }
220
221 pub fn to_bech32m_string(&self, network: Network) -> String {
238 let hrp_str = Self::hrp_for_network(network);
239 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
240
241 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
244 match self {
245 PlatformAddress::P2pkh(hash) => {
246 payload.push(Self::P2PKH_TYPE);
247 payload.extend_from_slice(hash);
248 }
249 PlatformAddress::P2sh(hash) => {
250 payload.push(Self::P2SH_TYPE);
251 payload.extend_from_slice(hash);
252 }
253 }
254
255 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
257 }
258
259 pub fn from_bech32m_string(s: &str) -> Result<Self, ProtocolError> {
270 let (hrp, data) =
271 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
272
273 classify_platform_hrp(&hrp.as_str().to_ascii_lowercase())?;
274
275 if data.len() != 1 + ADDRESS_HASH_SIZE {
277 return Err(ProtocolError::DecodingError(format!(
278 "invalid Platform address length: expected {} bytes, got {}",
279 1 + ADDRESS_HASH_SIZE,
280 data.len()
281 )));
282 }
283
284 let address_type = data[0];
286 let hash: [u8; 20] = data[1..21]
287 .try_into()
288 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
289
290 let address = match address_type {
291 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
292 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
293 _ => Err(ProtocolError::DecodingError(format!(
294 "invalid address type: 0x{:02x}",
295 address_type
296 ))),
297 }?;
298
299 Ok(address)
300 }
301
302 pub fn is_mainnet_bech32m(s: &str) -> Result<bool, ProtocolError> {
313 let (hrp, _) =
314 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{e}")))?;
315 classify_platform_hrp(&hrp.to_lowercase())
316 }
317
318 pub fn to_address_with_network(&self, network: Network) -> Address {
320 match self {
321 PlatformAddress::P2pkh(hash) => Address::new(
322 network,
323 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
324 ),
325 PlatformAddress::P2sh(hash) => Address::new(
326 network,
327 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
328 ),
329 }
330 }
331
332 pub fn to_bytes(&self) -> Vec<u8> {
338 bincode::encode_to_vec(self, bincode::config::standard())
339 .expect("PlatformAddress serialization cannot fail")
340 }
341
342 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
345 use base64::engine::general_purpose::STANDARD;
346 use base64::Engine;
347
348 let mut bytes = self.to_bytes();
349 bytes.extend_from_slice(&nonce.to_be_bytes());
350
351 STANDARD.encode(bytes)
352 }
353
354 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
359 let (address, _): (Self, usize) =
360 bincode::decode_from_slice(bytes, bincode::config::standard()).map_err(|e| {
361 ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e))
362 })?;
363 Ok(address)
364 }
365
366 pub fn hash(&self) -> &[u8; 20] {
368 match self {
369 PlatformAddress::P2pkh(hash) => hash,
370 PlatformAddress::P2sh(hash) => hash,
371 }
372 }
373
374 pub fn is_p2pkh(&self) -> bool {
376 matches!(self, PlatformAddress::P2pkh(_))
377 }
378
379 pub fn is_p2sh(&self) -> bool {
381 matches!(self, PlatformAddress::P2sh(_))
382 }
383
384 pub fn verify_bytes_against_witness(
404 &self,
405 witness: &AddressWitness,
406 signable_bytes: &[u8],
407 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
408 match (self, witness) {
409 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
410 let data_hash = dashcore::signer::double_sha(signable_bytes);
418 dashcore::signer::verify_hash_signature(
419 &data_hash,
420 signature.as_slice(),
421 pubkey_hash,
422 )
423 .map_err(|e| {
424 ProtocolError::AddressWitnessError(format!(
425 "P2PKH signature verification failed: {}",
426 e
427 ))
428 })?;
429
430 Ok(AddressWitnessVerificationOperations::for_p2pkh(
431 signable_bytes.len(),
432 ))
433 }
434 (
435 PlatformAddress::P2sh(script_hash),
436 AddressWitness::P2sh {
437 signatures,
438 redeem_script,
439 },
440 ) => {
441 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
443 let computed_hash = script.script_hash();
444 if computed_hash.as_byte_array() != script_hash {
445 return Err(ProtocolError::AddressWitnessError(format!(
446 "Script hash {} does not match address hash {}",
447 hex::encode(computed_hash.as_byte_array()),
448 hex::encode(script_hash)
449 )));
450 }
451
452 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
455
456 let valid_signatures: Vec<_> = signatures
458 .iter()
459 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
460 .collect();
461
462 if valid_signatures.len() < threshold {
463 return Err(ProtocolError::AddressWitnessError(format!(
464 "Not enough signatures: got {}, need {}",
465 valid_signatures.len(),
466 threshold
467 )));
468 }
469
470 let mut sig_idx = 0;
473 let mut pubkey_idx = 0;
474 let mut matched = 0;
475 let mut signature_verifications: u16 = 0;
476
477 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
478 let msg = Message::from_digest(signable_bytes_hash);
479 let secp = Secp256k1::new();
480
481 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
482 signature_verifications += 1;
483
484 let sig = RecoverableSignature::from_compact_signature(
485 valid_signatures[sig_idx].as_slice(),
486 )
487 .map_err(|e| {
488 ProtocolError::AddressWitnessError(format!(
489 "Invalid signature format: {}",
490 e
491 ))
492 })?;
493
494 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
495 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
496 })?;
497
498 if secp
499 .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner)
500 .is_ok()
501 {
502 matched += 1;
503 sig_idx += 1;
504 }
505 pubkey_idx += 1;
506 }
507
508 if matched >= threshold {
509 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
510 signature_verifications,
511 signable_bytes.len(),
512 ))
513 } else {
514 Err(ProtocolError::AddressWitnessError(format!(
515 "Not enough valid signatures: verified {}, need {}",
516 matched, threshold
517 )))
518 }
519 }
520 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
521 Err(ProtocolError::AddressWitnessError(
522 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
523 ))
524 }
525 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
526 Err(ProtocolError::AddressWitnessError(
527 "P2SH address requires P2sh witness, got P2pkh".to_string(),
528 ))
529 }
530 }
531 }
532
533 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
548 use dashcore::blockdata::opcodes::all::*;
549
550 let mut instructions = script.instructions();
551 let mut pubkeys = Vec::new();
552
553 let threshold = match instructions.next() {
555 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
556 let byte = op.to_u8();
557 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
558 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
559 } else {
560 return Err(ProtocolError::AddressWitnessError(format!(
561 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
562 First opcode was 0x{:02x}, expected OP_1 through OP_16",
563 byte
564 )));
565 }
566 }
567 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
568 return Err(ProtocolError::AddressWitnessError(
569 "Unsupported P2SH script type: only standard multisig is supported. \
570 Script starts with a data push instead of OP_M threshold."
571 .to_string(),
572 ))
573 }
574 Some(Err(e)) => {
575 return Err(ProtocolError::AddressWitnessError(format!(
576 "Error parsing P2SH script: {:?}",
577 e
578 )))
579 }
580 None => {
581 return Err(ProtocolError::AddressWitnessError(
582 "Empty P2SH redeem script".to_string(),
583 ))
584 }
585 };
586
587 loop {
589 match instructions.next() {
590 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
591 let len = bytes.len();
593 if len != 33 {
594 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
595 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
596 len,
597 ),
598 ));
599 }
600 pubkeys.push(bytes.as_bytes().to_vec());
601 }
602 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
603 let byte = op.to_u8();
604 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
605 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
607 if pubkeys.len() != n {
608 return Err(ProtocolError::AddressWitnessError(format!(
609 "Multisig script declares {} keys but contains {}",
610 n,
611 pubkeys.len()
612 )));
613 }
614 break;
615 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
616 return Err(ProtocolError::AddressWitnessError(
618 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
619 ));
620 } else {
621 return Err(ProtocolError::AddressWitnessError(format!(
622 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
623 byte
624 )));
625 }
626 }
627 Some(Err(e)) => {
628 return Err(ProtocolError::AddressWitnessError(format!(
629 "Error parsing multisig script: {:?}",
630 e
631 )))
632 }
633 None => {
634 return Err(ProtocolError::AddressWitnessError(
635 "Incomplete multisig script: unexpected end before OP_N".to_string(),
636 ))
637 }
638 }
639 }
640
641 if threshold > pubkeys.len() {
643 return Err(ProtocolError::AddressWitnessError(format!(
644 "Invalid multisig: threshold {} exceeds number of keys {}",
645 threshold,
646 pubkeys.len()
647 )));
648 }
649
650 match instructions.next() {
652 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
653 if op == OP_CHECKMULTISIG {
654 if instructions.next().is_some() {
656 return Err(ProtocolError::AddressWitnessError(
657 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
658 ));
659 }
660 Ok((threshold, pubkeys))
661 } else if op == OP_CHECKMULTISIGVERIFY {
662 Err(ProtocolError::AddressWitnessError(
663 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
664 .to_string(),
665 ))
666 } else {
667 Err(ProtocolError::AddressWitnessError(format!(
668 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
669 op.to_u8()
670 )))
671 }
672 }
673 _ => Err(ProtocolError::AddressWitnessError(
674 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
675 )),
676 }
677 }
678}
679
680impl std::fmt::Display for PlatformAddress {
681 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
682 match self {
683 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
684 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
685 }
686 }
687}
688
689#[derive(Debug, Clone, PartialEq, Eq)]
691pub struct PlatformAddressParseError(pub String);
692
693impl std::fmt::Display for PlatformAddressParseError {
694 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
695 write!(f, "{}", self.0)
696 }
697}
698
699impl std::error::Error for PlatformAddressParseError {}
700
701impl FromStr for PlatformAddress {
702 type Err = PlatformAddressParseError;
703
704 fn from_str(s: &str) -> Result<Self, Self::Err> {
713 Self::from_bech32m_string(s).map_err(|e| PlatformAddressParseError(e.to_string()))
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use dashcore::blockdata::opcodes::all::*;
721 use dashcore::hashes::Hash;
722 use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey};
723 use dashcore::PublicKey;
724 use platform_value::BinaryData;
725
726 #[test]
732 fn hrp_is_shared_across_all_non_mainnet_networks() {
733 let testnet = PlatformAddress::hrp_for_network(Network::Testnet);
734 assert_eq!(testnet, PLATFORM_HRP_TESTNET);
735 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), testnet);
736 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), testnet);
737 assert_ne!(
738 PlatformAddress::hrp_for_network(Network::Mainnet),
739 testnet,
740 "mainnet must use a distinct HRP"
741 );
742 }
743
744 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
746 let secp = Secp256k1::new();
747 let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key");
748 let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key);
749 let public_key = PublicKey::new(raw_public_key);
750 (secret_key, public_key)
751 }
752
753 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
755 dashcore::signer::sign(data, secret_key.as_ref())
756 .expect("signing should succeed")
757 .to_vec()
758 }
759
760 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
762 let mut script = Vec::new();
763
764 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
766
767 for pubkey in pubkeys {
769 let bytes = pubkey.to_bytes();
770 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
772 }
773
774 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
776
777 script.push(OP_CHECKMULTISIG.to_u8());
779
780 script
781 }
782
783 #[test]
784 fn test_platform_address_from_private_key() {
785 let seed = [1u8; 32];
787 let (secret_key, public_key) = create_keypair(seed);
788
789 let private_key = PrivateKey::new(secret_key, Network::Testnet);
791
792 let address_from_private = PlatformAddress::from(&private_key);
794
795 let pubkey_hash = public_key.pubkey_hash();
798 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
799
800 assert_eq!(
802 address_from_private, address_from_pubkey,
803 "Address derived from private key should match Hash160(compressed_pubkey)"
804 );
805
806 assert!(address_from_private.is_p2pkh());
808 }
809
810 #[test]
811 fn test_p2pkh_verify_signature_success() {
812 let seed = [1u8; 32];
814 let (secret_key, public_key) = create_keypair(seed);
815
816 let pubkey_hash = public_key.pubkey_hash();
818 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
819
820 let signable_bytes = b"test message for P2PKH verification";
822
823 let signature = sign_data(signable_bytes, &secret_key);
825
826 let witness = AddressWitness::P2pkh {
828 signature: BinaryData::new(signature),
829 };
830
831 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
833 assert!(
834 result.is_ok(),
835 "P2PKH verification should succeed: {:?}",
836 result
837 );
838 }
839
840 #[test]
841 fn test_p2pkh_verify_wrong_signature_fails() {
842 let seed = [1u8; 32];
844 let (secret_key, public_key) = create_keypair(seed);
845
846 let pubkey_hash = public_key.pubkey_hash();
848 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
849
850 let sign_bytes = b"original message";
852 let verify_bytes = b"different message";
853 let signature = sign_data(sign_bytes, &secret_key);
854
855 let witness = AddressWitness::P2pkh {
857 signature: BinaryData::new(signature),
858 };
859
860 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
862 assert!(
863 result.is_err(),
864 "P2PKH verification should fail with wrong data"
865 );
866 }
867
868 #[test]
869 fn test_p2pkh_verify_wrong_key_fails() {
870 let seed1 = [1u8; 32];
872 let seed2 = [2u8; 32];
873 let (_secret_key1, public_key1) = create_keypair(seed1);
874 let (secret_key2, _public_key2) = create_keypair(seed2);
875
876 let pubkey_hash = public_key1.pubkey_hash();
878 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
879
880 let signable_bytes = b"test message";
882 let signature = sign_data(signable_bytes, &secret_key2);
883
884 let witness = AddressWitness::P2pkh {
886 signature: BinaryData::new(signature),
887 };
888
889 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
891 assert!(
892 result.is_err(),
893 "P2PKH verification should fail when signed with wrong key"
894 );
895 }
896
897 #[test]
902 fn test_p2sh_2_of_3_multisig_verify_success() {
903 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
905 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
906 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
907
908 let redeem_script = create_multisig_script(2, &pubkeys);
910
911 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
913 let script_hash = script_buf.script_hash();
914 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
915
916 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
918
919 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
921 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
922
923 let witness = AddressWitness::P2sh {
926 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
927 redeem_script: BinaryData::new(redeem_script),
928 };
929
930 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
932 assert!(
933 result.is_ok(),
934 "P2SH 2-of-3 multisig verification should succeed: {:?}",
935 result
936 );
937 }
938
939 #[test]
940 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
941 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
943 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
944 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
945
946 let redeem_script = create_multisig_script(2, &pubkeys);
948
949 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
951 let script_hash = script_buf.script_hash();
952 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
953
954 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
956
957 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
959 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
960
961 let witness = AddressWitness::P2sh {
963 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
964 redeem_script: BinaryData::new(redeem_script),
965 };
966
967 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
969 assert!(
970 result.is_ok(),
971 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
972 result
973 );
974 }
975
976 #[test]
977 fn test_p2sh_not_enough_signatures_fails() {
978 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
980 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
981 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
982
983 let redeem_script = create_multisig_script(2, &pubkeys);
985
986 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
988 let script_hash = script_buf.script_hash();
989 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
990
991 let signable_bytes = b"test message";
993
994 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
996
997 let witness = AddressWitness::P2sh {
999 signatures: vec![BinaryData::new(sig0)],
1000 redeem_script: BinaryData::new(redeem_script),
1001 };
1002
1003 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1005 assert!(
1006 result.is_err(),
1007 "P2SH should fail with only 1 signature when 2 required"
1008 );
1009 assert!(
1010 result.unwrap_err().to_string().contains("Not enough"),
1011 "Error should mention not enough signatures"
1012 );
1013 }
1014
1015 #[test]
1016 fn test_p2sh_wrong_script_hash_fails() {
1017 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1019 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1020 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1021
1022 let redeem_script = create_multisig_script(2, &pubkeys);
1024
1025 let wrong_hash = [0xABu8; 20];
1027 let address = PlatformAddress::P2sh(wrong_hash);
1028
1029 let signable_bytes = b"test message";
1031
1032 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1034 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1035
1036 let witness = AddressWitness::P2sh {
1038 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
1039 redeem_script: BinaryData::new(redeem_script),
1040 };
1041
1042 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1044 assert!(
1045 result.is_err(),
1046 "P2SH should fail when script hash doesn't match address"
1047 );
1048 assert!(
1049 result
1050 .unwrap_err()
1051 .to_string()
1052 .contains("does not match address hash"),
1053 "Error should mention hash mismatch"
1054 );
1055 }
1056
1057 #[test]
1058 fn test_p2pkh_and_p2sh_together() {
1059 let p2pkh_seed = [10u8; 32];
1063 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
1064 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
1065 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
1066
1067 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
1069 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
1070 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
1071 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
1072 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1073 let script_hash = script_buf.script_hash();
1074 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1075
1076 let signable_bytes = b"combined transaction data to redeem both outputs";
1078
1079 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
1081 let p2pkh_witness = AddressWitness::P2pkh {
1082 signature: BinaryData::new(p2pkh_sig),
1083 };
1084 let p2pkh_result =
1085 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1086 assert!(
1087 p2pkh_result.is_ok(),
1088 "P2PKH redemption should succeed: {:?}",
1089 p2pkh_result
1090 );
1091
1092 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
1094 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
1095 let p2sh_witness = AddressWitness::P2sh {
1096 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
1097 redeem_script: BinaryData::new(redeem_script),
1098 };
1099 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1100 assert!(
1101 p2sh_result.is_ok(),
1102 "P2SH redemption should succeed: {:?}",
1103 p2sh_result
1104 );
1105
1106 }
1108
1109 #[test]
1110 fn test_witness_type_mismatch() {
1111 let seed = [1u8; 32];
1113 let (_, public_key) = create_keypair(seed);
1114 let pubkey_hash = public_key.pubkey_hash();
1115 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
1116
1117 let p2sh_hash = [0xABu8; 20];
1119 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
1120
1121 let signable_bytes = b"test data";
1122
1123 let p2sh_witness = AddressWitness::P2sh {
1125 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
1126 redeem_script: BinaryData::new(vec![0x52]),
1127 };
1128 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1129 assert!(result.is_err());
1130 assert!(result
1131 .unwrap_err()
1132 .to_string()
1133 .contains("P2PKH address requires P2pkh witness"));
1134
1135 let p2pkh_witness = AddressWitness::P2pkh {
1137 signature: BinaryData::new(vec![0x30, 0x44]),
1138 };
1139 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1140 assert!(result.is_err());
1141 assert!(result
1142 .unwrap_err()
1143 .to_string()
1144 .contains("P2SH address requires P2sh witness"));
1145 }
1146
1147 #[test]
1152 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1153 let hash: [u8; 20] = [
1155 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1156 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1157 ];
1158 let address = PlatformAddress::P2pkh(hash);
1159
1160 let encoded = address.to_bech32m_string(Network::Mainnet);
1162
1163 assert_eq!(
1165 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1166 "P2PKH mainnet encoding mismatch"
1167 );
1168
1169 let decoded =
1171 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1172 assert_eq!(decoded, address);
1173 }
1174
1175 #[test]
1176 fn test_bech32m_p2pkh_testnet_roundtrip() {
1177 let hash: [u8; 20] = [
1179 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1180 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1181 ];
1182 let address = PlatformAddress::P2pkh(hash);
1183
1184 let encoded = address.to_bech32m_string(Network::Testnet);
1186
1187 assert_eq!(
1189 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1190 "P2PKH testnet encoding mismatch"
1191 );
1192
1193 let decoded =
1195 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1196 assert_eq!(decoded, address);
1197 }
1198
1199 #[test]
1200 fn test_bech32m_p2sh_mainnet_roundtrip() {
1201 let hash: [u8; 20] = [
1203 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1204 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1205 ];
1206 let address = PlatformAddress::P2sh(hash);
1207
1208 let encoded = address.to_bech32m_string(Network::Mainnet);
1210
1211 assert_eq!(
1213 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1214 "P2SH mainnet encoding mismatch"
1215 );
1216
1217 let decoded =
1219 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1220 assert_eq!(decoded, address);
1221 }
1222
1223 #[test]
1224 fn test_bech32m_p2sh_testnet_roundtrip() {
1225 let hash: [u8; 20] = [
1227 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1228 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1229 ];
1230 let address = PlatformAddress::P2sh(hash);
1231
1232 let encoded = address.to_bech32m_string(Network::Testnet);
1234
1235 assert_eq!(
1237 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1238 "P2SH testnet encoding mismatch"
1239 );
1240
1241 let decoded =
1243 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1244 assert_eq!(decoded, address);
1245 }
1246
1247 #[test]
1248 fn test_bech32m_devnet_uses_testnet_hrp() {
1249 let hash: [u8; 20] = [0xAB; 20];
1250 let address = PlatformAddress::P2pkh(hash);
1251
1252 let encoded = address.to_bech32m_string(Network::Devnet);
1254 assert!(
1255 encoded.starts_with("tdash1"),
1256 "Devnet address should start with 'tdash1', got: {}",
1257 encoded
1258 );
1259 }
1260
1261 #[test]
1262 fn test_bech32m_regtest_uses_testnet_hrp() {
1263 let hash: [u8; 20] = [0xAB; 20];
1264 let address = PlatformAddress::P2pkh(hash);
1265
1266 let encoded = address.to_bech32m_string(Network::Regtest);
1268 assert!(
1269 encoded.starts_with("tdash1"),
1270 "Regtest address should start with 'tdash1', got: {}",
1271 encoded
1272 );
1273 }
1274
1275 #[test]
1276 fn test_bech32m_invalid_hrp_fails() {
1277 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1278 let payload: [u8; 21] = [0x00; 21];
1279 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1280
1281 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1282 assert!(result.is_err());
1283 let err = result.unwrap_err();
1284 assert!(
1285 err.to_string().contains("not a platform address"),
1286 "Error should mention non-platform HRP: {}",
1287 err
1288 );
1289 }
1290
1291 #[test]
1292 fn test_bech32m_invalid_checksum_fails() {
1293 let hash: [u8; 20] = [0xAB; 20];
1295 let address = PlatformAddress::P2pkh(hash);
1296 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1297
1298 let last_char = encoded.pop().unwrap();
1300 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1301 encoded.push(corrupted_char);
1302
1303 let result = PlatformAddress::from_bech32m_string(&encoded);
1304 assert!(result.is_err(), "Should fail with corrupted checksum");
1305 }
1306
1307 #[test]
1308 fn test_bech32m_invalid_type_byte_fails() {
1309 let hrp = Hrp::parse("dash").unwrap();
1312 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1314
1315 let result = PlatformAddress::from_bech32m_string(&encoded);
1316 assert!(result.is_err());
1317 let err = result.unwrap_err();
1318 assert!(
1319 err.to_string().contains("invalid address type"),
1320 "Error should mention invalid type: {}",
1321 err
1322 );
1323 }
1324
1325 #[test]
1326 fn test_bech32m_too_short_fails() {
1327 let hrp = Hrp::parse("dash").unwrap();
1329 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1331
1332 let result = PlatformAddress::from_bech32m_string(&encoded);
1333 assert!(result.is_err());
1334 let err = result.unwrap_err();
1335 assert!(
1336 err.to_string().contains("invalid Platform address length"),
1337 "Error should mention invalid length: {}",
1338 err
1339 );
1340 }
1341
1342 #[test]
1343 fn test_bech32m_from_str_trait() {
1344 let hash: [u8; 20] = [
1346 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1347 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1348 ];
1349 let original = PlatformAddress::P2pkh(hash);
1350
1351 let encoded = original.to_bech32m_string(Network::Testnet);
1353 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1354
1355 assert_eq!(parsed, original);
1356 }
1357
1358 #[test]
1359 fn test_bech32m_case_insensitive() {
1360 let hash: [u8; 20] = [0xAB; 20];
1363 let address = PlatformAddress::P2pkh(hash);
1364
1365 let lowercase = address.to_bech32m_string(Network::Mainnet);
1366 let uppercase = lowercase.to_uppercase();
1367
1368 let decoded_lower = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1370 let decoded_upper = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1371
1372 assert_eq!(decoded_lower, decoded_upper);
1373 assert_eq!(decoded_lower, address);
1374 }
1375
1376 #[test]
1377 fn test_bech32m_all_zeros_p2pkh() {
1378 let address = PlatformAddress::P2pkh([0u8; 20]);
1380 let encoded = address.to_bech32m_string(Network::Mainnet);
1381 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1382 assert_eq!(decoded, address);
1383 }
1384
1385 #[test]
1386 fn test_bech32m_all_ones_p2sh() {
1387 let address = PlatformAddress::P2sh([0xFF; 20]);
1389 let encoded = address.to_bech32m_string(Network::Mainnet);
1390 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1391 assert_eq!(decoded, address);
1392 }
1393
1394 #[test]
1395 fn test_hrp_for_network() {
1396 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1397 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1398 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1399 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1400 }
1401
1402 #[test]
1403 fn test_storage_bytes_format() {
1404 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1408 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1409
1410 let p2pkh_bytes = p2pkh.to_bytes();
1411 let p2sh_bytes = p2sh.to_bytes();
1412
1413 assert_eq!(p2pkh_bytes.len(), 21);
1415 assert_eq!(p2sh_bytes.len(), 21);
1416 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1417 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1418
1419 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1421 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1422 assert_eq!(p2pkh_decoded, p2pkh);
1423 assert_eq!(p2sh_decoded, p2sh);
1424 }
1425
1426 #[test]
1427 fn test_bech32m_uses_different_type_bytes_than_storage() {
1428 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1431 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1432
1433 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1435 assert_eq!(p2sh.to_bytes()[0], 0x01);
1436
1437 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1439 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1440
1441 let p2pkh_decoded = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1442 let p2sh_decoded = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1443
1444 assert_eq!(p2pkh_decoded, p2pkh);
1445 assert_eq!(p2sh_decoded, p2sh);
1446 }
1447
1448 #[test]
1449 fn test_is_mainnet_bech32m_mainnet_is_true() {
1450 let encoded = PlatformAddress::P2pkh([0x11; 20]).to_bech32m_string(Network::Mainnet);
1451 assert!(encoded.starts_with("dash1"));
1452 assert!(PlatformAddress::is_mainnet_bech32m(&encoded).unwrap());
1453 }
1454
1455 #[test]
1456 fn test_is_mainnet_bech32m_all_non_mainnet_networks_are_false() {
1457 for network in [Network::Testnet, Network::Devnet, Network::Regtest] {
1461 let encoded = PlatformAddress::P2pkh([0x22; 20]).to_bech32m_string(network);
1462 assert!(encoded.starts_with("tdash1"), "network {network:?}");
1463 assert!(
1464 !PlatformAddress::is_mainnet_bech32m(&encoded).unwrap(),
1465 "network {network:?} must classify as non-mainnet"
1466 );
1467 }
1468 }
1469
1470 #[test]
1471 fn test_is_mainnet_bech32m_is_case_insensitive() {
1472 let mainnet = PlatformAddress::P2pkh([0x33; 20])
1473 .to_bech32m_string(Network::Mainnet)
1474 .to_uppercase();
1475 assert!(mainnet.starts_with("DASH1"));
1476 assert!(PlatformAddress::is_mainnet_bech32m(&mainnet).unwrap());
1477
1478 let testnet = PlatformAddress::P2pkh([0x44; 20])
1479 .to_bech32m_string(Network::Testnet)
1480 .to_uppercase();
1481 assert!(testnet.starts_with("TDASH1"));
1482 assert!(!PlatformAddress::is_mainnet_bech32m(&testnet).unwrap());
1483 }
1484
1485 #[test]
1486 fn test_is_mainnet_bech32m_non_platform_hrp_errors() {
1487 let err = PlatformAddress::is_mainnet_bech32m("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
1489 .unwrap_err();
1490 assert!(
1491 err.to_string().contains("not a platform address"),
1492 "unexpected error: {err}"
1493 );
1494 }
1495
1496 #[test]
1497 fn test_is_mainnet_bech32m_malformed_data_part_errors() {
1498 assert!(
1502 PlatformAddress::is_mainnet_bech32m("dash1!").is_err(),
1503 "dash1! must error, not return Ok(true)"
1504 );
1505 }
1506
1507 #[test]
1508 fn test_is_mainnet_bech32m_missing_separator_errors() {
1509 let err = PlatformAddress::is_mainnet_bech32m("nodelimiterhere").unwrap_err();
1510 assert!(
1512 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1513 "unexpected error: {err}"
1514 );
1515 }
1516
1517 #[test]
1518 fn test_is_mainnet_bech32m_empty_errors() {
1519 let err = PlatformAddress::is_mainnet_bech32m("").unwrap_err();
1520 assert!(
1521 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1522 "unexpected error: {err}"
1523 );
1524 }
1525}