1use crate::address_funds::AddressWitness;
2use crate::address_funds::AddressWitnessVerificationOperations;
3use crate::prelude::AddressNonce;
4#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
5use crate::serialization::JsonConvertible;
6#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
7use crate::serialization::ValueConvertible;
8use crate::ProtocolError;
9use bech32::{Bech32m, Hrp};
10use bincode::{Decode, DecodeUntrusted, Encode};
11use dashcore::address::Payload;
12use dashcore::blockdata::script::ScriptBuf;
13use dashcore::hashes::{sha256d, Hash};
14use dashcore::{
15 sign_message, signer, Address, Network, PrivateKey, PubkeyHash, PublicKey, ScriptHash,
16};
17use platform_serialization_derive::{
18 PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
19};
20#[cfg(feature = "serde-conversion")]
21use serde::{Deserialize, Serialize};
22use std::convert::TryFrom;
23use std::str::FromStr;
24
25pub const ADDRESS_HASH_SIZE: usize = 20;
27
28#[derive(
29 Debug,
30 PartialEq,
31 Eq,
32 Clone,
33 Copy,
34 Hash,
35 Ord,
36 PartialOrd,
37 Encode,
38 Decode,
39 PlatformSerialize,
40 PlatformDeserializeTrusted,
41 PlatformDeserializeUntrusted,
42 DecodeUntrusted,
43)]
44#[platform_serialize(unversioned)]
45pub enum PlatformAddress {
46 P2pkh([u8; 20]),
50 P2sh([u8; 20]),
54}
55
56#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
57impl JsonConvertible for PlatformAddress {}
58
59#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
60impl ValueConvertible for PlatformAddress {}
61
62#[cfg(all(
63 test,
64 feature = "json-conversion",
65 feature = "value-conversion",
66 feature = "serde-conversion"
67))]
68mod json_convertible_tests {
69 use super::*;
70 use platform_value::Value;
71 use serde_json::json;
72
73 #[test]
79 fn json_round_trip_p2pkh() {
80 use crate::serialization::JsonConvertible;
81 let original = PlatformAddress::P2pkh([0xab; 20]);
82 let json = original.to_json().expect("to_json");
83 assert_eq!(json, json!("00abababababababababababababababababababab"));
85 let recovered = PlatformAddress::from_json(json).expect("from_json");
86 assert_eq!(original, recovered);
87 }
88
89 #[test]
90 fn json_round_trip_p2sh() {
91 use crate::serialization::JsonConvertible;
92 let original = PlatformAddress::P2sh([0xcd; 20]);
93 let json = original.to_json().expect("to_json");
94 assert_eq!(json, json!("01cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"));
96 let recovered = PlatformAddress::from_json(json).expect("from_json");
97 assert_eq!(original, recovered);
98 }
99
100 #[test]
101 fn value_round_trip_p2pkh() {
102 use crate::serialization::ValueConvertible;
103 let original = PlatformAddress::P2pkh([0xab; 20]);
104 let value = original.to_object().expect("to_object");
105 let mut expected = vec![0x00];
108 expected.extend_from_slice(&[0xab; 20]);
109 assert_eq!(value, Value::Bytes(expected));
110 let recovered = PlatformAddress::from_object(value).expect("from_object");
111 assert_eq!(original, recovered);
112 }
113
114 #[test]
115 fn value_round_trip_p2sh() {
116 use crate::serialization::ValueConvertible;
117 let original = PlatformAddress::P2sh([0xcd; 20]);
118 let value = original.to_object().expect("to_object");
119 let mut expected = vec![0x01];
120 expected.extend_from_slice(&[0xcd; 20]);
121 assert_eq!(value, Value::Bytes(expected));
122 let recovered = PlatformAddress::from_object(value).expect("from_object");
123 assert_eq!(original, recovered);
124 }
125}
126
127#[cfg(feature = "serde-conversion")]
133impl Serialize for PlatformAddress {
134 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
135 where
136 S: serde::Serializer,
137 {
138 let bytes = self.to_bytes();
139 if serializer.is_human_readable() {
140 serializer.serialize_str(&hex::encode(&bytes))
141 } else {
142 serializer.serialize_bytes(&bytes)
143 }
144 }
145}
146
147#[cfg(feature = "serde-conversion")]
148impl<'de> Deserialize<'de> for PlatformAddress {
149 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150 where
151 D: serde::Deserializer<'de>,
152 {
153 use serde::de::{self, Visitor};
154 use std::fmt;
155
156 const PLATFORM_ADDRESS_BYTE_LEN: usize = 21;
158
159 struct PlatformAddressVisitor;
160
161 impl<'de> Visitor<'de> for PlatformAddressVisitor {
162 type Value = PlatformAddress;
163
164 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
165 formatter.write_str("PlatformAddress as 21 bytes or hex string")
166 }
167
168 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
169 let bytes =
170 hex::decode(value).map_err(|err| E::custom(format!("invalid hex: {}", err)))?;
171 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
172 return Err(E::invalid_length(bytes.len(), &self));
173 }
174 PlatformAddress::from_bytes(&bytes).map_err(|err| E::custom(err.to_string()))
175 }
176
177 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
178 self.visit_str(&value)
179 }
180
181 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
182 if value.len() != PLATFORM_ADDRESS_BYTE_LEN {
183 return Err(E::invalid_length(value.len(), &self));
184 }
185 PlatformAddress::from_bytes(value).map_err(|err| E::custom(err.to_string()))
186 }
187
188 fn visit_byte_buf<E: de::Error>(self, value: Vec<u8>) -> Result<Self::Value, E> {
189 self.visit_bytes(&value)
190 }
191
192 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
193 where
194 A: de::SeqAccess<'de>,
195 {
196 let mut bytes = Vec::with_capacity(PLATFORM_ADDRESS_BYTE_LEN);
199 while let Some(byte) = seq.next_element::<u8>()? {
200 if bytes.len() >= PLATFORM_ADDRESS_BYTE_LEN {
201 return Err(de::Error::invalid_length(
202 bytes.len() + 1,
203 &"at most 21 bytes",
204 ));
205 }
206 bytes.push(byte);
207 }
208 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
209 return Err(de::Error::invalid_length(bytes.len(), &self));
210 }
211 PlatformAddress::from_bytes(&bytes)
212 .map_err(|err| de::Error::custom(err.to_string()))
213 }
214 }
215
216 if deserializer.is_human_readable() {
220 deserializer.deserialize_str(PlatformAddressVisitor)
221 } else {
222 deserializer.deserialize_bytes(PlatformAddressVisitor)
223 }
224 }
225}
226
227impl TryFrom<Address> for PlatformAddress {
228 type Error = ProtocolError;
229
230 fn try_from(address: Address) -> Result<Self, Self::Error> {
231 match address.payload() {
232 Payload::PubkeyHash(hash) => Ok(PlatformAddress::P2pkh(*hash.as_ref())),
233 Payload::ScriptHash(hash) => Ok(PlatformAddress::P2sh(*hash.as_ref())),
234 _ => Err(ProtocolError::DecodingError(
235 "unsupported address type for PlatformAddress: only P2PKH and P2SH are supported"
236 .to_string(),
237 )),
238 }
239 }
240}
241
242impl From<&PrivateKey> for PlatformAddress {
243 fn from(private_key: &PrivateKey) -> Self {
248 let pubkey_hash = private_key.public_key().pubkey_hash();
249 PlatformAddress::P2pkh(*pubkey_hash.as_byte_array())
250 }
251}
252
253impl Default for PlatformAddress {
254 fn default() -> Self {
255 PlatformAddress::P2pkh([0u8; 20])
256 }
257}
258
259pub const PLATFORM_HRP_MAINNET: &str = "dash";
261pub const PLATFORM_HRP_TESTNET: &str = "tdash";
263
264pub(crate) fn classify_platform_hrp(hrp: &str) -> Result<bool, ProtocolError> {
269 match hrp {
270 PLATFORM_HRP_MAINNET => Ok(true),
271 PLATFORM_HRP_TESTNET => Ok(false),
272 other => Err(ProtocolError::DecodingError(format!(
273 "not a platform address: HRP '{other}' is neither \
274 '{PLATFORM_HRP_MAINNET}' nor '{PLATFORM_HRP_TESTNET}'"
275 ))),
276 }
277}
278
279impl PlatformAddress {
280 pub const P2PKH_TYPE: u8 = 0xb0;
282 pub const P2SH_TYPE: u8 = 0x80;
284
285 pub fn hrp_for_network(network: Network) -> &'static str {
291 match network {
292 Network::Mainnet => PLATFORM_HRP_MAINNET,
293 Network::Testnet | Network::Devnet | Network::Regtest => PLATFORM_HRP_TESTNET,
294 }
295 }
296
297 pub fn to_bech32m_string(&self, network: Network) -> String {
314 let hrp_str = Self::hrp_for_network(network);
315 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
316
317 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
320 match self {
321 PlatformAddress::P2pkh(hash) => {
322 payload.push(Self::P2PKH_TYPE);
323 payload.extend_from_slice(hash);
324 }
325 PlatformAddress::P2sh(hash) => {
326 payload.push(Self::P2SH_TYPE);
327 payload.extend_from_slice(hash);
328 }
329 }
330
331 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
333 }
334
335 pub fn from_bech32m_string(s: &str) -> Result<Self, ProtocolError> {
346 let (hrp, data) =
347 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
348
349 classify_platform_hrp(&hrp.as_str().to_ascii_lowercase())?;
350
351 if data.len() != 1 + ADDRESS_HASH_SIZE {
353 return Err(ProtocolError::DecodingError(format!(
354 "invalid Platform address length: expected {} bytes, got {}",
355 1 + ADDRESS_HASH_SIZE,
356 data.len()
357 )));
358 }
359
360 let address_type = data[0];
362 let hash: [u8; 20] = data[1..21]
363 .try_into()
364 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
365
366 let address = match address_type {
367 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
368 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
369 _ => Err(ProtocolError::DecodingError(format!(
370 "invalid address type: 0x{:02x}",
371 address_type
372 ))),
373 }?;
374
375 Ok(address)
376 }
377
378 pub fn is_mainnet_bech32m(s: &str) -> Result<bool, ProtocolError> {
389 let (hrp, _) =
390 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{e}")))?;
391 classify_platform_hrp(&hrp.to_lowercase())
392 }
393
394 pub fn to_address_with_network(&self, network: Network) -> Address {
396 match self {
397 PlatformAddress::P2pkh(hash) => Address::new(
398 network,
399 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
400 ),
401 PlatformAddress::P2sh(hash) => Address::new(
402 network,
403 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
404 ),
405 }
406 }
407
408 pub fn to_bytes(&self) -> Vec<u8> {
414 bincode::encode_to_vec(self, bincode::config::standard())
415 .expect("PlatformAddress serialization cannot fail")
416 }
417
418 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
421 use base64::engine::general_purpose::STANDARD;
422 use base64::Engine;
423
424 let mut bytes = self.to_bytes();
425 bytes.extend_from_slice(&nonce.to_be_bytes());
426
427 STANDARD.encode(bytes)
428 }
429
430 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
435 let (address, _): (Self, usize) =
436 bincode::decode_from_slice_untrusted(bytes, bincode::config::standard()).map_err(
437 |e| ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e)),
438 )?;
439 Ok(address)
440 }
441
442 pub fn hash(&self) -> &[u8; 20] {
444 match self {
445 PlatformAddress::P2pkh(hash) => hash,
446 PlatformAddress::P2sh(hash) => hash,
447 }
448 }
449
450 pub fn is_p2pkh(&self) -> bool {
452 matches!(self, PlatformAddress::P2pkh(_))
453 }
454
455 pub fn is_p2sh(&self) -> bool {
457 matches!(self, PlatformAddress::P2sh(_))
458 }
459
460 pub fn verify_bytes_against_witness(
480 &self,
481 witness: &AddressWitness,
482 signable_bytes: &[u8],
483 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
484 match (self, witness) {
485 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
486 let data_hash = dashcore::signer::double_sha(signable_bytes);
494 signer::verify_hash_signature(&data_hash, signature.as_slice(), pubkey_hash)
495 .map_err(|e| {
496 ProtocolError::AddressWitnessError(format!(
497 "P2PKH signature verification failed: {}",
498 e
499 ))
500 })?;
501
502 Ok(AddressWitnessVerificationOperations::for_p2pkh(
503 signable_bytes.len(),
504 ))
505 }
506 (
507 PlatformAddress::P2sh(script_hash),
508 AddressWitness::P2sh {
509 signatures,
510 redeem_script,
511 },
512 ) => {
513 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
515 let computed_hash = script.script_hash();
516 if computed_hash.as_byte_array() != script_hash {
517 return Err(ProtocolError::AddressWitnessError(format!(
518 "Script hash {} does not match address hash {}",
519 hex::encode(computed_hash.as_byte_array()),
520 hex::encode(script_hash)
521 )));
522 }
523
524 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
527
528 let valid_signatures: Vec<_> = signatures
530 .iter()
531 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
532 .collect();
533
534 if valid_signatures.len() < threshold {
535 return Err(ProtocolError::AddressWitnessError(format!(
536 "Not enough signatures: got {}, need {}",
537 valid_signatures.len(),
538 threshold
539 )));
540 }
541
542 let mut sig_idx = 0;
545 let mut pubkey_idx = 0;
546 let mut matched = 0;
547 let mut signature_verifications: u16 = 0;
548
549 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
550
551 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
552 signature_verifications += 1;
553
554 let sig = sign_message::from_slice(valid_signatures[sig_idx].as_slice())
555 .and_then(|signature| signature.signature().map_err(Into::into))
556 .map_err(|e| {
557 ProtocolError::AddressWitnessError(format!(
558 "Invalid signature format: {}",
559 e
560 ))
561 })?;
562
563 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
564 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
565 })?;
566
567 if pub_key.inner.verify(&signable_bytes_hash, sig).is_ok() {
568 matched += 1;
569 sig_idx += 1;
570 }
571 pubkey_idx += 1;
572 }
573
574 if matched >= threshold {
575 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
576 signature_verifications,
577 signable_bytes.len(),
578 ))
579 } else {
580 Err(ProtocolError::AddressWitnessError(format!(
581 "Not enough valid signatures: verified {}, need {}",
582 matched, threshold
583 )))
584 }
585 }
586 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
587 Err(ProtocolError::AddressWitnessError(
588 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
589 ))
590 }
591 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
592 Err(ProtocolError::AddressWitnessError(
593 "P2SH address requires P2sh witness, got P2pkh".to_string(),
594 ))
595 }
596 }
597 }
598
599 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
614 use dashcore::blockdata::opcodes::all::*;
615
616 let mut instructions = script.instructions();
617 let mut pubkeys = Vec::new();
618
619 let threshold = match instructions.next() {
621 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
622 let byte = op.to_u8();
623 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
624 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
625 } else {
626 return Err(ProtocolError::AddressWitnessError(format!(
627 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
628 First opcode was 0x{:02x}, expected OP_1 through OP_16",
629 byte
630 )));
631 }
632 }
633 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
634 return Err(ProtocolError::AddressWitnessError(
635 "Unsupported P2SH script type: only standard multisig is supported. \
636 Script starts with a data push instead of OP_M threshold."
637 .to_string(),
638 ))
639 }
640 Some(Err(e)) => {
641 return Err(ProtocolError::AddressWitnessError(format!(
642 "Error parsing P2SH script: {:?}",
643 e
644 )))
645 }
646 None => {
647 return Err(ProtocolError::AddressWitnessError(
648 "Empty P2SH redeem script".to_string(),
649 ))
650 }
651 };
652
653 loop {
655 match instructions.next() {
656 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
657 let len = bytes.len();
659 if len != 33 {
660 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
661 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
662 len,
663 ),
664 ));
665 }
666 pubkeys.push(bytes.as_bytes().to_vec());
667 }
668 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
669 let byte = op.to_u8();
670 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
671 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
673 if pubkeys.len() != n {
674 return Err(ProtocolError::AddressWitnessError(format!(
675 "Multisig script declares {} keys but contains {}",
676 n,
677 pubkeys.len()
678 )));
679 }
680 break;
681 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
682 return Err(ProtocolError::AddressWitnessError(
684 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
685 ));
686 } else {
687 return Err(ProtocolError::AddressWitnessError(format!(
688 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
689 byte
690 )));
691 }
692 }
693 Some(Err(e)) => {
694 return Err(ProtocolError::AddressWitnessError(format!(
695 "Error parsing multisig script: {:?}",
696 e
697 )))
698 }
699 None => {
700 return Err(ProtocolError::AddressWitnessError(
701 "Incomplete multisig script: unexpected end before OP_N".to_string(),
702 ))
703 }
704 }
705 }
706
707 if threshold > pubkeys.len() {
709 return Err(ProtocolError::AddressWitnessError(format!(
710 "Invalid multisig: threshold {} exceeds number of keys {}",
711 threshold,
712 pubkeys.len()
713 )));
714 }
715
716 match instructions.next() {
718 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
719 if op == OP_CHECKMULTISIG {
720 if instructions.next().is_some() {
722 return Err(ProtocolError::AddressWitnessError(
723 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
724 ));
725 }
726 Ok((threshold, pubkeys))
727 } else if op == OP_CHECKMULTISIGVERIFY {
728 Err(ProtocolError::AddressWitnessError(
729 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
730 .to_string(),
731 ))
732 } else {
733 Err(ProtocolError::AddressWitnessError(format!(
734 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
735 op.to_u8()
736 )))
737 }
738 }
739 _ => Err(ProtocolError::AddressWitnessError(
740 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
741 )),
742 }
743 }
744}
745
746impl std::fmt::Display for PlatformAddress {
747 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
748 match self {
749 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
750 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
751 }
752 }
753}
754
755#[derive(Debug, Clone, PartialEq, Eq)]
757pub struct PlatformAddressParseError(pub String);
758
759impl std::fmt::Display for PlatformAddressParseError {
760 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
761 write!(f, "{}", self.0)
762 }
763}
764
765impl std::error::Error for PlatformAddressParseError {}
766
767impl FromStr for PlatformAddress {
768 type Err = PlatformAddressParseError;
769
770 fn from_str(s: &str) -> Result<Self, Self::Err> {
779 Self::from_bech32m_string(s).map_err(|e| PlatformAddressParseError(e.to_string()))
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use dashcore::blockdata::opcodes::all::*;
787 use dashcore::hashes::Hash;
788 use dashcore::PublicKey;
789 use platform_value::BinaryData;
790 use secp256k1::{PublicKey as RawPublicKey, SecretKey as RawSecretKey};
791
792 #[test]
798 fn hrp_is_shared_across_all_non_mainnet_networks() {
799 let testnet = PlatformAddress::hrp_for_network(Network::Testnet);
800 assert_eq!(testnet, PLATFORM_HRP_TESTNET);
801 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), testnet);
802 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), testnet);
803 assert_ne!(
804 PlatformAddress::hrp_for_network(Network::Mainnet),
805 testnet,
806 "mainnet must use a distinct HRP"
807 );
808 }
809
810 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
812 let secret_key = RawSecretKey::from_secret_bytes(seed).expect("valid secret key");
813 let raw_public_key = RawPublicKey::from_secret_key(&secret_key);
814 let public_key = PublicKey::new(raw_public_key);
815 (secret_key, public_key)
816 }
817
818 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
820 dashcore::signer::sign(data, secret_key.as_secret_bytes())
821 .expect("signing should succeed")
822 .to_vec()
823 }
824
825 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
827 let mut script = Vec::new();
828
829 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
831
832 for pubkey in pubkeys {
834 let bytes = pubkey.to_bytes();
835 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
837 }
838
839 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
841
842 script.push(OP_CHECKMULTISIG.to_u8());
844
845 script
846 }
847
848 #[test]
849 fn test_platform_address_from_private_key() {
850 let seed = [1u8; 32];
852 let (secret_key, public_key) = create_keypair(seed);
853
854 let private_key =
856 PrivateKey::from_byte_array(secret_key.as_secret_bytes(), Network::Testnet)
857 .expect("valid test secret key");
858
859 let address_from_private = PlatformAddress::from(&private_key);
861
862 let pubkey_hash = public_key.pubkey_hash();
865 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
866
867 assert_eq!(
869 address_from_private, address_from_pubkey,
870 "Address derived from private key should match Hash160(compressed_pubkey)"
871 );
872
873 assert!(address_from_private.is_p2pkh());
875 }
876
877 #[test]
878 fn test_p2pkh_verify_signature_success() {
879 let seed = [1u8; 32];
881 let (secret_key, public_key) = create_keypair(seed);
882
883 let pubkey_hash = public_key.pubkey_hash();
885 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
886
887 let signable_bytes = b"test message for P2PKH verification";
889
890 let signature = sign_data(signable_bytes, &secret_key);
892
893 let witness = AddressWitness::P2pkh {
895 signature: BinaryData::new(signature),
896 };
897
898 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
900 assert!(
901 result.is_ok(),
902 "P2PKH verification should succeed: {:?}",
903 result
904 );
905 }
906
907 #[test]
908 fn test_p2pkh_verify_wrong_signature_fails() {
909 let seed = [1u8; 32];
911 let (secret_key, public_key) = create_keypair(seed);
912
913 let pubkey_hash = public_key.pubkey_hash();
915 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
916
917 let sign_bytes = b"original message";
919 let verify_bytes = b"different message";
920 let signature = sign_data(sign_bytes, &secret_key);
921
922 let witness = AddressWitness::P2pkh {
924 signature: BinaryData::new(signature),
925 };
926
927 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
929 assert!(
930 result.is_err(),
931 "P2PKH verification should fail with wrong data"
932 );
933 }
934
935 #[test]
936 fn test_p2pkh_verify_wrong_key_fails() {
937 let seed1 = [1u8; 32];
939 let seed2 = [2u8; 32];
940 let (_secret_key1, public_key1) = create_keypair(seed1);
941 let (secret_key2, _public_key2) = create_keypair(seed2);
942
943 let pubkey_hash = public_key1.pubkey_hash();
945 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
946
947 let signable_bytes = b"test message";
949 let signature = sign_data(signable_bytes, &secret_key2);
950
951 let witness = AddressWitness::P2pkh {
953 signature: BinaryData::new(signature),
954 };
955
956 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
958 assert!(
959 result.is_err(),
960 "P2PKH verification should fail when signed with wrong key"
961 );
962 }
963
964 #[test]
969 fn test_p2sh_2_of_3_multisig_verify_success() {
970 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
972 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
973 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
974
975 let redeem_script = create_multisig_script(2, &pubkeys);
977
978 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
980 let script_hash = script_buf.script_hash();
981 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
982
983 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
985
986 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
988 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
989
990 let witness = AddressWitness::P2sh {
993 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
994 redeem_script: BinaryData::new(redeem_script),
995 };
996
997 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
999 assert!(
1000 result.is_ok(),
1001 "P2SH 2-of-3 multisig verification should succeed: {:?}",
1002 result
1003 );
1004 }
1005
1006 #[test]
1007 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
1008 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1010 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1011 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1012
1013 let redeem_script = create_multisig_script(2, &pubkeys);
1015
1016 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1018 let script_hash = script_buf.script_hash();
1019 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1020
1021 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
1023
1024 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1026 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
1027
1028 let witness = AddressWitness::P2sh {
1030 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
1031 redeem_script: BinaryData::new(redeem_script),
1032 };
1033
1034 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1036 assert!(
1037 result.is_ok(),
1038 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
1039 result
1040 );
1041 }
1042
1043 #[test]
1044 fn test_p2sh_not_enough_signatures_fails() {
1045 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1047 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1048 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1049
1050 let redeem_script = create_multisig_script(2, &pubkeys);
1052
1053 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1055 let script_hash = script_buf.script_hash();
1056 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1057
1058 let signable_bytes = b"test message";
1060
1061 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1063
1064 let witness = AddressWitness::P2sh {
1066 signatures: vec![BinaryData::new(sig0)],
1067 redeem_script: BinaryData::new(redeem_script),
1068 };
1069
1070 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1072 assert!(
1073 result.is_err(),
1074 "P2SH should fail with only 1 signature when 2 required"
1075 );
1076 assert!(
1077 result.unwrap_err().to_string().contains("Not enough"),
1078 "Error should mention not enough signatures"
1079 );
1080 }
1081
1082 #[test]
1083 fn test_p2sh_wrong_script_hash_fails() {
1084 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1086 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1087 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1088
1089 let redeem_script = create_multisig_script(2, &pubkeys);
1091
1092 let wrong_hash = [0xABu8; 20];
1094 let address = PlatformAddress::P2sh(wrong_hash);
1095
1096 let signable_bytes = b"test message";
1098
1099 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1101 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1102
1103 let witness = AddressWitness::P2sh {
1105 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
1106 redeem_script: BinaryData::new(redeem_script),
1107 };
1108
1109 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1111 assert!(
1112 result.is_err(),
1113 "P2SH should fail when script hash doesn't match address"
1114 );
1115 assert!(
1116 result
1117 .unwrap_err()
1118 .to_string()
1119 .contains("does not match address hash"),
1120 "Error should mention hash mismatch"
1121 );
1122 }
1123
1124 #[test]
1125 fn test_p2pkh_and_p2sh_together() {
1126 let p2pkh_seed = [10u8; 32];
1130 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
1131 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
1132 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
1133
1134 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
1136 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
1137 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
1138 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
1139 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1140 let script_hash = script_buf.script_hash();
1141 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1142
1143 let signable_bytes = b"combined transaction data to redeem both outputs";
1145
1146 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
1148 let p2pkh_witness = AddressWitness::P2pkh {
1149 signature: BinaryData::new(p2pkh_sig),
1150 };
1151 let p2pkh_result =
1152 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1153 assert!(
1154 p2pkh_result.is_ok(),
1155 "P2PKH redemption should succeed: {:?}",
1156 p2pkh_result
1157 );
1158
1159 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
1161 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
1162 let p2sh_witness = AddressWitness::P2sh {
1163 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
1164 redeem_script: BinaryData::new(redeem_script),
1165 };
1166 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1167 assert!(
1168 p2sh_result.is_ok(),
1169 "P2SH redemption should succeed: {:?}",
1170 p2sh_result
1171 );
1172
1173 }
1175
1176 #[test]
1177 fn test_witness_type_mismatch() {
1178 let seed = [1u8; 32];
1180 let (_, public_key) = create_keypair(seed);
1181 let pubkey_hash = public_key.pubkey_hash();
1182 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
1183
1184 let p2sh_hash = [0xABu8; 20];
1186 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
1187
1188 let signable_bytes = b"test data";
1189
1190 let p2sh_witness = AddressWitness::P2sh {
1192 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
1193 redeem_script: BinaryData::new(vec![0x52]),
1194 };
1195 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1196 assert!(result.is_err());
1197 assert!(result
1198 .unwrap_err()
1199 .to_string()
1200 .contains("P2PKH address requires P2pkh witness"));
1201
1202 let p2pkh_witness = AddressWitness::P2pkh {
1204 signature: BinaryData::new(vec![0x30, 0x44]),
1205 };
1206 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1207 assert!(result.is_err());
1208 assert!(result
1209 .unwrap_err()
1210 .to_string()
1211 .contains("P2SH address requires P2sh witness"));
1212 }
1213
1214 #[test]
1219 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1220 let hash: [u8; 20] = [
1222 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1223 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1224 ];
1225 let address = PlatformAddress::P2pkh(hash);
1226
1227 let encoded = address.to_bech32m_string(Network::Mainnet);
1229
1230 assert_eq!(
1232 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1233 "P2PKH mainnet encoding mismatch"
1234 );
1235
1236 let decoded =
1238 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1239 assert_eq!(decoded, address);
1240 }
1241
1242 #[test]
1243 fn test_bech32m_p2pkh_testnet_roundtrip() {
1244 let hash: [u8; 20] = [
1246 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1247 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1248 ];
1249 let address = PlatformAddress::P2pkh(hash);
1250
1251 let encoded = address.to_bech32m_string(Network::Testnet);
1253
1254 assert_eq!(
1256 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1257 "P2PKH testnet encoding mismatch"
1258 );
1259
1260 let decoded =
1262 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1263 assert_eq!(decoded, address);
1264 }
1265
1266 #[test]
1267 fn test_bech32m_p2sh_mainnet_roundtrip() {
1268 let hash: [u8; 20] = [
1270 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1271 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1272 ];
1273 let address = PlatformAddress::P2sh(hash);
1274
1275 let encoded = address.to_bech32m_string(Network::Mainnet);
1277
1278 assert_eq!(
1280 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1281 "P2SH mainnet encoding mismatch"
1282 );
1283
1284 let decoded =
1286 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1287 assert_eq!(decoded, address);
1288 }
1289
1290 #[test]
1291 fn test_bech32m_p2sh_testnet_roundtrip() {
1292 let hash: [u8; 20] = [
1294 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1295 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1296 ];
1297 let address = PlatformAddress::P2sh(hash);
1298
1299 let encoded = address.to_bech32m_string(Network::Testnet);
1301
1302 assert_eq!(
1304 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1305 "P2SH testnet encoding mismatch"
1306 );
1307
1308 let decoded =
1310 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1311 assert_eq!(decoded, address);
1312 }
1313
1314 #[test]
1315 fn test_bech32m_devnet_uses_testnet_hrp() {
1316 let hash: [u8; 20] = [0xAB; 20];
1317 let address = PlatformAddress::P2pkh(hash);
1318
1319 let encoded = address.to_bech32m_string(Network::Devnet);
1321 assert!(
1322 encoded.starts_with("tdash1"),
1323 "Devnet address should start with 'tdash1', got: {}",
1324 encoded
1325 );
1326 }
1327
1328 #[test]
1329 fn test_bech32m_regtest_uses_testnet_hrp() {
1330 let hash: [u8; 20] = [0xAB; 20];
1331 let address = PlatformAddress::P2pkh(hash);
1332
1333 let encoded = address.to_bech32m_string(Network::Regtest);
1335 assert!(
1336 encoded.starts_with("tdash1"),
1337 "Regtest address should start with 'tdash1', got: {}",
1338 encoded
1339 );
1340 }
1341
1342 #[test]
1343 fn test_bech32m_invalid_hrp_fails() {
1344 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1345 let payload: [u8; 21] = [0x00; 21];
1346 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1347
1348 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1349 assert!(result.is_err());
1350 let err = result.unwrap_err();
1351 assert!(
1352 err.to_string().contains("not a platform address"),
1353 "Error should mention non-platform HRP: {}",
1354 err
1355 );
1356 }
1357
1358 #[test]
1359 fn test_bech32m_invalid_checksum_fails() {
1360 let hash: [u8; 20] = [0xAB; 20];
1362 let address = PlatformAddress::P2pkh(hash);
1363 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1364
1365 let last_char = encoded.pop().unwrap();
1367 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1368 encoded.push(corrupted_char);
1369
1370 let result = PlatformAddress::from_bech32m_string(&encoded);
1371 assert!(result.is_err(), "Should fail with corrupted checksum");
1372 }
1373
1374 #[test]
1375 fn test_bech32m_invalid_type_byte_fails() {
1376 let hrp = Hrp::parse("dash").unwrap();
1379 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1381
1382 let result = PlatformAddress::from_bech32m_string(&encoded);
1383 assert!(result.is_err());
1384 let err = result.unwrap_err();
1385 assert!(
1386 err.to_string().contains("invalid address type"),
1387 "Error should mention invalid type: {}",
1388 err
1389 );
1390 }
1391
1392 #[test]
1393 fn test_bech32m_too_short_fails() {
1394 let hrp = Hrp::parse("dash").unwrap();
1396 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1398
1399 let result = PlatformAddress::from_bech32m_string(&encoded);
1400 assert!(result.is_err());
1401 let err = result.unwrap_err();
1402 assert!(
1403 err.to_string().contains("invalid Platform address length"),
1404 "Error should mention invalid length: {}",
1405 err
1406 );
1407 }
1408
1409 #[test]
1410 fn test_bech32m_from_str_trait() {
1411 let hash: [u8; 20] = [
1413 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1414 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1415 ];
1416 let original = PlatformAddress::P2pkh(hash);
1417
1418 let encoded = original.to_bech32m_string(Network::Testnet);
1420 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1421
1422 assert_eq!(parsed, original);
1423 }
1424
1425 #[test]
1426 fn test_bech32m_case_insensitive() {
1427 let hash: [u8; 20] = [0xAB; 20];
1430 let address = PlatformAddress::P2pkh(hash);
1431
1432 let lowercase = address.to_bech32m_string(Network::Mainnet);
1433 let uppercase = lowercase.to_uppercase();
1434
1435 let decoded_lower = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1437 let decoded_upper = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1438
1439 assert_eq!(decoded_lower, decoded_upper);
1440 assert_eq!(decoded_lower, address);
1441 }
1442
1443 #[test]
1444 fn test_bech32m_all_zeros_p2pkh() {
1445 let address = PlatformAddress::P2pkh([0u8; 20]);
1447 let encoded = address.to_bech32m_string(Network::Mainnet);
1448 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1449 assert_eq!(decoded, address);
1450 }
1451
1452 #[test]
1453 fn test_bech32m_all_ones_p2sh() {
1454 let address = PlatformAddress::P2sh([0xFF; 20]);
1456 let encoded = address.to_bech32m_string(Network::Mainnet);
1457 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1458 assert_eq!(decoded, address);
1459 }
1460
1461 #[test]
1462 fn test_hrp_for_network() {
1463 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1464 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1465 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1466 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1467 }
1468
1469 #[test]
1470 fn test_storage_bytes_format() {
1471 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1475 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1476
1477 let p2pkh_bytes = p2pkh.to_bytes();
1478 let p2sh_bytes = p2sh.to_bytes();
1479
1480 assert_eq!(p2pkh_bytes.len(), 21);
1482 assert_eq!(p2sh_bytes.len(), 21);
1483 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1484 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1485
1486 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1488 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1489 assert_eq!(p2pkh_decoded, p2pkh);
1490 assert_eq!(p2sh_decoded, p2sh);
1491 }
1492
1493 #[test]
1494 fn test_bech32m_uses_different_type_bytes_than_storage() {
1495 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1498 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1499
1500 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1502 assert_eq!(p2sh.to_bytes()[0], 0x01);
1503
1504 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1506 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1507
1508 let p2pkh_decoded = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1509 let p2sh_decoded = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1510
1511 assert_eq!(p2pkh_decoded, p2pkh);
1512 assert_eq!(p2sh_decoded, p2sh);
1513 }
1514
1515 #[test]
1516 fn test_is_mainnet_bech32m_mainnet_is_true() {
1517 let encoded = PlatformAddress::P2pkh([0x11; 20]).to_bech32m_string(Network::Mainnet);
1518 assert!(encoded.starts_with("dash1"));
1519 assert!(PlatformAddress::is_mainnet_bech32m(&encoded).unwrap());
1520 }
1521
1522 #[test]
1523 fn test_is_mainnet_bech32m_all_non_mainnet_networks_are_false() {
1524 for network in [Network::Testnet, Network::Devnet, Network::Regtest] {
1528 let encoded = PlatformAddress::P2pkh([0x22; 20]).to_bech32m_string(network);
1529 assert!(encoded.starts_with("tdash1"), "network {network:?}");
1530 assert!(
1531 !PlatformAddress::is_mainnet_bech32m(&encoded).unwrap(),
1532 "network {network:?} must classify as non-mainnet"
1533 );
1534 }
1535 }
1536
1537 #[test]
1538 fn test_is_mainnet_bech32m_is_case_insensitive() {
1539 let mainnet = PlatformAddress::P2pkh([0x33; 20])
1540 .to_bech32m_string(Network::Mainnet)
1541 .to_uppercase();
1542 assert!(mainnet.starts_with("DASH1"));
1543 assert!(PlatformAddress::is_mainnet_bech32m(&mainnet).unwrap());
1544
1545 let testnet = PlatformAddress::P2pkh([0x44; 20])
1546 .to_bech32m_string(Network::Testnet)
1547 .to_uppercase();
1548 assert!(testnet.starts_with("TDASH1"));
1549 assert!(!PlatformAddress::is_mainnet_bech32m(&testnet).unwrap());
1550 }
1551
1552 #[test]
1553 fn test_is_mainnet_bech32m_non_platform_hrp_errors() {
1554 let err = PlatformAddress::is_mainnet_bech32m("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
1556 .unwrap_err();
1557 assert!(
1558 err.to_string().contains("not a platform address"),
1559 "unexpected error: {err}"
1560 );
1561 }
1562
1563 #[test]
1564 fn test_is_mainnet_bech32m_malformed_data_part_errors() {
1565 assert!(
1569 PlatformAddress::is_mainnet_bech32m("dash1!").is_err(),
1570 "dash1! must error, not return Ok(true)"
1571 );
1572 }
1573
1574 #[test]
1575 fn test_is_mainnet_bech32m_missing_separator_errors() {
1576 let err = PlatformAddress::is_mainnet_bech32m("nodelimiterhere").unwrap_err();
1577 assert!(
1579 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1580 "unexpected error: {err}"
1581 );
1582 }
1583
1584 #[test]
1585 fn test_is_mainnet_bech32m_empty_errors() {
1586 let err = PlatformAddress::is_mainnet_bech32m("").unwrap_err();
1587 assert!(
1588 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1589 "unexpected error: {err}"
1590 );
1591 }
1592}