Skip to main content

dpp/address_funds/
witness.rs

1use bincode::de::{BorrowDecoder, Decoder};
2use bincode::enc::Encoder;
3use bincode::error::{DecodeError, EncodeError};
4use bincode::{Decode, Encode};
5use platform_value::BinaryData;
6#[cfg(feature = "serde-conversion")]
7use serde::{Deserialize, Serialize};
8
9/// Maximum number of entries in a P2SH signatures vector.
10/// This is 16 (max keys from OP_PUSHNUM_16) + 1 (CHECKMULTISIG dummy byte).
11pub const MAX_P2SH_SIGNATURES: usize = 17;
12
13/// The input witness data required to spend from a PlatformAddress.
14///
15/// This enum captures the different spending patterns for P2PKH and P2SH addresses.
16#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
17pub enum AddressWitness {
18    /// P2PKH witness: recoverable signature only
19    ///
20    /// Used for spending from a Pay-to-Public-Key-Hash address.
21    /// The public key is recovered from the signature during verification,
22    /// saving 33 bytes per witness compared to including the public key.
23    P2pkh {
24        /// The recoverable ECDSA signature (65 bytes with recovery byte prefix)
25        signature: BinaryData, //todo change to [u8;65]
26    },
27    /// P2SH witness: signatures + redeem script
28    ///
29    /// Used for spending from a Pay-to-Script-Hash address (e.g., multisig).
30    /// For a 2-of-3 multisig, signatures would be `[OP_0, sig1, sig2]` and
31    /// redeem_script would be `OP_2 <pub1> <pub2> <pub3> OP_3 OP_CHECKMULTISIG`.
32    P2sh {
33        /// The signatures (may include placeholder bytes like OP_0 for CHECKMULTISIG bug)
34        signatures: Vec<BinaryData>,
35        /// The redeem script that hashes to the address
36        redeem_script: BinaryData,
37    },
38}
39
40impl Encode for AddressWitness {
41    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
42        match self {
43            AddressWitness::P2pkh { signature } => {
44                0u8.encode(encoder)?;
45                signature.encode(encoder)?;
46            }
47            AddressWitness::P2sh {
48                signatures,
49                redeem_script,
50            } => {
51                1u8.encode(encoder)?;
52                signatures.encode(encoder)?;
53                redeem_script.encode(encoder)?;
54            }
55        }
56        Ok(())
57    }
58}
59
60impl<C> Decode<C> for AddressWitness {
61    fn decode<D: Decoder<Context = C>>(decoder: &mut D) -> Result<Self, DecodeError> {
62        let discriminant = u8::decode(decoder)?;
63        match discriminant {
64            0 => {
65                let signature = BinaryData::decode(decoder)?;
66                Ok(AddressWitness::P2pkh { signature })
67            }
68            1 => {
69                let signatures = Vec::<BinaryData>::decode(decoder)?;
70                if signatures.len() > MAX_P2SH_SIGNATURES {
71                    return Err(DecodeError::OtherString(format!(
72                        "P2SH signatures count {} exceeds maximum {}",
73                        signatures.len(),
74                        MAX_P2SH_SIGNATURES,
75                    )));
76                }
77                let redeem_script = BinaryData::decode(decoder)?;
78                Ok(AddressWitness::P2sh {
79                    signatures,
80                    redeem_script,
81                })
82            }
83            _ => Err(DecodeError::OtherString(format!(
84                "Invalid AddressWitness discriminant: {}",
85                discriminant
86            ))),
87        }
88    }
89}
90
91impl<'de, C> bincode::BorrowDecode<'de, C> for AddressWitness {
92    fn borrow_decode<D: BorrowDecoder<'de, Context = C>>(
93        decoder: &mut D,
94    ) -> Result<Self, DecodeError> {
95        let discriminant = u8::borrow_decode(decoder)?;
96        match discriminant {
97            0 => {
98                let signature = BinaryData::borrow_decode(decoder)?;
99                Ok(AddressWitness::P2pkh { signature })
100            }
101            1 => {
102                let signatures = Vec::<BinaryData>::borrow_decode(decoder)?;
103                if signatures.len() > MAX_P2SH_SIGNATURES {
104                    return Err(DecodeError::OtherString(format!(
105                        "P2SH signatures count {} exceeds maximum {}",
106                        signatures.len(),
107                        MAX_P2SH_SIGNATURES,
108                    )));
109                }
110                let redeem_script = BinaryData::borrow_decode(decoder)?;
111                Ok(AddressWitness::P2sh {
112                    signatures,
113                    redeem_script,
114                })
115            }
116            _ => Err(DecodeError::OtherString(format!(
117                "Invalid AddressWitness discriminant: {}",
118                discriminant
119            ))),
120        }
121    }
122}
123
124#[cfg(feature = "serde-conversion")]
125impl Serialize for AddressWitness {
126    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127    where
128        S: serde::Serializer,
129    {
130        use serde::ser::SerializeStruct;
131
132        match self {
133            AddressWitness::P2pkh { signature } => {
134                let mut state = serializer.serialize_struct("AddressWitness", 2)?;
135                state.serialize_field("type", "p2pkh")?;
136                state.serialize_field("signature", signature)?;
137                state.end()
138            }
139            AddressWitness::P2sh {
140                signatures,
141                redeem_script,
142            } => {
143                let mut state = serializer.serialize_struct("AddressWitness", 3)?;
144                state.serialize_field("type", "p2sh")?;
145                state.serialize_field("signatures", signatures)?;
146                state.serialize_field("redeemScript", redeem_script)?;
147                state.end()
148            }
149        }
150    }
151}
152
153#[cfg(feature = "serde-conversion")]
154impl<'de> Deserialize<'de> for AddressWitness {
155    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
156    where
157        D: serde::Deserializer<'de>,
158    {
159        use serde::de::{self, MapAccess, Visitor};
160        use std::fmt;
161
162        struct AddressWitnessVisitor;
163
164        impl<'de> Visitor<'de> for AddressWitnessVisitor {
165            type Value = AddressWitness;
166
167            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
168                formatter.write_str("an AddressWitness struct")
169            }
170
171            fn visit_map<V>(self, mut map: V) -> Result<AddressWitness, V::Error>
172            where
173                V: MapAccess<'de>,
174            {
175                let mut witness_type: Option<String> = None;
176                let mut signature: Option<BinaryData> = None;
177                let mut signatures: Option<Vec<BinaryData>> = None;
178                let mut redeem_script: Option<BinaryData> = None;
179
180                while let Some(key) = map.next_key::<String>()? {
181                    match key.as_str() {
182                        "type" => {
183                            witness_type = Some(map.next_value()?);
184                        }
185                        "signature" => {
186                            signature = Some(map.next_value()?);
187                        }
188                        "signatures" => {
189                            signatures = Some(map.next_value()?);
190                        }
191                        "redeemScript" => {
192                            redeem_script = Some(map.next_value()?);
193                        }
194                        _ => {
195                            let _: serde::de::IgnoredAny = map.next_value()?;
196                        }
197                    }
198                }
199
200                let witness_type = witness_type.ok_or_else(|| de::Error::missing_field("type"))?;
201
202                match witness_type.as_str() {
203                    "p2pkh" => {
204                        let signature =
205                            signature.ok_or_else(|| de::Error::missing_field("signature"))?;
206                        Ok(AddressWitness::P2pkh { signature })
207                    }
208                    "p2sh" => {
209                        let signatures =
210                            signatures.ok_or_else(|| de::Error::missing_field("signatures"))?;
211                        if signatures.len() > MAX_P2SH_SIGNATURES {
212                            return Err(de::Error::custom(format!(
213                                "P2SH signatures count {} exceeds maximum {}",
214                                signatures.len(),
215                                MAX_P2SH_SIGNATURES,
216                            )));
217                        }
218                        let redeem_script = redeem_script
219                            .ok_or_else(|| de::Error::missing_field("redeemScript"))?;
220                        Ok(AddressWitness::P2sh {
221                            signatures,
222                            redeem_script,
223                        })
224                    }
225                    _ => Err(de::Error::unknown_variant(
226                        &witness_type,
227                        &["p2pkh", "p2sh"],
228                    )),
229                }
230            }
231        }
232
233        deserializer.deserialize_struct(
234            "AddressWitness",
235            &["type", "signature", "signatures", "redeemScript"],
236            AddressWitnessVisitor,
237        )
238    }
239}
240
241impl AddressWitness {
242    /// Generates a unique identifier for this witness based on its contents.
243    ///
244    /// This is used for deduplication purposes in unique_identifiers() implementations.
245    pub fn unique_id(&self) -> String {
246        use base64::prelude::BASE64_STANDARD;
247        use base64::Engine;
248
249        let mut data = Vec::new();
250
251        match self {
252            AddressWitness::P2pkh { signature } => {
253                data.push(0u8);
254                data.extend_from_slice(signature.as_slice());
255            }
256            AddressWitness::P2sh {
257                signatures,
258                redeem_script,
259            } => {
260                data.push(1u8);
261                data.extend_from_slice(redeem_script.as_slice());
262                for sig in signatures {
263                    data.extend_from_slice(sig.as_slice());
264                }
265            }
266        }
267
268        BASE64_STANDARD.encode(&data)
269    }
270
271    /// Returns the redeem script if this is a P2SH witness
272    pub fn redeem_script(&self) -> Option<&BinaryData> {
273        match self {
274            AddressWitness::P2pkh { .. } => None,
275            AddressWitness::P2sh { redeem_script, .. } => Some(redeem_script),
276        }
277    }
278
279    /// Returns true if this is a P2PKH witness
280    pub fn is_p2pkh(&self) -> bool {
281        matches!(self, AddressWitness::P2pkh { .. })
282    }
283
284    /// Returns true if this is a P2SH witness
285    pub fn is_p2sh(&self) -> bool {
286        matches!(self, AddressWitness::P2sh { .. })
287    }
288}
289
290#[cfg(test)]
291#[allow(clippy::needless_borrows_for_generic_args)]
292mod tests {
293    use super::*;
294    use bincode::config;
295
296    #[test]
297    fn test_p2pkh_witness_encode_decode() {
298        let witness = AddressWitness::P2pkh {
299            signature: BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]),
300        };
301
302        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
303        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
304            .unwrap()
305            .0;
306
307        assert_eq!(witness, decoded);
308        assert!(decoded.is_p2pkh());
309        assert!(!decoded.is_p2sh());
310    }
311
312    #[test]
313    fn test_p2sh_witness_encode_decode() {
314        let witness = AddressWitness::P2sh {
315            signatures: vec![
316                BinaryData::new(vec![0x00]),                   // OP_0 placeholder
317                BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]), // sig1
318                BinaryData::new(vec![0x30, 0x45, 0x02, 0x21]), // sig2
319            ],
320            redeem_script: BinaryData::new(vec![
321                0x52, // OP_2
322                0x21, // push 33 bytes (pubkey1)
323                0x02, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
324                0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
325                0x12, 0x12, 0x12, 0x12, 0x12, 0x53, // OP_3
326                0xae, // OP_CHECKMULTISIG
327            ]),
328        };
329
330        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
331        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
332            .unwrap()
333            .0;
334
335        assert_eq!(witness, decoded);
336        assert!(!decoded.is_p2pkh());
337        assert!(decoded.is_p2sh());
338    }
339
340    #[test]
341    fn test_unique_id_p2pkh() {
342        let witness = AddressWitness::P2pkh {
343            signature: BinaryData::new(vec![0x30, 0x44]),
344        };
345
346        let id = witness.unique_id();
347        assert!(!id.is_empty());
348
349        // Different signature should produce different ID
350        let witness2 = AddressWitness::P2pkh {
351            signature: BinaryData::new(vec![0x30, 0x45]),
352        };
353        assert_ne!(id, witness2.unique_id());
354    }
355
356    #[test]
357    fn test_unique_id_p2sh() {
358        let witness = AddressWitness::P2sh {
359            signatures: vec![
360                BinaryData::new(vec![0x00]),
361                BinaryData::new(vec![0x30, 0x44]),
362            ],
363            redeem_script: BinaryData::new(vec![0x52, 0xae]),
364        };
365
366        let id = witness.unique_id();
367        assert!(!id.is_empty());
368
369        // Different redeem script should produce different ID
370        let witness2 = AddressWitness::P2sh {
371            signatures: vec![
372                BinaryData::new(vec![0x00]),
373                BinaryData::new(vec![0x30, 0x44]),
374            ],
375            redeem_script: BinaryData::new(vec![0x53, 0xae]),
376        };
377        assert_ne!(id, witness2.unique_id());
378    }
379
380    #[cfg(feature = "serde-conversion")]
381    #[test]
382    fn test_p2pkh_serde() {
383        let witness = AddressWitness::P2pkh {
384            signature: BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]),
385        };
386
387        let json = serde_json::to_string(&witness).unwrap();
388        let deserialized: AddressWitness = serde_json::from_str(&json).unwrap();
389
390        assert_eq!(witness, deserialized);
391    }
392
393    #[cfg(feature = "serde-conversion")]
394    #[test]
395    fn test_p2sh_serde() {
396        let witness = AddressWitness::P2sh {
397            signatures: vec![
398                BinaryData::new(vec![0x00]),
399                BinaryData::new(vec![0x30, 0x44]),
400            ],
401            redeem_script: BinaryData::new(vec![0x52, 0xae]),
402        };
403
404        let json = serde_json::to_string(&witness).unwrap();
405        let deserialized: AddressWitness = serde_json::from_str(&json).unwrap();
406
407        assert_eq!(witness, deserialized);
408    }
409
410    /// AUDIT L1: Unbounded P2SH witness size during deserialization.
411    ///
412    /// The `Decode` impl for `AddressWitness::P2sh` now enforces
413    /// `MAX_P2SH_SIGNATURES` during deserialization. A payload with more
414    /// signatures than the limit is rejected with a decode error.
415    ///
416    /// Location: rs-dpp/src/address_funds/witness.rs
417    #[test]
418    fn test_p2sh_witness_rejects_excessive_signatures() {
419        // Create a P2SH witness with 1000 signatures — far above MAX_P2SH_SIGNATURES
420        let num_signatures = 1000;
421        let signatures: Vec<BinaryData> = (0..num_signatures)
422            .map(|i| BinaryData::new(vec![0x30, 0x44, i as u8]))
423            .collect();
424
425        let witness = AddressWitness::P2sh {
426            signatures,
427            redeem_script: BinaryData::new(vec![0x52, 0xae]),
428        };
429
430        // Encode succeeds (encoding has no limit), but decode must reject
431        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
432        let result: Result<(AddressWitness, usize), _> =
433            bincode::decode_from_slice(&encoded, config::standard());
434
435        assert!(
436            result.is_err(),
437            "AUDIT L1: P2SH witness with {} signatures should be rejected during \
438            deserialization. MAX_P2SH_SIGNATURES = {}.",
439            num_signatures,
440            MAX_P2SH_SIGNATURES,
441        );
442    }
443
444    /// AUDIT L3: No maximum length check on P2SH signatures vector.
445    ///
446    /// The deserialization now enforces `MAX_P2SH_SIGNATURES` (17). Signature
447    /// counts above this limit are rejected during decode. The boundary value
448    /// (17) is accepted, and 18+ is rejected.
449    ///
450    /// Location: rs-dpp/src/address_funds/witness.rs
451    #[test]
452    fn test_p2sh_witness_max_signatures_boundary() {
453        // Counts above MAX_P2SH_SIGNATURES should be rejected during decode
454        for count in [50, 100, 500] {
455            let signatures: Vec<BinaryData> = (0..count)
456                .map(|_| BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]))
457                .collect();
458
459            let witness = AddressWitness::P2sh {
460                signatures,
461                redeem_script: BinaryData::new(vec![0x52, 0xae]),
462            };
463
464            let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
465            let result: Result<(AddressWitness, usize), _> =
466                bincode::decode_from_slice(&encoded, config::standard());
467
468            assert!(
469                result.is_err(),
470                "AUDIT L3: P2SH witness with {} signatures should be rejected during \
471                deserialization. MAX_P2SH_SIGNATURES = {}.",
472                count,
473                MAX_P2SH_SIGNATURES,
474            );
475        }
476
477        // MAX_P2SH_SIGNATURES (17) should be accepted
478        let signatures: Vec<BinaryData> = (0..MAX_P2SH_SIGNATURES)
479            .map(|_| BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]))
480            .collect();
481
482        let witness = AddressWitness::P2sh {
483            signatures,
484            redeem_script: BinaryData::new(vec![0x52, 0xae]),
485        };
486
487        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
488        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
489            .unwrap()
490            .0;
491
492        assert_eq!(witness, decoded);
493
494        // MAX_P2SH_SIGNATURES + 1 should be rejected
495        let signatures: Vec<BinaryData> = (0..MAX_P2SH_SIGNATURES + 1)
496            .map(|_| BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]))
497            .collect();
498
499        let witness = AddressWitness::P2sh {
500            signatures,
501            redeem_script: BinaryData::new(vec![0x52, 0xae]),
502        };
503
504        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
505        let result: Result<(AddressWitness, usize), _> =
506            bincode::decode_from_slice(&encoded, config::standard());
507
508        assert!(
509            result.is_err(),
510            "P2SH witness with {} signatures (MAX + 1) should be rejected",
511            MAX_P2SH_SIGNATURES + 1,
512        );
513    }
514
515    // --- Additional encode/decode round-trip tests ---
516
517    #[test]
518    fn test_p2pkh_empty_signature_round_trip() {
519        let witness = AddressWitness::P2pkh {
520            signature: BinaryData::new(vec![]),
521        };
522
523        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
524        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
525            .unwrap()
526            .0;
527
528        assert_eq!(witness, decoded);
529        assert!(decoded.is_p2pkh());
530    }
531
532    #[test]
533    fn test_p2pkh_65_byte_signature_round_trip() {
534        // Typical recoverable ECDSA signature is 65 bytes
535        let signature_data: Vec<u8> = (0..65).collect();
536        let witness = AddressWitness::P2pkh {
537            signature: BinaryData::new(signature_data),
538        };
539
540        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
541        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
542            .unwrap()
543            .0;
544
545        assert_eq!(witness, decoded);
546    }
547
548    #[test]
549    fn test_p2sh_single_signature_round_trip() {
550        let witness = AddressWitness::P2sh {
551            signatures: vec![BinaryData::new(vec![0x30, 0x44, 0x02, 0x20])],
552            redeem_script: BinaryData::new(vec![0x51, 0xae]),
553        };
554
555        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
556        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
557            .unwrap()
558            .0;
559
560        assert_eq!(witness, decoded);
561        assert!(decoded.is_p2sh());
562        assert_eq!(
563            decoded.redeem_script(),
564            Some(&BinaryData::new(vec![0x51, 0xae]))
565        );
566    }
567
568    #[test]
569    fn test_p2sh_empty_signatures_vec_round_trip() {
570        let witness = AddressWitness::P2sh {
571            signatures: vec![],
572            redeem_script: BinaryData::new(vec![0x52, 0xae]),
573        };
574
575        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
576        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
577            .unwrap()
578            .0;
579
580        assert_eq!(witness, decoded);
581    }
582
583    #[test]
584    fn test_p2sh_empty_redeem_script_round_trip() {
585        let witness = AddressWitness::P2sh {
586            signatures: vec![BinaryData::new(vec![0x00])],
587            redeem_script: BinaryData::new(vec![]),
588        };
589
590        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
591        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
592            .unwrap()
593            .0;
594
595        assert_eq!(witness, decoded);
596    }
597
598    // --- Error path tests ---
599
600    #[test]
601    fn test_invalid_discriminant_decode_fails() {
602        // Manually craft a payload with discriminant 2 (invalid)
603        let mut data = vec![];
604        bincode::encode_into_std_write(&2u8, &mut data, config::standard()).unwrap();
605        // Add some dummy data
606        data.extend_from_slice(&[0x00, 0x00, 0x00]);
607
608        let result: Result<(AddressWitness, usize), _> =
609            bincode::decode_from_slice(&data, config::standard());
610        assert!(result.is_err());
611        let err_msg = format!("{}", result.unwrap_err());
612        assert!(err_msg.contains("Invalid AddressWitness discriminant"));
613    }
614
615    #[test]
616    fn test_invalid_discriminant_255_decode_fails() {
617        let mut data = vec![];
618        bincode::encode_into_std_write(&255u8, &mut data, config::standard()).unwrap();
619
620        let result: Result<(AddressWitness, usize), _> =
621            bincode::decode_from_slice(&data, config::standard());
622        assert!(result.is_err());
623    }
624
625    #[test]
626    fn test_truncated_p2pkh_payload_fails() {
627        // Encode only the discriminant, no signature data
628        let data = vec![0u8]; // discriminant for P2pkh
629        let result: Result<(AddressWitness, usize), _> =
630            bincode::decode_from_slice(&data, config::standard());
631        assert!(result.is_err());
632    }
633
634    #[test]
635    fn test_truncated_p2sh_payload_fails() {
636        // Encode discriminant for P2sh but no signatures/redeem_script
637        let data = vec![1u8]; // discriminant for P2sh
638        let result: Result<(AddressWitness, usize), _> =
639            bincode::decode_from_slice(&data, config::standard());
640        assert!(result.is_err());
641    }
642
643    #[test]
644    fn test_empty_payload_fails() {
645        let data: Vec<u8> = vec![];
646        let result: Result<(AddressWitness, usize), _> =
647            bincode::decode_from_slice(&data, config::standard());
648        assert!(result.is_err());
649    }
650
651    // --- Accessor tests ---
652
653    #[test]
654    fn test_redeem_script_returns_none_for_p2pkh() {
655        let witness = AddressWitness::P2pkh {
656            signature: BinaryData::new(vec![0x30]),
657        };
658        assert!(witness.redeem_script().is_none());
659    }
660
661    #[test]
662    fn test_redeem_script_returns_some_for_p2sh() {
663        let script = BinaryData::new(vec![0x52, 0xae]);
664        let witness = AddressWitness::P2sh {
665            signatures: vec![],
666            redeem_script: script.clone(),
667        };
668        assert_eq!(witness.redeem_script(), Some(&script));
669    }
670
671    // --- BorrowDecode path tests ---
672
673    #[test]
674    fn test_borrow_decode_p2pkh_round_trip() {
675        let witness = AddressWitness::P2pkh {
676            signature: BinaryData::new(vec![0xAB, 0xCD, 0xEF]),
677        };
678
679        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
680        // borrow_decode is exercised through decode_from_slice
681        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
682            .unwrap()
683            .0;
684        assert_eq!(witness, decoded);
685    }
686
687    #[test]
688    fn test_borrow_decode_p2sh_round_trip() {
689        let witness = AddressWitness::P2sh {
690            signatures: vec![
691                BinaryData::new(vec![0x00]),
692                BinaryData::new(vec![0x30, 0x44]),
693                BinaryData::new(vec![0x30, 0x45]),
694            ],
695            redeem_script: BinaryData::new(vec![0x52, 0x53, 0xae]),
696        };
697
698        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
699        let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
700            .unwrap()
701            .0;
702        assert_eq!(witness, decoded);
703    }
704
705    #[test]
706    fn test_borrow_decode_rejects_excessive_signatures() {
707        // Ensure BorrowDecode also rejects > MAX_P2SH_SIGNATURES
708        let signatures: Vec<BinaryData> = (0..MAX_P2SH_SIGNATURES + 1)
709            .map(|_| BinaryData::new(vec![0x30]))
710            .collect();
711
712        let witness = AddressWitness::P2sh {
713            signatures,
714            redeem_script: BinaryData::new(vec![0xae]),
715        };
716
717        let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
718        let result: Result<(AddressWitness, usize), _> =
719            bincode::decode_from_slice(&encoded, config::standard());
720        assert!(result.is_err());
721    }
722
723    #[test]
724    fn test_borrow_decode_invalid_discriminant_fails() {
725        let mut data = vec![];
726        bincode::encode_into_std_write(&3u8, &mut data, config::standard()).unwrap();
727        data.extend_from_slice(&[0x00; 10]);
728
729        let result: Result<(AddressWitness, usize), _> =
730            bincode::decode_from_slice(&data, config::standard());
731        assert!(result.is_err());
732    }
733}