dpp/address_funds/serde_helpers/
address_input_map.rs1use crate::address_funds::PlatformAddress;
7use crate::fee::Credits;
8use crate::prelude::AddressNonce;
9use crate::serialization::json_safe_fields;
10use serde::de::{self, SeqAccess, Visitor};
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12use std::collections::BTreeMap;
13use std::fmt;
14
15#[json_safe_fields]
16#[derive(Serialize, Deserialize)]
17struct AddressInputEntry {
18 address: PlatformAddress,
19 nonce: AddressNonce,
20 amount: Credits,
21}
22
23pub fn serialize<S>(
24 map: &BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
25 serializer: S,
26) -> Result<S::Ok, S::Error>
27where
28 S: Serializer,
29{
30 use serde::ser::SerializeSeq;
31 let mut seq = serializer.serialize_seq(Some(map.len()))?;
32 for (address, (nonce, amount)) in map {
33 seq.serialize_element(&AddressInputEntry {
34 address: *address,
35 nonce: *nonce,
36 amount: *amount,
37 })?;
38 }
39 seq.end()
40}
41
42pub fn deserialize<'de, D>(
43 deserializer: D,
44) -> Result<BTreeMap<PlatformAddress, (AddressNonce, Credits)>, D::Error>
45where
46 D: Deserializer<'de>,
47{
48 deserializer.deserialize_seq(AddressInputMapVisitor)
49}
50
51struct AddressInputMapVisitor;
52
53impl<'de> Visitor<'de> for AddressInputMapVisitor {
54 type Value = BTreeMap<PlatformAddress, (AddressNonce, Credits)>;
55
56 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
57 formatter.write_str("an array of { address, nonce, amount } objects")
58 }
59
60 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
61 where
62 A: SeqAccess<'de>,
63 {
64 let mut map = BTreeMap::new();
65 while let Some(entry) = seq.next_element::<AddressInputEntry>()? {
66 if map
67 .insert(entry.address, (entry.nonce, entry.amount))
68 .is_some()
69 {
70 return Err(de::Error::custom(format!(
71 "duplicate input address: {}",
72 hex::encode(entry.address.to_bytes())
73 )));
74 }
75 }
76 Ok(map)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 fn p2pkh(byte: u8) -> PlatformAddress {
85 PlatformAddress::P2pkh([byte; 20])
86 }
87
88 #[derive(Serialize, Deserialize, PartialEq, Debug)]
90 struct Wrapper(#[serde(with = "super")] BTreeMap<PlatformAddress, (AddressNonce, Credits)>);
91
92 #[test]
93 fn empty_round_trips() {
94 let original = Wrapper(BTreeMap::new());
95 let json = serde_json::to_string(&original).expect("serialize empty map");
96 assert_eq!(json, "[]");
97 let restored: Wrapper = serde_json::from_str(&json).expect("deserialize empty array");
98 assert_eq!(original, restored);
99 }
100
101 #[test]
102 fn single_entry_round_trips() {
103 let mut map = BTreeMap::new();
104 map.insert(p2pkh(1), (5u32, 1_000u64));
105 let original = Wrapper(map);
106
107 let value = serde_json::to_value(&original).expect("serialize single entry");
108 assert_eq!(
109 value,
110 serde_json::json!([
111 { "address": "00".to_string() + &"01".repeat(20), "nonce": 5, "amount": 1000 }
112 ])
113 );
114
115 let restored: Wrapper = serde_json::from_value(value).expect("deserialize single entry");
116 assert_eq!(original, restored);
117 }
118
119 #[test]
120 fn multiple_entries_emit_in_sorted_address_order() {
121 let mut map = BTreeMap::new();
122 map.insert(p2pkh(2), (2u32, 200u64));
123 map.insert(p2pkh(1), (1u32, 100u64));
124 map.insert(p2pkh(3), (3u32, 300u64));
125
126 let original = Wrapper(map);
127 let value = serde_json::to_value(&original).expect("serialize multi entry");
128 let arr = value.as_array().expect("emitted JSON array");
129 let nonces: Vec<u64> = arr
130 .iter()
131 .map(|entry| entry["nonce"].as_u64().expect("nonce as u64"))
132 .collect();
133 assert_eq!(nonces, vec![1, 2, 3]);
134
135 let restored: Wrapper = serde_json::from_value(value).expect("deserialize multi entry");
136 assert_eq!(original, restored);
137 }
138
139 #[test]
140 fn rejects_duplicate_addresses() {
141 let json = serde_json::json!([
142 { "address": "00".to_string() + &"01".repeat(20), "nonce": 1, "amount": 100 },
143 { "address": "00".to_string() + &"01".repeat(20), "nonce": 2, "amount": 200 }
144 ]);
145 let result: Result<Wrapper, _> = serde_json::from_value(json);
146 assert!(result.is_err());
147 }
148}