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