Skip to main content

chains_sdk/bitcoin/
multisig.rs

1//! Bitcoin multisig support: P2SH, P2WSH, and P2SH-P2WSH (nested SegWit).
2//!
3//! Implements m-of-n OP_CHECKMULTISIG scripts and the three standard
4//! wrapping formats used by wallets and custodians.
5//!
6//! # Example
7//! ```no_run
8//! use chains_sdk::bitcoin::multisig::*;
9//!
10//! let pubkeys: Vec<[u8; 33]> = vec![[0x02; 33]; 3];
11//! let redeem = multisig_redeem_script(2, &pubkeys).unwrap();
12//! let address = p2sh_address(&redeem, false);
13//! ```
14
15use crate::encoding;
16use crate::error::SignerError;
17use sha2::{Digest, Sha256};
18
19// ═══════════════════════════════════════════════════════════════════
20// Constants
21// ═══════════════════════════════════════════════════════════════════
22
23/// Maximum number of public keys in a standard multisig (consensus rule).
24pub const MAX_MULTISIG_KEYS: usize = 15;
25
26/// Maximum redeem script size for P2SH (consensus: 520 bytes).
27pub const MAX_REDEEM_SCRIPT_SIZE: usize = 520;
28
29// ═══════════════════════════════════════════════════════════════════
30// Redeem Script Construction
31// ═══════════════════════════════════════════════════════════════════
32
33/// Build a standard m-of-n `OP_CHECKMULTISIG` redeem script.
34///
35/// Script: `OP_m <pubkey1> <pubkey2> ... <pubkeyn> OP_n OP_CHECKMULTISIG`
36///
37/// # Arguments
38/// - `threshold` — Number of required signatures (`m`)
39/// - `pubkeys` — Compressed public keys (33 bytes each)
40///
41/// # Errors
42/// - `threshold == 0` or `threshold > pubkeys.len()`
43/// - `pubkeys.len() > 15` (consensus limit)
44/// - Empty `pubkeys`
45pub fn multisig_redeem_script(
46    threshold: usize,
47    pubkeys: &[[u8; 33]],
48) -> Result<Vec<u8>, SignerError> {
49    let n = pubkeys.len();
50
51    if n == 0 {
52        return Err(SignerError::ParseError("no public keys provided".into()));
53    }
54    if n > MAX_MULTISIG_KEYS {
55        return Err(SignerError::ParseError(format!(
56            "too many public keys: {n} (max {MAX_MULTISIG_KEYS})"
57        )));
58    }
59    if threshold == 0 {
60        return Err(SignerError::ParseError("threshold must be >= 1".into()));
61    }
62    if threshold > n {
63        return Err(SignerError::ParseError(format!(
64            "threshold {threshold} exceeds key count {n}"
65        )));
66    }
67
68    // OP_m (OP_1..OP_16)
69    let op_m = 0x50 + threshold as u8;
70    let op_n = 0x50 + n as u8;
71
72    // Size: 1 (OP_m) + n*(1+33) (push + key) + 1 (OP_n) + 1 (OP_CHECKMULTISIG)
73    let mut script = Vec::with_capacity(3 + n * 34);
74    script.push(op_m);
75
76    for pk in pubkeys {
77        script.push(33); // push 33 bytes
78        script.extend_from_slice(pk);
79    }
80
81    script.push(op_n);
82    script.push(0xAE); // OP_CHECKMULTISIG
83
84    if script.len() > MAX_REDEEM_SCRIPT_SIZE {
85        return Err(SignerError::ParseError(format!(
86            "redeem script too large: {} bytes (max {MAX_REDEEM_SCRIPT_SIZE})",
87            script.len()
88        )));
89    }
90
91    Ok(script)
92}
93
94// ═══════════════════════════════════════════════════════════════════
95// Script Hashing
96// ═══════════════════════════════════════════════════════════════════
97
98/// Compute the HASH160 of a script (for P2SH).
99///
100/// `HASH160(script) = RIPEMD160(SHA256(script))`
101pub fn script_hash160(script: &[u8]) -> [u8; 20] {
102    super::hash160(script)
103}
104
105/// Compute the SHA256 of a witness script (for P2WSH).
106///
107/// P2WSH uses single SHA256 (not HASH160) of the witness script.
108pub fn witness_script_hash(script: &[u8]) -> [u8; 32] {
109    let mut hasher = Sha256::new();
110    hasher.update(script);
111    let result = hasher.finalize();
112    let mut out = [0u8; 32];
113    out.copy_from_slice(&result);
114    out
115}
116
117// ═══════════════════════════════════════════════════════════════════
118// ScriptPubKey Construction
119// ═══════════════════════════════════════════════════════════════════
120
121/// Build a P2SH scriptPubKey from a script hash.
122///
123/// Format: `OP_HASH160 PUSH20 <script_hash> OP_EQUAL`
124#[must_use]
125pub fn p2sh_script_pubkey(script_hash: &[u8; 20]) -> Vec<u8> {
126    let mut spk = Vec::with_capacity(23);
127    spk.push(0xA9); // OP_HASH160
128    spk.push(0x14); // PUSH 20 bytes
129    spk.extend_from_slice(script_hash);
130    spk.push(0x87); // OP_EQUAL
131    spk
132}
133
134/// Build a P2WSH scriptPubKey from a witness script hash.
135///
136/// Format: `OP_0 PUSH32 <sha256(witness_script)>`
137#[must_use]
138pub fn p2wsh_script_pubkey(script_hash: &[u8; 32]) -> Vec<u8> {
139    let mut spk = Vec::with_capacity(34);
140    spk.push(0x00); // OP_0 (witness version 0)
141    spk.push(0x20); // PUSH 32 bytes
142    spk.extend_from_slice(script_hash);
143    spk
144}
145
146/// Build a P2SH-P2WSH (nested SegWit) scriptPubKey.
147///
148/// The "inner" P2WSH scriptPubKey is wrapped in P2SH:
149/// 1. Build P2WSH scriptPubKey from witness script hash
150/// 2. HASH160 the P2WSH scriptPubKey → redeem script hash
151/// 3. Build P2SH scriptPubKey from the redeem script hash
152#[must_use]
153pub fn p2sh_p2wsh_script_pubkey(witness_script_hash: &[u8; 32]) -> Vec<u8> {
154    // The redeem script IS the P2WSH scriptPubKey
155    let inner = p2wsh_script_pubkey(witness_script_hash);
156    let hash = script_hash160(&inner);
157    p2sh_script_pubkey(&hash)
158}
159
160// ═══════════════════════════════════════════════════════════════════
161// Address Generation
162// ═══════════════════════════════════════════════════════════════════
163
164/// Generate a P2SH address from a redeem script.
165///
166/// - Mainnet: version byte `0x05`, addresses start with `3`
167/// - Testnet: version byte `0xC4`, addresses start with `2`
168#[must_use]
169pub fn p2sh_address(redeem_script: &[u8], testnet: bool) -> String {
170    let hash = script_hash160(redeem_script);
171    let version = if testnet { 0xC4 } else { 0x05 };
172    encoding::base58check_encode(version, &hash)
173}
174
175/// Generate a P2WSH (native SegWit) address from a witness script.
176///
177/// Uses Bech32 encoding with witness version 0 and a 32-byte program.
178///
179/// - Mainnet: `bc1q...` (actually `bc1q` for v0 with 32-byte program)
180/// - Testnet: `tb1q...`
181pub fn p2wsh_address(witness_script: &[u8], testnet: bool) -> Result<String, SignerError> {
182    let hash = witness_script_hash(witness_script);
183    let hrp = if testnet { "tb" } else { "bc" };
184    encoding::bech32_encode(hrp, 0, &hash)
185}
186
187/// Generate a P2SH-P2WSH (nested SegWit) address from a witness script.
188///
189/// The P2WSH scriptPubKey (OP_0 || 32-byte hash) is used as the redeem script,
190/// then wrapped in P2SH.
191///
192/// - Mainnet: addresses start with `3`
193/// - Testnet: addresses start with `2`
194#[must_use]
195pub fn p2sh_p2wsh_address(witness_script: &[u8], testnet: bool) -> String {
196    let wsh = witness_script_hash(witness_script);
197    let inner = p2wsh_script_pubkey(&wsh);
198    p2sh_address(&inner, testnet)
199}
200
201// ═══════════════════════════════════════════════════════════════════
202// Witness Construction
203// ═══════════════════════════════════════════════════════════════════
204
205/// Build the witness stack for spending a P2WSH multisig output.
206///
207/// Witness: `<empty> <sig1> <sig2> ... <sigm> <witness_script>`
208///
209/// The leading empty element is required by the OP_CHECKMULTISIG off-by-one bug.
210///
211/// # Arguments
212/// - `signatures` — DER-encoded signatures (with sighash byte appended)
213/// - `witness_script` — The full witness script (m-of-n OP_CHECKMULTISIG)
214#[must_use]
215pub fn multisig_witness(signatures: &[Vec<u8>], witness_script: &[u8]) -> Vec<Vec<u8>> {
216    let mut witness = Vec::with_capacity(2 + signatures.len());
217    witness.push(vec![]); // OP_0 dummy (CHECKMULTISIG bug)
218    for sig in signatures {
219        witness.push(sig.clone());
220    }
221    witness.push(witness_script.to_vec());
222    witness
223}
224
225/// Build the scriptSig for spending a P2SH multisig output.
226///
227/// scriptSig: `OP_0 <sig1> <sig2> ... <sigm> <serialized_redeem_script>`
228///
229/// Each element is length-prefixed with PUSHDATA opcodes as needed.
230#[must_use]
231pub fn multisig_script_sig(signatures: &[Vec<u8>], redeem_script: &[u8]) -> Vec<u8> {
232    let mut script_sig = Vec::new();
233    script_sig.push(0x00); // OP_0 dummy
234
235    for sig in signatures {
236        push_data_script(&mut script_sig, sig);
237    }
238    push_data_script(&mut script_sig, redeem_script);
239
240    script_sig
241}
242
243/// Build the P2SH-P2WSH scriptSig (just the redeem script push).
244///
245/// scriptSig: `<serialized P2WSH scriptPubKey>`
246///
247/// The actual signatures go in the witness.
248#[must_use]
249pub fn p2sh_p2wsh_script_sig(witness_script_hash: &[u8; 32]) -> Vec<u8> {
250    let inner = p2wsh_script_pubkey(witness_script_hash);
251    let mut script_sig = Vec::with_capacity(1 + inner.len());
252    push_data_script(&mut script_sig, &inner);
253    script_sig
254}
255
256// ═══════════════════════════════════════════════════════════════════
257// Helpers
258// ═══════════════════════════════════════════════════════════════════
259
260/// Push data with appropriate PUSHDATA opcodes.
261fn push_data_script(script: &mut Vec<u8>, data: &[u8]) {
262    let len = data.len();
263    if len <= 75 {
264        script.push(len as u8);
265    } else if len <= 255 {
266        script.push(0x4C); // OP_PUSHDATA1
267        script.push(len as u8);
268    } else if len <= 65535 {
269        script.push(0x4D); // OP_PUSHDATA2
270        script.extend_from_slice(&(len as u16).to_le_bytes());
271    }
272    script.extend_from_slice(data);
273}
274
275/// Check if a script is a valid P2SH scriptPubKey.
276///
277/// P2SH: `OP_HASH160 <20 bytes> OP_EQUAL` (exactly 23 bytes)
278#[must_use]
279pub fn is_p2sh(script: &[u8]) -> bool {
280    script.len() == 23 && script[0] == 0xA9 && script[1] == 0x14 && script[22] == 0x87
281}
282
283/// Check if a script is a valid P2WSH scriptPubKey.
284///
285/// P2WSH: `OP_0 <32 bytes>` (exactly 34 bytes)
286#[must_use]
287pub fn is_p2wsh(script: &[u8]) -> bool {
288    script.len() == 34 && script[0] == 0x00 && script[1] == 0x20
289}
290
291/// Decode the threshold and key count from a standard multisig redeem script.
292///
293/// Returns `Some((m, n))` if the script is a standard m-of-n multisig,
294/// or `None` if the script is not recognized.
295pub fn decode_multisig_script(script: &[u8]) -> Option<(usize, usize)> {
296    // Minimum: OP_m OP_n OP_CHECKMULTISIG = 3 bytes
297    if script.len() < 3 {
298        return None;
299    }
300
301    let last = script[script.len() - 1];
302    if last != 0xAE {
303        // OP_CHECKMULTISIG
304        return None;
305    }
306
307    let op_m = script[0];
308    let op_n = script[script.len() - 2];
309
310    // OP_1..OP_16 = 0x51..0x60
311    if !(0x51..=0x60).contains(&op_m) || !(0x51..=0x60).contains(&op_n) {
312        return None;
313    }
314
315    let m = (op_m - 0x50) as usize;
316    let n = (op_n - 0x50) as usize;
317
318    if m > n || n > MAX_MULTISIG_KEYS {
319        return None;
320    }
321
322    // Verify the script has the expected length: 1 + n*(1+33) + 1 + 1
323    let expected_len = 3 + n * 34;
324    if script.len() != expected_len {
325        return None;
326    }
327
328    // Verify each key push is 33 bytes
329    let mut pos = 1;
330    for _ in 0..n {
331        if pos >= script.len() || script[pos] != 33 {
332            return None;
333        }
334        pos += 34; // push byte + 33 key bytes
335    }
336
337    Some((m, n))
338}
339
340/// Extract public keys from a standard multisig redeem script.
341///
342/// Returns `None` if the script is not a valid standard multisig.
343pub fn extract_pubkeys(script: &[u8]) -> Option<Vec<[u8; 33]>> {
344    let (_, n) = decode_multisig_script(script)?;
345
346    let mut keys = Vec::with_capacity(n);
347    let mut pos = 1; // skip OP_m
348    for _ in 0..n {
349        if script[pos] != 33 {
350            return None;
351        }
352        let mut key = [0u8; 33];
353        key.copy_from_slice(&script[pos + 1..pos + 34]);
354        keys.push(key);
355        pos += 34;
356    }
357
358    Some(keys)
359}
360
361// ═══════════════════════════════════════════════════════════════════
362// Tests
363// ═══════════════════════════════════════════════════════════════════
364
365#[cfg(test)]
366#[allow(clippy::unwrap_used, clippy::expect_used)]
367mod tests {
368    use super::*;
369
370    fn dummy_keys(n: usize) -> Vec<[u8; 33]> {
371        (0..n)
372            .map(|i| {
373                let mut key = [0x02u8; 33];
374                key[32] = i as u8;
375                key
376            })
377            .collect()
378    }
379
380    // Real secp256k1 compressed public keys (from BIP-11 / Bitcoin Core test vectors)
381    fn real_pubkeys() -> Vec<[u8; 33]> {
382        let hex_keys = [
383            "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", // G point
384            "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5", // 2G
385            "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9", // 3G
386        ];
387        hex_keys
388            .iter()
389            .map(|h| {
390                let bytes = hex::decode(h).unwrap();
391                let mut key = [0u8; 33];
392                key.copy_from_slice(&bytes);
393                key
394            })
395            .collect()
396    }
397
398    // ─── Redeem Script Construction ──────────────────────────────
399
400    #[test]
401    fn test_multisig_2_of_3() {
402        let keys = dummy_keys(3);
403        let script = multisig_redeem_script(2, &keys).unwrap();
404        assert_eq!(script[0], 0x52); // OP_2
405        assert_eq!(script[script.len() - 2], 0x53); // OP_3
406        assert_eq!(script[script.len() - 1], 0xAE); // OP_CHECKMULTISIG
407        assert_eq!(script.len(), 3 + 3 * 34);
408    }
409
410    #[test]
411    fn test_multisig_1_of_1() {
412        let keys = dummy_keys(1);
413        let script = multisig_redeem_script(1, &keys).unwrap();
414        assert_eq!(script[0], 0x51);
415        assert_eq!(script[script.len() - 2], 0x51);
416    }
417
418    #[test]
419    fn test_multisig_15_of_15() {
420        let keys = dummy_keys(15);
421        let script = multisig_redeem_script(15, &keys).unwrap();
422        assert_eq!(script[0], 0x5F);
423        assert_eq!(script[script.len() - 2], 0x5F);
424    }
425
426    #[test]
427    fn test_multisig_threshold_zero() {
428        let keys = dummy_keys(3);
429        assert!(multisig_redeem_script(0, &keys).is_err());
430    }
431
432    #[test]
433    fn test_multisig_threshold_exceeds_n() {
434        let keys = dummy_keys(2);
435        assert!(multisig_redeem_script(3, &keys).is_err());
436    }
437
438    #[test]
439    fn test_multisig_empty_keys() {
440        assert!(multisig_redeem_script(1, &[]).is_err());
441    }
442
443    #[test]
444    fn test_multisig_too_many_keys() {
445        let keys = dummy_keys(16);
446        assert!(multisig_redeem_script(1, &keys).is_err());
447    }
448
449    #[test]
450    fn test_multisig_contains_all_keys() {
451        let keys = dummy_keys(3);
452        let script = multisig_redeem_script(2, &keys).unwrap();
453        for key in &keys {
454            assert!(script.windows(33).any(|w| w == key));
455        }
456    }
457
458    // ─── Test Vector: Real Secp256k1 Keys ────────────────────────
459
460    #[test]
461    fn test_real_pubkey_2_of_3_redeem_script() {
462        let keys = real_pubkeys();
463        let script = multisig_redeem_script(2, &keys).unwrap();
464        // Byte-level verification: OP_2 <33> <pk1> <33> <pk2> <33> <pk3> OP_3 OP_CHECKMULTISIG
465        assert_eq!(script[0], 0x52); // OP_2
466        assert_eq!(script[1], 33); // push length
467        assert_eq!(&script[2..35], &keys[0]);
468        assert_eq!(script[35], 33);
469        assert_eq!(&script[36..69], &keys[1]);
470        assert_eq!(script[69], 33);
471        assert_eq!(&script[70..103], &keys[2]);
472        assert_eq!(script[103], 0x53); // OP_3
473        assert_eq!(script[104], 0xAE); // OP_CHECKMULTISIG
474        assert_eq!(script.len(), 105);
475    }
476
477    #[test]
478    fn test_real_pubkey_p2sh_address_stable() {
479        let keys = real_pubkeys();
480        let script = multisig_redeem_script(2, &keys).unwrap();
481        let addr = p2sh_address(&script, false);
482        // Same keys always produce the same P2SH address
483        let addr2 = p2sh_address(&script, false);
484        assert_eq!(addr, addr2);
485        assert!(addr.starts_with('3'));
486        // P2SH addresses are 34 chars (base58check of 1+20+4 bytes)
487        assert_eq!(addr.len(), 34);
488    }
489
490    #[test]
491    fn test_real_pubkey_p2wsh_address_length() {
492        let keys = real_pubkeys();
493        let script = multisig_redeem_script(2, &keys).unwrap();
494        let addr = p2wsh_address(&script, false).unwrap();
495        // Bech32 P2WSH addresses: bc1q + 58 chars = 62 total
496        assert!(addr.starts_with("bc1q"));
497        assert_eq!(addr.len(), 62);
498    }
499
500    #[test]
501    fn test_real_pubkey_decode_roundtrip() {
502        let keys = real_pubkeys();
503        let script = multisig_redeem_script(2, &keys).unwrap();
504        let (m, n) = decode_multisig_script(&script).unwrap();
505        assert_eq!(m, 2);
506        assert_eq!(n, 3);
507        let extracted = extract_pubkeys(&script).unwrap();
508        assert_eq!(extracted, keys);
509    }
510
511    // ─── Exhaustive m-of-n Combinations ──────────────────────────
512
513    #[test]
514    fn test_all_valid_thresholds_1_to_15() {
515        for n in 1..=15usize {
516            let keys = dummy_keys(n);
517            for m in 1..=n {
518                let script = multisig_redeem_script(m, &keys).unwrap();
519                assert_eq!(script[0], 0x50 + m as u8, "OP_m for {m}-of-{n}");
520                assert_eq!(
521                    script[script.len() - 2],
522                    0x50 + n as u8,
523                    "OP_n for {m}-of-{n}"
524                );
525                assert_eq!(script[script.len() - 1], 0xAE, "OP_CMS for {m}-of-{n}");
526                assert_eq!(script.len(), 3 + n * 34, "length for {m}-of-{n}");
527
528                // Roundtrip decode
529                let (dm, dn) = decode_multisig_script(&script).unwrap();
530                assert_eq!(dm, m, "decoded m for {m}-of-{n}");
531                assert_eq!(dn, n, "decoded n for {m}-of-{n}");
532
533                // Extract keys roundtrip
534                let extracted = extract_pubkeys(&script).unwrap();
535                assert_eq!(extracted.len(), n);
536                assert_eq!(extracted, keys, "keys roundtrip for {m}-of-{n}");
537            }
538        }
539    }
540
541    // ─── SHA256 Known Test Vector ────────────────────────────────
542
543    #[test]
544    fn test_witness_script_hash_known_vector() {
545        // SHA256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
546        let hash = witness_script_hash(b"");
547        assert_eq!(
548            hex::encode(hash),
549            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
550        );
551    }
552
553    #[test]
554    fn test_witness_script_hash_abc() {
555        // SHA256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
556        let hash = witness_script_hash(b"abc");
557        assert_eq!(
558            hex::encode(hash),
559            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
560        );
561    }
562
563    // ─── Script Hashing ──────────────────────────────────────────
564
565    #[test]
566    fn test_script_hash160_deterministic() {
567        let data = b"test script";
568        assert_eq!(script_hash160(data), script_hash160(data));
569    }
570
571    #[test]
572    fn test_witness_script_hash_deterministic() {
573        let data = b"test witness script";
574        assert_eq!(witness_script_hash(data), witness_script_hash(data));
575    }
576
577    #[test]
578    fn test_witness_script_hash_differs_from_hash160() {
579        let data = b"test";
580        let h160 = script_hash160(data);
581        let wsh = witness_script_hash(data);
582        assert_ne!(&h160[..], &wsh[..20]);
583    }
584
585    #[test]
586    fn test_hash160_empty_input() {
587        // HASH160("") is deterministic — known value
588        let h = script_hash160(b"");
589        assert_eq!(h.len(), 20);
590        // Verify it matches RIPEMD160(SHA256(""))
591        let expected = "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb";
592        assert_eq!(hex::encode(h), expected);
593    }
594
595    // ─── ScriptPubKey Construction ───────────────────────────────
596
597    #[test]
598    fn test_p2sh_script_pubkey_structure() {
599        let hash = [0xAA; 20];
600        let spk = p2sh_script_pubkey(&hash);
601        assert_eq!(spk.len(), 23);
602        assert_eq!(spk[0], 0xA9);
603        assert_eq!(spk[1], 0x14);
604        assert_eq!(&spk[2..22], &hash);
605        assert_eq!(spk[22], 0x87);
606    }
607
608    #[test]
609    fn test_p2wsh_script_pubkey_structure() {
610        let hash = [0xBB; 32];
611        let spk = p2wsh_script_pubkey(&hash);
612        assert_eq!(spk.len(), 34);
613        assert_eq!(spk[0], 0x00);
614        assert_eq!(spk[1], 0x20);
615        assert_eq!(&spk[2..34], &hash);
616    }
617
618    #[test]
619    fn test_p2sh_p2wsh_script_pubkey_is_p2sh() {
620        let wsh = [0xCC; 32];
621        let spk = p2sh_p2wsh_script_pubkey(&wsh);
622        assert!(is_p2sh(&spk));
623    }
624
625    #[test]
626    fn test_p2sh_script_pubkey_hex_encoding() {
627        // Known P2SH script: OP_HASH160 <20 zero bytes> OP_EQUAL
628        let hash = [0u8; 20];
629        let spk = p2sh_script_pubkey(&hash);
630        let expected = "a914000000000000000000000000000000000000000087";
631        assert_eq!(hex::encode(&spk), expected);
632    }
633
634    #[test]
635    fn test_p2wsh_script_pubkey_hex_encoding() {
636        // Known P2WSH script: OP_0 <32 zero bytes>
637        let hash = [0u8; 32];
638        let spk = p2wsh_script_pubkey(&hash);
639        let expected = "00200000000000000000000000000000000000000000000000000000000000000000";
640        assert_eq!(hex::encode(&spk), expected);
641    }
642
643    // ─── Address Generation ──────────────────────────────────────
644
645    #[test]
646    fn test_p2sh_address_mainnet_starts_with_3() {
647        let keys = dummy_keys(2);
648        let script = multisig_redeem_script(2, &keys).unwrap();
649        let addr = p2sh_address(&script, false);
650        assert!(addr.starts_with('3'));
651    }
652
653    #[test]
654    fn test_p2sh_address_testnet_starts_with_2() {
655        let keys = dummy_keys(2);
656        let script = multisig_redeem_script(2, &keys).unwrap();
657        let addr = p2sh_address(&script, true);
658        assert!(addr.starts_with('2'));
659    }
660
661    #[test]
662    fn test_p2wsh_address_mainnet_starts_with_bc1() {
663        let keys = dummy_keys(2);
664        let script = multisig_redeem_script(2, &keys).unwrap();
665        let addr = p2wsh_address(&script, false).unwrap();
666        assert!(addr.starts_with("bc1"));
667    }
668
669    #[test]
670    fn test_p2wsh_address_testnet_starts_with_tb1() {
671        let keys = dummy_keys(2);
672        let script = multisig_redeem_script(2, &keys).unwrap();
673        let addr = p2wsh_address(&script, true).unwrap();
674        assert!(addr.starts_with("tb1"));
675    }
676
677    #[test]
678    fn test_p2sh_p2wsh_address_mainnet() {
679        let keys = dummy_keys(3);
680        let script = multisig_redeem_script(2, &keys).unwrap();
681        let addr = p2sh_p2wsh_address(&script, false);
682        assert!(addr.starts_with('3'));
683    }
684
685    #[test]
686    fn test_p2sh_p2wsh_address_testnet() {
687        let keys = dummy_keys(3);
688        let script = multisig_redeem_script(2, &keys).unwrap();
689        let addr = p2sh_p2wsh_address(&script, true);
690        assert!(addr.starts_with('2'));
691    }
692
693    #[test]
694    fn test_different_wrapping_produces_different_addresses() {
695        let keys = dummy_keys(2);
696        let script = multisig_redeem_script(2, &keys).unwrap();
697        let a1 = p2sh_address(&script, false);
698        let a2 = p2wsh_address(&script, false).unwrap();
699        let a3 = p2sh_p2wsh_address(&script, false);
700        assert_ne!(a1, a2);
701        assert_ne!(a1, a3);
702        assert_ne!(a2, a3);
703    }
704
705    #[test]
706    fn test_mainnet_vs_testnet_addresses_differ() {
707        let keys = dummy_keys(2);
708        let script = multisig_redeem_script(2, &keys).unwrap();
709        assert_ne!(p2sh_address(&script, false), p2sh_address(&script, true));
710        assert_ne!(
711            p2wsh_address(&script, false).unwrap(),
712            p2wsh_address(&script, true).unwrap()
713        );
714        assert_ne!(
715            p2sh_p2wsh_address(&script, false),
716            p2sh_p2wsh_address(&script, true),
717        );
718    }
719
720    // ─── Witness / ScriptSig ─────────────────────────────────────
721
722    #[test]
723    fn test_multisig_witness_structure() {
724        let sigs = vec![vec![0x30; 71], vec![0x30; 72]];
725        let script = vec![0xAE; 10];
726        let witness = multisig_witness(&sigs, &script);
727        assert_eq!(witness.len(), 4);
728        assert!(witness[0].is_empty());
729        assert_eq!(witness[1], sigs[0]);
730        assert_eq!(witness[2], sigs[1]);
731        assert_eq!(witness[3], script);
732    }
733
734    #[test]
735    fn test_multisig_witness_single_sig() {
736        let sigs = vec![vec![0x30; 72]];
737        let script = vec![0xAE];
738        let witness = multisig_witness(&sigs, &script);
739        assert_eq!(witness.len(), 3); // empty + 1 sig + script
740    }
741
742    #[test]
743    fn test_multisig_witness_empty_sigs() {
744        let witness = multisig_witness(&[], &[0xAE]);
745        assert_eq!(witness.len(), 2); // empty + script
746        assert!(witness[0].is_empty());
747        assert_eq!(witness[1], vec![0xAE]);
748    }
749
750    #[test]
751    fn test_multisig_script_sig_structure() {
752        let sigs = vec![vec![0x30, 0x44], vec![0x30, 0x45]];
753        let redeem = vec![0xAE; 10];
754        let script_sig = multisig_script_sig(&sigs, &redeem);
755        assert_eq!(script_sig[0], 0x00);
756        assert!(script_sig.len() > 3 + sigs[0].len() + sigs[1].len() + redeem.len());
757    }
758
759    #[test]
760    fn test_multisig_script_sig_with_real_script() {
761        let keys = dummy_keys(3);
762        let redeem = multisig_redeem_script(2, &keys).unwrap();
763        // Mock DER sigs (30 44 ... 01 = DER + SIGHASH_ALL)
764        let sig1 = vec![0x30; 72];
765        let sig2 = vec![0x30; 71];
766        let ss = multisig_script_sig(&[sig1.clone(), sig2.clone()], &redeem);
767
768        // OP_0 + push(sig1) + push(sig2) + OP_PUSHDATA1(redeem)
769        assert_eq!(ss[0], 0x00);
770        // sig1 is 72 bytes → push byte 72
771        assert_eq!(ss[1], 72);
772        assert_eq!(&ss[2..74], &sig1[..]);
773        // sig2 is 71 bytes → push byte 71
774        assert_eq!(ss[74], 71);
775        assert_eq!(&ss[75..146], &sig2[..]);
776        // redeem is 105 bytes → OP_PUSHDATA1 (0x4C) then length
777        assert_eq!(ss[146], 0x4C);
778        assert_eq!(ss[147], 105);
779    }
780
781    #[test]
782    fn test_p2sh_p2wsh_script_sig_structure() {
783        let wsh = [0xCC; 32];
784        let script_sig = p2sh_p2wsh_script_sig(&wsh);
785        assert_eq!(script_sig[0], 34);
786        assert_eq!(script_sig[1], 0x00);
787        assert_eq!(script_sig[2], 0x20);
788        assert_eq!(script_sig.len(), 35);
789        // Verify embedded WSH
790        assert_eq!(&script_sig[3..35], &wsh);
791    }
792
793    // ─── Script Detection ────────────────────────────────────────
794
795    #[test]
796    fn test_is_p2sh() {
797        let hash = [0xAA; 20];
798        let spk = p2sh_script_pubkey(&hash);
799        assert!(is_p2sh(&spk));
800        assert!(!is_p2sh(&[0u8; 34]));
801        assert!(!is_p2sh(&[]));
802    }
803
804    #[test]
805    fn test_is_p2sh_wrong_length() {
806        assert!(!is_p2sh(&[0xA9, 0x14, 0x00])); // too short
807        let mut too_long = vec![0xA9, 0x14];
808        too_long.extend_from_slice(&[0; 20]);
809        too_long.push(0x87);
810        too_long.push(0xFF); // extra byte
811        assert!(!is_p2sh(&too_long));
812    }
813
814    #[test]
815    fn test_is_p2sh_wrong_opcodes() {
816        let mut bad = vec![0x00, 0x14]; // wrong first opcode
817        bad.extend_from_slice(&[0; 20]);
818        bad.push(0x87);
819        assert!(!is_p2sh(&bad));
820    }
821
822    #[test]
823    fn test_is_p2wsh() {
824        let hash = [0xBB; 32];
825        let spk = p2wsh_script_pubkey(&hash);
826        assert!(is_p2wsh(&spk));
827        assert!(!is_p2wsh(&[0u8; 23]));
828        assert!(!is_p2wsh(&[]));
829    }
830
831    #[test]
832    fn test_is_p2wsh_wrong_version() {
833        let mut bad = vec![0x01, 0x20]; // witness version 1, not 0
834        bad.extend_from_slice(&[0; 32]);
835        assert!(!is_p2wsh(&bad));
836    }
837
838    // ─── Decode Multisig Script ──────────────────────────────────
839
840    #[test]
841    fn test_decode_multisig_2_of_3() {
842        let keys = dummy_keys(3);
843        let script = multisig_redeem_script(2, &keys).unwrap();
844        let (m, n) = decode_multisig_script(&script).unwrap();
845        assert_eq!(m, 2);
846        assert_eq!(n, 3);
847    }
848
849    #[test]
850    fn test_decode_multisig_1_of_1() {
851        let keys = dummy_keys(1);
852        let script = multisig_redeem_script(1, &keys).unwrap();
853        let (m, n) = decode_multisig_script(&script).unwrap();
854        assert_eq!(m, 1);
855        assert_eq!(n, 1);
856    }
857
858    #[test]
859    fn test_decode_multisig_invalid() {
860        assert!(decode_multisig_script(&[]).is_none());
861        assert!(decode_multisig_script(&[0x00, 0x00]).is_none());
862        assert!(decode_multisig_script(&[0x52, 0x53, 0xAB]).is_none());
863    }
864
865    #[test]
866    fn test_decode_multisig_corrupted_push_byte() {
867        let keys = dummy_keys(2);
868        let mut script = multisig_redeem_script(2, &keys).unwrap();
869        // Corrupt the push byte of the first key
870        script[1] = 32; // should be 33
871        assert!(decode_multisig_script(&script).is_none());
872    }
873
874    #[test]
875    fn test_decode_multisig_wrong_length() {
876        // Script that claims 2-of-3 but has wrong length
877        let mut script = vec![0x52]; // OP_2
878        script.push(33);
879        script.extend_from_slice(&[0x02; 33]);
880        // Only 1 key, so OP_3 + OP_CMS would be wrong
881        script.push(0x53);
882        script.push(0xAE);
883        assert!(decode_multisig_script(&script).is_none());
884    }
885
886    #[test]
887    fn test_decode_m_greater_than_n() {
888        // Manually craft a script with m > n (invalid but well-formed bytes)
889        // OP_3 <key1> OP_2 OP_CHECKMULTISIG — 3 > 2 but only 1 key
890        let mut script = vec![0x53]; // OP_3
891        script.push(33);
892        script.extend_from_slice(&[0x02; 33]);
893        script.push(0x52); // OP_2
894        script.push(0xAE);
895        assert!(decode_multisig_script(&script).is_none());
896    }
897
898    // ─── Extract Pubkeys ─────────────────────────────────────────
899
900    #[test]
901    fn test_extract_pubkeys() {
902        let keys = dummy_keys(3);
903        let script = multisig_redeem_script(2, &keys).unwrap();
904        let extracted = extract_pubkeys(&script).unwrap();
905        assert_eq!(extracted.len(), 3);
906        assert_eq!(extracted, keys);
907    }
908
909    #[test]
910    fn test_extract_pubkeys_invalid() {
911        assert!(extract_pubkeys(&[]).is_none());
912    }
913
914    #[test]
915    fn test_extract_pubkeys_real_keys() {
916        let keys = real_pubkeys();
917        let script = multisig_redeem_script(2, &keys).unwrap();
918        let extracted = extract_pubkeys(&script).unwrap();
919        assert_eq!(extracted[0], keys[0]);
920        assert_eq!(extracted[1], keys[1]);
921        assert_eq!(extracted[2], keys[2]);
922    }
923
924    // ─── End-to-End Flows ────────────────────────────────────────
925
926    #[test]
927    fn test_full_p2sh_multisig_flow() {
928        let keys = dummy_keys(3);
929        let script = multisig_redeem_script(2, &keys).unwrap();
930        let addr = p2sh_address(&script, false);
931        assert!(addr.starts_with('3'));
932
933        let sigs = vec![vec![0x30; 71], vec![0x30; 72]];
934        let ss = multisig_script_sig(&sigs, &script);
935        assert!(!ss.is_empty());
936
937        let (m, n) = decode_multisig_script(&script).unwrap();
938        assert_eq!(m, 2);
939        assert_eq!(n, 3);
940    }
941
942    #[test]
943    fn test_full_p2wsh_multisig_flow() {
944        let keys = dummy_keys(3);
945        let ws = multisig_redeem_script(2, &keys).unwrap();
946        let addr = p2wsh_address(&ws, false).unwrap();
947        assert!(addr.starts_with("bc1"));
948
949        let sigs = vec![vec![0x30; 71], vec![0x30; 72]];
950        let witness = multisig_witness(&sigs, &ws);
951        assert_eq!(witness.len(), 4);
952    }
953
954    #[test]
955    fn test_full_p2sh_p2wsh_flow() {
956        let keys = dummy_keys(3);
957        let ws = multisig_redeem_script(2, &keys).unwrap();
958        let wsh = witness_script_hash(&ws);
959        let addr = p2sh_p2wsh_address(&ws, false);
960        assert!(addr.starts_with('3'));
961
962        let ss = p2sh_p2wsh_script_sig(&wsh);
963        assert_eq!(ss.len(), 35);
964
965        let sigs = vec![vec![0x30; 71], vec![0x30; 72]];
966        let witness = multisig_witness(&sigs, &ws);
967        assert_eq!(witness.len(), 4);
968    }
969
970    #[test]
971    fn test_full_flow_real_keys() {
972        let keys = real_pubkeys();
973
974        // P2SH
975        let script = multisig_redeem_script(2, &keys).unwrap();
976        let p2sh_addr = p2sh_address(&script, false);
977        assert!(p2sh_addr.starts_with('3'));
978
979        // P2WSH
980        let p2wsh_addr = p2wsh_address(&script, false).unwrap();
981        assert!(p2wsh_addr.starts_with("bc1q"));
982
983        // P2SH-P2WSH
984        let p2sh_p2wsh_addr = p2sh_p2wsh_address(&script, false);
985        assert!(p2sh_p2wsh_addr.starts_with('3'));
986
987        // All different
988        assert_ne!(p2sh_addr, p2sh_p2wsh_addr);
989        assert_ne!(p2sh_addr, p2wsh_addr);
990
991        // ScriptPubKey detection
992        let wsh = witness_script_hash(&script);
993        let spk_p2sh = p2sh_script_pubkey(&script_hash160(&script));
994        let spk_p2wsh = p2wsh_script_pubkey(&wsh);
995        assert!(is_p2sh(&spk_p2sh));
996        assert!(is_p2wsh(&spk_p2wsh));
997        assert!(!is_p2sh(&spk_p2wsh));
998        assert!(!is_p2wsh(&spk_p2sh));
999    }
1000
1001    // ─── Push Data ───────────────────────────────────────────────
1002
1003    #[test]
1004    fn test_push_data_small() {
1005        let mut s = Vec::new();
1006        push_data_script(&mut s, &[0xAA; 10]);
1007        assert_eq!(s[0], 10);
1008        assert_eq!(&s[1..], &[0xAA; 10]);
1009    }
1010
1011    #[test]
1012    fn test_push_data_boundary_75() {
1013        // 75 bytes is the max for direct push
1014        let mut s = Vec::new();
1015        let data = vec![0xBB; 75];
1016        push_data_script(&mut s, &data);
1017        assert_eq!(s[0], 75);
1018        assert_eq!(s.len(), 76);
1019    }
1020
1021    #[test]
1022    fn test_push_data_boundary_76() {
1023        // 76 bytes triggers OP_PUSHDATA1
1024        let mut s = Vec::new();
1025        let data = vec![0xBB; 76];
1026        push_data_script(&mut s, &data);
1027        assert_eq!(s[0], 0x4C); // OP_PUSHDATA1
1028        assert_eq!(s[1], 76);
1029        assert_eq!(s.len(), 78);
1030    }
1031
1032    #[test]
1033    fn test_push_data_medium() {
1034        let mut s = Vec::new();
1035        let data = vec![0xBB; 100];
1036        push_data_script(&mut s, &data);
1037        assert_eq!(s[0], 0x4C);
1038        assert_eq!(s[1], 100);
1039        assert_eq!(&s[2..], &data[..]);
1040    }
1041
1042    #[test]
1043    fn test_push_data_boundary_255() {
1044        let mut s = Vec::new();
1045        let data = vec![0xCC; 255];
1046        push_data_script(&mut s, &data);
1047        assert_eq!(s[0], 0x4C); // Still OP_PUSHDATA1
1048        assert_eq!(s[1], 255);
1049    }
1050
1051    #[test]
1052    fn test_push_data_boundary_256() {
1053        // 256 bytes triggers OP_PUSHDATA2
1054        let mut s = Vec::new();
1055        let data = vec![0xCC; 256];
1056        push_data_script(&mut s, &data);
1057        assert_eq!(s[0], 0x4D);
1058        let len = u16::from_le_bytes([s[1], s[2]]);
1059        assert_eq!(len, 256);
1060        assert_eq!(s.len(), 259);
1061    }
1062
1063    #[test]
1064    fn test_push_data_large() {
1065        let mut s = Vec::new();
1066        let data = vec![0xCC; 300];
1067        push_data_script(&mut s, &data);
1068        assert_eq!(s[0], 0x4D);
1069        let len = u16::from_le_bytes([s[1], s[2]]);
1070        assert_eq!(len, 300);
1071    }
1072
1073    #[test]
1074    fn test_push_data_empty() {
1075        let mut s = Vec::new();
1076        push_data_script(&mut s, &[]);
1077        assert_eq!(s, vec![0x00]); // push 0 bytes
1078    }
1079
1080    #[test]
1081    fn test_push_data_single_byte() {
1082        let mut s = Vec::new();
1083        push_data_script(&mut s, &[0xFF]);
1084        assert_eq!(s, vec![0x01, 0xFF]);
1085    }
1086
1087    // ─── Deterministic Addresses ─────────────────────────────────
1088
1089    #[test]
1090    fn test_same_keys_same_address() {
1091        let keys = dummy_keys(3);
1092        let s1 = multisig_redeem_script(2, &keys).unwrap();
1093        let s2 = multisig_redeem_script(2, &keys).unwrap();
1094        assert_eq!(p2sh_address(&s1, false), p2sh_address(&s2, false));
1095    }
1096
1097    #[test]
1098    fn test_different_threshold_different_address() {
1099        let keys = dummy_keys(3);
1100        let s1 = multisig_redeem_script(1, &keys).unwrap();
1101        let s2 = multisig_redeem_script(2, &keys).unwrap();
1102        assert_ne!(p2sh_address(&s1, false), p2sh_address(&s2, false));
1103    }
1104
1105    #[test]
1106    fn test_different_key_order_different_address() {
1107        let keys1 = dummy_keys(3);
1108        let mut keys2 = keys1.clone();
1109        keys2.swap(0, 1);
1110        let s1 = multisig_redeem_script(2, &keys1).unwrap();
1111        let s2 = multisig_redeem_script(2, &keys2).unwrap();
1112        assert_ne!(p2sh_address(&s1, false), p2sh_address(&s2, false));
1113    }
1114
1115    // ─── Cross-wrapping Consistency ──────────────────────────────
1116
1117    #[test]
1118    fn test_p2sh_p2wsh_is_hash_of_p2wsh() {
1119        let keys = dummy_keys(3);
1120        let script = multisig_redeem_script(2, &keys).unwrap();
1121        let wsh = witness_script_hash(&script);
1122
1123        // The P2SH-P2WSH address wraps the P2WSH scriptPubKey
1124        let p2wsh_spk = p2wsh_script_pubkey(&wsh);
1125        let wrapped_addr = p2sh_address(&p2wsh_spk, false);
1126        let direct_addr = p2sh_p2wsh_address(&script, false);
1127        assert_eq!(wrapped_addr, direct_addr);
1128    }
1129
1130    #[test]
1131    fn test_p2sh_p2wsh_script_pubkey_consistency() {
1132        let keys = dummy_keys(2);
1133        let script = multisig_redeem_script(2, &keys).unwrap();
1134        let wsh = witness_script_hash(&script);
1135
1136        // Build P2SH-P2WSH scriptPubKey two ways
1137        let spk1 = p2sh_p2wsh_script_pubkey(&wsh);
1138        let inner = p2wsh_script_pubkey(&wsh);
1139        let spk2 = p2sh_script_pubkey(&script_hash160(&inner));
1140        assert_eq!(spk1, spk2);
1141    }
1142
1143    // ─── Script Size Limits ──────────────────────────────────────
1144
1145    #[test]
1146    fn test_redeem_script_size_under_520() {
1147        // 15 keys = 3 + 15*34 = 513 bytes < 520
1148        let keys = dummy_keys(15);
1149        let script = multisig_redeem_script(1, &keys).unwrap();
1150        assert!(script.len() <= MAX_REDEEM_SCRIPT_SIZE);
1151        assert_eq!(script.len(), 513);
1152    }
1153
1154    #[test]
1155    fn test_threshold_equal_to_n() {
1156        // m == n is valid
1157        let keys = dummy_keys(5);
1158        let script = multisig_redeem_script(5, &keys).unwrap();
1159        let (m, n) = decode_multisig_script(&script).unwrap();
1160        assert_eq!(m, 5);
1161        assert_eq!(n, 5);
1162    }
1163
1164    #[test]
1165    fn test_threshold_1_of_15() {
1166        let keys = dummy_keys(15);
1167        let script = multisig_redeem_script(1, &keys).unwrap();
1168        let (m, n) = decode_multisig_script(&script).unwrap();
1169        assert_eq!(m, 1);
1170        assert_eq!(n, 15);
1171    }
1172
1173    // ─── Error Message Content ───────────────────────────────────
1174
1175    #[test]
1176    fn test_error_message_empty_keys() {
1177        let err = multisig_redeem_script(1, &[]).unwrap_err();
1178        let msg = format!("{err}");
1179        assert!(msg.contains("no public keys"), "got: {msg}");
1180    }
1181
1182    #[test]
1183    fn test_error_message_too_many() {
1184        let keys = dummy_keys(16);
1185        let err = multisig_redeem_script(1, &keys).unwrap_err();
1186        let msg = format!("{err}");
1187        assert!(msg.contains("too many") || msg.contains("16"), "got: {msg}");
1188    }
1189
1190    #[test]
1191    fn test_error_message_threshold_zero() {
1192        let keys = dummy_keys(2);
1193        let err = multisig_redeem_script(0, &keys).unwrap_err();
1194        let msg = format!("{err}");
1195        assert!(
1196            msg.contains("threshold") || msg.contains(">= 1"),
1197            "got: {msg}"
1198        );
1199    }
1200
1201    #[test]
1202    fn test_error_message_threshold_exceeds() {
1203        let keys = dummy_keys(2);
1204        let err = multisig_redeem_script(5, &keys).unwrap_err();
1205        let msg = format!("{err}");
1206        assert!(msg.contains("exceeds") || msg.contains("5"), "got: {msg}");
1207    }
1208}