1use crate::address_funds::AddressWitness;
2use crate::address_funds::AddressWitnessVerificationOperations;
3use crate::prelude::AddressNonce;
4use crate::ProtocolError;
5use bech32::{Bech32m, Hrp};
6use bincode::{Decode, Encode};
7use dashcore::address::Payload;
8use dashcore::blockdata::script::ScriptBuf;
9use dashcore::hashes::{sha256d, Hash};
10use dashcore::key::Secp256k1;
11use dashcore::secp256k1::ecdsa::RecoverableSignature;
12use dashcore::secp256k1::Message;
13use dashcore::signer::CompactSignature;
14use dashcore::{Address, Network, PrivateKey, PubkeyHash, PublicKey, ScriptHash};
15use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize};
16#[cfg(feature = "serde-conversion")]
17use serde::{Deserialize, Serialize};
18use std::convert::TryFrom;
19use std::str::FromStr;
20
21pub const ADDRESS_HASH_SIZE: usize = 20;
23
24#[derive(
25 Debug,
26 PartialEq,
27 Eq,
28 Clone,
29 Copy,
30 Hash,
31 Ord,
32 PartialOrd,
33 Encode,
34 Decode,
35 PlatformSerialize,
36 PlatformDeserialize,
37)]
38#[platform_serialize(unversioned)]
39pub enum PlatformAddress {
40 P2pkh([u8; 20]),
44 P2sh([u8; 20]),
48}
49
50#[cfg(feature = "serde-conversion")]
56impl Serialize for PlatformAddress {
57 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58 where
59 S: serde::Serializer,
60 {
61 let bytes = self.to_bytes();
62 if serializer.is_human_readable() {
63 serializer.serialize_str(&hex::encode(&bytes))
64 } else {
65 serializer.serialize_bytes(&bytes)
66 }
67 }
68}
69
70#[cfg(feature = "serde-conversion")]
71impl<'de> Deserialize<'de> for PlatformAddress {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: serde::Deserializer<'de>,
75 {
76 use serde::de::{self, Visitor};
77 use std::fmt;
78
79 const PLATFORM_ADDRESS_BYTE_LEN: usize = 21;
81
82 struct PlatformAddressVisitor;
83
84 impl<'de> Visitor<'de> for PlatformAddressVisitor {
85 type Value = PlatformAddress;
86
87 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
88 formatter.write_str("PlatformAddress as 21 bytes or hex string")
89 }
90
91 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
92 let bytes =
93 hex::decode(value).map_err(|err| E::custom(format!("invalid hex: {}", err)))?;
94 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
95 return Err(E::invalid_length(bytes.len(), &self));
96 }
97 PlatformAddress::from_bytes(&bytes).map_err(|err| E::custom(err.to_string()))
98 }
99
100 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
101 self.visit_str(&value)
102 }
103
104 fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
105 if value.len() != PLATFORM_ADDRESS_BYTE_LEN {
106 return Err(E::invalid_length(value.len(), &self));
107 }
108 PlatformAddress::from_bytes(value).map_err(|err| E::custom(err.to_string()))
109 }
110
111 fn visit_byte_buf<E: de::Error>(self, value: Vec<u8>) -> Result<Self::Value, E> {
112 self.visit_bytes(&value)
113 }
114
115 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
116 where
117 A: de::SeqAccess<'de>,
118 {
119 let mut bytes = Vec::with_capacity(PLATFORM_ADDRESS_BYTE_LEN);
122 while let Some(byte) = seq.next_element::<u8>()? {
123 if bytes.len() >= PLATFORM_ADDRESS_BYTE_LEN {
124 return Err(de::Error::invalid_length(
125 bytes.len() + 1,
126 &"at most 21 bytes",
127 ));
128 }
129 bytes.push(byte);
130 }
131 if bytes.len() != PLATFORM_ADDRESS_BYTE_LEN {
132 return Err(de::Error::invalid_length(bytes.len(), &self));
133 }
134 PlatformAddress::from_bytes(&bytes)
135 .map_err(|err| de::Error::custom(err.to_string()))
136 }
137 }
138
139 if deserializer.is_human_readable() {
143 deserializer.deserialize_str(PlatformAddressVisitor)
144 } else {
145 deserializer.deserialize_bytes(PlatformAddressVisitor)
146 }
147 }
148}
149
150impl TryFrom<Address> for PlatformAddress {
151 type Error = ProtocolError;
152
153 fn try_from(address: Address) -> Result<Self, Self::Error> {
154 match address.payload() {
155 Payload::PubkeyHash(hash) => Ok(PlatformAddress::P2pkh(*hash.as_ref())),
156 Payload::ScriptHash(hash) => Ok(PlatformAddress::P2sh(*hash.as_ref())),
157 _ => Err(ProtocolError::DecodingError(
158 "unsupported address type for PlatformAddress: only P2PKH and P2SH are supported"
159 .to_string(),
160 )),
161 }
162 }
163}
164
165impl From<&PrivateKey> for PlatformAddress {
166 fn from(private_key: &PrivateKey) -> Self {
171 let secp = Secp256k1::new();
172 let pubkey_hash = private_key.public_key(&secp).pubkey_hash();
173 PlatformAddress::P2pkh(*pubkey_hash.as_byte_array())
174 }
175}
176
177impl Default for PlatformAddress {
178 fn default() -> Self {
179 PlatformAddress::P2pkh([0u8; 20])
180 }
181}
182
183pub const PLATFORM_HRP_MAINNET: &str = "dash";
185pub const PLATFORM_HRP_TESTNET: &str = "tdash";
187
188impl PlatformAddress {
189 pub const P2PKH_TYPE: u8 = 0xb0;
191 pub const P2SH_TYPE: u8 = 0x80;
193
194 pub fn hrp_for_network(network: Network) -> &'static str {
200 match network {
201 Network::Mainnet => PLATFORM_HRP_MAINNET,
202 Network::Testnet | Network::Devnet | Network::Regtest => PLATFORM_HRP_TESTNET,
203 }
204 }
205
206 pub fn to_bech32m_string(&self, network: Network) -> String {
223 let hrp_str = Self::hrp_for_network(network);
224 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
225
226 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
229 match self {
230 PlatformAddress::P2pkh(hash) => {
231 payload.push(Self::P2PKH_TYPE);
232 payload.extend_from_slice(hash);
233 }
234 PlatformAddress::P2sh(hash) => {
235 payload.push(Self::P2SH_TYPE);
236 payload.extend_from_slice(hash);
237 }
238 }
239
240 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
242 }
243
244 pub fn from_bech32m_string(s: &str) -> Result<(Self, Network), ProtocolError> {
253 let (hrp, data) =
255 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
256
257 let hrp_lower = hrp.as_str().to_ascii_lowercase();
259 let network = match hrp_lower.as_str() {
260 s if s == PLATFORM_HRP_MAINNET => Network::Mainnet,
261 s if s == PLATFORM_HRP_TESTNET => Network::Testnet,
262 _ => {
263 return Err(ProtocolError::DecodingError(format!(
264 "invalid HRP '{}': expected '{}' or '{}'",
265 hrp, PLATFORM_HRP_MAINNET, PLATFORM_HRP_TESTNET
266 )))
267 }
268 };
269
270 if data.len() != 1 + ADDRESS_HASH_SIZE {
272 return Err(ProtocolError::DecodingError(format!(
273 "invalid Platform address length: expected {} bytes, got {}",
274 1 + ADDRESS_HASH_SIZE,
275 data.len()
276 )));
277 }
278
279 let address_type = data[0];
281 let hash: [u8; 20] = data[1..21]
282 .try_into()
283 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
284
285 let address = match address_type {
286 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
287 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
288 _ => Err(ProtocolError::DecodingError(format!(
289 "invalid address type: 0x{:02x}",
290 address_type
291 ))),
292 }?;
293
294 Ok((address, network))
295 }
296
297 pub fn to_address_with_network(&self, network: Network) -> Address {
299 match self {
300 PlatformAddress::P2pkh(hash) => Address::new(
301 network,
302 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
303 ),
304 PlatformAddress::P2sh(hash) => Address::new(
305 network,
306 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
307 ),
308 }
309 }
310
311 pub fn to_bytes(&self) -> Vec<u8> {
317 bincode::encode_to_vec(self, bincode::config::standard())
318 .expect("PlatformAddress serialization cannot fail")
319 }
320
321 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
324 use base64::engine::general_purpose::STANDARD;
325 use base64::Engine;
326
327 let mut bytes = self.to_bytes();
328 bytes.extend_from_slice(&nonce.to_be_bytes());
329
330 STANDARD.encode(bytes)
331 }
332
333 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
338 let (address, _): (Self, usize) =
339 bincode::decode_from_slice(bytes, bincode::config::standard()).map_err(|e| {
340 ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e))
341 })?;
342 Ok(address)
343 }
344
345 pub fn hash(&self) -> &[u8; 20] {
347 match self {
348 PlatformAddress::P2pkh(hash) => hash,
349 PlatformAddress::P2sh(hash) => hash,
350 }
351 }
352
353 pub fn is_p2pkh(&self) -> bool {
355 matches!(self, PlatformAddress::P2pkh(_))
356 }
357
358 pub fn is_p2sh(&self) -> bool {
360 matches!(self, PlatformAddress::P2sh(_))
361 }
362
363 pub fn verify_bytes_against_witness(
383 &self,
384 witness: &AddressWitness,
385 signable_bytes: &[u8],
386 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
387 match (self, witness) {
388 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
389 let data_hash = dashcore::signer::double_sha(signable_bytes);
397 dashcore::signer::verify_hash_signature(
398 &data_hash,
399 signature.as_slice(),
400 pubkey_hash,
401 )
402 .map_err(|e| {
403 ProtocolError::AddressWitnessError(format!(
404 "P2PKH signature verification failed: {}",
405 e
406 ))
407 })?;
408
409 Ok(AddressWitnessVerificationOperations::for_p2pkh(
410 signable_bytes.len(),
411 ))
412 }
413 (
414 PlatformAddress::P2sh(script_hash),
415 AddressWitness::P2sh {
416 signatures,
417 redeem_script,
418 },
419 ) => {
420 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
422 let computed_hash = script.script_hash();
423 if computed_hash.as_byte_array() != script_hash {
424 return Err(ProtocolError::AddressWitnessError(format!(
425 "Script hash {} does not match address hash {}",
426 hex::encode(computed_hash.as_byte_array()),
427 hex::encode(script_hash)
428 )));
429 }
430
431 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
434
435 let valid_signatures: Vec<_> = signatures
437 .iter()
438 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
439 .collect();
440
441 if valid_signatures.len() < threshold {
442 return Err(ProtocolError::AddressWitnessError(format!(
443 "Not enough signatures: got {}, need {}",
444 valid_signatures.len(),
445 threshold
446 )));
447 }
448
449 let mut sig_idx = 0;
452 let mut pubkey_idx = 0;
453 let mut matched = 0;
454 let mut signature_verifications: u16 = 0;
455
456 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
457 let msg = Message::from_digest(signable_bytes_hash);
458 let secp = Secp256k1::new();
459
460 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
461 signature_verifications += 1;
462
463 let sig = RecoverableSignature::from_compact_signature(
464 valid_signatures[sig_idx].as_slice(),
465 )
466 .map_err(|e| {
467 ProtocolError::AddressWitnessError(format!(
468 "Invalid signature format: {}",
469 e
470 ))
471 })?;
472
473 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
474 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
475 })?;
476
477 if secp
478 .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner)
479 .is_ok()
480 {
481 matched += 1;
482 sig_idx += 1;
483 }
484 pubkey_idx += 1;
485 }
486
487 if matched >= threshold {
488 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
489 signature_verifications,
490 signable_bytes.len(),
491 ))
492 } else {
493 Err(ProtocolError::AddressWitnessError(format!(
494 "Not enough valid signatures: verified {}, need {}",
495 matched, threshold
496 )))
497 }
498 }
499 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
500 Err(ProtocolError::AddressWitnessError(
501 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
502 ))
503 }
504 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
505 Err(ProtocolError::AddressWitnessError(
506 "P2SH address requires P2sh witness, got P2pkh".to_string(),
507 ))
508 }
509 }
510 }
511
512 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
527 use dashcore::blockdata::opcodes::all::*;
528
529 let mut instructions = script.instructions();
530 let mut pubkeys = Vec::new();
531
532 let threshold = match instructions.next() {
534 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
535 let byte = op.to_u8();
536 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
537 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
538 } else {
539 return Err(ProtocolError::AddressWitnessError(format!(
540 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
541 First opcode was 0x{:02x}, expected OP_1 through OP_16",
542 byte
543 )));
544 }
545 }
546 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
547 return Err(ProtocolError::AddressWitnessError(
548 "Unsupported P2SH script type: only standard multisig is supported. \
549 Script starts with a data push instead of OP_M threshold."
550 .to_string(),
551 ))
552 }
553 Some(Err(e)) => {
554 return Err(ProtocolError::AddressWitnessError(format!(
555 "Error parsing P2SH script: {:?}",
556 e
557 )))
558 }
559 None => {
560 return Err(ProtocolError::AddressWitnessError(
561 "Empty P2SH redeem script".to_string(),
562 ))
563 }
564 };
565
566 loop {
568 match instructions.next() {
569 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
570 let len = bytes.len();
572 if len != 33 {
573 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
574 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
575 len,
576 ),
577 ));
578 }
579 pubkeys.push(bytes.as_bytes().to_vec());
580 }
581 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
582 let byte = op.to_u8();
583 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
584 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
586 if pubkeys.len() != n {
587 return Err(ProtocolError::AddressWitnessError(format!(
588 "Multisig script declares {} keys but contains {}",
589 n,
590 pubkeys.len()
591 )));
592 }
593 break;
594 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
595 return Err(ProtocolError::AddressWitnessError(
597 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
598 ));
599 } else {
600 return Err(ProtocolError::AddressWitnessError(format!(
601 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
602 byte
603 )));
604 }
605 }
606 Some(Err(e)) => {
607 return Err(ProtocolError::AddressWitnessError(format!(
608 "Error parsing multisig script: {:?}",
609 e
610 )))
611 }
612 None => {
613 return Err(ProtocolError::AddressWitnessError(
614 "Incomplete multisig script: unexpected end before OP_N".to_string(),
615 ))
616 }
617 }
618 }
619
620 if threshold > pubkeys.len() {
622 return Err(ProtocolError::AddressWitnessError(format!(
623 "Invalid multisig: threshold {} exceeds number of keys {}",
624 threshold,
625 pubkeys.len()
626 )));
627 }
628
629 match instructions.next() {
631 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
632 if op == OP_CHECKMULTISIG {
633 if instructions.next().is_some() {
635 return Err(ProtocolError::AddressWitnessError(
636 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
637 ));
638 }
639 Ok((threshold, pubkeys))
640 } else if op == OP_CHECKMULTISIGVERIFY {
641 Err(ProtocolError::AddressWitnessError(
642 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
643 .to_string(),
644 ))
645 } else {
646 Err(ProtocolError::AddressWitnessError(format!(
647 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
648 op.to_u8()
649 )))
650 }
651 }
652 _ => Err(ProtocolError::AddressWitnessError(
653 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
654 )),
655 }
656 }
657}
658
659impl std::fmt::Display for PlatformAddress {
660 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661 match self {
662 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
663 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
664 }
665 }
666}
667
668#[derive(Debug, Clone, PartialEq, Eq)]
670pub struct PlatformAddressParseError(pub String);
671
672impl std::fmt::Display for PlatformAddressParseError {
673 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674 write!(f, "{}", self.0)
675 }
676}
677
678impl std::error::Error for PlatformAddressParseError {}
679
680impl FromStr for PlatformAddress {
681 type Err = PlatformAddressParseError;
682
683 fn from_str(s: &str) -> Result<Self, Self::Err> {
694 Self::from_bech32m_string(s)
695 .map(|(addr, _network)| addr)
696 .map_err(|e| PlatformAddressParseError(e.to_string()))
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703 use dashcore::blockdata::opcodes::all::*;
704 use dashcore::hashes::Hash;
705 use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey};
706 use dashcore::PublicKey;
707 use platform_value::BinaryData;
708
709 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
711 let secp = Secp256k1::new();
712 let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key");
713 let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key);
714 let public_key = PublicKey::new(raw_public_key);
715 (secret_key, public_key)
716 }
717
718 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
720 dashcore::signer::sign(data, secret_key.as_ref())
721 .expect("signing should succeed")
722 .to_vec()
723 }
724
725 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
727 let mut script = Vec::new();
728
729 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
731
732 for pubkey in pubkeys {
734 let bytes = pubkey.to_bytes();
735 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
737 }
738
739 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
741
742 script.push(OP_CHECKMULTISIG.to_u8());
744
745 script
746 }
747
748 #[test]
749 fn test_platform_address_from_private_key() {
750 let seed = [1u8; 32];
752 let (secret_key, public_key) = create_keypair(seed);
753
754 let private_key = PrivateKey::new(secret_key, Network::Testnet);
756
757 let address_from_private = PlatformAddress::from(&private_key);
759
760 let pubkey_hash = public_key.pubkey_hash();
763 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
764
765 assert_eq!(
767 address_from_private, address_from_pubkey,
768 "Address derived from private key should match Hash160(compressed_pubkey)"
769 );
770
771 assert!(address_from_private.is_p2pkh());
773 }
774
775 #[test]
776 fn test_p2pkh_verify_signature_success() {
777 let seed = [1u8; 32];
779 let (secret_key, public_key) = create_keypair(seed);
780
781 let pubkey_hash = public_key.pubkey_hash();
783 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
784
785 let signable_bytes = b"test message for P2PKH verification";
787
788 let signature = sign_data(signable_bytes, &secret_key);
790
791 let witness = AddressWitness::P2pkh {
793 signature: BinaryData::new(signature),
794 };
795
796 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
798 assert!(
799 result.is_ok(),
800 "P2PKH verification should succeed: {:?}",
801 result
802 );
803 }
804
805 #[test]
806 fn test_p2pkh_verify_wrong_signature_fails() {
807 let seed = [1u8; 32];
809 let (secret_key, public_key) = create_keypair(seed);
810
811 let pubkey_hash = public_key.pubkey_hash();
813 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
814
815 let sign_bytes = b"original message";
817 let verify_bytes = b"different message";
818 let signature = sign_data(sign_bytes, &secret_key);
819
820 let witness = AddressWitness::P2pkh {
822 signature: BinaryData::new(signature),
823 };
824
825 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
827 assert!(
828 result.is_err(),
829 "P2PKH verification should fail with wrong data"
830 );
831 }
832
833 #[test]
834 fn test_p2pkh_verify_wrong_key_fails() {
835 let seed1 = [1u8; 32];
837 let seed2 = [2u8; 32];
838 let (_secret_key1, public_key1) = create_keypair(seed1);
839 let (secret_key2, _public_key2) = create_keypair(seed2);
840
841 let pubkey_hash = public_key1.pubkey_hash();
843 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
844
845 let signable_bytes = b"test message";
847 let signature = sign_data(signable_bytes, &secret_key2);
848
849 let witness = AddressWitness::P2pkh {
851 signature: BinaryData::new(signature),
852 };
853
854 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
856 assert!(
857 result.is_err(),
858 "P2PKH verification should fail when signed with wrong key"
859 );
860 }
861
862 #[test]
867 fn test_p2sh_2_of_3_multisig_verify_success() {
868 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
870 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
871 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
872
873 let redeem_script = create_multisig_script(2, &pubkeys);
875
876 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
878 let script_hash = script_buf.script_hash();
879 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
880
881 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
883
884 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
886 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
887
888 let witness = AddressWitness::P2sh {
891 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
892 redeem_script: BinaryData::new(redeem_script),
893 };
894
895 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
897 assert!(
898 result.is_ok(),
899 "P2SH 2-of-3 multisig verification should succeed: {:?}",
900 result
901 );
902 }
903
904 #[test]
905 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
906 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
908 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
909 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
910
911 let redeem_script = create_multisig_script(2, &pubkeys);
913
914 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
916 let script_hash = script_buf.script_hash();
917 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
918
919 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
921
922 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
924 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
925
926 let witness = AddressWitness::P2sh {
928 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
929 redeem_script: BinaryData::new(redeem_script),
930 };
931
932 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
934 assert!(
935 result.is_ok(),
936 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
937 result
938 );
939 }
940
941 #[test]
942 fn test_p2sh_not_enough_signatures_fails() {
943 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
945 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
946 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
947
948 let redeem_script = create_multisig_script(2, &pubkeys);
950
951 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
953 let script_hash = script_buf.script_hash();
954 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
955
956 let signable_bytes = b"test message";
958
959 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
961
962 let witness = AddressWitness::P2sh {
964 signatures: vec![BinaryData::new(sig0)],
965 redeem_script: BinaryData::new(redeem_script),
966 };
967
968 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
970 assert!(
971 result.is_err(),
972 "P2SH should fail with only 1 signature when 2 required"
973 );
974 assert!(
975 result.unwrap_err().to_string().contains("Not enough"),
976 "Error should mention not enough signatures"
977 );
978 }
979
980 #[test]
981 fn test_p2sh_wrong_script_hash_fails() {
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 wrong_hash = [0xABu8; 20];
992 let address = PlatformAddress::P2sh(wrong_hash);
993
994 let signable_bytes = b"test message";
996
997 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
999 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
1000
1001 let witness = AddressWitness::P2sh {
1003 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
1004 redeem_script: BinaryData::new(redeem_script),
1005 };
1006
1007 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
1009 assert!(
1010 result.is_err(),
1011 "P2SH should fail when script hash doesn't match address"
1012 );
1013 assert!(
1014 result
1015 .unwrap_err()
1016 .to_string()
1017 .contains("does not match address hash"),
1018 "Error should mention hash mismatch"
1019 );
1020 }
1021
1022 #[test]
1023 fn test_p2pkh_and_p2sh_together() {
1024 let p2pkh_seed = [10u8; 32];
1028 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
1029 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
1030 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
1031
1032 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
1034 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
1035 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
1036 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
1037 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
1038 let script_hash = script_buf.script_hash();
1039 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
1040
1041 let signable_bytes = b"combined transaction data to redeem both outputs";
1043
1044 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
1046 let p2pkh_witness = AddressWitness::P2pkh {
1047 signature: BinaryData::new(p2pkh_sig),
1048 };
1049 let p2pkh_result =
1050 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1051 assert!(
1052 p2pkh_result.is_ok(),
1053 "P2PKH redemption should succeed: {:?}",
1054 p2pkh_result
1055 );
1056
1057 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
1059 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
1060 let p2sh_witness = AddressWitness::P2sh {
1061 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
1062 redeem_script: BinaryData::new(redeem_script),
1063 };
1064 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1065 assert!(
1066 p2sh_result.is_ok(),
1067 "P2SH redemption should succeed: {:?}",
1068 p2sh_result
1069 );
1070
1071 }
1073
1074 #[test]
1075 fn test_witness_type_mismatch() {
1076 let seed = [1u8; 32];
1078 let (_, public_key) = create_keypair(seed);
1079 let pubkey_hash = public_key.pubkey_hash();
1080 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
1081
1082 let p2sh_hash = [0xABu8; 20];
1084 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
1085
1086 let signable_bytes = b"test data";
1087
1088 let p2sh_witness = AddressWitness::P2sh {
1090 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
1091 redeem_script: BinaryData::new(vec![0x52]),
1092 };
1093 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1094 assert!(result.is_err());
1095 assert!(result
1096 .unwrap_err()
1097 .to_string()
1098 .contains("P2PKH address requires P2pkh witness"));
1099
1100 let p2pkh_witness = AddressWitness::P2pkh {
1102 signature: BinaryData::new(vec![0x30, 0x44]),
1103 };
1104 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1105 assert!(result.is_err());
1106 assert!(result
1107 .unwrap_err()
1108 .to_string()
1109 .contains("P2SH address requires P2sh witness"));
1110 }
1111
1112 #[test]
1117 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1118 let hash: [u8; 20] = [
1120 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1121 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1122 ];
1123 let address = PlatformAddress::P2pkh(hash);
1124
1125 let encoded = address.to_bech32m_string(Network::Mainnet);
1127
1128 assert_eq!(
1130 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1131 "P2PKH mainnet encoding mismatch"
1132 );
1133
1134 let (decoded, network) =
1136 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1137 assert_eq!(decoded, address);
1138 assert_eq!(network, Network::Mainnet);
1139 }
1140
1141 #[test]
1142 fn test_bech32m_p2pkh_testnet_roundtrip() {
1143 let hash: [u8; 20] = [
1145 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1146 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1147 ];
1148 let address = PlatformAddress::P2pkh(hash);
1149
1150 let encoded = address.to_bech32m_string(Network::Testnet);
1152
1153 assert_eq!(
1155 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1156 "P2PKH testnet encoding mismatch"
1157 );
1158
1159 let (decoded, network) =
1161 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1162 assert_eq!(decoded, address);
1163 assert_eq!(network, Network::Testnet);
1164 }
1165
1166 #[test]
1167 fn test_bech32m_p2sh_mainnet_roundtrip() {
1168 let hash: [u8; 20] = [
1170 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1171 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1172 ];
1173 let address = PlatformAddress::P2sh(hash);
1174
1175 let encoded = address.to_bech32m_string(Network::Mainnet);
1177
1178 assert_eq!(
1180 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1181 "P2SH mainnet encoding mismatch"
1182 );
1183
1184 let (decoded, network) =
1186 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1187 assert_eq!(decoded, address);
1188 assert_eq!(network, Network::Mainnet);
1189 }
1190
1191 #[test]
1192 fn test_bech32m_p2sh_testnet_roundtrip() {
1193 let hash: [u8; 20] = [
1195 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1196 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1197 ];
1198 let address = PlatformAddress::P2sh(hash);
1199
1200 let encoded = address.to_bech32m_string(Network::Testnet);
1202
1203 assert_eq!(
1205 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1206 "P2SH testnet encoding mismatch"
1207 );
1208
1209 let (decoded, network) =
1211 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1212 assert_eq!(decoded, address);
1213 assert_eq!(network, Network::Testnet);
1214 }
1215
1216 #[test]
1217 fn test_bech32m_devnet_uses_testnet_hrp() {
1218 let hash: [u8; 20] = [0xAB; 20];
1219 let address = PlatformAddress::P2pkh(hash);
1220
1221 let encoded = address.to_bech32m_string(Network::Devnet);
1223 assert!(
1224 encoded.starts_with("tdash1"),
1225 "Devnet address should start with 'tdash1', got: {}",
1226 encoded
1227 );
1228 }
1229
1230 #[test]
1231 fn test_bech32m_regtest_uses_testnet_hrp() {
1232 let hash: [u8; 20] = [0xAB; 20];
1233 let address = PlatformAddress::P2pkh(hash);
1234
1235 let encoded = address.to_bech32m_string(Network::Regtest);
1237 assert!(
1238 encoded.starts_with("tdash1"),
1239 "Regtest address should start with 'tdash1', got: {}",
1240 encoded
1241 );
1242 }
1243
1244 #[test]
1245 fn test_bech32m_invalid_hrp_fails() {
1246 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1248 let payload: [u8; 21] = [0x00; 21];
1249 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1250
1251 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1252 assert!(result.is_err());
1253 let err = result.unwrap_err();
1254 assert!(
1255 err.to_string().contains("invalid HRP"),
1256 "Error should mention invalid HRP: {}",
1257 err
1258 );
1259 }
1260
1261 #[test]
1262 fn test_bech32m_invalid_checksum_fails() {
1263 let hash: [u8; 20] = [0xAB; 20];
1265 let address = PlatformAddress::P2pkh(hash);
1266 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1267
1268 let last_char = encoded.pop().unwrap();
1270 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1271 encoded.push(corrupted_char);
1272
1273 let result = PlatformAddress::from_bech32m_string(&encoded);
1274 assert!(result.is_err(), "Should fail with corrupted checksum");
1275 }
1276
1277 #[test]
1278 fn test_bech32m_invalid_type_byte_fails() {
1279 let hrp = Hrp::parse("dash").unwrap();
1282 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1284
1285 let result = PlatformAddress::from_bech32m_string(&encoded);
1286 assert!(result.is_err());
1287 let err = result.unwrap_err();
1288 assert!(
1289 err.to_string().contains("invalid address type"),
1290 "Error should mention invalid type: {}",
1291 err
1292 );
1293 }
1294
1295 #[test]
1296 fn test_bech32m_too_short_fails() {
1297 let hrp = Hrp::parse("dash").unwrap();
1299 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1301
1302 let result = PlatformAddress::from_bech32m_string(&encoded);
1303 assert!(result.is_err());
1304 let err = result.unwrap_err();
1305 assert!(
1306 err.to_string().contains("invalid Platform address length"),
1307 "Error should mention invalid length: {}",
1308 err
1309 );
1310 }
1311
1312 #[test]
1313 fn test_bech32m_from_str_trait() {
1314 let hash: [u8; 20] = [
1316 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1317 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1318 ];
1319 let original = PlatformAddress::P2pkh(hash);
1320
1321 let encoded = original.to_bech32m_string(Network::Testnet);
1323 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1324
1325 assert_eq!(parsed, original);
1326 }
1327
1328 #[test]
1329 fn test_bech32m_case_insensitive() {
1330 let hash: [u8; 20] = [0xAB; 20];
1333 let address = PlatformAddress::P2pkh(hash);
1334
1335 let lowercase = address.to_bech32m_string(Network::Mainnet);
1336 let uppercase = lowercase.to_uppercase();
1337
1338 let (decoded_lower, _) = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1340 let (decoded_upper, _) = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1341
1342 assert_eq!(decoded_lower, decoded_upper);
1343 assert_eq!(decoded_lower, address);
1344 }
1345
1346 #[test]
1347 fn test_bech32m_all_zeros_p2pkh() {
1348 let address = PlatformAddress::P2pkh([0u8; 20]);
1350 let encoded = address.to_bech32m_string(Network::Mainnet);
1351 let (decoded, _) = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1352 assert_eq!(decoded, address);
1353 }
1354
1355 #[test]
1356 fn test_bech32m_all_ones_p2sh() {
1357 let address = PlatformAddress::P2sh([0xFF; 20]);
1359 let encoded = address.to_bech32m_string(Network::Mainnet);
1360 let (decoded, _) = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1361 assert_eq!(decoded, address);
1362 }
1363
1364 #[test]
1365 fn test_hrp_for_network() {
1366 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1367 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1368 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1369 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1370 }
1371
1372 #[test]
1373 fn test_storage_bytes_format() {
1374 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1378 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1379
1380 let p2pkh_bytes = p2pkh.to_bytes();
1381 let p2sh_bytes = p2sh.to_bytes();
1382
1383 assert_eq!(p2pkh_bytes.len(), 21);
1385 assert_eq!(p2sh_bytes.len(), 21);
1386 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1387 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1388
1389 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1391 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1392 assert_eq!(p2pkh_decoded, p2pkh);
1393 assert_eq!(p2sh_decoded, p2sh);
1394 }
1395
1396 #[test]
1397 fn test_bech32m_uses_different_type_bytes_than_storage() {
1398 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1401 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1402
1403 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1405 assert_eq!(p2sh.to_bytes()[0], 0x01);
1406
1407 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1409 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1410
1411 let (p2pkh_decoded, _) = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1412 let (p2sh_decoded, _) = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1413
1414 assert_eq!(p2pkh_decoded, p2pkh);
1415 assert_eq!(p2sh_decoded, p2sh);
1416 }
1417}