1use std::fmt;
4
5use alloy_primitives::{B256, keccak256};
6use serde::{Deserialize, Serialize};
7
8use cow_errors::CowError;
9
10use super::{
11 order_id,
12 types::{ConditionalOrderParams, ProofLocation},
13};
14
15#[derive(Debug, Clone)]
17pub struct OrderProof {
18 pub order_id: B256,
20 pub proof: Vec<B256>,
22 pub params: ConditionalOrderParams,
24}
25
26impl OrderProof {
27 #[must_use]
39 pub const fn new(order_id: B256, proof: Vec<B256>, params: ConditionalOrderParams) -> Self {
40 Self { order_id, proof, params }
41 }
42
43 #[must_use]
50 pub const fn proof_len(&self) -> usize {
51 self.proof.len()
52 }
53}
54
55#[derive(Debug, Clone)]
57pub struct ProofWithParams {
58 pub proof: Vec<B256>,
60 pub params: ConditionalOrderParams,
62}
63
64impl ProofWithParams {
65 #[must_use]
76 pub const fn new(proof: Vec<B256>, params: ConditionalOrderParams) -> Self {
77 Self { proof, params }
78 }
79
80 #[must_use]
87 pub const fn proof_len(&self) -> usize {
88 self.proof.len()
89 }
90}
91
92impl fmt::Display for OrderProof {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "order-proof({:#x}, {} siblings)", self.order_id, self.proof.len())
95 }
96}
97
98impl fmt::Display for ProofWithParams {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 write!(
101 f,
102 "proof-with-params({} siblings, handler={:#x})",
103 self.proof.len(),
104 self.params.handler
105 )
106 }
107}
108
109#[derive(Serialize, Deserialize)]
112struct MultiplexerJson {
113 proof_location: u8,
114 orders: Vec<ParamsJson>,
115}
116
117#[derive(Serialize, Deserialize)]
118struct ParamsJson {
119 handler: String,
120 salt: String,
121 static_input: String,
122}
123
124#[derive(Deserialize)]
126struct WatchtowerEntry {
127 proof: Vec<String>,
128 params: WatchtowerParams,
129}
130
131#[derive(Deserialize)]
133#[serde(rename_all = "camelCase")]
134struct WatchtowerParams {
135 handler: String,
136 salt: String,
137 static_input: String,
138}
139
140impl From<&ConditionalOrderParams> for ParamsJson {
141 fn from(p: &ConditionalOrderParams) -> Self {
142 Self {
143 handler: format!("{:?}", p.handler),
144 salt: format!("0x{}", alloy_primitives::hex::encode(p.salt.as_slice())),
145 static_input: format!("0x{}", alloy_primitives::hex::encode(&p.static_input)),
146 }
147 }
148}
149
150impl TryFrom<ParamsJson> for ConditionalOrderParams {
151 type Error = CowError;
152 fn try_from(j: ParamsJson) -> Result<Self, CowError> {
153 let handler = j
154 .handler
155 .parse()
156 .map_err(|e: alloy_primitives::hex::FromHexError| CowError::AppData(e.to_string()))?;
157 let salt_hex = j.salt.strip_prefix("0x").map_or(j.salt.as_str(), |s| s);
158 let salt_bytes = alloy_primitives::hex::decode(salt_hex)
159 .map_err(|e| CowError::AppData(format!("salt: {e}")))?;
160 let mut salt = [0u8; 32];
161 salt.copy_from_slice(&salt_bytes);
162 let input_hex = j.static_input.strip_prefix("0x").map_or(j.static_input.as_str(), |s| s);
163 let static_input = alloy_primitives::hex::decode(input_hex)
164 .map_err(|e| CowError::AppData(format!("static_input: {e}")))?;
165 Ok(Self { handler, salt: B256::new(salt), static_input })
166 }
167}
168
169#[derive(Debug, Clone, Default)]
180pub struct Multiplexer {
181 orders: Vec<ConditionalOrderParams>,
182 proof_location: ProofLocation,
183}
184
185impl Multiplexer {
186 #[must_use]
197 pub const fn new(proof_location: ProofLocation) -> Self {
198 Self { orders: Vec::new(), proof_location }
199 }
200
201 pub fn add(&mut self, params: ConditionalOrderParams) {
210 self.orders.push(params);
211 }
212
213 pub fn remove(&mut self, id: B256) {
221 self.orders.retain(|p| order_id(p) != id);
222 }
223
224 pub fn update(&mut self, index: usize, params: ConditionalOrderParams) -> Result<(), CowError> {
230 if index >= self.orders.len() {
231 return Err(CowError::AppData(format!(
232 "index {index} out of range (len {})",
233 self.orders.len()
234 )));
235 }
236 self.orders[index] = params;
237 Ok(())
238 }
239
240 #[must_use]
250 pub fn get_by_index(&self, index: usize) -> Option<&ConditionalOrderParams> {
251 self.orders.get(index)
252 }
253
254 #[must_use]
265 pub fn get_by_id(&self, id: B256) -> Option<&ConditionalOrderParams> {
266 self.orders.iter().find(|p| order_id(p) == id)
267 }
268
269 #[must_use]
275 pub const fn len(&self) -> usize {
276 self.orders.len()
277 }
278
279 #[must_use]
285 pub const fn is_empty(&self) -> bool {
286 self.orders.is_empty()
287 }
288
289 pub fn root(&self) -> Result<Option<B256>, CowError> {
301 if self.orders.is_empty() {
302 return Ok(None);
303 }
304 let leaves: Vec<B256> = self.orders.iter().map(leaf_hash).collect();
305 Ok(Some(merkle_root(&leaves)))
306 }
307
308 pub fn proof(&self, index: usize) -> Result<OrderProof, CowError> {
317 if index >= self.orders.len() {
318 return Err(CowError::AppData(format!(
319 "index {index} out of range (len {})",
320 self.orders.len()
321 )));
322 }
323 let leaves: Vec<B256> = self.orders.iter().map(leaf_hash).collect();
324 Ok(OrderProof {
325 order_id: order_id(&self.orders[index]),
326 proof: generate_proof(&leaves, index),
327 params: self.orders[index].clone(),
328 })
329 }
330
331 pub fn dump_proofs_and_params(&self) -> Result<Vec<ProofWithParams>, CowError> {
337 (0..self.orders.len())
338 .map(|i| {
339 let op = self.proof(i)?;
340 Ok(ProofWithParams { proof: op.proof, params: op.params })
341 })
342 .collect()
343 }
344
345 pub fn order_ids(&self) -> impl Iterator<Item = alloy_primitives::B256> + '_ {
354 self.orders.iter().map(order_id)
355 }
356
357 pub fn iter(&self) -> impl Iterator<Item = &ConditionalOrderParams> {
364 self.orders.iter()
365 }
366
367 #[must_use]
373 pub fn as_slice(&self) -> &[ConditionalOrderParams] {
374 &self.orders
375 }
376
377 pub fn clear(&mut self) {
379 self.orders.clear();
380 }
381
382 #[must_use]
389 pub const fn proof_location(&self) -> ProofLocation {
390 self.proof_location
391 }
392
393 #[must_use]
404 pub const fn with_proof_location(mut self, location: ProofLocation) -> Self {
405 self.proof_location = location;
406 self
407 }
408
409 #[must_use]
416 pub fn into_vec(self) -> Vec<ConditionalOrderParams> {
417 self.orders
418 }
419
420 pub fn to_json(&self) -> Result<String, CowError> {
430 let j = MultiplexerJson {
431 proof_location: self.proof_location as u8,
432 orders: self.orders.iter().map(ParamsJson::from).collect(),
433 };
434 serde_json::to_string(&j).map_err(|e| CowError::AppData(e.to_string()))
435 }
436
437 pub fn decode_proofs_from_json(json: &str) -> Result<Vec<ProofWithParams>, CowError> {
447 let entries: Vec<WatchtowerEntry> =
448 serde_json::from_str(json).map_err(|e| CowError::AppData(e.to_string()))?;
449 entries
450 .into_iter()
451 .map(|entry| {
452 let proof = entry
453 .proof
454 .iter()
455 .map(|s| {
456 let hex = s.strip_prefix("0x").map_or(s.as_str(), |h| h);
457 let bytes = alloy_primitives::hex::decode(hex)
458 .map_err(|e| CowError::AppData(format!("proof hash: {e}")))?;
459 let mut arr = [0u8; 32];
460 arr.copy_from_slice(&bytes);
461 Ok(B256::new(arr))
462 })
463 .collect::<Result<Vec<_>, CowError>>()?;
464 let p = entry.params;
465 let handler =
466 p.handler.parse().map_err(|e: alloy_primitives::hex::FromHexError| {
467 CowError::AppData(e.to_string())
468 })?;
469 let salt_hex = p.salt.strip_prefix("0x").map_or(p.salt.as_str(), |s| s);
470 let salt_bytes = alloy_primitives::hex::decode(salt_hex)
471 .map_err(|e| CowError::AppData(format!("salt: {e}")))?;
472 let mut salt = [0u8; 32];
473 salt.copy_from_slice(&salt_bytes);
474 let input_hex =
475 p.static_input.strip_prefix("0x").map_or(p.static_input.as_str(), |s| s);
476 let static_input = alloy_primitives::hex::decode(input_hex)
477 .map_err(|e| CowError::AppData(format!("staticInput: {e}")))?;
478 let params =
479 ConditionalOrderParams { handler, salt: B256::new(salt), static_input };
480 Ok(ProofWithParams { proof, params })
481 })
482 .collect()
483 }
484
485 pub fn from_json(json: &str) -> Result<Self, CowError> {
491 let j: MultiplexerJson =
492 serde_json::from_str(json).map_err(|e| CowError::AppData(e.to_string()))?;
493 let proof_location = match j.proof_location {
494 0 => ProofLocation::Private,
495 1 => ProofLocation::Emitted,
496 2 => ProofLocation::Swarm,
497 3 => ProofLocation::Waku,
498 4 => ProofLocation::Reserved,
499 5 => ProofLocation::Ipfs,
500 n => {
501 return Err(CowError::AppData(format!("unknown ProofLocation: {n}")));
502 }
503 };
504 let orders = j
505 .orders
506 .into_iter()
507 .map(ConditionalOrderParams::try_from)
508 .collect::<Result<Vec<_>, _>>()?;
509 Ok(Self { orders, proof_location })
510 }
511}
512impl fmt::Display for Multiplexer {
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514 write!(f, "multiplexer({} orders, {})", self.orders.len(), self.proof_location)
515 }
516}
517
518fn leaf_hash(params: &ConditionalOrderParams) -> B256 {
532 keccak256(order_id(params))
533}
534
535fn merkle_root(leaves: &[B256]) -> B256 {
547 if leaves.len() == 1 {
548 return leaves[0];
549 }
550 let mut layer = leaves.to_vec();
551 while layer.len() > 1 {
552 let mut next = Vec::with_capacity(layer.len().div_ceil(2));
553 let mut i = 0;
554 while i < layer.len() {
555 if i + 1 < layer.len() {
556 next.push(hash_pair(layer[i], layer[i + 1]));
557 } else {
558 next.push(layer[i]);
559 }
560 i += 2;
561 }
562 layer = next;
563 }
564 layer[0]
565}
566
567fn hash_pair(a: B256, b: B256) -> B256 {
579 let (lo, hi) = if a <= b { (a, b) } else { (b, a) };
580 let mut buf = [0u8; 64];
581 buf[..32].copy_from_slice(lo.as_slice());
582 buf[32..].copy_from_slice(hi.as_slice());
583 keccak256(buf)
584}
585
586fn generate_proof(leaves: &[B256], mut index: usize) -> Vec<B256> {
599 let mut proof = Vec::new();
600 let mut layer = leaves.to_vec();
601 while layer.len() > 1 {
602 let sibling = if index.is_multiple_of(2) {
603 (index + 1 < layer.len()).then(|| layer[index + 1])
604 } else {
605 Some(layer[index - 1])
606 };
607 if let Some(s) = sibling {
608 proof.push(s);
609 }
610 let mut next = Vec::with_capacity(layer.len().div_ceil(2));
611 let mut i = 0;
612 while i < layer.len() {
613 if i + 1 < layer.len() {
614 next.push(hash_pair(layer[i], layer[i + 1]));
615 } else {
616 next.push(layer[i]);
617 }
618 i += 2;
619 }
620 layer = next;
621 index /= 2;
622 }
623 proof
624}
625
626#[cfg(test)]
629mod tests {
630 use alloy_primitives::Address;
631
632 use super::*;
633
634 fn make_params(salt_byte: u8) -> ConditionalOrderParams {
635 ConditionalOrderParams {
636 handler: Address::ZERO,
637 salt: B256::new([salt_byte; 32]),
638 static_input: vec![salt_byte; 4],
639 }
640 }
641
642 #[test]
643 fn decode_proofs_from_json_roundtrip() {
644 let mut mux = Multiplexer::new(ProofLocation::Private);
646 mux.add(make_params(0xaa));
647 mux.add(make_params(0xbb));
648
649 let proofs = mux.dump_proofs_and_params().unwrap();
650
651 let json_entries: Vec<serde_json::Value> = proofs
653 .iter()
654 .map(|p| {
655 let proof_arr: Vec<String> = p
656 .proof
657 .iter()
658 .map(|h| format!("0x{}", alloy_primitives::hex::encode(h.as_slice())))
659 .collect();
660 serde_json::json!({
661 "proof": proof_arr,
662 "params": {
663 "handler": format!("{:#x}", p.params.handler),
664 "salt": format!("0x{}", alloy_primitives::hex::encode(p.params.salt.as_slice())),
665 "staticInput": format!("0x{}", alloy_primitives::hex::encode(&p.params.static_input)),
666 }
667 })
668 })
669 .collect();
670 let json = serde_json::to_string(&json_entries).unwrap();
671
672 let decoded = Multiplexer::decode_proofs_from_json(&json).unwrap();
673 assert_eq!(decoded.len(), 2);
674 assert_eq!(decoded[0].params.salt, proofs[0].params.salt);
675 assert_eq!(decoded[1].params.static_input, proofs[1].params.static_input);
676 }
677
678 #[test]
679 fn decode_proofs_from_json_invalid_returns_error() {
680 let result = Multiplexer::decode_proofs_from_json("not json");
681 assert!(result.is_err());
682 }
683
684 #[test]
685 fn multiplexer_root_single_order() {
686 let mut mux = Multiplexer::new(ProofLocation::Private);
687 mux.add(make_params(1));
688 let root = mux.root().unwrap();
689 assert!(root.is_some());
690 }
691
692 #[test]
693 fn multiplexer_root_empty() {
694 let mux = Multiplexer::new(ProofLocation::Private);
695 assert!(mux.root().unwrap().is_none());
696 }
697
698 #[test]
701 fn add_increases_len() {
702 let mut mux = Multiplexer::new(ProofLocation::Private);
703 assert!(mux.is_empty());
704 mux.add(make_params(1));
705 assert_eq!(mux.len(), 1);
706 mux.add(make_params(2));
707 assert_eq!(mux.len(), 2);
708 }
709
710 #[test]
711 fn remove_by_id() {
712 let mut mux = Multiplexer::new(ProofLocation::Private);
713 let p = make_params(0xaa);
714 let id = order_id(&p);
715 mux.add(p);
716 mux.add(make_params(0xbb));
717 assert_eq!(mux.len(), 2);
718 mux.remove(id);
719 assert_eq!(mux.len(), 1);
720 }
721
722 #[test]
723 fn remove_nonexistent_is_noop() {
724 let mut mux = Multiplexer::new(ProofLocation::Private);
725 mux.add(make_params(1));
726 mux.remove(B256::ZERO);
727 assert_eq!(mux.len(), 1);
728 }
729
730 #[test]
731 fn update_in_range() {
732 let mut mux = Multiplexer::new(ProofLocation::Private);
733 mux.add(make_params(1));
734 mux.add(make_params(2));
735 let new_params = make_params(99);
736 mux.update(1, new_params.clone()).unwrap();
737 assert_eq!(mux.get_by_index(1).unwrap().salt, new_params.salt);
738 }
739
740 #[test]
741 fn update_out_of_range() {
742 let mut mux = Multiplexer::new(ProofLocation::Private);
743 mux.add(make_params(1));
744 assert!(mux.update(5, make_params(2)).is_err());
745 }
746
747 #[test]
750 fn get_by_index_valid() {
751 let mut mux = Multiplexer::new(ProofLocation::Private);
752 let p = make_params(0xcc);
753 mux.add(p.clone());
754 let got = mux.get_by_index(0).unwrap();
755 assert_eq!(got.salt, p.salt);
756 }
757
758 #[test]
759 fn get_by_index_out_of_range() {
760 let mux = Multiplexer::new(ProofLocation::Private);
761 assert!(mux.get_by_index(0).is_none());
762 }
763
764 #[test]
765 fn get_by_id_found() {
766 let mut mux = Multiplexer::new(ProofLocation::Private);
767 let p = make_params(0xdd);
768 let id = order_id(&p);
769 mux.add(p.clone());
770 let got = mux.get_by_id(id).unwrap();
771 assert_eq!(got.salt, p.salt);
772 }
773
774 #[test]
775 fn get_by_id_not_found() {
776 let mut mux = Multiplexer::new(ProofLocation::Private);
777 mux.add(make_params(1));
778 assert!(mux.get_by_id(B256::ZERO).is_none());
779 }
780
781 #[test]
784 fn root_changes_when_order_added() {
785 let mut mux = Multiplexer::new(ProofLocation::Private);
786 mux.add(make_params(1));
787 let root1 = mux.root().unwrap().unwrap();
788 mux.add(make_params(2));
789 let root2 = mux.root().unwrap().unwrap();
790 assert_ne!(root1, root2);
791 }
792
793 #[test]
794 fn root_two_orders() {
795 let mut mux = Multiplexer::new(ProofLocation::Private);
796 mux.add(make_params(0xaa));
797 mux.add(make_params(0xbb));
798 let root = mux.root().unwrap();
799 assert!(root.is_some());
800 }
801
802 #[test]
803 fn proof_valid_index() {
804 let mut mux = Multiplexer::new(ProofLocation::Private);
805 mux.add(make_params(0xaa));
806 mux.add(make_params(0xbb));
807 let proof = mux.proof(0).unwrap();
808 assert!(!proof.proof.is_empty());
809 assert_eq!(proof.params.salt, make_params(0xaa).salt);
810 }
811
812 #[test]
813 fn proof_out_of_range() {
814 let mut mux = Multiplexer::new(ProofLocation::Private);
815 mux.add(make_params(1));
816 assert!(mux.proof(5).is_err());
817 }
818
819 #[test]
822 fn dump_proofs_and_params_returns_all() {
823 let mut mux = Multiplexer::new(ProofLocation::Private);
824 mux.add(make_params(0xaa));
825 mux.add(make_params(0xbb));
826 mux.add(make_params(0xcc));
827 let proofs = mux.dump_proofs_and_params().unwrap();
828 assert_eq!(proofs.len(), 3);
829 }
830
831 #[test]
834 fn to_json_from_json_roundtrip() {
835 let mut mux = Multiplexer::new(ProofLocation::Ipfs);
836 mux.add(make_params(0x11));
837 mux.add(make_params(0x22));
838
839 let json = mux.to_json().unwrap();
840 let restored = Multiplexer::from_json(&json).unwrap();
841
842 assert_eq!(restored.len(), 2);
843 assert_eq!(restored.proof_location(), ProofLocation::Ipfs);
844 assert_eq!(restored.get_by_index(0).unwrap().salt, make_params(0x11).salt);
845 assert_eq!(restored.get_by_index(1).unwrap().salt, make_params(0x22).salt);
846 }
847
848 #[test]
849 fn from_json_invalid() {
850 assert!(Multiplexer::from_json("not json").is_err());
851 }
852
853 #[test]
854 fn from_json_unknown_proof_location() {
855 let json = r#"{"proof_location": 99, "orders": []}"#;
856 assert!(Multiplexer::from_json(json).is_err());
857 }
858
859 #[test]
862 fn clear_empties_multiplexer() {
863 let mut mux = Multiplexer::new(ProofLocation::Private);
864 mux.add(make_params(1));
865 mux.add(make_params(2));
866 mux.clear();
867 assert!(mux.is_empty());
868 }
869
870 #[test]
871 fn order_ids_iterator() {
872 let mut mux = Multiplexer::new(ProofLocation::Private);
873 mux.add(make_params(0xaa));
874 mux.add(make_params(0xbb));
875 let ids: Vec<_> = mux.order_ids().collect();
876 assert_eq!(ids.len(), 2);
877 assert_eq!(ids[0], order_id(&make_params(0xaa)));
878 }
879
880 #[test]
881 fn iter_and_as_slice() {
882 let mut mux = Multiplexer::new(ProofLocation::Private);
883 mux.add(make_params(1));
884 mux.add(make_params(2));
885 assert_eq!(mux.iter().count(), 2);
886 assert_eq!(mux.as_slice().len(), 2);
887 }
888
889 #[test]
890 fn into_vec_returns_orders() {
891 let mut mux = Multiplexer::new(ProofLocation::Private);
892 mux.add(make_params(1));
893 let v = mux.into_vec();
894 assert_eq!(v.len(), 1);
895 }
896
897 #[test]
898 fn with_proof_location_builder() {
899 let mux =
900 Multiplexer::new(ProofLocation::Private).with_proof_location(ProofLocation::Swarm);
901 assert_eq!(mux.proof_location(), ProofLocation::Swarm);
902 }
903
904 #[test]
905 fn display_multiplexer() {
906 let mut mux = Multiplexer::new(ProofLocation::Private);
907 mux.add(make_params(1));
908 let s = format!("{mux}");
909 assert!(s.contains("1 orders"));
910 }
911
912 #[test]
913 fn display_order_proof() {
914 let mut mux = Multiplexer::new(ProofLocation::Private);
915 mux.add(make_params(0xaa));
916 mux.add(make_params(0xbb));
917 let proof = mux.proof(0).unwrap();
918 let s = format!("{proof}");
919 assert!(s.contains("order-proof"));
920 }
921
922 #[test]
923 fn from_json_all_proof_locations() {
924 for (val, expected) in [
925 (0, ProofLocation::Private),
926 (1, ProofLocation::Emitted),
927 (2, ProofLocation::Swarm),
928 (3, ProofLocation::Waku),
929 (4, ProofLocation::Reserved),
930 (5, ProofLocation::Ipfs),
931 ] {
932 let json = format!(r#"{{"proof_location": {val}, "orders": []}}"#);
933 let mux = Multiplexer::from_json(&json).unwrap();
934 assert_eq!(mux.proof_location(), expected);
935 }
936 }
937
938 #[test]
939 fn multiplexer_root_three_orders() {
940 let mut mux = Multiplexer::new(ProofLocation::Private);
941 mux.add(make_params(0xaa));
942 mux.add(make_params(0xbb));
943 mux.add(make_params(0xcc));
944 let root = mux.root().unwrap();
945 assert!(root.is_some());
946 for i in 0..3 {
948 let proof = mux.proof(i).unwrap();
949 assert!(!proof.proof.is_empty() || mux.len() == 1);
950 }
951 }
952
953 #[test]
954 fn order_proof_accessors() {
955 let params = make_params(0xaa);
956 let id = order_id(¶ms);
957 let proof = OrderProof::new(id, vec![B256::ZERO], params.clone());
958 assert_eq!(proof.order_id, id);
959 assert_eq!(proof.proof_len(), 1);
960 assert_eq!(proof.params.salt, params.salt);
961 }
962
963 #[test]
964 fn proof_with_params_accessors() {
965 let params = make_params(0xaa);
966 let pwp = ProofWithParams::new(vec![B256::ZERO, B256::ZERO], params);
967 assert_eq!(pwp.proof_len(), 2);
968 }
969
970 #[test]
971 fn display_proof_with_params() {
972 let mut mux = Multiplexer::new(ProofLocation::Private);
973 mux.add(make_params(0xaa));
974 mux.add(make_params(0xbb));
975 let proofs = mux.dump_proofs_and_params().unwrap();
976 let s = format!("{}", proofs[0]);
977 assert!(s.contains("proof-with-params"));
978 }
979
980 #[test]
981 fn order_proof_new_and_proof_len() {
982 let op = OrderProof::new(B256::ZERO, vec![B256::ZERO, B256::ZERO], make_params(1));
983 assert_eq!(op.proof_len(), 2);
984 }
985
986 #[test]
987 fn proof_with_params_new_and_proof_len() {
988 let pwp = ProofWithParams::new(vec![B256::ZERO], make_params(1));
989 assert_eq!(pwp.proof_len(), 1);
990 }
991}