1use alloy_primitives::{hex, U256};
2use alloy_rlp::{Buf, Decodable, Encodable, Header};
3use eyre::Context;
4use serde_json::Value;
5use std::fmt;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum Item {
13 Data(Vec<u8>),
14 Array(Vec<Item>),
15}
16
17impl Encodable for Item {
18 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
19 match self {
20 Self::Array(arr) => arr.encode(out),
21 Self::Data(data) => <[u8]>::encode(data, out),
22 }
23 }
24}
25
26impl Decodable for Item {
27 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
28 let h = Header::decode(buf)?;
29 if buf.len() < h.payload_length {
30 return Err(alloy_rlp::Error::InputTooShort);
31 }
32 let mut d = &buf[..h.payload_length];
33 let r = if h.list {
34 let view = &mut d;
35 let mut v = Vec::new();
36 while !view.is_empty() {
37 v.push(Self::decode(view)?);
38 }
39 Ok(Self::Array(v))
40 } else {
41 Ok(Self::Data(d.to_vec()))
42 };
43 buf.advance(h.payload_length);
44 r
45 }
46}
47
48impl Item {
49 pub(crate) fn value_to_item(value: &Value) -> eyre::Result<Self> {
50 match value {
51 Value::Null => Ok(Self::Data(vec![])),
52 Value::Bool(_) => {
53 eyre::bail!("RLP input can not contain booleans")
54 }
55 Value::Number(n) => {
56 Ok(Self::Data(n.to_string().parse::<U256>()?.to_be_bytes_trimmed_vec()))
57 }
58 Value::String(s) => Ok(Self::Data(hex::decode(s).wrap_err("Could not decode hex")?)),
59 Value::Array(values) => values.iter().map(Self::value_to_item).collect(),
60 Value::Object(_) => {
61 eyre::bail!("RLP input can not contain objects")
62 }
63 }
64 }
65}
66
67impl FromIterator<Self> for Item {
68 fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
69 Self::Array(Vec::from_iter(iter))
70 }
71}
72
73impl fmt::Display for Item {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
76 match self {
77 Self::Data(dat) => {
78 write!(f, "\"0x{}\"", hex::encode(dat))?;
79 }
80 Self::Array(items) => {
81 f.write_str("[")?;
82 for (i, item) in items.iter().enumerate() {
83 if i > 0 {
84 f.write_str(",")?;
85 }
86 fmt::Display::fmt(item, f)?;
87 }
88 f.write_str("]")?;
89 }
90 };
91 Ok(())
92 }
93}
94
95#[cfg(test)]
96mod test {
97 use crate::rlp_converter::Item;
98 use alloy_primitives::hex;
99 use alloy_rlp::{Bytes, Decodable};
100 use serde_json::Result as JsonResult;
101
102 fn array_von_neuman() -> Item {
104 Item::Array(vec![
105 Item::Array(vec![]),
106 Item::Array(vec![Item::Array(vec![])]),
107 Item::Array(vec![Item::Array(vec![]), Item::Array(vec![Item::Array(vec![])])]),
108 ])
109 }
110
111 #[test]
112 #[expect(clippy::disallowed_macros)]
113 fn encode_decode_test() -> alloy_rlp::Result<()> {
114 let parameters = vec![
115 (1, b"\xc0".to_vec(), Item::Array(vec![])),
116 (2, b"\xc1\x80".to_vec(), Item::Array(vec![Item::Data(vec![])])),
117 (3, b"\xc4\x83dog".to_vec(), Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])),
118 (
119 4,
120 b"\xc5\xc4\x83dog".to_vec(),
121 Item::Array(vec![Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])]),
122 ),
123 (
124 5,
125 b"\xc8\x83dog\x83cat".to_vec(),
126 Item::Array(vec![
127 Item::Data(vec![0x64, 0x6f, 0x67]),
128 Item::Data(vec![0x63, 0x61, 0x74]),
129 ]),
130 ),
131 (6, b"\xc7\xc0\xc1\xc0\xc3\xc0\xc1\xc0".to_vec(), array_von_neuman()),
132 (
133 7,
134 b"\xcd\x83\x6c\x6f\x6c\xc3\xc2\xc1\xc0\xc4\x83\x6f\x6c\x6f".to_vec(),
135 Item::Array(vec![
136 Item::Data(vec![b'\x6c', b'\x6f', b'\x6c']),
137 Item::Array(vec![Item::Array(vec![Item::Array(vec![Item::Array(vec![])])])]),
138 Item::Array(vec![Item::Data(vec![b'\x6f', b'\x6c', b'\x6f'])]),
139 ]),
140 ),
141 ];
142 for params in parameters {
143 let encoded = alloy_rlp::encode(¶ms.2);
144 assert_eq!(Item::decode(&mut &encoded[..])?, params.2);
145 let decoded = Item::decode(&mut ¶ms.1[..])?;
146 assert_eq!(alloy_rlp::encode(&decoded), params.1);
147 println!("case {} validated", params.0)
148 }
149
150 Ok(())
151 }
152
153 #[test]
154 #[expect(clippy::disallowed_macros)]
155 fn deserialize_from_str_test_hex() -> JsonResult<()> {
156 let parameters = vec![
157 (1, "[\"\"]", Item::Array(vec![Item::Data(vec![])])),
158 (2, "[\"0x646f67\"]", Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])),
159 (
160 3,
161 "[[\"646f67\"]]",
162 Item::Array(vec![Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])]),
163 ),
164 (
165 4,
166 "[\"646f67\",\"0x636174\"]",
167 Item::Array(vec![
168 Item::Data(vec![0x64, 0x6f, 0x67]),
169 Item::Data(vec![0x63, 0x61, 0x74]),
170 ]),
171 ),
172 (6, "[[],[[]],[[],[[]]]]", array_von_neuman()),
173 ];
174 for params in parameters {
175 let val = serde_json::from_str(params.1)?;
176 let item = Item::value_to_item(&val).unwrap();
177 assert_eq!(item, params.2);
178 println!("case {} validated", params.0);
179 }
180
181 Ok(())
182 }
183
184 #[test]
185 fn rlp_data() {
186 let hex_val_rlp = hex!("820002");
188 let item = Item::decode(&mut &hex_val_rlp[..]).unwrap();
189
190 let data = hex!("0002");
191 let encoded = alloy_rlp::encode(&data[..]);
192 let decoded: Bytes = alloy_rlp::decode_exact(&encoded[..]).unwrap();
193 assert_eq!(Item::Data(decoded.to_vec()), item);
194
195 let hex_val_rlp = hex!("00");
196 let item = Item::decode(&mut &hex_val_rlp[..]).unwrap();
197
198 let data = hex!("00");
199 let encoded = alloy_rlp::encode(&data[..]);
200 let decoded: Bytes = alloy_rlp::decode_exact(&encoded[..]).unwrap();
201 assert_eq!(Item::Data(decoded.to_vec()), item);
202 }
203}