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#[cfg_attr(
39 feature = "serde-conversion",
40 derive(Serialize, Deserialize),
41 serde(rename_all = "camelCase")
42)]
43#[platform_serialize(unversioned)]
44pub enum PlatformAddress {
45 P2pkh([u8; 20]),
49 P2sh([u8; 20]),
53}
54
55impl TryFrom<Address> for PlatformAddress {
56 type Error = ProtocolError;
57
58 fn try_from(address: Address) -> Result<Self, Self::Error> {
59 match address.payload() {
60 Payload::PubkeyHash(hash) => Ok(PlatformAddress::P2pkh(*hash.as_ref())),
61 Payload::ScriptHash(hash) => Ok(PlatformAddress::P2sh(*hash.as_ref())),
62 _ => Err(ProtocolError::DecodingError(
63 "unsupported address type for PlatformAddress: only P2PKH and P2SH are supported"
64 .to_string(),
65 )),
66 }
67 }
68}
69
70impl From<&PrivateKey> for PlatformAddress {
71 fn from(private_key: &PrivateKey) -> Self {
76 let secp = Secp256k1::new();
77 let pubkey_hash = private_key.public_key(&secp).pubkey_hash();
78 PlatformAddress::P2pkh(*pubkey_hash.as_byte_array())
79 }
80}
81
82impl Default for PlatformAddress {
83 fn default() -> Self {
84 PlatformAddress::P2pkh([0u8; 20])
85 }
86}
87
88pub const PLATFORM_HRP_MAINNET: &str = "dash";
90pub const PLATFORM_HRP_TESTNET: &str = "tdash";
92
93impl PlatformAddress {
94 pub const P2PKH_TYPE: u8 = 0xb0;
96 pub const P2SH_TYPE: u8 = 0x80;
98
99 pub fn hrp_for_network(network: Network) -> &'static str {
105 match network {
106 Network::Mainnet => PLATFORM_HRP_MAINNET,
107 Network::Testnet | Network::Devnet | Network::Regtest => PLATFORM_HRP_TESTNET,
108 _ => PLATFORM_HRP_TESTNET,
110 }
111 }
112
113 pub fn to_bech32m_string(&self, network: Network) -> String {
130 let hrp_str = Self::hrp_for_network(network);
131 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
132
133 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
136 match self {
137 PlatformAddress::P2pkh(hash) => {
138 payload.push(Self::P2PKH_TYPE);
139 payload.extend_from_slice(hash);
140 }
141 PlatformAddress::P2sh(hash) => {
142 payload.push(Self::P2SH_TYPE);
143 payload.extend_from_slice(hash);
144 }
145 }
146
147 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
149 }
150
151 pub fn from_bech32m_string(s: &str) -> Result<(Self, Network), ProtocolError> {
160 let (hrp, data) =
162 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
163
164 let hrp_lower = hrp.as_str().to_ascii_lowercase();
166 let network = match hrp_lower.as_str() {
167 s if s == PLATFORM_HRP_MAINNET => Network::Mainnet,
168 s if s == PLATFORM_HRP_TESTNET => Network::Testnet,
169 _ => {
170 return Err(ProtocolError::DecodingError(format!(
171 "invalid HRP '{}': expected '{}' or '{}'",
172 hrp, PLATFORM_HRP_MAINNET, PLATFORM_HRP_TESTNET
173 )))
174 }
175 };
176
177 if data.len() != 1 + ADDRESS_HASH_SIZE {
179 return Err(ProtocolError::DecodingError(format!(
180 "invalid Platform address length: expected {} bytes, got {}",
181 1 + ADDRESS_HASH_SIZE,
182 data.len()
183 )));
184 }
185
186 let address_type = data[0];
188 let hash: [u8; 20] = data[1..21]
189 .try_into()
190 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
191
192 let address = match address_type {
193 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
194 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
195 _ => Err(ProtocolError::DecodingError(format!(
196 "invalid address type: 0x{:02x}",
197 address_type
198 ))),
199 }?;
200
201 Ok((address, network))
202 }
203
204 pub fn to_address_with_network(&self, network: Network) -> Address {
206 match self {
207 PlatformAddress::P2pkh(hash) => Address::new(
208 network,
209 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
210 ),
211 PlatformAddress::P2sh(hash) => Address::new(
212 network,
213 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
214 ),
215 }
216 }
217
218 pub fn to_bytes(&self) -> Vec<u8> {
224 bincode::encode_to_vec(self, bincode::config::standard())
225 .expect("PlatformAddress serialization cannot fail")
226 }
227
228 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
231 use base64::engine::general_purpose::STANDARD;
232 use base64::Engine;
233
234 let mut bytes = self.to_bytes();
235 bytes.extend_from_slice(&nonce.to_be_bytes());
236
237 STANDARD.encode(bytes)
238 }
239
240 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
245 let (address, _): (Self, usize) =
246 bincode::decode_from_slice(bytes, bincode::config::standard()).map_err(|e| {
247 ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e))
248 })?;
249 Ok(address)
250 }
251
252 pub fn hash(&self) -> &[u8; 20] {
254 match self {
255 PlatformAddress::P2pkh(hash) => hash,
256 PlatformAddress::P2sh(hash) => hash,
257 }
258 }
259
260 pub fn is_p2pkh(&self) -> bool {
262 matches!(self, PlatformAddress::P2pkh(_))
263 }
264
265 pub fn is_p2sh(&self) -> bool {
267 matches!(self, PlatformAddress::P2sh(_))
268 }
269
270 pub fn verify_bytes_against_witness(
290 &self,
291 witness: &AddressWitness,
292 signable_bytes: &[u8],
293 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
294 match (self, witness) {
295 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
296 let data_hash = dashcore::signer::double_sha(signable_bytes);
304 dashcore::signer::verify_hash_signature(
305 &data_hash,
306 signature.as_slice(),
307 pubkey_hash,
308 )
309 .map_err(|e| {
310 ProtocolError::AddressWitnessError(format!(
311 "P2PKH signature verification failed: {}",
312 e
313 ))
314 })?;
315
316 Ok(AddressWitnessVerificationOperations::for_p2pkh(
317 signable_bytes.len(),
318 ))
319 }
320 (
321 PlatformAddress::P2sh(script_hash),
322 AddressWitness::P2sh {
323 signatures,
324 redeem_script,
325 },
326 ) => {
327 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
329 let computed_hash = script.script_hash();
330 if computed_hash.as_byte_array() != script_hash {
331 return Err(ProtocolError::AddressWitnessError(format!(
332 "Script hash {} does not match address hash {}",
333 hex::encode(computed_hash.as_byte_array()),
334 hex::encode(script_hash)
335 )));
336 }
337
338 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
341
342 let valid_signatures: Vec<_> = signatures
344 .iter()
345 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
346 .collect();
347
348 if valid_signatures.len() < threshold {
349 return Err(ProtocolError::AddressWitnessError(format!(
350 "Not enough signatures: got {}, need {}",
351 valid_signatures.len(),
352 threshold
353 )));
354 }
355
356 let mut sig_idx = 0;
359 let mut pubkey_idx = 0;
360 let mut matched = 0;
361 let mut signature_verifications: u16 = 0;
362
363 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
364 let msg = Message::from_digest(signable_bytes_hash);
365 let secp = Secp256k1::new();
366
367 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
368 signature_verifications += 1;
369
370 let sig = RecoverableSignature::from_compact_signature(
371 valid_signatures[sig_idx].as_slice(),
372 )
373 .map_err(|e| {
374 ProtocolError::AddressWitnessError(format!(
375 "Invalid signature format: {}",
376 e
377 ))
378 })?;
379
380 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
381 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
382 })?;
383
384 if secp
385 .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner)
386 .is_ok()
387 {
388 matched += 1;
389 sig_idx += 1;
390 }
391 pubkey_idx += 1;
392 }
393
394 if matched >= threshold {
395 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
396 signature_verifications,
397 signable_bytes.len(),
398 ))
399 } else {
400 Err(ProtocolError::AddressWitnessError(format!(
401 "Not enough valid signatures: verified {}, need {}",
402 matched, threshold
403 )))
404 }
405 }
406 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
407 Err(ProtocolError::AddressWitnessError(
408 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
409 ))
410 }
411 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
412 Err(ProtocolError::AddressWitnessError(
413 "P2SH address requires P2sh witness, got P2pkh".to_string(),
414 ))
415 }
416 }
417 }
418
419 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
434 use dashcore::blockdata::opcodes::all::*;
435
436 let mut instructions = script.instructions();
437 let mut pubkeys = Vec::new();
438
439 let threshold = match instructions.next() {
441 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
442 let byte = op.to_u8();
443 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
444 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
445 } else {
446 return Err(ProtocolError::AddressWitnessError(format!(
447 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
448 First opcode was 0x{:02x}, expected OP_1 through OP_16",
449 byte
450 )));
451 }
452 }
453 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
454 return Err(ProtocolError::AddressWitnessError(
455 "Unsupported P2SH script type: only standard multisig is supported. \
456 Script starts with a data push instead of OP_M threshold."
457 .to_string(),
458 ))
459 }
460 Some(Err(e)) => {
461 return Err(ProtocolError::AddressWitnessError(format!(
462 "Error parsing P2SH script: {:?}",
463 e
464 )))
465 }
466 None => {
467 return Err(ProtocolError::AddressWitnessError(
468 "Empty P2SH redeem script".to_string(),
469 ))
470 }
471 };
472
473 loop {
475 match instructions.next() {
476 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
477 let len = bytes.len();
479 if len != 33 {
480 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
481 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
482 len,
483 ),
484 ));
485 }
486 pubkeys.push(bytes.as_bytes().to_vec());
487 }
488 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
489 let byte = op.to_u8();
490 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
491 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
493 if pubkeys.len() != n {
494 return Err(ProtocolError::AddressWitnessError(format!(
495 "Multisig script declares {} keys but contains {}",
496 n,
497 pubkeys.len()
498 )));
499 }
500 break;
501 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
502 return Err(ProtocolError::AddressWitnessError(
504 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
505 ));
506 } else {
507 return Err(ProtocolError::AddressWitnessError(format!(
508 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
509 byte
510 )));
511 }
512 }
513 Some(Err(e)) => {
514 return Err(ProtocolError::AddressWitnessError(format!(
515 "Error parsing multisig script: {:?}",
516 e
517 )))
518 }
519 None => {
520 return Err(ProtocolError::AddressWitnessError(
521 "Incomplete multisig script: unexpected end before OP_N".to_string(),
522 ))
523 }
524 }
525 }
526
527 if threshold > pubkeys.len() {
529 return Err(ProtocolError::AddressWitnessError(format!(
530 "Invalid multisig: threshold {} exceeds number of keys {}",
531 threshold,
532 pubkeys.len()
533 )));
534 }
535
536 match instructions.next() {
538 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
539 if op == OP_CHECKMULTISIG {
540 if instructions.next().is_some() {
542 return Err(ProtocolError::AddressWitnessError(
543 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
544 ));
545 }
546 Ok((threshold, pubkeys))
547 } else if op == OP_CHECKMULTISIGVERIFY {
548 Err(ProtocolError::AddressWitnessError(
549 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
550 .to_string(),
551 ))
552 } else {
553 Err(ProtocolError::AddressWitnessError(format!(
554 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
555 op.to_u8()
556 )))
557 }
558 }
559 _ => Err(ProtocolError::AddressWitnessError(
560 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
561 )),
562 }
563 }
564}
565
566impl std::fmt::Display for PlatformAddress {
567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568 match self {
569 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
570 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
571 }
572 }
573}
574
575#[derive(Debug, Clone, PartialEq, Eq)]
577pub struct PlatformAddressParseError(pub String);
578
579impl std::fmt::Display for PlatformAddressParseError {
580 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
581 write!(f, "{}", self.0)
582 }
583}
584
585impl std::error::Error for PlatformAddressParseError {}
586
587impl FromStr for PlatformAddress {
588 type Err = PlatformAddressParseError;
589
590 fn from_str(s: &str) -> Result<Self, Self::Err> {
601 Self::from_bech32m_string(s)
602 .map(|(addr, _network)| addr)
603 .map_err(|e| PlatformAddressParseError(e.to_string()))
604 }
605}
606
607#[cfg(test)]
608mod tests {
609 use super::*;
610 use dashcore::blockdata::opcodes::all::*;
611 use dashcore::hashes::Hash;
612 use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey};
613 use dashcore::PublicKey;
614 use platform_value::BinaryData;
615
616 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
618 let secp = Secp256k1::new();
619 let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key");
620 let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key);
621 let public_key = PublicKey::new(raw_public_key);
622 (secret_key, public_key)
623 }
624
625 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
627 dashcore::signer::sign(data, secret_key.as_ref())
628 .expect("signing should succeed")
629 .to_vec()
630 }
631
632 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
634 let mut script = Vec::new();
635
636 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
638
639 for pubkey in pubkeys {
641 let bytes = pubkey.to_bytes();
642 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
644 }
645
646 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
648
649 script.push(OP_CHECKMULTISIG.to_u8());
651
652 script
653 }
654
655 #[test]
656 fn test_platform_address_from_private_key() {
657 let seed = [1u8; 32];
659 let (secret_key, public_key) = create_keypair(seed);
660
661 let private_key = PrivateKey::new(secret_key, Network::Testnet);
663
664 let address_from_private = PlatformAddress::from(&private_key);
666
667 let pubkey_hash = public_key.pubkey_hash();
670 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
671
672 assert_eq!(
674 address_from_private, address_from_pubkey,
675 "Address derived from private key should match Hash160(compressed_pubkey)"
676 );
677
678 assert!(address_from_private.is_p2pkh());
680 }
681
682 #[test]
683 fn test_p2pkh_verify_signature_success() {
684 let seed = [1u8; 32];
686 let (secret_key, public_key) = create_keypair(seed);
687
688 let pubkey_hash = public_key.pubkey_hash();
690 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
691
692 let signable_bytes = b"test message for P2PKH verification";
694
695 let signature = sign_data(signable_bytes, &secret_key);
697
698 let witness = AddressWitness::P2pkh {
700 signature: BinaryData::new(signature),
701 };
702
703 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
705 assert!(
706 result.is_ok(),
707 "P2PKH verification should succeed: {:?}",
708 result
709 );
710 }
711
712 #[test]
713 fn test_p2pkh_verify_wrong_signature_fails() {
714 let seed = [1u8; 32];
716 let (secret_key, public_key) = create_keypair(seed);
717
718 let pubkey_hash = public_key.pubkey_hash();
720 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
721
722 let sign_bytes = b"original message";
724 let verify_bytes = b"different message";
725 let signature = sign_data(sign_bytes, &secret_key);
726
727 let witness = AddressWitness::P2pkh {
729 signature: BinaryData::new(signature),
730 };
731
732 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
734 assert!(
735 result.is_err(),
736 "P2PKH verification should fail with wrong data"
737 );
738 }
739
740 #[test]
741 fn test_p2pkh_verify_wrong_key_fails() {
742 let seed1 = [1u8; 32];
744 let seed2 = [2u8; 32];
745 let (_secret_key1, public_key1) = create_keypair(seed1);
746 let (secret_key2, _public_key2) = create_keypair(seed2);
747
748 let pubkey_hash = public_key1.pubkey_hash();
750 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
751
752 let signable_bytes = b"test message";
754 let signature = sign_data(signable_bytes, &secret_key2);
755
756 let witness = AddressWitness::P2pkh {
758 signature: BinaryData::new(signature),
759 };
760
761 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
763 assert!(
764 result.is_err(),
765 "P2PKH verification should fail when signed with wrong key"
766 );
767 }
768
769 #[test]
774 fn test_p2sh_2_of_3_multisig_verify_success() {
775 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
777 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
778 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
779
780 let redeem_script = create_multisig_script(2, &pubkeys);
782
783 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
785 let script_hash = script_buf.script_hash();
786 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
787
788 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
790
791 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
793 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
794
795 let witness = AddressWitness::P2sh {
798 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
799 redeem_script: BinaryData::new(redeem_script),
800 };
801
802 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
804 assert!(
805 result.is_ok(),
806 "P2SH 2-of-3 multisig verification should succeed: {:?}",
807 result
808 );
809 }
810
811 #[test]
812 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
813 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
815 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
816 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
817
818 let redeem_script = create_multisig_script(2, &pubkeys);
820
821 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
823 let script_hash = script_buf.script_hash();
824 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
825
826 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
828
829 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
831 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
832
833 let witness = AddressWitness::P2sh {
835 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
836 redeem_script: BinaryData::new(redeem_script),
837 };
838
839 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
841 assert!(
842 result.is_ok(),
843 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
844 result
845 );
846 }
847
848 #[test]
849 fn test_p2sh_not_enough_signatures_fails() {
850 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
852 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
853 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
854
855 let redeem_script = create_multisig_script(2, &pubkeys);
857
858 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
860 let script_hash = script_buf.script_hash();
861 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
862
863 let signable_bytes = b"test message";
865
866 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
868
869 let witness = AddressWitness::P2sh {
871 signatures: vec![BinaryData::new(sig0)],
872 redeem_script: BinaryData::new(redeem_script),
873 };
874
875 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
877 assert!(
878 result.is_err(),
879 "P2SH should fail with only 1 signature when 2 required"
880 );
881 assert!(
882 result.unwrap_err().to_string().contains("Not enough"),
883 "Error should mention not enough signatures"
884 );
885 }
886
887 #[test]
888 fn test_p2sh_wrong_script_hash_fails() {
889 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
891 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
892 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
893
894 let redeem_script = create_multisig_script(2, &pubkeys);
896
897 let wrong_hash = [0xABu8; 20];
899 let address = PlatformAddress::P2sh(wrong_hash);
900
901 let signable_bytes = b"test message";
903
904 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
906 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
907
908 let witness = AddressWitness::P2sh {
910 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
911 redeem_script: BinaryData::new(redeem_script),
912 };
913
914 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
916 assert!(
917 result.is_err(),
918 "P2SH should fail when script hash doesn't match address"
919 );
920 assert!(
921 result
922 .unwrap_err()
923 .to_string()
924 .contains("does not match address hash"),
925 "Error should mention hash mismatch"
926 );
927 }
928
929 #[test]
930 fn test_p2pkh_and_p2sh_together() {
931 let p2pkh_seed = [10u8; 32];
935 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
936 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
937 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
938
939 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
941 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
942 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
943 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
944 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
945 let script_hash = script_buf.script_hash();
946 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
947
948 let signable_bytes = b"combined transaction data to redeem both outputs";
950
951 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
953 let p2pkh_witness = AddressWitness::P2pkh {
954 signature: BinaryData::new(p2pkh_sig),
955 };
956 let p2pkh_result =
957 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
958 assert!(
959 p2pkh_result.is_ok(),
960 "P2PKH redemption should succeed: {:?}",
961 p2pkh_result
962 );
963
964 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
966 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
967 let p2sh_witness = AddressWitness::P2sh {
968 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
969 redeem_script: BinaryData::new(redeem_script),
970 };
971 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
972 assert!(
973 p2sh_result.is_ok(),
974 "P2SH redemption should succeed: {:?}",
975 p2sh_result
976 );
977
978 }
980
981 #[test]
982 fn test_witness_type_mismatch() {
983 let seed = [1u8; 32];
985 let (_, public_key) = create_keypair(seed);
986 let pubkey_hash = public_key.pubkey_hash();
987 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
988
989 let p2sh_hash = [0xABu8; 20];
991 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
992
993 let signable_bytes = b"test data";
994
995 let p2sh_witness = AddressWitness::P2sh {
997 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
998 redeem_script: BinaryData::new(vec![0x52]),
999 };
1000 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
1001 assert!(result.is_err());
1002 assert!(result
1003 .unwrap_err()
1004 .to_string()
1005 .contains("P2PKH address requires P2pkh witness"));
1006
1007 let p2pkh_witness = AddressWitness::P2pkh {
1009 signature: BinaryData::new(vec![0x30, 0x44]),
1010 };
1011 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1012 assert!(result.is_err());
1013 assert!(result
1014 .unwrap_err()
1015 .to_string()
1016 .contains("P2SH address requires P2sh witness"));
1017 }
1018
1019 #[test]
1024 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1025 let hash: [u8; 20] = [
1027 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1028 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1029 ];
1030 let address = PlatformAddress::P2pkh(hash);
1031
1032 let encoded = address.to_bech32m_string(Network::Mainnet);
1034
1035 assert_eq!(
1037 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1038 "P2PKH mainnet encoding mismatch"
1039 );
1040
1041 let (decoded, network) =
1043 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1044 assert_eq!(decoded, address);
1045 assert_eq!(network, Network::Mainnet);
1046 }
1047
1048 #[test]
1049 fn test_bech32m_p2pkh_testnet_roundtrip() {
1050 let hash: [u8; 20] = [
1052 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1053 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1054 ];
1055 let address = PlatformAddress::P2pkh(hash);
1056
1057 let encoded = address.to_bech32m_string(Network::Testnet);
1059
1060 assert_eq!(
1062 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1063 "P2PKH testnet encoding mismatch"
1064 );
1065
1066 let (decoded, network) =
1068 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1069 assert_eq!(decoded, address);
1070 assert_eq!(network, Network::Testnet);
1071 }
1072
1073 #[test]
1074 fn test_bech32m_p2sh_mainnet_roundtrip() {
1075 let hash: [u8; 20] = [
1077 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1078 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1079 ];
1080 let address = PlatformAddress::P2sh(hash);
1081
1082 let encoded = address.to_bech32m_string(Network::Mainnet);
1084
1085 assert_eq!(
1087 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1088 "P2SH mainnet encoding mismatch"
1089 );
1090
1091 let (decoded, network) =
1093 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1094 assert_eq!(decoded, address);
1095 assert_eq!(network, Network::Mainnet);
1096 }
1097
1098 #[test]
1099 fn test_bech32m_p2sh_testnet_roundtrip() {
1100 let hash: [u8; 20] = [
1102 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1103 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1104 ];
1105 let address = PlatformAddress::P2sh(hash);
1106
1107 let encoded = address.to_bech32m_string(Network::Testnet);
1109
1110 assert_eq!(
1112 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1113 "P2SH testnet encoding mismatch"
1114 );
1115
1116 let (decoded, network) =
1118 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1119 assert_eq!(decoded, address);
1120 assert_eq!(network, Network::Testnet);
1121 }
1122
1123 #[test]
1124 fn test_bech32m_devnet_uses_testnet_hrp() {
1125 let hash: [u8; 20] = [0xAB; 20];
1126 let address = PlatformAddress::P2pkh(hash);
1127
1128 let encoded = address.to_bech32m_string(Network::Devnet);
1130 assert!(
1131 encoded.starts_with("tdash1"),
1132 "Devnet address should start with 'tdash1', got: {}",
1133 encoded
1134 );
1135 }
1136
1137 #[test]
1138 fn test_bech32m_regtest_uses_testnet_hrp() {
1139 let hash: [u8; 20] = [0xAB; 20];
1140 let address = PlatformAddress::P2pkh(hash);
1141
1142 let encoded = address.to_bech32m_string(Network::Regtest);
1144 assert!(
1145 encoded.starts_with("tdash1"),
1146 "Regtest address should start with 'tdash1', got: {}",
1147 encoded
1148 );
1149 }
1150
1151 #[test]
1152 fn test_bech32m_invalid_hrp_fails() {
1153 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1155 let payload: [u8; 21] = [0x00; 21];
1156 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1157
1158 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1159 assert!(result.is_err());
1160 let err = result.unwrap_err();
1161 assert!(
1162 err.to_string().contains("invalid HRP"),
1163 "Error should mention invalid HRP: {}",
1164 err
1165 );
1166 }
1167
1168 #[test]
1169 fn test_bech32m_invalid_checksum_fails() {
1170 let hash: [u8; 20] = [0xAB; 20];
1172 let address = PlatformAddress::P2pkh(hash);
1173 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1174
1175 let last_char = encoded.pop().unwrap();
1177 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1178 encoded.push(corrupted_char);
1179
1180 let result = PlatformAddress::from_bech32m_string(&encoded);
1181 assert!(result.is_err(), "Should fail with corrupted checksum");
1182 }
1183
1184 #[test]
1185 fn test_bech32m_invalid_type_byte_fails() {
1186 let hrp = Hrp::parse("dash").unwrap();
1189 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1191
1192 let result = PlatformAddress::from_bech32m_string(&encoded);
1193 assert!(result.is_err());
1194 let err = result.unwrap_err();
1195 assert!(
1196 err.to_string().contains("invalid address type"),
1197 "Error should mention invalid type: {}",
1198 err
1199 );
1200 }
1201
1202 #[test]
1203 fn test_bech32m_too_short_fails() {
1204 let hrp = Hrp::parse("dash").unwrap();
1206 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1208
1209 let result = PlatformAddress::from_bech32m_string(&encoded);
1210 assert!(result.is_err());
1211 let err = result.unwrap_err();
1212 assert!(
1213 err.to_string().contains("invalid Platform address length"),
1214 "Error should mention invalid length: {}",
1215 err
1216 );
1217 }
1218
1219 #[test]
1220 fn test_bech32m_from_str_trait() {
1221 let hash: [u8; 20] = [
1223 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1224 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1225 ];
1226 let original = PlatformAddress::P2pkh(hash);
1227
1228 let encoded = original.to_bech32m_string(Network::Testnet);
1230 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1231
1232 assert_eq!(parsed, original);
1233 }
1234
1235 #[test]
1236 fn test_bech32m_case_insensitive() {
1237 let hash: [u8; 20] = [0xAB; 20];
1240 let address = PlatformAddress::P2pkh(hash);
1241
1242 let lowercase = address.to_bech32m_string(Network::Mainnet);
1243 let uppercase = lowercase.to_uppercase();
1244
1245 let (decoded_lower, _) = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1247 let (decoded_upper, _) = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1248
1249 assert_eq!(decoded_lower, decoded_upper);
1250 assert_eq!(decoded_lower, address);
1251 }
1252
1253 #[test]
1254 fn test_bech32m_all_zeros_p2pkh() {
1255 let address = PlatformAddress::P2pkh([0u8; 20]);
1257 let encoded = address.to_bech32m_string(Network::Mainnet);
1258 let (decoded, _) = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1259 assert_eq!(decoded, address);
1260 }
1261
1262 #[test]
1263 fn test_bech32m_all_ones_p2sh() {
1264 let address = PlatformAddress::P2sh([0xFF; 20]);
1266 let encoded = address.to_bech32m_string(Network::Mainnet);
1267 let (decoded, _) = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1268 assert_eq!(decoded, address);
1269 }
1270
1271 #[test]
1272 fn test_hrp_for_network() {
1273 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1274 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1275 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1276 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1277 }
1278
1279 #[test]
1280 fn test_storage_bytes_format() {
1281 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1285 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1286
1287 let p2pkh_bytes = p2pkh.to_bytes();
1288 let p2sh_bytes = p2sh.to_bytes();
1289
1290 assert_eq!(p2pkh_bytes.len(), 21);
1292 assert_eq!(p2sh_bytes.len(), 21);
1293 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1294 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1295
1296 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1298 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1299 assert_eq!(p2pkh_decoded, p2pkh);
1300 assert_eq!(p2sh_decoded, p2sh);
1301 }
1302
1303 #[test]
1304 fn test_bech32m_uses_different_type_bytes_than_storage() {
1305 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1308 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1309
1310 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1312 assert_eq!(p2sh.to_bytes()[0], 0x01);
1313
1314 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1316 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1317
1318 let (p2pkh_decoded, _) = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1319 let (p2sh_decoded, _) = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1320
1321 assert_eq!(p2pkh_decoded, p2pkh);
1322 assert_eq!(p2sh_decoded, p2sh);
1323 }
1324}