risc0_steel/
serde.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Serde related helpers.
16use alloy_eips::{
17    eip2718::{Eip2718Envelope, Encodable2718},
18    Typed2718,
19};
20use alloy_primitives::{bytes::BufMut, hex, keccak256, Sealable, Sealed, B256};
21use alloy_rlp::{Decodable, Encodable};
22use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
23use std::{
24    fmt::{self, Debug},
25    ops,
26};
27
28/// An efficient wrapper for header types that do not support serde serialization.
29///
30/// It implements deserialization using RLP encoding and does not discard the RLP data after
31/// decoding, instead keeping it for faster hash computation.
32#[derive(Clone, Debug)]
33pub struct RlpHeader<H: Encodable> {
34    inner: H,
35    rlp: Option<Box<[u8]>>,
36}
37
38impl<H: Encodable> ops::Deref for RlpHeader<H> {
39    type Target = H;
40
41    #[inline]
42    fn deref(&self) -> &Self::Target {
43        &self.inner
44    }
45}
46
47impl<H: Encodable> RlpHeader<H> {
48    #[must_use]
49    pub const fn new(inner: H) -> Self {
50        Self { inner, rlp: None }
51    }
52
53    pub fn inner(&self) -> &H {
54        &self.inner
55    }
56
57    pub fn inner_mut(&mut self) -> &mut H {
58        &mut self.inner
59    }
60
61    pub fn into_inner(self) -> H {
62        self.inner
63    }
64}
65
66impl<H: Encodable> Sealable for RlpHeader<H> {
67    #[inline]
68    fn hash_slow(&self) -> B256 {
69        match &self.rlp {
70            Some(rlp) => keccak256(rlp),
71            None => keccak256(alloy_rlp::encode(&self.inner)),
72        }
73    }
74
75    #[inline]
76    fn seal_unchecked(mut self, seal: B256) -> Sealed<Self> {
77        self.rlp = None;
78        Sealed::new_unchecked(self, seal)
79    }
80}
81
82impl<H: Encodable> Serialize for RlpHeader<H> {
83    #[inline]
84    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
85        let encoded = alloy_rlp::encode(&self.inner);
86        if serializer.is_human_readable() {
87            hex::serialize(&encoded, serializer)
88        } else {
89            serializer.serialize_bytes(&encoded)
90        }
91    }
92}
93
94impl<'de, H: Encodable + Decodable> Deserialize<'de> for RlpHeader<H> {
95    #[inline]
96    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
97        let rlp = if deserializer.is_human_readable() {
98            deserializer.deserialize_any(BytesVisitor)?
99        } else {
100            deserializer.deserialize_byte_buf(BytesVisitor)?
101        };
102        let inner = alloy_rlp::decode_exact(&rlp).map_err(de::Error::custom)?;
103
104        Ok(RlpHeader {
105            inner,
106            rlp: Some(rlp.into_boxed_slice()),
107        })
108    }
109}
110
111#[cfg(feature = "host")]
112impl<H, I> TryFrom<alloy::rpc::types::Header<H>> for RlpHeader<I>
113where
114    I: Encodable + Decodable + TryFrom<H>,
115{
116    type Error = <I as TryFrom<H>>::Error;
117
118    #[inline]
119    fn try_from(value: alloy::rpc::types::Header<H>) -> Result<Self, Self::Error> {
120        Ok(Self::new(value.inner.try_into()?))
121    }
122}
123
124/// An efficient wrapper for [Eip2718Envelope] types that do not support serde serialization.
125///
126/// It implements deserialization using the EIP-2718 RLP encoding and does not discard the RLP data
127/// after decoding, instead keeping it for faster RLP encoding of the deserialized type.
128#[derive(Clone, Debug)]
129pub struct Eip2718Wrapper<T: Eip2718Envelope> {
130    inner: T,
131    encoding: Option<Box<[u8]>>,
132}
133
134impl<T: Eip2718Envelope> Eip2718Wrapper<T> {
135    #[must_use]
136    pub const fn new(inner: T) -> Self {
137        Self {
138            inner,
139            encoding: None,
140        }
141    }
142
143    pub fn inner(&self) -> &T {
144        &self.inner
145    }
146
147    pub fn into_inner(self) -> T {
148        self.inner
149    }
150}
151
152impl<T: Eip2718Envelope> ops::Deref for Eip2718Wrapper<T> {
153    type Target = T;
154
155    #[inline]
156    fn deref(&self) -> &Self::Target {
157        &self.inner
158    }
159}
160
161impl<T: Eip2718Envelope> Encodable for Eip2718Wrapper<T> {
162    fn encode(&self, out: &mut dyn BufMut) {
163        self.encode_2718(out);
164    }
165
166    fn length(&self) -> usize {
167        self.encode_2718_len()
168    }
169}
170
171impl<T: Eip2718Envelope> Typed2718 for Eip2718Wrapper<T> {
172    fn ty(&self) -> u8 {
173        self.inner().ty()
174    }
175}
176
177impl<T: Eip2718Envelope> Encodable2718 for Eip2718Wrapper<T> {
178    fn type_flag(&self) -> Option<u8> {
179        self.inner.type_flag()
180    }
181
182    fn encode_2718_len(&self) -> usize {
183        match &self.encoding {
184            None => self.inner.encode_2718_len(),
185            Some(bytes) => bytes.len(),
186        }
187    }
188
189    fn encode_2718(&self, out: &mut dyn BufMut) {
190        match &self.encoding {
191            Some(bytes) => out.put_slice(bytes),
192            None => self.inner.encode_2718(out),
193        }
194    }
195}
196
197impl<T: Eip2718Envelope> Serialize for Eip2718Wrapper<T> {
198    #[inline]
199    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
200        let encoded = self.inner.encoded_2718();
201        if serializer.is_human_readable() {
202            hex::serialize(&encoded, serializer)
203        } else {
204            serializer.serialize_bytes(&encoded)
205        }
206    }
207}
208
209impl<'de, T: Eip2718Envelope> Deserialize<'de> for Eip2718Wrapper<T> {
210    #[inline]
211    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
212        let bytes = if deserializer.is_human_readable() {
213            deserializer.deserialize_any(BytesVisitor)?
214        } else {
215            deserializer.deserialize_byte_buf(BytesVisitor)?
216        };
217        let mut buf = bytes.as_slice();
218        let inner = T::decode_2718(&mut buf).map_err(de::Error::custom)?;
219        if !buf.is_empty() {
220            return Err(de::Error::custom("unexpected length"));
221        }
222
223        Ok(Eip2718Wrapper {
224            inner,
225            encoding: Some(bytes.into_boxed_slice()),
226        })
227    }
228}
229
230struct BytesVisitor;
231
232impl<'de> de::Visitor<'de> for BytesVisitor {
233    type Value = Vec<u8>;
234
235    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
236        formatter.write_str("bytes represented as a hex string, sequence or raw bytes")
237    }
238    fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
239        hex::decode(v).map_err(de::Error::custom)
240    }
241    fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
242        Ok(v.to_vec())
243    }
244    fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
245        Ok(v)
246    }
247    fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
248        let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(0));
249        while let Some(value) = seq.next_element()? {
250            values.push(value);
251        }
252        Ok(values)
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259    use alloy_consensus::{Header, ReceiptEnvelope};
260    use alloy_primitives::Sealable;
261
262    #[test]
263    fn bincode_rlp_header() {
264        let value = RlpHeader::new(Header::default());
265        assert_eq!(value.hash_slow(), value.inner().hash_slow());
266
267        let bin = bincode::serialize(&value).unwrap();
268        let de: RlpHeader<Header> = bincode::deserialize(&bin).unwrap();
269        assert_eq!(de.inner(), value.inner());
270        assert_eq!(de.hash_slow(), value.inner().hash_slow());
271    }
272
273    #[test]
274    fn serde_rlp_header() {
275        let value = RlpHeader::new(Header::default());
276        assert_eq!(value.hash_slow(), value.inner().hash_slow());
277
278        let json = serde_json::to_string(&value).unwrap();
279        let de: RlpHeader<Header> = serde_json::from_str(&json).unwrap();
280        assert_eq!(de.inner(), value.inner());
281        assert_eq!(de.hash_slow(), value.inner().hash_slow());
282    }
283
284    #[test]
285    fn bincode_eip2718_wrapper() {
286        let value = Eip2718Wrapper::new(ReceiptEnvelope::Eip2930(Default::default()));
287        assert_eq!(value.encoded_2718(), value.inner().encoded_2718());
288
289        let bin = bincode::serialize(&value).unwrap();
290        let de: Eip2718Wrapper<ReceiptEnvelope> = bincode::deserialize(&bin).unwrap();
291        assert_eq!(de.inner(), value.inner());
292        assert_eq!(de.encoded_2718(), value.inner().encoded_2718());
293    }
294
295    #[test]
296    fn serde_eip2718_wrapper() {
297        let value = Eip2718Wrapper::new(ReceiptEnvelope::Eip2930(Default::default()));
298        assert_eq!(value.encoded_2718(), value.inner().encoded_2718());
299
300        let json = serde_json::to_string(&value).unwrap();
301        let de: Eip2718Wrapper<ReceiptEnvelope> = serde_json::from_str(&json).unwrap();
302        assert_eq!(de.inner(), value.inner());
303        assert_eq!(de.encoded_2718(), value.inner().encoded_2718());
304    }
305}