alloy_consensus/receipt/
status.rs1use alloy_primitives::B256;
2use alloy_rlp::{Buf, BufMut, Decodable, Encodable, Error, Header};
3
4const EIP658_STATUS_FAILED_RLP_PAYLOAD: &[u8] = &[];
5const EIP658_STATUS_SUCCESS_RLP_PAYLOAD: &[u8] = &[0x01];
6const PRE_BYZANTIUM_POST_STATE_ROOT_LENGTH: usize = 32;
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
10#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
11#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
12pub enum Eip658Value {
13 Eip658(bool),
17 PostState(B256),
21}
22
23impl Eip658Value {
24 pub const fn success() -> Self {
26 Self::Eip658(true)
27 }
28
29 pub const fn coerce_status(&self) -> bool {
34 matches!(self, Self::Eip658(true) | Self::PostState(_))
35 }
36
37 pub const fn coerced_eip658(&mut self) {
39 *self = Self::Eip658(self.coerce_status())
40 }
41
42 pub const fn is_post_state(&self) -> bool {
46 matches!(self, Self::PostState(_))
47 }
48
49 pub const fn is_eip658(&self) -> bool {
51 !matches!(self, Self::PostState(_))
52 }
53
54 pub const fn as_post_state(&self) -> Option<B256> {
56 match self {
57 Self::PostState(state) => Some(*state),
58 _ => None,
59 }
60 }
61
62 pub const fn as_eip658(&self) -> Option<bool> {
66 match self {
67 Self::Eip658(status) => Some(*status),
68 _ => None,
69 }
70 }
71}
72
73impl From<bool> for Eip658Value {
74 fn from(status: bool) -> Self {
75 Self::Eip658(status)
76 }
77}
78
79impl From<B256> for Eip658Value {
80 fn from(state: B256) -> Self {
81 Self::PostState(state)
82 }
83}
84
85impl Default for Eip658Value {
87 fn default() -> Self {
88 Self::Eip658(true)
89 }
90}
91
92#[cfg(feature = "serde")]
93mod serde_eip658 {
94 use super::*;
101 use serde::{Deserialize, Serialize};
102
103 #[derive(serde::Serialize, serde::Deserialize)]
104 #[serde(untagged)]
105 enum SerdeHelper {
106 Eip658 {
107 #[serde(with = "alloy_serde::quantity")]
108 status: bool,
109 },
110 PostState {
111 root: B256,
112 },
113 }
114
115 impl Serialize for Eip658Value {
116 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117 where
118 S: serde::Serializer,
119 {
120 match self {
121 Self::Eip658(status) => {
122 SerdeHelper::Eip658 { status: *status }.serialize(serializer)
123 }
124 Self::PostState(state) => {
125 SerdeHelper::PostState { root: *state }.serialize(serializer)
126 }
127 }
128 }
129 }
130
131 impl<'de> Deserialize<'de> for Eip658Value {
132 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133 where
134 D: serde::Deserializer<'de>,
135 {
136 let helper = SerdeHelper::deserialize(deserializer)?;
137 match helper {
138 SerdeHelper::Eip658 { status } => Ok(Self::Eip658(status)),
139 SerdeHelper::PostState { root } => Ok(Self::PostState(root)),
140 }
141 }
142 }
143}
144
145impl Encodable for Eip658Value {
146 fn encode(&self, buf: &mut dyn BufMut) {
147 match self {
148 Self::Eip658(status) => {
149 status.encode(buf);
150 }
151 Self::PostState(state) => {
152 state.encode(buf);
153 }
154 }
155 }
156
157 fn length(&self) -> usize {
158 match self {
159 Self::Eip658(inner) => inner.length(),
160 Self::PostState(inner) => inner.length(),
161 }
162 }
163}
164
165impl Decodable for Eip658Value {
166 fn decode(buf: &mut &[u8]) -> Result<Self, Error> {
167 let h = Header::decode(buf)?;
168 if h.list {
169 return Err(Error::UnexpectedList);
170 }
171
172 match h.payload_length {
173 len if len == EIP658_STATUS_FAILED_RLP_PAYLOAD.len() => Ok(Self::Eip658(false)),
174 len if len == EIP658_STATUS_SUCCESS_RLP_PAYLOAD.len() => {
175 if buf.remaining() < len {
176 return Err(Error::InputTooShort);
177 }
178 let is_success = &buf[..len] == EIP658_STATUS_SUCCESS_RLP_PAYLOAD;
179 buf.advance(len);
180 if is_success {
181 Ok(Self::Eip658(true))
182 } else {
183 Err(Error::Custom("invalid EIP-658 status, must be 0 or 1"))
184 }
185 }
186 PRE_BYZANTIUM_POST_STATE_ROOT_LENGTH => {
187 if buf.remaining() < PRE_BYZANTIUM_POST_STATE_ROOT_LENGTH {
188 return Err(Error::InputTooShort);
189 }
190 let mut state = B256::default();
191 buf.copy_to_slice(state.as_mut_slice());
192 Ok(state.into())
193 }
194 _ => Err(Error::UnexpectedLength),
195 }
196 }
197}
198
199#[cfg(test)]
200mod test {
201 use super::*;
202
203 #[test]
204 fn rlp_sanity() {
205 let mut buf = Vec::new();
206 let status = Eip658Value::Eip658(true);
207 status.encode(&mut buf);
208 assert_eq!(Eip658Value::decode(&mut buf.as_slice()), Ok(status));
209
210 let mut buf = Vec::new();
211 let state = Eip658Value::PostState(B256::default());
212 state.encode(&mut buf);
213 assert_eq!(Eip658Value::decode(&mut buf.as_slice()), Ok(state));
214 }
215
216 #[test]
217 fn rejects_non_canonical_status() {
218 for encoded in [[0x00], [0x02], [0x7f]] {
219 assert!(Eip658Value::decode(&mut encoded.as_slice()).is_err());
220 }
221
222 assert!(Eip658Value::decode(&mut [0xc0].as_slice()).is_err());
223
224 let mut state_list = [0_u8; 33];
225 state_list[0] = 0xe0;
226 assert!(Eip658Value::decode(&mut state_list.as_slice()).is_err());
227 }
228
229 #[cfg(feature = "serde")]
230 #[test]
231 fn serde_sanity() {
232 let status: Eip658Value = true.into();
233 let json = serde_json::to_string(&status).unwrap();
234 assert_eq!(json, r#"{"status":"0x1"}"#);
235 assert_eq!(serde_json::from_str::<Eip658Value>(&json).unwrap(), status);
236
237 let state: Eip658Value = false.into();
238 let json = serde_json::to_string(&state).unwrap();
239 assert_eq!(json, r#"{"status":"0x0"}"#);
240
241 let state: Eip658Value = B256::repeat_byte(1).into();
242 let json = serde_json::to_string(&state).unwrap();
243 assert_eq!(
244 json,
245 r#"{"root":"0x0101010101010101010101010101010101010101010101010101010101010101"}"#
246 );
247 }
248}