Skip to main content

dpp/address_funds/serde_helpers/
address_input_map.rs

1//! `#[serde(with = "address_input_map")]` helper.
2//!
3//! Reshapes `BTreeMap<PlatformAddress, (AddressNonce, Credits)>` to/from an
4//! array of `{ address, nonce, amount }` entries on the JSON / Object wire.
5
6use 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    /// Newtype wrapper so we can drive the helper through a serde derive.
89    #[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}