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::key::Secp256k1;
15use dashcore::secp256k1::ecdsa::RecoverableSignature;
16use dashcore::secp256k1::Message;
17use dashcore::signer::CompactSignature;
18use dashcore::{Address, Network, PrivateKey, PubkeyHash, PublicKey, ScriptHash};
19use platform_serialization_derive::{
20 PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize,
21};
22#[cfg(feature = "serde-conversion")]
23use serde::{Deserialize, Serialize};
24use std::convert::TryFrom;
25use std::str::FromStr;
26
27pub const ADDRESS_HASH_SIZE: usize = 20;
29
30#[derive(
31 Debug,
32 PartialEq,
33 Eq,
34 Clone,
35 Copy,
36 Hash,
37 Ord,
38 PartialOrd,
39 Encode,
40 Decode,
41 PlatformSerialize,
42 PlatformDeserializeTrusted,
43 PlatformDeserializeUntrusted,
44 DecodeUntrusted,
45)]
46#[platform_serialize(unversioned)]
47pub enum PlatformAddress {
48 P2pkh([u8; 20]),
52 P2sh([u8; 20]),
56}
57
58#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
59impl JsonConvertible for PlatformAddress {}
60
61#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
62impl ValueConvertible for PlatformAddress {}
63
64#[cfg(all(
65 test,
66 feature = "json-conversion",
67 feature = "value-conversion",
68 feature = "serde-conversion"
69))]
70mod json_convertible_tests {
71 use super::*;
72 use platform_value::Value;
73 use serde_json::json;
74
75 #[test]
81 fn json_round_trip_p2pkh() {
82 use crate::serialization::JsonConvertible;
83 let original = PlatformAddress::P2pkh([0xab; 20]);
84 let json = original.to_json().expect("to_json");
85 assert_eq!(json, json!("00abababababababababababababababababababab"));
87 let recovered = PlatformAddress::from_json(json).expect("from_json");
88 assert_eq!(original, recovered);
89 }
90
91 #[test]
92 fn json_round_trip_p2sh() {
93 use crate::serialization::JsonConvertible;
94 let original = PlatformAddress::P2sh([0xcd; 20]);
95 let json = original.to_json().expect("to_json");
96 assert_eq!(json, json!("01cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd"));
98 let recovered = PlatformAddress::from_json(json).expect("from_json");
99 assert_eq!(original, recovered);
100 }
101
102 #[test]
103 fn value_round_trip_p2pkh() {
104 use crate::serialization::ValueConvertible;
105 let original = PlatformAddress::P2pkh([0xab; 20]);
106 let value = original.to_object().expect("to_object");
107 let mut expected = vec![0x00];
110 expected.extend_from_slice(&[0xab; 20]);
111 assert_eq!(value, Value::Bytes(expected));
112 let recovered = PlatformAddress::from_object(value).expect("from_object");
113 assert_eq!(original, recovered);
114 }
115
116 #[test]
117 fn value_round_trip_p2sh() {
118 use crate::serialization::ValueConvertible;
119 let original = PlatformAddress::P2sh([0xcd; 20]);
120 let value = original.to_object().expect("to_object");
121 let mut expected = vec![0x01];
122 expected.extend_from_slice(&[0xcd; 20]);
123 assert_eq!(value, Value::Bytes(expected));
124 let recovered = PlatformAddress::from_object(value).expect("from_object");
125 assert_eq!(original, recovered);
126 }
127}
128
129#[cfg(feature = "serde-conversion")]
135impl Serialize for PlatformAddress {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: serde::Serializer,
139 {
140 let bytes = self.to_bytes();
141 if serializer.is_human_readable() {
142 serializer.serialize_str(&hex::encode(&bytes))
143 } else {
144 serializer.serialize_bytes(&bytes)
145 }
146 }
147}
148
149#[cfg(feature = "serde-conversion")]
150impl<'de> Deserialize<'de> for PlatformAddress {
151 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
152 where
153 D: serde::Deserializer<'de>,
154 {
155 use serde::de::{self, Visitor};
156 use std::fmt;
157
158 const PLATFORM_ADDRESS_BYTE_LEN: usize = 21;
160
161 struct PlatformAddressVisitor;
162
163 impl<'de> Visitor<'de> for PlatformAddressVisitor {
164 type Value = PlatformAddress;
165
166 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
167 formatter.write_str("PlatformAddress as 21 bytes or hex string")
168 }
169
170 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
171 let bytes =
172 hex::decode(value).map_err(|err| E::custom(format!("invalid hex: {}", err)))?;
173 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
174 return Err(E::invalid_length(bytes.len(), &self));
175 }
176 PlatformAddress::from_bytes(&bytes).map_err(|err| E::custom(err.to_string()))
177 }
178
179 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
180 self.visit_str(&value)
181 }
182
183 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
184 if value.len() != PLATFORM_ADDRESS_BYTE_LEN {
185 return Err(E::invalid_length(value.len(), &self));
186 }
187 PlatformAddress::from_bytes(value).map_err(|err| E::custom(err.to_string()))
188 }
189
190 fn visit_byte_buf<E: de::Error>(self, value: Vec<u8>) -> Result<Self::Value, E> {
191 self.visit_bytes(&value)
192 }
193
194 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
195 where
196 A: de::SeqAccess<'de>,
197 {
198 let mut bytes = Vec::with_capacity(PLATFORM_ADDRESS_BYTE_LEN);
201 while let Some(byte) = seq.next_element::<u8>()? {
202 if bytes.len() >= PLATFORM_ADDRESS_BYTE_LEN {
203 return Err(de::Error::invalid_length(
204 bytes.len() + 1,
205 &"at most 21 bytes",
206 ));
207 }
208 bytes.push(byte);
209 }
210 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
211 return Err(de::Error::invalid_length(bytes.len(), &self));
212 }
213 PlatformAddress::from_bytes(&bytes)
214 .map_err(|err| de::Error::custom(err.to_string()))
215 }
216 }
217
218 if deserializer.is_human_readable() {
222 deserializer.deserialize_str(PlatformAddressVisitor)
223 } else {
224 deserializer.deserialize_bytes(PlatformAddressVisitor)
225 }
226 }
227}
228
229impl TryFrom<Address> for PlatformAddress {
230 type Error = ProtocolError;
231
232 fn try_from(address: Address) -> Result<Self, Self::Error> {
233 match address.payload() {
234 Payload::PubkeyHash(hash) => Ok(PlatformAddress::P2pkh(*hash.as_ref())),
235 Payload::ScriptHash(hash) => Ok(PlatformAddress::P2sh(*hash.as_ref())),
236 _ => Err(ProtocolError::DecodingError(
237 "unsupported address type for PlatformAddress: only P2PKH and P2SH are supported"
238 .to_string(),
239 )),
240 }
241 }
242}
243
244impl From<&PrivateKey> for PlatformAddress {
245 fn from(private_key: &PrivateKey) -> Self {
250 let secp = Secp256k1::new();
251 let pubkey_hash = private_key.public_key(&secp).pubkey_hash();
252 PlatformAddress::P2pkh(*pubkey_hash.as_byte_array())
253 }
254}
255
256impl Default for PlatformAddress {
257 fn default() -> Self {
258 PlatformAddress::P2pkh([0u8; 20])
259 }
260}
261
262pub const PLATFORM_HRP_MAINNET: &str = "dash";
264pub const PLATFORM_HRP_TESTNET: &str = "tdash";
266
267pub(crate) fn classify_platform_hrp(hrp: &str) -> Result<bool, ProtocolError> {
272 match hrp {
273 PLATFORM_HRP_MAINNET => Ok(true),
274 PLATFORM_HRP_TESTNET => Ok(false),
275 other => Err(ProtocolError::DecodingError(format!(
276 "not a platform address: HRP '{other}' is neither \
277 '{PLATFORM_HRP_MAINNET}' nor '{PLATFORM_HRP_TESTNET}'"
278 ))),
279 }
280}
281
282impl PlatformAddress {
283 pub const P2PKH_TYPE: u8 = 0xb0;
285 pub const P2SH_TYPE: u8 = 0x80;
287
288 pub fn hrp_for_network(network: Network) -> &'static str {
294 match network {
295 Network::Mainnet => PLATFORM_HRP_MAINNET,
296 Network::Testnet | Network::Devnet | Network::Regtest => PLATFORM_HRP_TESTNET,
297 }
298 }
299
300 pub fn to_bech32m_string(&self, network: Network) -> String {
317 let hrp_str = Self::hrp_for_network(network);
318 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
319
320 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
323 match self {
324 PlatformAddress::P2pkh(hash) => {
325 payload.push(Self::P2PKH_TYPE);
326 payload.extend_from_slice(hash);
327 }
328 PlatformAddress::P2sh(hash) => {
329 payload.push(Self::P2SH_TYPE);
330 payload.extend_from_slice(hash);
331 }
332 }
333
334 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
336 }
337
338 pub fn from_bech32m_string(s: &str) -> Result<Self, ProtocolError> {
349 let (hrp, data) =
350 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
351
352 classify_platform_hrp(&hrp.as_str().to_ascii_lowercase())?;
353
354 if data.len() != 1 + ADDRESS_HASH_SIZE {
356 return Err(ProtocolError::DecodingError(format!(
357 "invalid Platform address length: expected {} bytes, got {}",
358 1 + ADDRESS_HASH_SIZE,
359 data.len()
360 )));
361 }
362
363 let address_type = data[0];
365 let hash: [u8; 20] = data[1..21]
366 .try_into()
367 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
368
369 let address = match address_type {
370 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
371 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
372 _ => Err(ProtocolError::DecodingError(format!(
373 "invalid address type: 0x{:02x}",
374 address_type
375 ))),
376 }?;
377
378 Ok(address)
379 }
380
381 pub fn is_mainnet_bech32m(s: &str) -> Result<bool, ProtocolError> {
392 let (hrp, _) =
393 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{e}")))?;
394 classify_platform_hrp(&hrp.to_lowercase())
395 }
396
397 pub fn to_address_with_network(&self, network: Network) -> Address {
399 match self {
400 PlatformAddress::P2pkh(hash) => Address::new(
401 network,
402 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
403 ),
404 PlatformAddress::P2sh(hash) => Address::new(
405 network,
406 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
407 ),
408 }
409 }
410
411 pub fn to_bytes(&self) -> Vec<u8> {
417 bincode::encode_to_vec(self, bincode::config::standard())
418 .expect("PlatformAddress serialization cannot fail")
419 }
420
421 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
424 use base64::engine::general_purpose::STANDARD;
425 use base64::Engine;
426
427 let mut bytes = self.to_bytes();
428 bytes.extend_from_slice(&nonce.to_be_bytes());
429
430 STANDARD.encode(bytes)
431 }
432
433 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
438 let (address, _): (Self, usize) =
439 bincode::decode_from_slice_untrusted(bytes, bincode::config::standard()).map_err(
440 |e| ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e)),
441 )?;
442 Ok(address)
443 }
444
445 pub fn hash(&self) -> &[u8; 20] {
447 match self {
448 PlatformAddress::P2pkh(hash) => hash,
449 PlatformAddress::P2sh(hash) => hash,
450 }
451 }
452
453 pub fn is_p2pkh(&self) -> bool {
455 matches!(self, PlatformAddress::P2pkh(_))
456 }
457
458 pub fn is_p2sh(&self) -> bool {
460 matches!(self, PlatformAddress::P2sh(_))
461 }
462
463 pub fn verify_bytes_against_witness(
483 &self,
484 witness: &AddressWitness,
485 signable_bytes: &[u8],
486 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
487 match (self, witness) {
488 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
489 let data_hash = dashcore::signer::double_sha(signable_bytes);
497 dashcore::signer::verify_hash_signature(
498 &data_hash,
499 signature.as_slice(),
500 pubkey_hash,
501 )
502 .map_err(|e| {
503 ProtocolError::AddressWitnessError(format!(
504 "P2PKH signature verification failed: {}",
505 e
506 ))
507 })?;
508
509 Ok(AddressWitnessVerificationOperations::for_p2pkh(
510 signable_bytes.len(),
511 ))
512 }
513 (
514 PlatformAddress::P2sh(script_hash),
515 AddressWitness::P2sh {
516 signatures,
517 redeem_script,
518 },
519 ) => {
520 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
522 let computed_hash = script.script_hash();
523 if computed_hash.as_byte_array() != script_hash {
524 return Err(ProtocolError::AddressWitnessError(format!(
525 "Script hash {} does not match address hash {}",
526 hex::encode(computed_hash.as_byte_array()),
527 hex::encode(script_hash)
528 )));
529 }
530
531 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
534
535 let valid_signatures: Vec<_> = signatures
537 .iter()
538 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
539 .collect();
540
541 if valid_signatures.len() < threshold {
542 return Err(ProtocolError::AddressWitnessError(format!(
543 "Not enough signatures: got {}, need {}",
544 valid_signatures.len(),
545 threshold
546 )));
547 }
548
549 let mut sig_idx = 0;
552 let mut pubkey_idx = 0;
553 let mut matched = 0;
554 let mut signature_verifications: u16 = 0;
555
556 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
557 let msg = Message::from_digest(signable_bytes_hash);
558 let secp = Secp256k1::new();
559
560 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
561 signature_verifications += 1;
562
563 let sig = RecoverableSignature::from_compact_signature(
564 valid_signatures[sig_idx].as_slice(),
565 )
566 .map_err(|e| {
567 ProtocolError::AddressWitnessError(format!(
568 "Invalid signature format: {}",
569 e
570 ))
571 })?;
572
573 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
574 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
575 })?;
576
577 if secp
578 .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner)
579 .is_ok()
580 {
581 matched += 1;
582 sig_idx += 1;
583 }
584 pubkey_idx += 1;
585 }
586
587 if matched >= threshold {
588 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
589 signature_verifications,
590 signable_bytes.len(),
591 ))
592 } else {
593 Err(ProtocolError::AddressWitnessError(format!(
594 "Not enough valid signatures: verified {}, need {}",
595 matched, threshold
596 )))
597 }
598 }
599 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
600 Err(ProtocolError::AddressWitnessError(
601 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
602 ))
603 }
604 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
605 Err(ProtocolError::AddressWitnessError(
606 "P2SH address requires P2sh witness, got P2pkh".to_string(),
607 ))
608 }
609 }
610 }
611
612 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
627 use dashcore::blockdata::opcodes::all::*;
628
629 let mut instructions = script.instructions();
630 let mut pubkeys = Vec::new();
631
632 let threshold = match instructions.next() {
634 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
635 let byte = op.to_u8();
636 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
637 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
638 } else {
639 return Err(ProtocolError::AddressWitnessError(format!(
640 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
641 First opcode was 0x{:02x}, expected OP_1 through OP_16",
642 byte
643 )));
644 }
645 }
646 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
647 return Err(ProtocolError::AddressWitnessError(
648 "Unsupported P2SH script type: only standard multisig is supported. \
649 Script starts with a data push instead of OP_M threshold."
650 .to_string(),
651 ))
652 }
653 Some(Err(e)) => {
654 return Err(ProtocolError::AddressWitnessError(format!(
655 "Error parsing P2SH script: {:?}",
656 e
657 )))
658 }
659 None => {
660 return Err(ProtocolError::AddressWitnessError(
661 "Empty P2SH redeem script".to_string(),
662 ))
663 }
664 };
665
666 loop {
668 match instructions.next() {
669 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
670 let len = bytes.len();
672 if len != 33 {
673 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
674 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
675 len,
676 ),
677 ));
678 }
679 pubkeys.push(bytes.as_bytes().to_vec());
680 }
681 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
682 let byte = op.to_u8();
683 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
684 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
686 if pubkeys.len() != n {
687 return Err(ProtocolError::AddressWitnessError(format!(
688 "Multisig script declares {} keys but contains {}",
689 n,
690 pubkeys.len()
691 )));
692 }
693 break;
694 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
695 return Err(ProtocolError::AddressWitnessError(
697 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
698 ));
699 } else {
700 return Err(ProtocolError::AddressWitnessError(format!(
701 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
702 byte
703 )));
704 }
705 }
706 Some(Err(e)) => {
707 return Err(ProtocolError::AddressWitnessError(format!(
708 "Error parsing multisig script: {:?}",
709 e
710 )))
711 }
712 None => {
713 return Err(ProtocolError::AddressWitnessError(
714 "Incomplete multisig script: unexpected end before OP_N".to_string(),
715 ))
716 }
717 }
718 }
719
720 if threshold > pubkeys.len() {
722 return Err(ProtocolError::AddressWitnessError(format!(
723 "Invalid multisig: threshold {} exceeds number of keys {}",
724 threshold,
725 pubkeys.len()
726 )));
727 }
728
729 match instructions.next() {
731 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
732 if op == OP_CHECKMULTISIG {
733 if instructions.next().is_some() {
735 return Err(ProtocolError::AddressWitnessError(
736 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
737 ));
738 }
739 Ok((threshold, pubkeys))
740 } else if op == OP_CHECKMULTISIGVERIFY {
741 Err(ProtocolError::AddressWitnessError(
742 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
743 .to_string(),
744 ))
745 } else {
746 Err(ProtocolError::AddressWitnessError(format!(
747 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
748 op.to_u8()
749 )))
750 }
751 }
752 _ => Err(ProtocolError::AddressWitnessError(
753 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
754 )),
755 }
756 }
757}
758
759impl std::fmt::Display for PlatformAddress {
760 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
761 match self {
762 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
763 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
764 }
765 }
766}
767
768#[derive(Debug, Clone, PartialEq, Eq)]
770pub struct PlatformAddressParseError(pub String);
771
772impl std::fmt::Display for PlatformAddressParseError {
773 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
774 write!(f, "{}", self.0)
775 }
776}
777
778impl std::error::Error for PlatformAddressParseError {}
779
780impl FromStr for PlatformAddress {
781 type Err = PlatformAddressParseError;
782
783 fn from_str(s: &str) -> Result<Self, Self::Err> {
792 Self::from_bech32m_string(s).map_err(|e| PlatformAddressParseError(e.to_string()))
793 }
794}
795
796#[cfg(test)]
797mod tests {
798 use super::*;
799 use dashcore::blockdata::opcodes::all::*;
800 use dashcore::hashes::Hash;
801 use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey};
802 use dashcore::PublicKey;
803 use platform_value::BinaryData;
804
805 #[test]
811 fn hrp_is_shared_across_all_non_mainnet_networks() {
812 let testnet = PlatformAddress::hrp_for_network(Network::Testnet);
813 assert_eq!(testnet, PLATFORM_HRP_TESTNET);
814 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), testnet);
815 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), testnet);
816 assert_ne!(
817 PlatformAddress::hrp_for_network(Network::Mainnet),
818 testnet,
819 "mainnet must use a distinct HRP"
820 );
821 }
822
823 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
825 let secp = Secp256k1::new();
826 let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key");
827 let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key);
828 let public_key = PublicKey::new(raw_public_key);
829 (secret_key, public_key)
830 }
831
832 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
834 dashcore::signer::sign(data, secret_key.as_ref())
835 .expect("signing should succeed")
836 .to_vec()
837 }
838
839 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
841 let mut script = Vec::new();
842
843 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
845
846 for pubkey in pubkeys {
848 let bytes = pubkey.to_bytes();
849 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
851 }
852
853 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
855
856 script.push(OP_CHECKMULTISIG.to_u8());
858
859 script
860 }
861
862 #[test]
863 fn test_platform_address_from_private_key() {
864 let seed = [1u8; 32];
866 let (secret_key, public_key) = create_keypair(seed);
867
868 let private_key = PrivateKey::new(secret_key, Network::Testnet);
870
871 let address_from_private = PlatformAddress::from(&private_key);
873
874 let pubkey_hash = public_key.pubkey_hash();
877 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
878
879 assert_eq!(
881 address_from_private, address_from_pubkey,
882 "Address derived from private key should match Hash160(compressed_pubkey)"
883 );
884
885 assert!(address_from_private.is_p2pkh());
887 }
888
889 #[test]
890 fn test_p2pkh_verify_signature_success() {
891 let seed = [1u8; 32];
893 let (secret_key, public_key) = create_keypair(seed);
894
895 let pubkey_hash = public_key.pubkey_hash();
897 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
898
899 let signable_bytes = b"test message for P2PKH verification";
901
902 let signature = sign_data(signable_bytes, &secret_key);
904
905 let witness = AddressWitness::P2pkh {
907 signature: BinaryData::new(signature),
908 };
909
910 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
912 assert!(
913 result.is_ok(),
914 "P2PKH verification should succeed: {:?}",
915 result
916 );
917 }
918
919 #[test]
920 fn test_p2pkh_verify_wrong_signature_fails() {
921 let seed = [1u8; 32];
923 let (secret_key, public_key) = create_keypair(seed);
924
925 let pubkey_hash = public_key.pubkey_hash();
927 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
928
929 let sign_bytes = b"original message";
931 let verify_bytes = b"different message";
932 let signature = sign_data(sign_bytes, &secret_key);
933
934 let witness = AddressWitness::P2pkh {
936 signature: BinaryData::new(signature),
937 };
938
939 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
941 assert!(
942 result.is_err(),
943 "P2PKH verification should fail with wrong data"
944 );
945 }
946
947 #[test]
948 fn test_p2pkh_verify_wrong_key_fails() {
949 let seed1 = [1u8; 32];
951 let seed2 = [2u8; 32];
952 let (_secret_key1, public_key1) = create_keypair(seed1);
953 let (secret_key2, _public_key2) = create_keypair(seed2);
954
955 let pubkey_hash = public_key1.pubkey_hash();
957 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
958
959 let signable_bytes = b"test message";
961 let signature = sign_data(signable_bytes, &secret_key2);
962
963 let witness = AddressWitness::P2pkh {
965 signature: BinaryData::new(signature),
966 };
967
968 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
970 assert!(
971 result.is_err(),
972 "P2PKH verification should fail when signed with wrong key"
973 );
974 }
975
976 #[test]
981 fn test_p2sh_2_of_3_multisig_verify_success() {
982 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
984 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
985 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
986
987 let redeem_script = create_multisig_script(2, &pubkeys);
989
990 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
992 let script_hash = script_buf.script_hash();
993 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
994
995 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
997
998 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1000 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1001
1002 let witness = AddressWitness::P2sh {
1005 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
1006 redeem_script: BinaryData::new(redeem_script),
1007 };
1008
1009 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1011 assert!(
1012 result.is_ok(),
1013 "P2SH 2-of-3 multisig verification should succeed: {:?}",
1014 result
1015 );
1016 }
1017
1018 #[test]
1019 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
1020 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1022 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1023 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1024
1025 let redeem_script = create_multisig_script(2, &pubkeys);
1027
1028 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1030 let script_hash = script_buf.script_hash();
1031 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1032
1033 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
1035
1036 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1038 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
1039
1040 let witness = AddressWitness::P2sh {
1042 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
1043 redeem_script: BinaryData::new(redeem_script),
1044 };
1045
1046 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1048 assert!(
1049 result.is_ok(),
1050 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
1051 result
1052 );
1053 }
1054
1055 #[test]
1056 fn test_p2sh_not_enough_signatures_fails() {
1057 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1059 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1060 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1061
1062 let redeem_script = create_multisig_script(2, &pubkeys);
1064
1065 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1067 let script_hash = script_buf.script_hash();
1068 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1069
1070 let signable_bytes = b"test message";
1072
1073 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1075
1076 let witness = AddressWitness::P2sh {
1078 signatures: vec![BinaryData::new(sig0)],
1079 redeem_script: BinaryData::new(redeem_script),
1080 };
1081
1082 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1084 assert!(
1085 result.is_err(),
1086 "P2SH should fail with only 1 signature when 2 required"
1087 );
1088 assert!(
1089 result.unwrap_err().to_string().contains("Not enough"),
1090 "Error should mention not enough signatures"
1091 );
1092 }
1093
1094 #[test]
1095 fn test_p2sh_wrong_script_hash_fails() {
1096 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
1098 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
1099 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
1100
1101 let redeem_script = create_multisig_script(2, &pubkeys);
1103
1104 let wrong_hash = [0xABu8; 20];
1106 let address = PlatformAddress::P2sh(wrong_hash);
1107
1108 let signable_bytes = b"test message";
1110
1111 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
1113 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1114
1115 let witness = AddressWitness::P2sh {
1117 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
1118 redeem_script: BinaryData::new(redeem_script),
1119 };
1120
1121 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1123 assert!(
1124 result.is_err(),
1125 "P2SH should fail when script hash doesn't match address"
1126 );
1127 assert!(
1128 result
1129 .unwrap_err()
1130 .to_string()
1131 .contains("does not match address hash"),
1132 "Error should mention hash mismatch"
1133 );
1134 }
1135
1136 #[test]
1137 fn test_p2pkh_and_p2sh_together() {
1138 let p2pkh_seed = [10u8; 32];
1142 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
1143 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
1144 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
1145
1146 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
1148 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
1149 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
1150 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
1151 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1152 let script_hash = script_buf.script_hash();
1153 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1154
1155 let signable_bytes = b"combined transaction data to redeem both outputs";
1157
1158 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
1160 let p2pkh_witness = AddressWitness::P2pkh {
1161 signature: BinaryData::new(p2pkh_sig),
1162 };
1163 let p2pkh_result =
1164 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1165 assert!(
1166 p2pkh_result.is_ok(),
1167 "P2PKH redemption should succeed: {:?}",
1168 p2pkh_result
1169 );
1170
1171 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
1173 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
1174 let p2sh_witness = AddressWitness::P2sh {
1175 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
1176 redeem_script: BinaryData::new(redeem_script),
1177 };
1178 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1179 assert!(
1180 p2sh_result.is_ok(),
1181 "P2SH redemption should succeed: {:?}",
1182 p2sh_result
1183 );
1184
1185 }
1187
1188 #[test]
1189 fn test_witness_type_mismatch() {
1190 let seed = [1u8; 32];
1192 let (_, public_key) = create_keypair(seed);
1193 let pubkey_hash = public_key.pubkey_hash();
1194 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
1195
1196 let p2sh_hash = [0xABu8; 20];
1198 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
1199
1200 let signable_bytes = b"test data";
1201
1202 let p2sh_witness = AddressWitness::P2sh {
1204 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
1205 redeem_script: BinaryData::new(vec![0x52]),
1206 };
1207 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1208 assert!(result.is_err());
1209 assert!(result
1210 .unwrap_err()
1211 .to_string()
1212 .contains("P2PKH address requires P2pkh witness"));
1213
1214 let p2pkh_witness = AddressWitness::P2pkh {
1216 signature: BinaryData::new(vec![0x30, 0x44]),
1217 };
1218 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1219 assert!(result.is_err());
1220 assert!(result
1221 .unwrap_err()
1222 .to_string()
1223 .contains("P2SH address requires P2sh witness"));
1224 }
1225
1226 #[test]
1231 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1232 let hash: [u8; 20] = [
1234 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1235 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1236 ];
1237 let address = PlatformAddress::P2pkh(hash);
1238
1239 let encoded = address.to_bech32m_string(Network::Mainnet);
1241
1242 assert_eq!(
1244 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1245 "P2PKH mainnet encoding mismatch"
1246 );
1247
1248 let decoded =
1250 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1251 assert_eq!(decoded, address);
1252 }
1253
1254 #[test]
1255 fn test_bech32m_p2pkh_testnet_roundtrip() {
1256 let hash: [u8; 20] = [
1258 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1259 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1260 ];
1261 let address = PlatformAddress::P2pkh(hash);
1262
1263 let encoded = address.to_bech32m_string(Network::Testnet);
1265
1266 assert_eq!(
1268 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1269 "P2PKH testnet encoding mismatch"
1270 );
1271
1272 let decoded =
1274 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1275 assert_eq!(decoded, address);
1276 }
1277
1278 #[test]
1279 fn test_bech32m_p2sh_mainnet_roundtrip() {
1280 let hash: [u8; 20] = [
1282 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1283 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1284 ];
1285 let address = PlatformAddress::P2sh(hash);
1286
1287 let encoded = address.to_bech32m_string(Network::Mainnet);
1289
1290 assert_eq!(
1292 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1293 "P2SH mainnet encoding mismatch"
1294 );
1295
1296 let decoded =
1298 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1299 assert_eq!(decoded, address);
1300 }
1301
1302 #[test]
1303 fn test_bech32m_p2sh_testnet_roundtrip() {
1304 let hash: [u8; 20] = [
1306 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1307 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1308 ];
1309 let address = PlatformAddress::P2sh(hash);
1310
1311 let encoded = address.to_bech32m_string(Network::Testnet);
1313
1314 assert_eq!(
1316 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1317 "P2SH testnet encoding mismatch"
1318 );
1319
1320 let decoded =
1322 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1323 assert_eq!(decoded, address);
1324 }
1325
1326 #[test]
1327 fn test_bech32m_devnet_uses_testnet_hrp() {
1328 let hash: [u8; 20] = [0xAB; 20];
1329 let address = PlatformAddress::P2pkh(hash);
1330
1331 let encoded = address.to_bech32m_string(Network::Devnet);
1333 assert!(
1334 encoded.starts_with("tdash1"),
1335 "Devnet address should start with 'tdash1', got: {}",
1336 encoded
1337 );
1338 }
1339
1340 #[test]
1341 fn test_bech32m_regtest_uses_testnet_hrp() {
1342 let hash: [u8; 20] = [0xAB; 20];
1343 let address = PlatformAddress::P2pkh(hash);
1344
1345 let encoded = address.to_bech32m_string(Network::Regtest);
1347 assert!(
1348 encoded.starts_with("tdash1"),
1349 "Regtest address should start with 'tdash1', got: {}",
1350 encoded
1351 );
1352 }
1353
1354 #[test]
1355 fn test_bech32m_invalid_hrp_fails() {
1356 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1357 let payload: [u8; 21] = [0x00; 21];
1358 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1359
1360 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1361 assert!(result.is_err());
1362 let err = result.unwrap_err();
1363 assert!(
1364 err.to_string().contains("not a platform address"),
1365 "Error should mention non-platform HRP: {}",
1366 err
1367 );
1368 }
1369
1370 #[test]
1371 fn test_bech32m_invalid_checksum_fails() {
1372 let hash: [u8; 20] = [0xAB; 20];
1374 let address = PlatformAddress::P2pkh(hash);
1375 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1376
1377 let last_char = encoded.pop().unwrap();
1379 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1380 encoded.push(corrupted_char);
1381
1382 let result = PlatformAddress::from_bech32m_string(&encoded);
1383 assert!(result.is_err(), "Should fail with corrupted checksum");
1384 }
1385
1386 #[test]
1387 fn test_bech32m_invalid_type_byte_fails() {
1388 let hrp = Hrp::parse("dash").unwrap();
1391 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1393
1394 let result = PlatformAddress::from_bech32m_string(&encoded);
1395 assert!(result.is_err());
1396 let err = result.unwrap_err();
1397 assert!(
1398 err.to_string().contains("invalid address type"),
1399 "Error should mention invalid type: {}",
1400 err
1401 );
1402 }
1403
1404 #[test]
1405 fn test_bech32m_too_short_fails() {
1406 let hrp = Hrp::parse("dash").unwrap();
1408 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1410
1411 let result = PlatformAddress::from_bech32m_string(&encoded);
1412 assert!(result.is_err());
1413 let err = result.unwrap_err();
1414 assert!(
1415 err.to_string().contains("invalid Platform address length"),
1416 "Error should mention invalid length: {}",
1417 err
1418 );
1419 }
1420
1421 #[test]
1422 fn test_bech32m_from_str_trait() {
1423 let hash: [u8; 20] = [
1425 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1426 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1427 ];
1428 let original = PlatformAddress::P2pkh(hash);
1429
1430 let encoded = original.to_bech32m_string(Network::Testnet);
1432 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1433
1434 assert_eq!(parsed, original);
1435 }
1436
1437 #[test]
1438 fn test_bech32m_case_insensitive() {
1439 let hash: [u8; 20] = [0xAB; 20];
1442 let address = PlatformAddress::P2pkh(hash);
1443
1444 let lowercase = address.to_bech32m_string(Network::Mainnet);
1445 let uppercase = lowercase.to_uppercase();
1446
1447 let decoded_lower = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1449 let decoded_upper = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1450
1451 assert_eq!(decoded_lower, decoded_upper);
1452 assert_eq!(decoded_lower, address);
1453 }
1454
1455 #[test]
1456 fn test_bech32m_all_zeros_p2pkh() {
1457 let address = PlatformAddress::P2pkh([0u8; 20]);
1459 let encoded = address.to_bech32m_string(Network::Mainnet);
1460 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1461 assert_eq!(decoded, address);
1462 }
1463
1464 #[test]
1465 fn test_bech32m_all_ones_p2sh() {
1466 let address = PlatformAddress::P2sh([0xFF; 20]);
1468 let encoded = address.to_bech32m_string(Network::Mainnet);
1469 let decoded = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1470 assert_eq!(decoded, address);
1471 }
1472
1473 #[test]
1474 fn test_hrp_for_network() {
1475 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1476 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1477 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1478 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1479 }
1480
1481 #[test]
1482 fn test_storage_bytes_format() {
1483 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1487 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1488
1489 let p2pkh_bytes = p2pkh.to_bytes();
1490 let p2sh_bytes = p2sh.to_bytes();
1491
1492 assert_eq!(p2pkh_bytes.len(), 21);
1494 assert_eq!(p2sh_bytes.len(), 21);
1495 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1496 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1497
1498 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1500 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1501 assert_eq!(p2pkh_decoded, p2pkh);
1502 assert_eq!(p2sh_decoded, p2sh);
1503 }
1504
1505 #[test]
1506 fn test_bech32m_uses_different_type_bytes_than_storage() {
1507 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1510 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1511
1512 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1514 assert_eq!(p2sh.to_bytes()[0], 0x01);
1515
1516 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1518 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1519
1520 let p2pkh_decoded = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1521 let p2sh_decoded = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1522
1523 assert_eq!(p2pkh_decoded, p2pkh);
1524 assert_eq!(p2sh_decoded, p2sh);
1525 }
1526
1527 #[test]
1528 fn test_is_mainnet_bech32m_mainnet_is_true() {
1529 let encoded = PlatformAddress::P2pkh([0x11; 20]).to_bech32m_string(Network::Mainnet);
1530 assert!(encoded.starts_with("dash1"));
1531 assert!(PlatformAddress::is_mainnet_bech32m(&encoded).unwrap());
1532 }
1533
1534 #[test]
1535 fn test_is_mainnet_bech32m_all_non_mainnet_networks_are_false() {
1536 for network in [Network::Testnet, Network::Devnet, Network::Regtest] {
1540 let encoded = PlatformAddress::P2pkh([0x22; 20]).to_bech32m_string(network);
1541 assert!(encoded.starts_with("tdash1"), "network {network:?}");
1542 assert!(
1543 !PlatformAddress::is_mainnet_bech32m(&encoded).unwrap(),
1544 "network {network:?} must classify as non-mainnet"
1545 );
1546 }
1547 }
1548
1549 #[test]
1550 fn test_is_mainnet_bech32m_is_case_insensitive() {
1551 let mainnet = PlatformAddress::P2pkh([0x33; 20])
1552 .to_bech32m_string(Network::Mainnet)
1553 .to_uppercase();
1554 assert!(mainnet.starts_with("DASH1"));
1555 assert!(PlatformAddress::is_mainnet_bech32m(&mainnet).unwrap());
1556
1557 let testnet = PlatformAddress::P2pkh([0x44; 20])
1558 .to_bech32m_string(Network::Testnet)
1559 .to_uppercase();
1560 assert!(testnet.starts_with("TDASH1"));
1561 assert!(!PlatformAddress::is_mainnet_bech32m(&testnet).unwrap());
1562 }
1563
1564 #[test]
1565 fn test_is_mainnet_bech32m_non_platform_hrp_errors() {
1566 let err = PlatformAddress::is_mainnet_bech32m("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4")
1568 .unwrap_err();
1569 assert!(
1570 err.to_string().contains("not a platform address"),
1571 "unexpected error: {err}"
1572 );
1573 }
1574
1575 #[test]
1576 fn test_is_mainnet_bech32m_malformed_data_part_errors() {
1577 assert!(
1581 PlatformAddress::is_mainnet_bech32m("dash1!").is_err(),
1582 "dash1! must error, not return Ok(true)"
1583 );
1584 }
1585
1586 #[test]
1587 fn test_is_mainnet_bech32m_missing_separator_errors() {
1588 let err = PlatformAddress::is_mainnet_bech32m("nodelimiterhere").unwrap_err();
1589 assert!(
1591 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1592 "unexpected error: {err}"
1593 );
1594 }
1595
1596 #[test]
1597 fn test_is_mainnet_bech32m_empty_errors() {
1598 let err = PlatformAddress::is_mainnet_bech32m("").unwrap_err();
1599 assert!(
1600 err.to_string().contains("parsing failed") || err.to_string().contains("separator"),
1601 "unexpected error: {err}"
1602 );
1603 }
1604}