Skip to main content

dpp/address_funds/
witness.rs

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