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
9pub const MAX_P2SH_SIGNATURES: usize = 17;
12
13#[derive(Debug, Clone, PartialEq, Ord, PartialOrd, Eq)]
17pub enum AddressWitness {
18 P2pkh {
24 signature: BinaryData, },
27 P2sh {
33 signatures: Vec<BinaryData>,
35 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 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 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 pub fn is_p2pkh(&self) -> bool {
281 matches!(self, AddressWitness::P2pkh { .. })
282 }
283
284 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]), BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]), BinaryData::new(vec![0x30, 0x45, 0x02, 0x21]), ],
320 redeem_script: BinaryData::new(vec![
321 0x52, 0x21, 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, 0xae, ]),
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 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 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 #[test]
418 fn test_p2sh_witness_rejects_excessive_signatures() {
419 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 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 #[test]
452 fn test_p2sh_witness_max_signatures_boundary() {
453 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 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 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 #[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 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 #[test]
601 fn test_invalid_discriminant_decode_fails() {
602 let mut data = vec![];
604 bincode::encode_into_std_write(&2u8, &mut data, config::standard()).unwrap();
605 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 let data = vec![0u8]; 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 let data = vec![1u8]; 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 #[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 #[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 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 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}