ethrex_common/types/
requests.rs1use crate::H256;
2use bytes::Bytes;
3use ethereum_types::Address;
4use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError};
5use serde::{Deserialize, Serialize};
6use sha2::{Digest, Sha256};
7use tracing::error;
8
9use super::{Bytes48, Receipt};
10use crate::constants::DEPOSIT_TOPIC;
11use crate::serde_utils;
12
13pub type Bytes32 = [u8; 32];
14pub type Bytes96 = [u8; 96];
15const DEPOSIT_TYPE: u8 = 0x00;
16const WITHDRAWAL_TYPE: u8 = 0x01;
17const CONSOLIDATION_TYPE: u8 = 0x02;
18const BUILDER_DEPOSIT_TYPE: u8 = 0x03;
21const BUILDER_EXIT_TYPE: u8 = 0x04;
22
23#[derive(Clone, Debug)]
24pub struct EncodedRequests(pub Bytes);
25
26impl EncodedRequests {
27 pub fn is_empty(&self) -> bool {
28 self.0.len() <= 1
29 }
30}
31
32impl<'de> Deserialize<'de> for EncodedRequests {
33 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
34 where
35 D: serde::Deserializer<'de>,
36 {
37 Ok(EncodedRequests(serde_utils::bytes::deserialize(
38 deserializer,
39 )?))
40 }
41}
42
43impl Serialize for EncodedRequests {
44 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
45 where
46 S: serde::Serializer,
47 {
48 serde_utils::bytes::serialize(&self.0, serializer)
49 }
50}
51
52impl RLPEncode for EncodedRequests {
53 fn encode(&self, buf: &mut dyn bytes::BufMut) {
54 self.0.encode(buf)
55 }
56}
57
58impl RLPDecode for EncodedRequests {
59 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
60 let (bytes, rest) = RLPDecode::decode_unfinished(rlp)?;
61 Ok((EncodedRequests(bytes), rest))
62 }
63}
64
65#[derive(Clone, Debug)]
66pub enum Requests {
67 Deposit(Vec<Deposit>),
68 Withdrawal(Vec<u8>),
69 Consolidation(Vec<u8>),
70 BuilderDeposit(Vec<u8>),
71 BuilderExit(Vec<u8>),
72}
73
74impl Requests {
75 pub fn encode(&self) -> EncodedRequests {
76 let bytes: Vec<u8> = match self {
77 Requests::Deposit(deposits) => {
78 let deposit_data = deposits.iter().flat_map(|d| d.to_summarized_byte_array());
79 std::iter::once(DEPOSIT_TYPE).chain(deposit_data).collect()
80 }
81 Requests::Withdrawal(data) => std::iter::once(WITHDRAWAL_TYPE)
82 .chain(data.iter().cloned())
83 .collect(),
84 Requests::Consolidation(data) => std::iter::once(CONSOLIDATION_TYPE)
85 .chain(data.iter().cloned())
86 .collect(),
87 Requests::BuilderDeposit(data) => std::iter::once(BUILDER_DEPOSIT_TYPE)
88 .chain(data.iter().cloned())
89 .collect(),
90 Requests::BuilderExit(data) => std::iter::once(BUILDER_EXIT_TYPE)
91 .chain(data.iter().cloned())
92 .collect(),
93 };
94
95 EncodedRequests(Bytes::from(bytes))
96 }
97
98 pub fn from_deposit_receipts(
100 deposit_contract_address: Address,
101 receipts: &[Receipt],
102 ) -> Option<Requests> {
103 let mut deposits = vec![];
104
105 for r in receipts {
106 for log in &r.logs {
107 if log.address == deposit_contract_address
108 && log
109 .topics
110 .first()
111 .is_some_and(|topic| topic == &*DEPOSIT_TOPIC)
112 {
113 deposits.push(Deposit::from_abi_byte_array(&log.data)?);
114 }
115 }
116 }
117 Some(Self::Deposit(deposits))
118 }
119
120 pub fn from_withdrawals_data(data: Vec<u8>) -> Requests {
121 Requests::Withdrawal(data)
122 }
123
124 pub fn from_consolidation_data(data: Vec<u8>) -> Requests {
125 Requests::Consolidation(data)
126 }
127
128 pub fn from_builder_deposit_data(data: Vec<u8>) -> Requests {
129 Requests::BuilderDeposit(data)
130 }
131
132 pub fn from_builder_exit_data(data: Vec<u8>) -> Requests {
133 Requests::BuilderExit(data)
134 }
135}
136
137#[derive(Debug, Clone)]
138pub struct Deposit {
139 pub pub_key: Bytes48,
140 pub withdrawal_credentials: Bytes32,
141 pub amount: u64,
142 pub signature: Bytes96,
143 pub index: u64,
144}
145
146impl Deposit {
149 pub fn from_abi_byte_array(data: &[u8]) -> Option<Deposit> {
150 if data.len() != 576 {
151 error!("Wrong data length when parsing deposit.");
152 return None;
153 }
154
155 const WORD: usize = 32;
167 const U32_TAIL: usize = WORD - 4;
168
169 const PUB_KEY_OFFSET: u32 = 160;
170 const WITHDRAWAL_CREDENTIALS_OFFSET: u32 = 256;
171 const AMOUNT_OFFSET: u32 = 320;
172 const SIGNATURE_OFFSET: u32 = 384;
173 const INDEX_OFFSET: u32 = 512;
174
175 const OFFSETS: [u32; 5] = [
176 PUB_KEY_OFFSET,
177 WITHDRAWAL_CREDENTIALS_OFFSET,
178 AMOUNT_OFFSET,
179 SIGNATURE_OFFSET,
180 INDEX_OFFSET,
181 ];
182
183 const PUB_KEY_SIZE: u32 = 48;
184 const WITHDRAWAL_CREDENTIALS_SIZE: u32 = 32;
185 const AMOUNT_SIZE: u32 = 8;
186 const SIGNATURE_SIZE: u32 = 96;
187 const INDEX_SIZE: u32 = 8;
188
189 const SIZES: [u32; 5] = [
190 PUB_KEY_SIZE,
191 WITHDRAWAL_CREDENTIALS_SIZE,
192 AMOUNT_SIZE,
193 SIGNATURE_SIZE,
194 INDEX_SIZE,
195 ];
196
197 for (i, (expected_offset, expected_size)) in
199 OFFSETS.into_iter().zip(SIZES.into_iter()).enumerate()
200 {
201 let offset = fixed_bytes::<WORD>(data, i * WORD)?;
202 let size = fixed_bytes::<WORD>(data, expected_offset as usize)?;
203 if offset[U32_TAIL..] != expected_offset.to_be_bytes()
204 || size[U32_TAIL..] != expected_size.to_be_bytes()
205 {
206 return None;
207 }
208 }
209
210 let pub_key: Bytes48 =
212 fixed_bytes::<{ PUB_KEY_SIZE as usize }>(data, PUB_KEY_OFFSET as usize + WORD)?;
213 let withdrawal_credentials: Bytes32 =
214 fixed_bytes::<{ WITHDRAWAL_CREDENTIALS_SIZE as usize }>(
215 data,
216 WITHDRAWAL_CREDENTIALS_OFFSET as usize + WORD,
217 )?;
218 let amount: u64 = u64::from_le_bytes(fixed_bytes::<{ AMOUNT_SIZE as usize }>(
219 data,
220 AMOUNT_OFFSET as usize + WORD,
221 )?);
222 let signature: Bytes96 =
223 fixed_bytes::<{ SIGNATURE_SIZE as usize }>(data, SIGNATURE_OFFSET as usize + WORD)?;
224 let index: u64 = u64::from_le_bytes(fixed_bytes::<{ INDEX_SIZE as usize }>(
225 data,
226 INDEX_OFFSET as usize + WORD,
227 )?);
228
229 Some(Deposit {
230 pub_key,
231 withdrawal_credentials,
232 amount,
233 signature,
234 index,
235 })
236 }
237
238 pub fn to_summarized_byte_array(&self) -> [u8; 192] {
239 let mut buffer = [0u8; 192];
240 let mut p = 0;
242 buffer[p..48].clone_from_slice(&self.pub_key);
243 p += 48;
244 buffer[p..p + 32].clone_from_slice(&self.withdrawal_credentials);
245 p += 32;
246 buffer[p..p + 8].clone_from_slice(&self.amount.to_le_bytes());
247 p += 8;
248 buffer[p..p + 96].clone_from_slice(&self.signature);
249 p += 96;
250 buffer[p..p + 8].clone_from_slice(&self.index.to_le_bytes());
251
252 buffer
253 }
254}
255
256fn fixed_bytes<const N: usize>(data: &[u8], offset: usize) -> Option<[u8; N]> {
257 data.get(offset..offset + N)?.try_into().ok()
258}
259
260pub fn compute_requests_hash(requests: &[EncodedRequests]) -> H256 {
262 let mut hasher = Sha256::new();
263 for request in requests {
264 let request_bytes = request.0.as_ref();
265 if request_bytes.len() > 1 {
266 hasher.update(Sha256::digest(request_bytes));
267 }
268 }
269 H256::from_slice(&hasher.finalize())
270}