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 }
109 }
110
111 pub fn to_bech32m_string(&self, network: Network) -> String {
128 let hrp_str = Self::hrp_for_network(network);
129 let hrp = Hrp::parse(hrp_str).expect("HRP is valid");
130
131 let mut payload = Vec::with_capacity(1 + ADDRESS_HASH_SIZE);
134 match self {
135 PlatformAddress::P2pkh(hash) => {
136 payload.push(Self::P2PKH_TYPE);
137 payload.extend_from_slice(hash);
138 }
139 PlatformAddress::P2sh(hash) => {
140 payload.push(Self::P2SH_TYPE);
141 payload.extend_from_slice(hash);
142 }
143 }
144
145 bech32::encode::<Bech32m>(hrp, &payload).expect("encoding should succeed")
147 }
148
149 pub fn from_bech32m_string(s: &str) -> Result<(Self, Network), ProtocolError> {
158 let (hrp, data) =
160 bech32::decode(s).map_err(|e| ProtocolError::DecodingError(format!("{}", e)))?;
161
162 let hrp_lower = hrp.as_str().to_ascii_lowercase();
164 let network = match hrp_lower.as_str() {
165 s if s == PLATFORM_HRP_MAINNET => Network::Mainnet,
166 s if s == PLATFORM_HRP_TESTNET => Network::Testnet,
167 _ => {
168 return Err(ProtocolError::DecodingError(format!(
169 "invalid HRP '{}': expected '{}' or '{}'",
170 hrp, PLATFORM_HRP_MAINNET, PLATFORM_HRP_TESTNET
171 )))
172 }
173 };
174
175 if data.len() != 1 + ADDRESS_HASH_SIZE {
177 return Err(ProtocolError::DecodingError(format!(
178 "invalid Platform address length: expected {} bytes, got {}",
179 1 + ADDRESS_HASH_SIZE,
180 data.len()
181 )));
182 }
183
184 let address_type = data[0];
186 let hash: [u8; 20] = data[1..21]
187 .try_into()
188 .map_err(|_| ProtocolError::DecodingError("invalid hash length".to_string()))?;
189
190 let address = match address_type {
191 Self::P2PKH_TYPE => Ok(PlatformAddress::P2pkh(hash)),
192 Self::P2SH_TYPE => Ok(PlatformAddress::P2sh(hash)),
193 _ => Err(ProtocolError::DecodingError(format!(
194 "invalid address type: 0x{:02x}",
195 address_type
196 ))),
197 }?;
198
199 Ok((address, network))
200 }
201
202 pub fn to_address_with_network(&self, network: Network) -> Address {
204 match self {
205 PlatformAddress::P2pkh(hash) => Address::new(
206 network,
207 Payload::PubkeyHash(PubkeyHash::from_byte_array(*hash)),
208 ),
209 PlatformAddress::P2sh(hash) => Address::new(
210 network,
211 Payload::ScriptHash(ScriptHash::from_byte_array(*hash)),
212 ),
213 }
214 }
215
216 pub fn to_bytes(&self) -> Vec<u8> {
222 bincode::encode_to_vec(self, bincode::config::standard())
223 .expect("PlatformAddress serialization cannot fail")
224 }
225
226 pub fn base64_string_with_nonce(&self, nonce: AddressNonce) -> String {
229 use base64::engine::general_purpose::STANDARD;
230 use base64::Engine;
231
232 let mut bytes = self.to_bytes();
233 bytes.extend_from_slice(&nonce.to_be_bytes());
234
235 STANDARD.encode(bytes)
236 }
237
238 pub fn from_bytes(bytes: &[u8]) -> Result<Self, ProtocolError> {
243 let (address, _): (Self, usize) =
244 bincode::decode_from_slice(bytes, bincode::config::standard()).map_err(|e| {
245 ProtocolError::DecodingError(format!("cannot decode PlatformAddress: {}", e))
246 })?;
247 Ok(address)
248 }
249
250 pub fn hash(&self) -> &[u8; 20] {
252 match self {
253 PlatformAddress::P2pkh(hash) => hash,
254 PlatformAddress::P2sh(hash) => hash,
255 }
256 }
257
258 pub fn is_p2pkh(&self) -> bool {
260 matches!(self, PlatformAddress::P2pkh(_))
261 }
262
263 pub fn is_p2sh(&self) -> bool {
265 matches!(self, PlatformAddress::P2sh(_))
266 }
267
268 pub fn verify_bytes_against_witness(
288 &self,
289 witness: &AddressWitness,
290 signable_bytes: &[u8],
291 ) -> Result<AddressWitnessVerificationOperations, ProtocolError> {
292 match (self, witness) {
293 (PlatformAddress::P2pkh(pubkey_hash), AddressWitness::P2pkh { signature }) => {
294 let data_hash = dashcore::signer::double_sha(signable_bytes);
302 dashcore::signer::verify_hash_signature(
303 &data_hash,
304 signature.as_slice(),
305 pubkey_hash,
306 )
307 .map_err(|e| {
308 ProtocolError::AddressWitnessError(format!(
309 "P2PKH signature verification failed: {}",
310 e
311 ))
312 })?;
313
314 Ok(AddressWitnessVerificationOperations::for_p2pkh(
315 signable_bytes.len(),
316 ))
317 }
318 (
319 PlatformAddress::P2sh(script_hash),
320 AddressWitness::P2sh {
321 signatures,
322 redeem_script,
323 },
324 ) => {
325 let script = ScriptBuf::from_bytes(redeem_script.to_vec());
327 let computed_hash = script.script_hash();
328 if computed_hash.as_byte_array() != script_hash {
329 return Err(ProtocolError::AddressWitnessError(format!(
330 "Script hash {} does not match address hash {}",
331 hex::encode(computed_hash.as_byte_array()),
332 hex::encode(script_hash)
333 )));
334 }
335
336 let (threshold, pubkeys) = Self::parse_multisig_script(&script)?;
339
340 let valid_signatures: Vec<_> = signatures
342 .iter()
343 .filter(|sig| !sig.is_empty() && sig.as_slice() != [0x00])
344 .collect();
345
346 if valid_signatures.len() < threshold {
347 return Err(ProtocolError::AddressWitnessError(format!(
348 "Not enough signatures: got {}, need {}",
349 valid_signatures.len(),
350 threshold
351 )));
352 }
353
354 let mut sig_idx = 0;
357 let mut pubkey_idx = 0;
358 let mut matched = 0;
359 let mut signature_verifications: u16 = 0;
360
361 let signable_bytes_hash = sha256d::Hash::hash(signable_bytes).to_byte_array();
362 let msg = Message::from_digest(signable_bytes_hash);
363 let secp = Secp256k1::new();
364
365 while sig_idx < valid_signatures.len() && pubkey_idx < pubkeys.len() {
366 signature_verifications += 1;
367
368 let sig = RecoverableSignature::from_compact_signature(
369 valid_signatures[sig_idx].as_slice(),
370 )
371 .map_err(|e| {
372 ProtocolError::AddressWitnessError(format!(
373 "Invalid signature format: {}",
374 e
375 ))
376 })?;
377
378 let pub_key = PublicKey::from_slice(&pubkeys[pubkey_idx]).map_err(|e| {
379 ProtocolError::AddressWitnessError(format!("Invalid public key: {}", e))
380 })?;
381
382 if secp
383 .verify_ecdsa(&msg, &sig.to_standard(), &pub_key.inner)
384 .is_ok()
385 {
386 matched += 1;
387 sig_idx += 1;
388 }
389 pubkey_idx += 1;
390 }
391
392 if matched >= threshold {
393 Ok(AddressWitnessVerificationOperations::for_p2sh_multisig(
394 signature_verifications,
395 signable_bytes.len(),
396 ))
397 } else {
398 Err(ProtocolError::AddressWitnessError(format!(
399 "Not enough valid signatures: verified {}, need {}",
400 matched, threshold
401 )))
402 }
403 }
404 (PlatformAddress::P2pkh(_), AddressWitness::P2sh { .. }) => {
405 Err(ProtocolError::AddressWitnessError(
406 "P2PKH address requires P2pkh witness, got P2sh".to_string(),
407 ))
408 }
409 (PlatformAddress::P2sh(_), AddressWitness::P2pkh { .. }) => {
410 Err(ProtocolError::AddressWitnessError(
411 "P2SH address requires P2sh witness, got P2pkh".to_string(),
412 ))
413 }
414 }
415 }
416
417 fn parse_multisig_script(script: &ScriptBuf) -> Result<(usize, Vec<Vec<u8>>), ProtocolError> {
432 use dashcore::blockdata::opcodes::all::*;
433
434 let mut instructions = script.instructions();
435 let mut pubkeys = Vec::new();
436
437 let threshold = match instructions.next() {
439 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
440 let byte = op.to_u8();
441 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
442 (byte - OP_PUSHNUM_1.to_u8() + 1) as usize
443 } else {
444 return Err(ProtocolError::AddressWitnessError(format!(
445 "Unsupported P2SH script type: only standard multisig (OP_M ... OP_N OP_CHECKMULTISIG) is supported. \
446 First opcode was 0x{:02x}, expected OP_1 through OP_16",
447 byte
448 )));
449 }
450 }
451 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(_))) => {
452 return Err(ProtocolError::AddressWitnessError(
453 "Unsupported P2SH script type: only standard multisig is supported. \
454 Script starts with a data push instead of OP_M threshold."
455 .to_string(),
456 ))
457 }
458 Some(Err(e)) => {
459 return Err(ProtocolError::AddressWitnessError(format!(
460 "Error parsing P2SH script: {:?}",
461 e
462 )))
463 }
464 None => {
465 return Err(ProtocolError::AddressWitnessError(
466 "Empty P2SH redeem script".to_string(),
467 ))
468 }
469 };
470
471 loop {
473 match instructions.next() {
474 Some(Ok(dashcore::blockdata::script::Instruction::PushBytes(bytes))) => {
475 let len = bytes.len();
477 if len != 33 {
478 return Err(ProtocolError::UncompressedPublicKeyNotAllowedError(
479 crate::consensus::signature::UncompressedPublicKeyNotAllowedError::new(
480 len,
481 ),
482 ));
483 }
484 pubkeys.push(bytes.as_bytes().to_vec());
485 }
486 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
487 let byte = op.to_u8();
488 if byte >= OP_PUSHNUM_1.to_u8() && byte <= OP_PUSHNUM_16.to_u8() {
489 let n = (byte - OP_PUSHNUM_1.to_u8() + 1) as usize;
491 if pubkeys.len() != n {
492 return Err(ProtocolError::AddressWitnessError(format!(
493 "Multisig script declares {} keys but contains {}",
494 n,
495 pubkeys.len()
496 )));
497 }
498 break;
499 } else if op == OP_CHECKMULTISIG || op == OP_CHECKMULTISIGVERIFY {
500 return Err(ProtocolError::AddressWitnessError(
502 "Malformed multisig script: OP_CHECKMULTISIG before OP_N".to_string(),
503 ));
504 } else {
505 return Err(ProtocolError::AddressWitnessError(format!(
506 "Unsupported opcode 0x{:02x} in P2SH script. Only standard multisig is supported.",
507 byte
508 )));
509 }
510 }
511 Some(Err(e)) => {
512 return Err(ProtocolError::AddressWitnessError(format!(
513 "Error parsing multisig script: {:?}",
514 e
515 )))
516 }
517 None => {
518 return Err(ProtocolError::AddressWitnessError(
519 "Incomplete multisig script: unexpected end before OP_N".to_string(),
520 ))
521 }
522 }
523 }
524
525 if threshold > pubkeys.len() {
527 return Err(ProtocolError::AddressWitnessError(format!(
528 "Invalid multisig: threshold {} exceeds number of keys {}",
529 threshold,
530 pubkeys.len()
531 )));
532 }
533
534 match instructions.next() {
536 Some(Ok(dashcore::blockdata::script::Instruction::Op(op))) => {
537 if op == OP_CHECKMULTISIG {
538 if instructions.next().is_some() {
540 return Err(ProtocolError::AddressWitnessError(
541 "Multisig script has extra data after OP_CHECKMULTISIG".to_string(),
542 ));
543 }
544 Ok((threshold, pubkeys))
545 } else if op == OP_CHECKMULTISIGVERIFY {
546 Err(ProtocolError::AddressWitnessError(
547 "OP_CHECKMULTISIGVERIFY is not supported, only OP_CHECKMULTISIG"
548 .to_string(),
549 ))
550 } else {
551 Err(ProtocolError::AddressWitnessError(format!(
552 "Expected OP_CHECKMULTISIG, got opcode 0x{:02x}",
553 op.to_u8()
554 )))
555 }
556 }
557 _ => Err(ProtocolError::AddressWitnessError(
558 "Invalid multisig script: expected OP_CHECKMULTISIG after OP_N".to_string(),
559 )),
560 }
561 }
562}
563
564impl std::fmt::Display for PlatformAddress {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 match self {
567 PlatformAddress::P2pkh(hash) => write!(f, "P2PKH({})", hex::encode(hash)),
568 PlatformAddress::P2sh(hash) => write!(f, "P2SH({})", hex::encode(hash)),
569 }
570 }
571}
572
573#[derive(Debug, Clone, PartialEq, Eq)]
575pub struct PlatformAddressParseError(pub String);
576
577impl std::fmt::Display for PlatformAddressParseError {
578 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579 write!(f, "{}", self.0)
580 }
581}
582
583impl std::error::Error for PlatformAddressParseError {}
584
585impl FromStr for PlatformAddress {
586 type Err = PlatformAddressParseError;
587
588 fn from_str(s: &str) -> Result<Self, Self::Err> {
599 Self::from_bech32m_string(s)
600 .map(|(addr, _network)| addr)
601 .map_err(|e| PlatformAddressParseError(e.to_string()))
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608 use dashcore::blockdata::opcodes::all::*;
609 use dashcore::hashes::Hash;
610 use dashcore::secp256k1::{PublicKey as RawPublicKey, Secp256k1, SecretKey as RawSecretKey};
611 use dashcore::PublicKey;
612 use platform_value::BinaryData;
613
614 fn create_keypair(seed: [u8; 32]) -> (RawSecretKey, PublicKey) {
616 let secp = Secp256k1::new();
617 let secret_key = RawSecretKey::from_byte_array(&seed).expect("valid secret key");
618 let raw_public_key = RawPublicKey::from_secret_key(&secp, &secret_key);
619 let public_key = PublicKey::new(raw_public_key);
620 (secret_key, public_key)
621 }
622
623 fn sign_data(data: &[u8], secret_key: &RawSecretKey) -> Vec<u8> {
625 dashcore::signer::sign(data, secret_key.as_ref())
626 .expect("signing should succeed")
627 .to_vec()
628 }
629
630 fn create_multisig_script(threshold: u8, pubkeys: &[PublicKey]) -> Vec<u8> {
632 let mut script = Vec::new();
633
634 script.push(OP_PUSHNUM_1.to_u8() + threshold - 1);
636
637 for pubkey in pubkeys {
639 let bytes = pubkey.to_bytes();
640 script.push(bytes.len() as u8); script.extend_from_slice(&bytes);
642 }
643
644 script.push(OP_PUSHNUM_1.to_u8() + pubkeys.len() as u8 - 1);
646
647 script.push(OP_CHECKMULTISIG.to_u8());
649
650 script
651 }
652
653 #[test]
654 fn test_platform_address_from_private_key() {
655 let seed = [1u8; 32];
657 let (secret_key, public_key) = create_keypair(seed);
658
659 let private_key = PrivateKey::new(secret_key, Network::Testnet);
661
662 let address_from_private = PlatformAddress::from(&private_key);
664
665 let pubkey_hash = public_key.pubkey_hash();
668 let address_from_pubkey = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
669
670 assert_eq!(
672 address_from_private, address_from_pubkey,
673 "Address derived from private key should match Hash160(compressed_pubkey)"
674 );
675
676 assert!(address_from_private.is_p2pkh());
678 }
679
680 #[test]
681 fn test_p2pkh_verify_signature_success() {
682 let seed = [1u8; 32];
684 let (secret_key, public_key) = create_keypair(seed);
685
686 let pubkey_hash = public_key.pubkey_hash();
688 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
689
690 let signable_bytes = b"test message for P2PKH verification";
692
693 let signature = sign_data(signable_bytes, &secret_key);
695
696 let witness = AddressWitness::P2pkh {
698 signature: BinaryData::new(signature),
699 };
700
701 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
703 assert!(
704 result.is_ok(),
705 "P2PKH verification should succeed: {:?}",
706 result
707 );
708 }
709
710 #[test]
711 fn test_p2pkh_verify_wrong_signature_fails() {
712 let seed = [1u8; 32];
714 let (secret_key, public_key) = create_keypair(seed);
715
716 let pubkey_hash = public_key.pubkey_hash();
718 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
719
720 let sign_bytes = b"original message";
722 let verify_bytes = b"different message";
723 let signature = sign_data(sign_bytes, &secret_key);
724
725 let witness = AddressWitness::P2pkh {
727 signature: BinaryData::new(signature),
728 };
729
730 let result = address.verify_bytes_against_witness(&witness, verify_bytes);
732 assert!(
733 result.is_err(),
734 "P2PKH verification should fail with wrong data"
735 );
736 }
737
738 #[test]
739 fn test_p2pkh_verify_wrong_key_fails() {
740 let seed1 = [1u8; 32];
742 let seed2 = [2u8; 32];
743 let (_secret_key1, public_key1) = create_keypair(seed1);
744 let (secret_key2, _public_key2) = create_keypair(seed2);
745
746 let pubkey_hash = public_key1.pubkey_hash();
748 let address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
749
750 let signable_bytes = b"test message";
752 let signature = sign_data(signable_bytes, &secret_key2);
753
754 let witness = AddressWitness::P2pkh {
756 signature: BinaryData::new(signature),
757 };
758
759 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
761 assert!(
762 result.is_err(),
763 "P2PKH verification should fail when signed with wrong key"
764 );
765 }
766
767 #[test]
772 fn test_p2sh_2_of_3_multisig_verify_success() {
773 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
775 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
776 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
777
778 let redeem_script = create_multisig_script(2, &pubkeys);
780
781 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
783 let script_hash = script_buf.script_hash();
784 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
785
786 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
788
789 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
791 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
792
793 let witness = AddressWitness::P2sh {
796 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
797 redeem_script: BinaryData::new(redeem_script),
798 };
799
800 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
802 assert!(
803 result.is_ok(),
804 "P2SH 2-of-3 multisig verification should succeed: {:?}",
805 result
806 );
807 }
808
809 #[test]
810 fn test_p2sh_2_of_3_multisig_with_keys_1_and_2_success() {
811 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
813 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
814 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
815
816 let redeem_script = create_multisig_script(2, &pubkeys);
818
819 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
821 let script_hash = script_buf.script_hash();
822 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
823
824 let signable_bytes = b"test message for P2SH 2-of-3 multisig";
826
827 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
829 let sig2 = sign_data(signable_bytes, &keypairs[2].0);
830
831 let witness = AddressWitness::P2sh {
833 signatures: vec![BinaryData::new(sig1), BinaryData::new(sig2)],
834 redeem_script: BinaryData::new(redeem_script),
835 };
836
837 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
839 assert!(
840 result.is_ok(),
841 "P2SH 2-of-3 multisig with keys 1 and 2 should succeed: {:?}",
842 result
843 );
844 }
845
846 #[test]
847 fn test_p2sh_not_enough_signatures_fails() {
848 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
850 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
851 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
852
853 let redeem_script = create_multisig_script(2, &pubkeys);
855
856 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
858 let script_hash = script_buf.script_hash();
859 let address = PlatformAddress::P2sh(*script_hash.as_byte_array());
860
861 let signable_bytes = b"test message";
863
864 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
866
867 let witness = AddressWitness::P2sh {
869 signatures: vec![BinaryData::new(sig0)],
870 redeem_script: BinaryData::new(redeem_script),
871 };
872
873 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
875 assert!(
876 result.is_err(),
877 "P2SH should fail with only 1 signature when 2 required"
878 );
879 assert!(
880 result.unwrap_err().to_string().contains("Not enough"),
881 "Error should mention not enough signatures"
882 );
883 }
884
885 #[test]
886 fn test_p2sh_wrong_script_hash_fails() {
887 let seeds: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
889 let keypairs: Vec<_> = seeds.iter().map(|s| create_keypair(*s)).collect();
890 let pubkeys: Vec<_> = keypairs.iter().map(|(_, pk)| *pk).collect();
891
892 let redeem_script = create_multisig_script(2, &pubkeys);
894
895 let wrong_hash = [0xABu8; 20];
897 let address = PlatformAddress::P2sh(wrong_hash);
898
899 let signable_bytes = b"test message";
901
902 let sig0 = sign_data(signable_bytes, &keypairs[0].0);
904 let sig1 = sign_data(signable_bytes, &keypairs[1].0);
905
906 let witness = AddressWitness::P2sh {
908 signatures: vec![BinaryData::new(sig0), BinaryData::new(sig1)],
909 redeem_script: BinaryData::new(redeem_script),
910 };
911
912 let result = address.verify_bytes_against_witness(&witness, signable_bytes);
914 assert!(
915 result.is_err(),
916 "P2SH should fail when script hash doesn't match address"
917 );
918 assert!(
919 result
920 .unwrap_err()
921 .to_string()
922 .contains("does not match address hash"),
923 "Error should mention hash mismatch"
924 );
925 }
926
927 #[test]
928 fn test_p2pkh_and_p2sh_together() {
929 let p2pkh_seed = [10u8; 32];
933 let (p2pkh_secret, p2pkh_pubkey) = create_keypair(p2pkh_seed);
934 let p2pkh_hash = p2pkh_pubkey.pubkey_hash();
935 let p2pkh_address = PlatformAddress::P2pkh(*p2pkh_hash.as_byte_array());
936
937 let p2sh_seeds: [[u8; 32]; 3] = [[20u8; 32], [21u8; 32], [22u8; 32]];
939 let p2sh_keypairs: Vec<_> = p2sh_seeds.iter().map(|s| create_keypair(*s)).collect();
940 let p2sh_pubkeys: Vec<_> = p2sh_keypairs.iter().map(|(_, pk)| *pk).collect();
941 let redeem_script = create_multisig_script(2, &p2sh_pubkeys);
942 let script_buf = ScriptBuf::from_bytes(redeem_script.clone());
943 let script_hash = script_buf.script_hash();
944 let p2sh_address = PlatformAddress::P2sh(*script_hash.as_byte_array());
945
946 let signable_bytes = b"combined transaction data to redeem both outputs";
948
949 let p2pkh_sig = sign_data(signable_bytes, &p2pkh_secret);
951 let p2pkh_witness = AddressWitness::P2pkh {
952 signature: BinaryData::new(p2pkh_sig),
953 };
954 let p2pkh_result =
955 p2pkh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
956 assert!(
957 p2pkh_result.is_ok(),
958 "P2PKH redemption should succeed: {:?}",
959 p2pkh_result
960 );
961
962 let p2sh_sig0 = sign_data(signable_bytes, &p2sh_keypairs[0].0);
964 let p2sh_sig2 = sign_data(signable_bytes, &p2sh_keypairs[2].0);
965 let p2sh_witness = AddressWitness::P2sh {
966 signatures: vec![BinaryData::new(p2sh_sig0), BinaryData::new(p2sh_sig2)],
967 redeem_script: BinaryData::new(redeem_script),
968 };
969 let p2sh_result = p2sh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
970 assert!(
971 p2sh_result.is_ok(),
972 "P2SH redemption should succeed: {:?}",
973 p2sh_result
974 );
975
976 }
978
979 #[test]
980 fn test_witness_type_mismatch() {
981 let seed = [1u8; 32];
983 let (_, public_key) = create_keypair(seed);
984 let pubkey_hash = public_key.pubkey_hash();
985 let p2pkh_address = PlatformAddress::P2pkh(*pubkey_hash.as_byte_array());
986
987 let p2sh_hash = [0xABu8; 20];
989 let p2sh_address = PlatformAddress::P2sh(p2sh_hash);
990
991 let signable_bytes = b"test data";
992
993 let p2sh_witness = AddressWitness::P2sh {
995 signatures: vec![BinaryData::new(vec![0x30, 0x44])],
996 redeem_script: BinaryData::new(vec![0x52]),
997 };
998 let result = p2pkh_address.verify_bytes_against_witness(&p2sh_witness, signable_bytes);
999 assert!(result.is_err());
1000 assert!(result
1001 .unwrap_err()
1002 .to_string()
1003 .contains("P2PKH address requires P2pkh witness"));
1004
1005 let p2pkh_witness = AddressWitness::P2pkh {
1007 signature: BinaryData::new(vec![0x30, 0x44]),
1008 };
1009 let result = p2sh_address.verify_bytes_against_witness(&p2pkh_witness, signable_bytes);
1010 assert!(result.is_err());
1011 assert!(result
1012 .unwrap_err()
1013 .to_string()
1014 .contains("P2SH address requires P2sh witness"));
1015 }
1016
1017 #[test]
1022 fn test_bech32m_p2pkh_mainnet_roundtrip() {
1023 let hash: [u8; 20] = [
1025 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1026 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1027 ];
1028 let address = PlatformAddress::P2pkh(hash);
1029
1030 let encoded = address.to_bech32m_string(Network::Mainnet);
1032
1033 assert_eq!(
1035 encoded, "dash1krma5z3ttj75la4m93xcndna9ullamq9y5e9n5rs",
1036 "P2PKH mainnet encoding mismatch"
1037 );
1038
1039 let (decoded, network) =
1041 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1042 assert_eq!(decoded, address);
1043 assert_eq!(network, Network::Mainnet);
1044 }
1045
1046 #[test]
1047 fn test_bech32m_p2pkh_testnet_roundtrip() {
1048 let hash: [u8; 20] = [
1050 0xf7, 0xda, 0x0a, 0x2b, 0x5c, 0xbd, 0x4f, 0xf6, 0xbb, 0x2c, 0x4d, 0x89, 0xb6, 0x7d,
1051 0x2f, 0x3f, 0xfe, 0xec, 0x05, 0x25,
1052 ];
1053 let address = PlatformAddress::P2pkh(hash);
1054
1055 let encoded = address.to_bech32m_string(Network::Testnet);
1057
1058 assert_eq!(
1060 encoded, "tdash1krma5z3ttj75la4m93xcndna9ullamq9y5fzq2j7",
1061 "P2PKH testnet encoding mismatch"
1062 );
1063
1064 let (decoded, network) =
1066 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1067 assert_eq!(decoded, address);
1068 assert_eq!(network, Network::Testnet);
1069 }
1070
1071 #[test]
1072 fn test_bech32m_p2sh_mainnet_roundtrip() {
1073 let hash: [u8; 20] = [
1075 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1076 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1077 ];
1078 let address = PlatformAddress::P2sh(hash);
1079
1080 let encoded = address.to_bech32m_string(Network::Mainnet);
1082
1083 assert_eq!(
1085 encoded, "dash1sppl5xpu70aka8nacc4kj2htflydspzkxch4cad6",
1086 "P2SH mainnet encoding mismatch"
1087 );
1088
1089 let (decoded, network) =
1091 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1092 assert_eq!(decoded, address);
1093 assert_eq!(network, Network::Mainnet);
1094 }
1095
1096 #[test]
1097 fn test_bech32m_p2sh_testnet_roundtrip() {
1098 let hash: [u8; 20] = [
1100 0x43, 0xfa, 0x18, 0x3c, 0xf3, 0xfb, 0x6e, 0x9e, 0x7d, 0xc6, 0x2b, 0x69, 0x2a, 0xeb,
1101 0x4f, 0xc8, 0xd8, 0x04, 0x56, 0x36,
1102 ];
1103 let address = PlatformAddress::P2sh(hash);
1104
1105 let encoded = address.to_bech32m_string(Network::Testnet);
1107
1108 assert_eq!(
1110 encoded, "tdash1sppl5xpu70aka8nacc4kj2htflydspzkxc8jtru5",
1111 "P2SH testnet encoding mismatch"
1112 );
1113
1114 let (decoded, network) =
1116 PlatformAddress::from_bech32m_string(&encoded).expect("decoding should succeed");
1117 assert_eq!(decoded, address);
1118 assert_eq!(network, Network::Testnet);
1119 }
1120
1121 #[test]
1122 fn test_bech32m_devnet_uses_testnet_hrp() {
1123 let hash: [u8; 20] = [0xAB; 20];
1124 let address = PlatformAddress::P2pkh(hash);
1125
1126 let encoded = address.to_bech32m_string(Network::Devnet);
1128 assert!(
1129 encoded.starts_with("tdash1"),
1130 "Devnet address should start with 'tdash1', got: {}",
1131 encoded
1132 );
1133 }
1134
1135 #[test]
1136 fn test_bech32m_regtest_uses_testnet_hrp() {
1137 let hash: [u8; 20] = [0xAB; 20];
1138 let address = PlatformAddress::P2pkh(hash);
1139
1140 let encoded = address.to_bech32m_string(Network::Regtest);
1142 assert!(
1143 encoded.starts_with("tdash1"),
1144 "Regtest address should start with 'tdash1', got: {}",
1145 encoded
1146 );
1147 }
1148
1149 #[test]
1150 fn test_bech32m_invalid_hrp_fails() {
1151 let wrong_hrp = Hrp::parse("bitcoin").unwrap();
1153 let payload: [u8; 21] = [0x00; 21];
1154 let wrong_hrp_address = bech32::encode::<Bech32m>(wrong_hrp, &payload).unwrap();
1155
1156 let result = PlatformAddress::from_bech32m_string(&wrong_hrp_address);
1157 assert!(result.is_err());
1158 let err = result.unwrap_err();
1159 assert!(
1160 err.to_string().contains("invalid HRP"),
1161 "Error should mention invalid HRP: {}",
1162 err
1163 );
1164 }
1165
1166 #[test]
1167 fn test_bech32m_invalid_checksum_fails() {
1168 let hash: [u8; 20] = [0xAB; 20];
1170 let address = PlatformAddress::P2pkh(hash);
1171 let mut encoded = address.to_bech32m_string(Network::Mainnet);
1172
1173 let last_char = encoded.pop().unwrap();
1175 let corrupted_char = if last_char == 'q' { 'p' } else { 'q' };
1176 encoded.push(corrupted_char);
1177
1178 let result = PlatformAddress::from_bech32m_string(&encoded);
1179 assert!(result.is_err(), "Should fail with corrupted checksum");
1180 }
1181
1182 #[test]
1183 fn test_bech32m_invalid_type_byte_fails() {
1184 let hrp = Hrp::parse("dash").unwrap();
1187 let invalid_payload: [u8; 21] = [0x02; 21]; let encoded = bech32::encode::<Bech32m>(hrp, &invalid_payload).unwrap();
1189
1190 let result = PlatformAddress::from_bech32m_string(&encoded);
1191 assert!(result.is_err());
1192 let err = result.unwrap_err();
1193 assert!(
1194 err.to_string().contains("invalid address type"),
1195 "Error should mention invalid type: {}",
1196 err
1197 );
1198 }
1199
1200 #[test]
1201 fn test_bech32m_too_short_fails() {
1202 let hrp = Hrp::parse("dash").unwrap();
1204 let short_payload: [u8; 10] = [0xb0; 10]; let encoded = bech32::encode::<Bech32m>(hrp, &short_payload).unwrap();
1206
1207 let result = PlatformAddress::from_bech32m_string(&encoded);
1208 assert!(result.is_err());
1209 let err = result.unwrap_err();
1210 assert!(
1211 err.to_string().contains("invalid Platform address length"),
1212 "Error should mention invalid length: {}",
1213 err
1214 );
1215 }
1216
1217 #[test]
1218 fn test_bech32m_from_str_trait() {
1219 let hash: [u8; 20] = [
1221 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
1222 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
1223 ];
1224 let original = PlatformAddress::P2pkh(hash);
1225
1226 let encoded = original.to_bech32m_string(Network::Testnet);
1228 let parsed: PlatformAddress = encoded.parse().expect("parsing should succeed");
1229
1230 assert_eq!(parsed, original);
1231 }
1232
1233 #[test]
1234 fn test_bech32m_case_insensitive() {
1235 let hash: [u8; 20] = [0xAB; 20];
1238 let address = PlatformAddress::P2pkh(hash);
1239
1240 let lowercase = address.to_bech32m_string(Network::Mainnet);
1241 let uppercase = lowercase.to_uppercase();
1242
1243 let (decoded_lower, _) = PlatformAddress::from_bech32m_string(&lowercase).unwrap();
1245 let (decoded_upper, _) = PlatformAddress::from_bech32m_string(&uppercase).unwrap();
1246
1247 assert_eq!(decoded_lower, decoded_upper);
1248 assert_eq!(decoded_lower, address);
1249 }
1250
1251 #[test]
1252 fn test_bech32m_all_zeros_p2pkh() {
1253 let address = PlatformAddress::P2pkh([0u8; 20]);
1255 let encoded = address.to_bech32m_string(Network::Mainnet);
1256 let (decoded, _) = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1257 assert_eq!(decoded, address);
1258 }
1259
1260 #[test]
1261 fn test_bech32m_all_ones_p2sh() {
1262 let address = PlatformAddress::P2sh([0xFF; 20]);
1264 let encoded = address.to_bech32m_string(Network::Mainnet);
1265 let (decoded, _) = PlatformAddress::from_bech32m_string(&encoded).unwrap();
1266 assert_eq!(decoded, address);
1267 }
1268
1269 #[test]
1270 fn test_hrp_for_network() {
1271 assert_eq!(PlatformAddress::hrp_for_network(Network::Mainnet), "dash");
1272 assert_eq!(PlatformAddress::hrp_for_network(Network::Testnet), "tdash");
1273 assert_eq!(PlatformAddress::hrp_for_network(Network::Devnet), "tdash");
1274 assert_eq!(PlatformAddress::hrp_for_network(Network::Regtest), "tdash");
1275 }
1276
1277 #[test]
1278 fn test_storage_bytes_format() {
1279 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1283 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1284
1285 let p2pkh_bytes = p2pkh.to_bytes();
1286 let p2sh_bytes = p2sh.to_bytes();
1287
1288 assert_eq!(p2pkh_bytes.len(), 21);
1290 assert_eq!(p2sh_bytes.len(), 21);
1291 assert_eq!(p2pkh_bytes[0], 0x00, "P2pkh variant index must be 0x00");
1292 assert_eq!(p2sh_bytes[0], 0x01, "P2sh variant index must be 0x01");
1293
1294 let p2pkh_decoded = PlatformAddress::from_bytes(&p2pkh_bytes).unwrap();
1296 let p2sh_decoded = PlatformAddress::from_bytes(&p2sh_bytes).unwrap();
1297 assert_eq!(p2pkh_decoded, p2pkh);
1298 assert_eq!(p2sh_decoded, p2sh);
1299 }
1300
1301 #[test]
1302 fn test_bech32m_uses_different_type_bytes_than_storage() {
1303 let p2pkh = PlatformAddress::P2pkh([0xAB; 20]);
1306 let p2sh = PlatformAddress::P2sh([0xCD; 20]);
1307
1308 assert_eq!(p2pkh.to_bytes()[0], 0x00);
1310 assert_eq!(p2sh.to_bytes()[0], 0x01);
1311
1312 let p2pkh_encoded = p2pkh.to_bech32m_string(Network::Mainnet);
1314 let p2sh_encoded = p2sh.to_bech32m_string(Network::Mainnet);
1315
1316 let (p2pkh_decoded, _) = PlatformAddress::from_bech32m_string(&p2pkh_encoded).unwrap();
1317 let (p2sh_decoded, _) = PlatformAddress::from_bech32m_string(&p2sh_encoded).unwrap();
1318
1319 assert_eq!(p2pkh_decoded, p2pkh);
1320 assert_eq!(p2sh_decoded, p2sh);
1321 }
1322}