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 pub(crate) const fn encoded_rlp_len(self) -> usize {
146 self.list.encoded_len()
147 }
148
149 pub(crate) fn encode_rlp(self, output: &mut [u8]) -> Result<usize, DecodeError> {
151 eth_valkyoth_codec::encode_decoded_list(self.list, output)
152 }
153}
154
155#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157pub struct AccessListEntry<'a> {
158 pub address: Address,
160 pub storage_keys: AccessListStorageKeys<'a>,
162}
163
164#[derive(Clone, Copy, Debug, Eq, PartialEq)]
166pub struct AccessListStorageKeys<'a> {
167 list: RlpList<'a>,
168}
169
170impl<'a> AccessListStorageKeys<'a> {
171 #[must_use]
173 pub const fn len(self) -> usize {
174 self.list.item_count()
175 }
176
177 #[must_use]
179 pub const fn is_empty(self) -> bool {
180 self.list.is_empty()
181 }
182
183 #[must_use]
189 pub const fn keys(self) -> AccessListStorageKeyItems<'a> {
190 AccessListStorageKeyItems {
191 items: self.list.items(),
192 }
193 }
194}
195
196#[derive(Clone, Debug)]
198pub struct AccessListEntries<'a> {
199 items: eth_valkyoth_codec::RlpListItems<'a>,
200}
201
202impl<'a> Iterator for AccessListEntries<'a> {
203 type Item = Result<AccessListEntry<'a>, AccessListTransactionDecodeError>;
204
205 fn next(&mut self) -> Option<Self::Item> {
206 self.items
207 .next()
208 .map(|item| decode_access_list_entry_item(item).map_err(map_access_list_decode_error))
209 }
210}
211
212impl core::iter::FusedIterator for AccessListEntries<'_> {}
213
214#[derive(Clone, Debug)]
216pub struct AccessListStorageKeyItems<'a> {
217 items: eth_valkyoth_codec::RlpListItems<'a>,
218}
219
220impl Iterator for AccessListStorageKeyItems<'_> {
221 type Item = Result<B256, AccessListTransactionDecodeError>;
222
223 fn next(&mut self) -> Option<Self::Item> {
224 self.items
225 .next()
226 .map(|item| decode_storage_key_item(item).map_err(map_access_list_decode_error))
227 }
228}
229
230impl core::iter::FusedIterator for AccessListStorageKeyItems<'_> {}
231
232#[non_exhaustive]
234#[derive(Clone, Copy, Debug, Eq, PartialEq)]
235pub enum AccessListTransactionField {
236 Payload,
238 ChainId,
240 Nonce,
242 GasPrice,
244 GasLimit,
246 To,
248 Value,
250 Data,
252 AccessList,
254 SignatureYParity,
256 SignatureR,
258 SignatureS,
260}
261
262pub fn decode_access_list_transaction<'a>(
264 input: &'a [u8],
265 limits: DecodeLimits,
266) -> Result<UnvalidatedAccessListTransaction<'a>, AccessListTransactionDecodeError> {
267 match decode_transaction_envelope(input, limits)
268 .map_err(AccessListTransactionDecodeError::Envelope)?
269 {
270 TransactionEnvelope::Typed(typed)
271 if typed.transaction_type.get() == ACCESS_LIST_TRANSACTION_TYPE =>
272 {
273 decode_access_list_payload(typed.payload, limits)
274 }
275 TransactionEnvelope::Typed(typed) => {
276 Err(AccessListTransactionDecodeError::WrongTransactionType {
277 type_byte: typed.transaction_type.get(),
278 })
279 }
280 TransactionEnvelope::Legacy(_) => {
281 Err(AccessListTransactionDecodeError::WrongTransactionType { type_byte: 0 })
282 }
283 }
284}
285
286fn decode_access_list_payload<'a>(
287 payload: &'a [u8],
288 limits: DecodeLimits,
289) -> Result<UnvalidatedAccessListTransaction<'a>, AccessListTransactionDecodeError> {
290 let list = eth_valkyoth_codec::decode_rlp_list(payload, limits)
291 .map_err(|source| field_error(AccessListTransactionField::Payload, source))?;
292 if list.item_count() != ACCESS_LIST_TRANSACTION_FIELD_COUNT {
293 return Err(AccessListTransactionDecodeError::WrongFieldCount {
294 expected: ACCESS_LIST_TRANSACTION_FIELD_COUNT,
295 found: list.item_count(),
296 });
297 }
298
299 let mut fields = list.items();
300 let chain_id = decode_shared_chain_id(
301 &mut fields,
302 AccessListTransactionField::ChainId,
303 field_error,
304 )?;
305 let nonce = Nonce::new(decode_shared_u64_field(
306 &mut fields,
307 AccessListTransactionField::Nonce,
308 field_error,
309 )?);
310 let gas_price = Wei::from_be_bytes(decode_shared_u256_field(
311 &mut fields,
312 AccessListTransactionField::GasPrice,
313 field_error,
314 )?);
315 let gas_limit = Gas::new(decode_shared_u64_field(
316 &mut fields,
317 AccessListTransactionField::GasLimit,
318 field_error,
319 )?);
320 let to = decode_shared_to(
321 next_shared_scalar(&mut fields, AccessListTransactionField::To, field_error)?,
322 |found| AccessListTransactionDecodeError::InvalidToLength { found },
323 )?;
324 let value = Wei::from_be_bytes(decode_shared_u256_field(
325 &mut fields,
326 AccessListTransactionField::Value,
327 field_error,
328 )?);
329 let input =
330 next_shared_scalar(&mut fields, AccessListTransactionField::Data, field_error)?.payload();
331 limits
332 .check_single_allocation_limit(input.len())
333 .map_err(|source| field_error(AccessListTransactionField::Data, source))?;
334 let access_list = decode_access_list(next_shared_list(
335 &mut fields,
336 AccessListTransactionField::AccessList,
337 field_error,
338 )?)
339 .map_err(map_access_list_decode_error)?;
340 let y_parity = SignatureYParity::try_new(decode_shared_u64_field(
341 &mut fields,
342 AccessListTransactionField::SignatureYParity,
343 field_error,
344 )?)
345 .map_err(|error| AccessListTransactionDecodeError::InvalidYParity {
346 value: error.value(),
347 })?;
348 let r = decode_shared_u256_field(
349 &mut fields,
350 AccessListTransactionField::SignatureR,
351 field_error,
352 )?;
353 let s = decode_shared_u256_field(
354 &mut fields,
355 AccessListTransactionField::SignatureS,
356 field_error,
357 )?;
358
359 Ok(UnvalidatedAccessListTransaction {
360 chain_id,
361 nonce,
362 gas_price,
363 gas_limit,
364 to,
365 value,
366 input,
367 access_list,
368 y_parity,
369 r,
370 s,
371 })
372}
373
374pub(crate) fn decode_access_list(
375 list: RlpList<'_>,
376) -> Result<AccessList<'_>, AccessListDecodeError> {
377 let mut storage_key_count = 0usize;
381 for item in list.items() {
382 let entry = decode_access_list_entry_item(item)?;
383 storage_key_count =
384 eth_valkyoth_codec::checked_len_add(storage_key_count, entry.storage_keys.len())
385 .map_err(AccessListDecodeError::FieldDecode)?;
386 }
387 Ok(AccessList {
388 list,
389 storage_key_count,
390 })
391}
392
393fn decode_access_list_entry_item(
394 item: Result<RlpItem<'_>, DecodeError>,
395) -> Result<AccessListEntry<'_>, AccessListDecodeError> {
396 let item = item.map_err(AccessListDecodeError::FieldDecode)?;
397 let RlpItem::List(list) = item else {
398 return Err(AccessListDecodeError::FieldDecode(
399 DecodeError::UnexpectedScalar,
400 ));
401 };
402 if list.item_count() != ACCESS_LIST_ENTRY_FIELD_COUNT {
403 return Err(AccessListDecodeError::InvalidAccessListEntryFieldCount {
404 found: list.item_count(),
405 });
406 }
407
408 let mut fields = list.items();
409 let address = decode_access_list_address(next_shared_scalar(
410 &mut fields,
411 AccessListTransactionField::AccessList,
412 |_, source| AccessListDecodeError::FieldDecode(source),
413 )?)?;
414 let storage_keys = AccessListStorageKeys {
415 list: next_shared_list(
416 &mut fields,
417 AccessListTransactionField::AccessList,
418 |_, source| AccessListDecodeError::FieldDecode(source),
419 )?,
420 };
421
422 for key in storage_keys.list.items() {
423 let _ = decode_storage_key_item(key)?;
424 }
425 Ok(AccessListEntry {
426 address,
427 storage_keys,
428 })
429}
430
431fn decode_storage_key_item(
432 item: Result<RlpItem<'_>, DecodeError>,
433) -> Result<B256, AccessListDecodeError> {
434 let item = item.map_err(AccessListDecodeError::FieldDecode)?;
435 let RlpItem::Scalar(scalar) = item else {
436 return Err(AccessListDecodeError::FieldDecode(
437 DecodeError::UnexpectedList,
438 ));
439 };
440 let found = scalar.payload().len();
441 let bytes: [u8; B256_BYTES] = scalar
442 .payload()
443 .try_into()
444 .map_err(|_| AccessListDecodeError::InvalidStorageKeyLength { found })?;
445 Ok(B256::from_bytes(bytes))
446}
447
448fn decode_access_list_address(scalar: RlpScalar<'_>) -> Result<Address, AccessListDecodeError> {
449 let found = scalar.payload().len();
450 let bytes: [u8; ADDRESS_BYTES] = scalar
451 .payload()
452 .try_into()
453 .map_err(|_| AccessListDecodeError::InvalidAccessListAddressLength { found })?;
454 Ok(Address::from_bytes(bytes))
455}
456
457const fn field_error(
458 field: AccessListTransactionField,
459 source: DecodeError,
460) -> AccessListTransactionDecodeError {
461 AccessListTransactionDecodeError::FieldDecode { field, source }
462}
463
464#[derive(Clone, Copy, Debug, Eq, PartialEq)]
466pub(crate) enum AccessListDecodeError {
467 FieldDecode(DecodeError),
468 InvalidAccessListEntryFieldCount { found: usize },
469 InvalidAccessListAddressLength { found: usize },
470 InvalidStorageKeyLength { found: usize },
471}
472
473pub(crate) const fn map_access_list_decode_error(
474 error: AccessListDecodeError,
475) -> AccessListTransactionDecodeError {
476 match error {
477 AccessListDecodeError::FieldDecode(source) => {
478 field_error(AccessListTransactionField::AccessList, source)
479 }
480 AccessListDecodeError::InvalidAccessListEntryFieldCount { found } => {
481 AccessListTransactionDecodeError::InvalidAccessListEntryFieldCount { found }
482 }
483 AccessListDecodeError::InvalidAccessListAddressLength { found } => {
484 AccessListTransactionDecodeError::InvalidAccessListAddressLength { found }
485 }
486 AccessListDecodeError::InvalidStorageKeyLength { found } => {
487 AccessListTransactionDecodeError::InvalidStorageKeyLength { found }
488 }
489 }
490}