1use alloy_primitives::{U256, hex};
2use alloy_rlp::{Decodable, Encodable, Header, PayloadView};
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<Self>),
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 struct ListFrame<'a> {
29 remaining: std::vec::IntoIter<&'a [u8]>,
30 items: Vec<Item>,
31 }
32
33 let items = match Header::decode_raw(buf)? {
34 PayloadView::String(data) => return Ok(Self::Data(data.to_vec())),
35 PayloadView::List(items) => items,
36 };
37
38 let mut frames = vec![ListFrame { remaining: items.into_iter(), items: Vec::new() }];
39 loop {
40 let Some(encoded) = frames.last_mut().unwrap().remaining.next() else {
41 let frame = frames.pop().unwrap();
42 let item = Self::Array(frame.items);
43 if let Some(parent) = frames.last_mut() {
44 parent.items.push(item);
45 continue;
46 }
47 return Ok(item);
48 };
49
50 match Header::decode_raw(&mut &encoded[..])? {
51 PayloadView::String(data) => {
52 frames.last_mut().unwrap().items.push(Self::Data(data.to_vec()));
53 }
54 PayloadView::List(items) => {
55 frames.push(ListFrame { remaining: items.into_iter(), items: Vec::new() });
56 }
57 }
58 }
59 }
60}
61
62impl Drop for Item {
63 fn drop(&mut self) {
64 let Self::Array(items) = self else { return };
66 let mut pending = std::mem::take(items);
67 while let Some(mut item) = pending.pop() {
68 if let Self::Array(children) = &mut item {
69 pending.append(children);
70 }
71 }
72 }
73}
74
75impl Item {
76 pub(crate) fn value_to_item(value: &Value) -> eyre::Result<Self> {
77 match value {
78 Value::Null => Ok(Self::Data(vec![])),
79 Value::Bool(_) => {
80 eyre::bail!("RLP input can not contain booleans");
81 }
82 Value::Number(n) => {
83 Ok(Self::Data(n.to_string().parse::<U256>()?.to_be_bytes_trimmed_vec()))
84 }
85 Value::String(s) => Ok(Self::Data(hex::decode(s).wrap_err("Could not decode hex")?)),
86 Value::Array(values) => values.iter().map(Self::value_to_item).collect(),
87 Value::Object(_) => {
88 eyre::bail!("RLP input can not contain objects");
89 }
90 }
91 }
92}
93
94impl FromIterator<Self> for Item {
95 fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
96 Self::Array(Vec::from_iter(iter))
97 }
98}
99
100impl fmt::Display for Item {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
103 enum Task<'a> {
104 Item(&'a Item),
105 Comma,
106 Close,
107 }
108
109 let mut tasks = vec![Task::Item(self)];
110 while let Some(task) = tasks.pop() {
111 match task {
112 Task::Item(Self::Data(data)) => write!(f, "\"0x{}\"", hex::encode(data))?,
113 Task::Item(Self::Array(items)) => {
114 f.write_str("[")?;
115 tasks.push(Task::Close);
116 for (i, item) in items.iter().enumerate().rev() {
117 tasks.push(Task::Item(item));
118 if i > 0 {
119 tasks.push(Task::Comma);
120 }
121 }
122 }
123 Task::Comma => f.write_str(",")?,
124 Task::Close => f.write_str("]")?,
125 }
126 }
127 Ok(())
128 }
129}
130
131#[cfg(test)]
132mod test {
133 use crate::rlp_converter::Item;
134 use alloy_primitives::hex;
135 use alloy_rlp::{Bytes, Decodable};
136 use serde_json::Result as JsonResult;
137
138 fn array_von_neuman() -> Item {
140 Item::Array(vec![
141 Item::Array(vec![]),
142 Item::Array(vec![Item::Array(vec![])]),
143 Item::Array(vec![Item::Array(vec![]), Item::Array(vec![Item::Array(vec![])])]),
144 ])
145 }
146
147 #[test]
148 #[expect(clippy::disallowed_macros)]
149 fn encode_decode_test() -> alloy_rlp::Result<()> {
150 let parameters = vec![
151 (1, b"\xc0".to_vec(), Item::Array(vec![])),
152 (2, b"\xc1\x80".to_vec(), Item::Array(vec![Item::Data(vec![])])),
153 (3, b"\xc4\x83dog".to_vec(), Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])),
154 (
155 4,
156 b"\xc5\xc4\x83dog".to_vec(),
157 Item::Array(vec![Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])]),
158 ),
159 (
160 5,
161 b"\xc8\x83dog\x83cat".to_vec(),
162 Item::Array(vec![
163 Item::Data(vec![0x64, 0x6f, 0x67]),
164 Item::Data(vec![0x63, 0x61, 0x74]),
165 ]),
166 ),
167 (6, b"\xc7\xc0\xc1\xc0\xc3\xc0\xc1\xc0".to_vec(), array_von_neuman()),
168 (
169 7,
170 b"\xcd\x83\x6c\x6f\x6c\xc3\xc2\xc1\xc0\xc4\x83\x6f\x6c\x6f".to_vec(),
171 Item::Array(vec![
172 Item::Data(vec![b'\x6c', b'\x6f', b'\x6c']),
173 Item::Array(vec![Item::Array(vec![Item::Array(vec![Item::Array(vec![])])])]),
174 Item::Array(vec![Item::Data(vec![b'\x6f', b'\x6c', b'\x6f'])]),
175 ]),
176 ),
177 ];
178 for params in parameters {
179 let encoded = alloy_rlp::encode(¶ms.2);
180 assert_eq!(Item::decode(&mut &encoded[..])?, params.2);
181 let decoded = Item::decode(&mut ¶ms.1[..])?;
182 assert_eq!(alloy_rlp::encode(&decoded), params.1);
183 println!("case {} validated", params.0)
184 }
185
186 Ok(())
187 }
188
189 #[test]
190 #[expect(clippy::disallowed_macros)]
191 fn deserialize_from_str_test_hex() -> JsonResult<()> {
192 let parameters = vec![
193 (1, "[\"\"]", Item::Array(vec![Item::Data(vec![])])),
194 (2, "[\"0x646f67\"]", Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])),
195 (
196 3,
197 "[[\"646f67\"]]",
198 Item::Array(vec![Item::Array(vec![Item::Data(vec![0x64, 0x6f, 0x67])])]),
199 ),
200 (
201 4,
202 "[\"646f67\",\"0x636174\"]",
203 Item::Array(vec![
204 Item::Data(vec![0x64, 0x6f, 0x67]),
205 Item::Data(vec![0x63, 0x61, 0x74]),
206 ]),
207 ),
208 (6, "[[],[[]],[[],[[]]]]", array_von_neuman()),
209 ];
210 for params in parameters {
211 let val = serde_json::from_str(params.1)?;
212 let item = Item::value_to_item(&val).unwrap();
213 assert_eq!(item, params.2);
214 println!("case {} validated", params.0);
215 }
216
217 Ok(())
218 }
219
220 #[test]
221 fn rlp_data() {
222 let hex_val_rlp = hex!("820002");
224 let item = Item::decode(&mut &hex_val_rlp[..]).unwrap();
225
226 let data = hex!("0002");
227 let encoded = alloy_rlp::encode(&data[..]);
228 let decoded: Bytes = alloy_rlp::decode_exact(&encoded[..]).unwrap();
229 assert_eq!(Item::Data(decoded.to_vec()), item);
230
231 let hex_val_rlp = hex!("00");
232 let item = Item::decode(&mut &hex_val_rlp[..]).unwrap();
233
234 let data = hex!("00");
235 let encoded = alloy_rlp::encode(&data[..]);
236 let decoded: Bytes = alloy_rlp::decode_exact(&encoded[..]).unwrap();
237 assert_eq!(Item::Data(decoded.to_vec()), item);
238 }
239}