1use eth_valkyoth_codec::{DecodeError, DecodeLimits, RlpItem, RlpList, RlpScalar};
2use eth_valkyoth_primitives::{Address, B256, ChainId, Gas, Nonce, Wei};
3
4use super::{TransactionEnvelope, decode_transaction_envelope};
5use crate::transaction::fields::{
6 ADDRESS_BYTES, decode_chain_id as decode_shared_chain_id, decode_to as decode_shared_to,
7 decode_u64_field as decode_shared_u64_field, decode_u256_field as decode_shared_u256_field,
8 next_list as next_shared_list, next_scalar as next_shared_scalar,
9};
10
11mod error;
12
13pub use error::{AccessListTransactionDecodeError, AccessListTransactionDecodeErrorCategory};
14
15pub const ACCESS_LIST_TRANSACTION_TYPE: u8 = 0x01;
17pub const ACCESS_LIST_TRANSACTION_FIELD_COUNT: usize = 11;
19
20const ACCESS_LIST_ENTRY_FIELD_COUNT: usize = 2;
21const B256_BYTES: usize = 32;
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub struct UnvalidatedAccessListTransaction<'a> {
30 pub chain_id: ChainId,
32 pub nonce: Nonce,
34 pub gas_price: Wei,
36 pub gas_limit: Gas,
38 pub to: AccessListTransactionTo,
40 pub value: Wei,
42 pub input: &'a [u8],
44 pub access_list: AccessList<'a>,
46 pub y_parity: SignatureYParity,
48 pub r: [u8; 32],
52 pub s: [u8; 32],
57}
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61pub enum AccessListTransactionTo {
62 Create,
64 Call(Address),
66}
67
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub enum SignatureYParity {
71 Even,
73 Odd,
75}
76
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub struct InvalidSignatureYParity {
80 value: u64,
81}
82
83impl InvalidSignatureYParity {
84 #[must_use]
86 pub const fn value(self) -> u64 {
87 self.value
88 }
89}
90
91impl SignatureYParity {
92 pub const fn try_new(value: u64) -> Result<Self, InvalidSignatureYParity> {
94 match value {
95 0 => Ok(Self::Even),
96 1 => Ok(Self::Odd),
97 _ => Err(InvalidSignatureYParity { value }),
98 }
99 }
100
101 #[must_use]
103 pub const fn get(self) -> u8 {
104 match self {
105 Self::Even => 0,
106 Self::Odd => 1,
107 }
108 }
109}
110
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub struct AccessList<'a> {
114 list: RlpList<'a>,
115 storage_key_count: usize,
116}
117
118impl<'a> AccessList<'a> {
119 #[must_use]
121 pub const fn address_count(self) -> usize {
122 self.list.item_count()
123 }
124
125 #[must_use]
127 pub const fn storage_key_count(self) -> usize {
128 self.storage_key_count
129 }
130
131 #[must_use]
138 pub const fn entries(self) -> AccessListEntries<'a> {
139 AccessListEntries {
140 items: self.list.items(),
141 }
142 }
143}
144
145#[derive(Clone, Copy, Debug, Eq, PartialEq)]
147pub struct AccessListEntry<'a> {
148 pub address: Address,
150 pub storage_keys: AccessListStorageKeys<'a>,
152}
153
154#[derive(Clone, Copy, Debug, Eq, PartialEq)]
156pub struct AccessListStorageKeys<'a> {
157 list: RlpList<'a>,
158}
159
160impl<'a> AccessListStorageKeys<'a> {
161 #[must_use]
163 pub const fn len(self) -> usize {
164 self.list.item_count()
165 }
166
167 #[must_use]
169 pub const fn is_empty(self) -> bool {
170 self.list.is_empty()
171 }
172
173 #[must_use]
179 pub const fn keys(self) -> AccessListStorageKeyItems<'a> {
180 AccessListStorageKeyItems {
181 items: self.list.items(),
182 }
183 }
184}
185
186#[derive(Clone, Debug)]
188pub struct AccessListEntries<'a> {
189 items: eth_valkyoth_codec::RlpListItems<'a>,
190}
191
192impl<'a> Iterator for AccessListEntries<'a> {
193 type Item = Result<AccessListEntry<'a>, AccessListTransactionDecodeError>;
194
195 fn next(&mut self) -> Option<Self::Item> {
196 self.items
197 .next()
198 .map(|item| decode_access_list_entry_item(item).map_err(map_access_list_decode_error))
199 }
200}
201
202impl core::iter::FusedIterator for AccessListEntries<'_> {}
203
204#[derive(Clone, Debug)]
206pub struct AccessListStorageKeyItems<'a> {
207 items: eth_valkyoth_codec::RlpListItems<'a>,
208}
209
210impl Iterator for AccessListStorageKeyItems<'_> {
211 type Item = Result<B256, AccessListTransactionDecodeError>;
212
213 fn next(&mut self) -> Option<Self::Item> {
214 self.items
215 .next()
216 .map(|item| decode_storage_key_item(item).map_err(map_access_list_decode_error))
217 }
218}
219
220impl core::iter::FusedIterator for AccessListStorageKeyItems<'_> {}
221
222#[non_exhaustive]
224#[derive(Clone, Copy, Debug, Eq, PartialEq)]
225pub enum AccessListTransactionField {
226 Payload,
228 ChainId,
230 Nonce,
232 GasPrice,
234 GasLimit,
236 To,
238 Value,
240 Data,
242 AccessList,
244 SignatureYParity,
246 SignatureR,
248 SignatureS,
250}
251
252pub fn decode_access_list_transaction<'a>(
254 input: &'a [u8],
255 limits: DecodeLimits,
256) -> Result<UnvalidatedAccessListTransaction<'a>, AccessListTransactionDecodeError> {
257 match decode_transaction_envelope(input, limits)
258 .map_err(AccessListTransactionDecodeError::Envelope)?
259 {
260 TransactionEnvelope::Typed(typed)
261 if typed.transaction_type.get() == ACCESS_LIST_TRANSACTION_TYPE =>
262 {
263 decode_access_list_payload(typed.payload, limits)
264 }
265 TransactionEnvelope::Typed(typed) => {
266 Err(AccessListTransactionDecodeError::WrongTransactionType {
267 type_byte: typed.transaction_type.get(),
268 })
269 }
270 TransactionEnvelope::Legacy(_) => {
271 Err(AccessListTransactionDecodeError::WrongTransactionType { type_byte: 0 })
272 }
273 }
274}
275
276fn decode_access_list_payload<'a>(
277 payload: &'a [u8],
278 limits: DecodeLimits,
279) -> Result<UnvalidatedAccessListTransaction<'a>, AccessListTransactionDecodeError> {
280 let list = eth_valkyoth_codec::decode_rlp_list(payload, limits)
281 .map_err(|source| field_error(AccessListTransactionField::Payload, source))?;
282 if list.item_count() != ACCESS_LIST_TRANSACTION_FIELD_COUNT {
283 return Err(AccessListTransactionDecodeError::WrongFieldCount {
284 expected: ACCESS_LIST_TRANSACTION_FIELD_COUNT,
285 found: list.item_count(),
286 });
287 }
288
289 let mut fields = list.items();
290 let chain_id = decode_shared_chain_id(
291 &mut fields,
292 AccessListTransactionField::ChainId,
293 field_error,
294 )?;
295 let nonce = Nonce::new(decode_shared_u64_field(
296 &mut fields,
297 AccessListTransactionField::Nonce,
298 field_error,
299 )?);
300 let gas_price = Wei::from_be_bytes(decode_shared_u256_field(
301 &mut fields,
302 AccessListTransactionField::GasPrice,
303 field_error,
304 )?);
305 let gas_limit = Gas::new(decode_shared_u64_field(
306 &mut fields,
307 AccessListTransactionField::GasLimit,
308 field_error,
309 )?);
310 let to = decode_shared_to(
311 next_shared_scalar(&mut fields, AccessListTransactionField::To, field_error)?,
312 |found| AccessListTransactionDecodeError::InvalidToLength { found },
313 )?;
314 let value = Wei::from_be_bytes(decode_shared_u256_field(
315 &mut fields,
316 AccessListTransactionField::Value,
317 field_error,
318 )?);
319 let input =
320 next_shared_scalar(&mut fields, AccessListTransactionField::Data, field_error)?.payload();
321 limits
322 .check_single_allocation_limit(input.len())
323 .map_err(|source| field_error(AccessListTransactionField::Data, source))?;
324 let access_list = decode_access_list(next_shared_list(
325 &mut fields,
326 AccessListTransactionField::AccessList,
327 field_error,
328 )?)
329 .map_err(map_access_list_decode_error)?;
330 let y_parity = SignatureYParity::try_new(decode_shared_u64_field(
331 &mut fields,
332 AccessListTransactionField::SignatureYParity,
333 field_error,
334 )?)
335 .map_err(|error| AccessListTransactionDecodeError::InvalidYParity {
336 value: error.value(),
337 })?;
338 let r = decode_shared_u256_field(
339 &mut fields,
340 AccessListTransactionField::SignatureR,
341 field_error,
342 )?;
343 let s = decode_shared_u256_field(
344 &mut fields,
345 AccessListTransactionField::SignatureS,
346 field_error,
347 )?;
348
349 Ok(UnvalidatedAccessListTransaction {
350 chain_id,
351 nonce,
352 gas_price,
353 gas_limit,
354 to,
355 value,
356 input,
357 access_list,
358 y_parity,
359 r,
360 s,
361 })
362}
363
364pub(crate) fn decode_access_list(
365 list: RlpList<'_>,
366) -> Result<AccessList<'_>, AccessListDecodeError> {
367 let mut storage_key_count = 0usize;
371 for item in list.items() {
372 let entry = decode_access_list_entry_item(item)?;
373 storage_key_count =
374 eth_valkyoth_codec::checked_len_add(storage_key_count, entry.storage_keys.len())
375 .map_err(AccessListDecodeError::FieldDecode)?;
376 }
377 Ok(AccessList {
378 list,
379 storage_key_count,
380 })
381}
382
383fn decode_access_list_entry_item(
384 item: Result<RlpItem<'_>, DecodeError>,
385) -> Result<AccessListEntry<'_>, AccessListDecodeError> {
386 let item = item.map_err(AccessListDecodeError::FieldDecode)?;
387 let RlpItem::List(list) = item else {
388 return Err(AccessListDecodeError::FieldDecode(
389 DecodeError::UnexpectedScalar,
390 ));
391 };
392 if list.item_count() != ACCESS_LIST_ENTRY_FIELD_COUNT {
393 return Err(AccessListDecodeError::InvalidAccessListEntryFieldCount {
394 found: list.item_count(),
395 });
396 }
397
398 let mut fields = list.items();
399 let address = decode_access_list_address(next_shared_scalar(
400 &mut fields,
401 AccessListTransactionField::AccessList,
402 |_, source| AccessListDecodeError::FieldDecode(source),
403 )?)?;
404 let storage_keys = AccessListStorageKeys {
405 list: next_shared_list(
406 &mut fields,
407 AccessListTransactionField::AccessList,
408 |_, source| AccessListDecodeError::FieldDecode(source),
409 )?,
410 };
411
412 for key in storage_keys.list.items() {
413 let _ = decode_storage_key_item(key)?;
414 }
415 Ok(AccessListEntry {
416 address,
417 storage_keys,
418 })
419}
420
421fn decode_storage_key_item(
422 item: Result<RlpItem<'_>, DecodeError>,
423) -> Result<B256, AccessListDecodeError> {
424 let item = item.map_err(AccessListDecodeError::FieldDecode)?;
425 let RlpItem::Scalar(scalar) = item else {
426 return Err(AccessListDecodeError::FieldDecode(
427 DecodeError::UnexpectedList,
428 ));
429 };
430 let found = scalar.payload().len();
431 let bytes: [u8; B256_BYTES] = scalar
432 .payload()
433 .try_into()
434 .map_err(|_| AccessListDecodeError::InvalidStorageKeyLength { found })?;
435 Ok(B256::from_bytes(bytes))
436}
437
438fn decode_access_list_address(scalar: RlpScalar<'_>) -> Result<Address, AccessListDecodeError> {
439 let found = scalar.payload().len();
440 let bytes: [u8; ADDRESS_BYTES] = scalar
441 .payload()
442 .try_into()
443 .map_err(|_| AccessListDecodeError::InvalidAccessListAddressLength { found })?;
444 Ok(Address::from_bytes(bytes))
445}
446
447const fn field_error(
448 field: AccessListTransactionField,
449 source: DecodeError,
450) -> AccessListTransactionDecodeError {
451 AccessListTransactionDecodeError::FieldDecode { field, source }
452}
453
454#[derive(Clone, Copy, Debug, Eq, PartialEq)]
456pub(crate) enum AccessListDecodeError {
457 FieldDecode(DecodeError),
458 InvalidAccessListEntryFieldCount { found: usize },
459 InvalidAccessListAddressLength { found: usize },
460 InvalidStorageKeyLength { found: usize },
461}
462
463pub(crate) const fn map_access_list_decode_error(
464 error: AccessListDecodeError,
465) -> AccessListTransactionDecodeError {
466 match error {
467 AccessListDecodeError::FieldDecode(source) => {
468 field_error(AccessListTransactionField::AccessList, source)
469 }
470 AccessListDecodeError::InvalidAccessListEntryFieldCount { found } => {
471 AccessListTransactionDecodeError::InvalidAccessListEntryFieldCount { found }
472 }
473 AccessListDecodeError::InvalidAccessListAddressLength { found } => {
474 AccessListTransactionDecodeError::InvalidAccessListAddressLength { found }
475 }
476 AccessListDecodeError::InvalidStorageKeyLength { found } => {
477 AccessListTransactionDecodeError::InvalidStorageKeyLength { found }
478 }
479 }
480}