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)]
291mod tests {
292 use super::*;
293 use bincode::config;
294
295 #[test]
296 fn test_p2pkh_witness_encode_decode() {
297 let witness = AddressWitness::P2pkh {
298 signature: BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]),
299 };
300
301 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
302 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
303 .unwrap()
304 .0;
305
306 assert_eq!(witness, decoded);
307 assert!(decoded.is_p2pkh());
308 assert!(!decoded.is_p2sh());
309 }
310
311 #[test]
312 fn test_p2sh_witness_encode_decode() {
313 let witness = AddressWitness::P2sh {
314 signatures: vec![
315 BinaryData::new(vec![0x00]), BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]), BinaryData::new(vec![0x30, 0x45, 0x02, 0x21]), ],
319 redeem_script: BinaryData::new(vec![
320 0x52, 0x21, 0x02, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
323 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
324 0x12, 0x12, 0x12, 0x12, 0x12, 0x53, 0xae, ]),
327 };
328
329 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
330 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
331 .unwrap()
332 .0;
333
334 assert_eq!(witness, decoded);
335 assert!(!decoded.is_p2pkh());
336 assert!(decoded.is_p2sh());
337 }
338
339 #[test]
340 fn test_unique_id_p2pkh() {
341 let witness = AddressWitness::P2pkh {
342 signature: BinaryData::new(vec![0x30, 0x44]),
343 };
344
345 let id = witness.unique_id();
346 assert!(!id.is_empty());
347
348 let witness2 = AddressWitness::P2pkh {
350 signature: BinaryData::new(vec![0x30, 0x45]),
351 };
352 assert_ne!(id, witness2.unique_id());
353 }
354
355 #[test]
356 fn test_unique_id_p2sh() {
357 let witness = AddressWitness::P2sh {
358 signatures: vec![
359 BinaryData::new(vec![0x00]),
360 BinaryData::new(vec![0x30, 0x44]),
361 ],
362 redeem_script: BinaryData::new(vec![0x52, 0xae]),
363 };
364
365 let id = witness.unique_id();
366 assert!(!id.is_empty());
367
368 let witness2 = AddressWitness::P2sh {
370 signatures: vec![
371 BinaryData::new(vec![0x00]),
372 BinaryData::new(vec![0x30, 0x44]),
373 ],
374 redeem_script: BinaryData::new(vec![0x53, 0xae]),
375 };
376 assert_ne!(id, witness2.unique_id());
377 }
378
379 #[cfg(feature = "serde-conversion")]
380 #[test]
381 fn test_p2pkh_serde() {
382 let witness = AddressWitness::P2pkh {
383 signature: BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]),
384 };
385
386 let json = serde_json::to_string(&witness).unwrap();
387 let deserialized: AddressWitness = serde_json::from_str(&json).unwrap();
388
389 assert_eq!(witness, deserialized);
390 }
391
392 #[cfg(feature = "serde-conversion")]
393 #[test]
394 fn test_p2sh_serde() {
395 let witness = AddressWitness::P2sh {
396 signatures: vec![
397 BinaryData::new(vec![0x00]),
398 BinaryData::new(vec![0x30, 0x44]),
399 ],
400 redeem_script: BinaryData::new(vec![0x52, 0xae]),
401 };
402
403 let json = serde_json::to_string(&witness).unwrap();
404 let deserialized: AddressWitness = serde_json::from_str(&json).unwrap();
405
406 assert_eq!(witness, deserialized);
407 }
408
409 #[test]
417 fn test_p2sh_witness_rejects_excessive_signatures() {
418 let num_signatures = 1000;
420 let signatures: Vec<BinaryData> = (0..num_signatures)
421 .map(|i| BinaryData::new(vec![0x30, 0x44, i as u8]))
422 .collect();
423
424 let witness = AddressWitness::P2sh {
425 signatures,
426 redeem_script: BinaryData::new(vec![0x52, 0xae]),
427 };
428
429 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
431 let result: Result<(AddressWitness, usize), _> =
432 bincode::decode_from_slice(&encoded, config::standard());
433
434 assert!(
435 result.is_err(),
436 "AUDIT L1: P2SH witness with {} signatures should be rejected during \
437 deserialization. MAX_P2SH_SIGNATURES = {}.",
438 num_signatures,
439 MAX_P2SH_SIGNATURES,
440 );
441 }
442
443 #[test]
451 fn test_p2sh_witness_max_signatures_boundary() {
452 for count in [50, 100, 500] {
454 let signatures: Vec<BinaryData> = (0..count)
455 .map(|_| BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]))
456 .collect();
457
458 let witness = AddressWitness::P2sh {
459 signatures,
460 redeem_script: BinaryData::new(vec![0x52, 0xae]),
461 };
462
463 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
464 let result: Result<(AddressWitness, usize), _> =
465 bincode::decode_from_slice(&encoded, config::standard());
466
467 assert!(
468 result.is_err(),
469 "AUDIT L3: P2SH witness with {} signatures should be rejected during \
470 deserialization. MAX_P2SH_SIGNATURES = {}.",
471 count,
472 MAX_P2SH_SIGNATURES,
473 );
474 }
475
476 let signatures: Vec<BinaryData> = (0..MAX_P2SH_SIGNATURES)
478 .map(|_| BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]))
479 .collect();
480
481 let witness = AddressWitness::P2sh {
482 signatures,
483 redeem_script: BinaryData::new(vec![0x52, 0xae]),
484 };
485
486 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
487 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
488 .unwrap()
489 .0;
490
491 assert_eq!(witness, decoded);
492
493 let signatures: Vec<BinaryData> = (0..MAX_P2SH_SIGNATURES + 1)
495 .map(|_| BinaryData::new(vec![0x30, 0x44, 0x02, 0x20]))
496 .collect();
497
498 let witness = AddressWitness::P2sh {
499 signatures,
500 redeem_script: BinaryData::new(vec![0x52, 0xae]),
501 };
502
503 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
504 let result: Result<(AddressWitness, usize), _> =
505 bincode::decode_from_slice(&encoded, config::standard());
506
507 assert!(
508 result.is_err(),
509 "P2SH witness with {} signatures (MAX + 1) should be rejected",
510 MAX_P2SH_SIGNATURES + 1,
511 );
512 }
513
514 #[test]
517 fn test_p2pkh_empty_signature_round_trip() {
518 let witness = AddressWitness::P2pkh {
519 signature: BinaryData::new(vec![]),
520 };
521
522 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
523 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
524 .unwrap()
525 .0;
526
527 assert_eq!(witness, decoded);
528 assert!(decoded.is_p2pkh());
529 }
530
531 #[test]
532 fn test_p2pkh_65_byte_signature_round_trip() {
533 let signature_data: Vec<u8> = (0..65).collect();
535 let witness = AddressWitness::P2pkh {
536 signature: BinaryData::new(signature_data),
537 };
538
539 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
540 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
541 .unwrap()
542 .0;
543
544 assert_eq!(witness, decoded);
545 }
546
547 #[test]
548 fn test_p2sh_single_signature_round_trip() {
549 let witness = AddressWitness::P2sh {
550 signatures: vec![BinaryData::new(vec![0x30, 0x44, 0x02, 0x20])],
551 redeem_script: BinaryData::new(vec![0x51, 0xae]),
552 };
553
554 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
555 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
556 .unwrap()
557 .0;
558
559 assert_eq!(witness, decoded);
560 assert!(decoded.is_p2sh());
561 assert_eq!(
562 decoded.redeem_script(),
563 Some(&BinaryData::new(vec![0x51, 0xae]))
564 );
565 }
566
567 #[test]
568 fn test_p2sh_empty_signatures_vec_round_trip() {
569 let witness = AddressWitness::P2sh {
570 signatures: vec![],
571 redeem_script: BinaryData::new(vec![0x52, 0xae]),
572 };
573
574 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
575 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
576 .unwrap()
577 .0;
578
579 assert_eq!(witness, decoded);
580 }
581
582 #[test]
583 fn test_p2sh_empty_redeem_script_round_trip() {
584 let witness = AddressWitness::P2sh {
585 signatures: vec![BinaryData::new(vec![0x00])],
586 redeem_script: BinaryData::new(vec![]),
587 };
588
589 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
590 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
591 .unwrap()
592 .0;
593
594 assert_eq!(witness, decoded);
595 }
596
597 #[test]
600 fn test_invalid_discriminant_decode_fails() {
601 let mut data = vec![];
603 bincode::encode_into_std_write(&2u8, &mut data, config::standard()).unwrap();
604 data.extend_from_slice(&[0x00, 0x00, 0x00]);
606
607 let result: Result<(AddressWitness, usize), _> =
608 bincode::decode_from_slice(&data, config::standard());
609 assert!(result.is_err());
610 let err_msg = format!("{}", result.unwrap_err());
611 assert!(err_msg.contains("Invalid AddressWitness discriminant"));
612 }
613
614 #[test]
615 fn test_invalid_discriminant_255_decode_fails() {
616 let mut data = vec![];
617 bincode::encode_into_std_write(&255u8, &mut data, config::standard()).unwrap();
618
619 let result: Result<(AddressWitness, usize), _> =
620 bincode::decode_from_slice(&data, config::standard());
621 assert!(result.is_err());
622 }
623
624 #[test]
625 fn test_truncated_p2pkh_payload_fails() {
626 let data = vec![0u8]; let result: Result<(AddressWitness, usize), _> =
629 bincode::decode_from_slice(&data, config::standard());
630 assert!(result.is_err());
631 }
632
633 #[test]
634 fn test_truncated_p2sh_payload_fails() {
635 let data = vec![1u8]; let result: Result<(AddressWitness, usize), _> =
638 bincode::decode_from_slice(&data, config::standard());
639 assert!(result.is_err());
640 }
641
642 #[test]
643 fn test_empty_payload_fails() {
644 let data: Vec<u8> = vec![];
645 let result: Result<(AddressWitness, usize), _> =
646 bincode::decode_from_slice(&data, config::standard());
647 assert!(result.is_err());
648 }
649
650 #[test]
653 fn test_redeem_script_returns_none_for_p2pkh() {
654 let witness = AddressWitness::P2pkh {
655 signature: BinaryData::new(vec![0x30]),
656 };
657 assert!(witness.redeem_script().is_none());
658 }
659
660 #[test]
661 fn test_redeem_script_returns_some_for_p2sh() {
662 let script = BinaryData::new(vec![0x52, 0xae]);
663 let witness = AddressWitness::P2sh {
664 signatures: vec![],
665 redeem_script: script.clone(),
666 };
667 assert_eq!(witness.redeem_script(), Some(&script));
668 }
669
670 #[test]
673 fn test_borrow_decode_p2pkh_round_trip() {
674 let witness = AddressWitness::P2pkh {
675 signature: BinaryData::new(vec![0xAB, 0xCD, 0xEF]),
676 };
677
678 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
679 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
681 .unwrap()
682 .0;
683 assert_eq!(witness, decoded);
684 }
685
686 #[test]
687 fn test_borrow_decode_p2sh_round_trip() {
688 let witness = AddressWitness::P2sh {
689 signatures: vec![
690 BinaryData::new(vec![0x00]),
691 BinaryData::new(vec![0x30, 0x44]),
692 BinaryData::new(vec![0x30, 0x45]),
693 ],
694 redeem_script: BinaryData::new(vec![0x52, 0x53, 0xae]),
695 };
696
697 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
698 let decoded: AddressWitness = bincode::decode_from_slice(&encoded, config::standard())
699 .unwrap()
700 .0;
701 assert_eq!(witness, decoded);
702 }
703
704 #[test]
705 fn test_borrow_decode_rejects_excessive_signatures() {
706 let signatures: Vec<BinaryData> = (0..MAX_P2SH_SIGNATURES + 1)
708 .map(|_| BinaryData::new(vec![0x30]))
709 .collect();
710
711 let witness = AddressWitness::P2sh {
712 signatures,
713 redeem_script: BinaryData::new(vec![0xae]),
714 };
715
716 let encoded = bincode::encode_to_vec(&witness, config::standard()).unwrap();
717 let result: Result<(AddressWitness, usize), _> =
718 bincode::decode_from_slice(&encoded, config::standard());
719 assert!(result.is_err());
720 }
721
722 #[test]
723 fn test_borrow_decode_invalid_discriminant_fails() {
724 let mut data = vec![];
725 bincode::encode_into_std_write(&3u8, &mut data, config::standard()).unwrap();
726 data.extend_from_slice(&[0x00; 10]);
727
728 let result: Result<(AddressWitness, usize), _> =
729 bincode::decode_from_slice(&data, config::standard());
730 assert!(result.is_err());
731 }
732}