1use eth_valkyoth_codec::{DecodeError, DecodeLimits, RlpInteger, RlpItem, RlpList, RlpScalar};
2use eth_valkyoth_primitives::{Address, ChainId, Gas, Nonce, Wei};
3
4use super::access_list::{AccessListDecodeError, decode_access_list};
5use super::{AccessList, SignatureYParity, TransactionEnvelope, decode_transaction_envelope};
6use crate::transaction::fields::{
7 ADDRESS_BYTES, decode_chain_id as decode_shared_chain_id, decode_required_address,
8 decode_u64_field as decode_shared_u64_field, decode_u256_field as decode_shared_u256_field,
9 next_list as next_shared_list, next_scalar as next_shared_scalar,
10};
11
12mod error;
13
14pub use error::{
15 SetCodeAuthorizationField, SetCodeTransactionDecodeError, SetCodeTransactionDecodeErrorCategory,
16};
17
18pub const SET_CODE_TRANSACTION_TYPE: u8 = 0x04;
20pub const SET_CODE_TRANSACTION_FIELD_COUNT: usize = 13;
22pub const SET_CODE_AUTHORIZATION_FIELD_COUNT: usize = 6;
24
25#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32pub struct UnvalidatedSetCodeTransaction<'a> {
33 pub chain_id: ChainId,
35 pub nonce: Nonce,
37 pub max_priority_fee_per_gas: Wei,
39 pub max_fee_per_gas: Wei,
41 pub gas_limit: Gas,
43 pub to: Address,
45 pub value: Wei,
47 pub input: &'a [u8],
49 pub access_list: AccessList<'a>,
51 pub authorization_list: SetCodeAuthorizationList<'a>,
53 pub y_parity: SignatureYParity,
55 pub r: [u8; 32],
59 pub s: [u8; 32],
64}
65
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
72pub struct SetCodeAuthorizationChainId {
73 bytes: [u8; 32],
74}
75
76impl SetCodeAuthorizationChainId {
77 #[must_use]
79 pub const fn from_be_bytes(bytes: [u8; 32]) -> Self {
80 Self { bytes }
81 }
82
83 #[must_use]
85 pub const fn to_be_bytes(self) -> [u8; 32] {
86 self.bytes
87 }
88
89 #[must_use]
91 pub fn is_universal(self) -> bool {
92 self.bytes.iter().all(|byte| *byte == 0)
93 }
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98pub struct SetCodeAuthorizationList<'a> {
99 list: RlpList<'a>,
100}
101
102impl<'a> SetCodeAuthorizationList<'a> {
103 #[must_use]
105 pub const fn len(self) -> usize {
106 self.list.item_count()
107 }
108
109 #[must_use]
115 pub const fn is_empty(self) -> bool {
116 self.list.is_empty()
117 }
118
119 #[must_use]
125 pub const fn authorizations(self) -> SetCodeAuthorizationItems<'a> {
126 SetCodeAuthorizationItems {
127 items: self.list.items(),
128 }
129 }
130
131 pub(crate) const fn encoded_rlp_len(self) -> usize {
133 self.list.encoded_len()
134 }
135
136 pub(crate) fn encode_rlp(self, output: &mut [u8]) -> Result<usize, DecodeError> {
138 eth_valkyoth_codec::encode_decoded_list(self.list, output)
139 }
140}
141
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub struct SetCodeAuthorization {
145 pub chain_id: SetCodeAuthorizationChainId,
147 pub address: Address,
149 pub nonce: Nonce,
151 pub y_parity: SignatureYParity,
153 pub r: [u8; 32],
155 pub s: [u8; 32],
157}
158
159#[derive(Clone, Debug)]
161pub struct SetCodeAuthorizationItems<'a> {
162 items: eth_valkyoth_codec::RlpListItems<'a>,
163}
164
165impl Iterator for SetCodeAuthorizationItems<'_> {
166 type Item = Result<SetCodeAuthorization, SetCodeTransactionDecodeError>;
167
168 fn next(&mut self) -> Option<Self::Item> {
169 self.items
170 .next()
171 .map(|item| decode_authorization_item(item).map_err(map_authorization_error))
172 }
173}
174
175impl core::iter::FusedIterator for SetCodeAuthorizationItems<'_> {}
176
177#[non_exhaustive]
179#[derive(Clone, Copy, Debug, Eq, PartialEq)]
180pub enum SetCodeTransactionField {
181 Payload,
183 ChainId,
185 Nonce,
187 MaxPriorityFeePerGas,
189 MaxFeePerGas,
191 GasLimit,
193 To,
195 Value,
197 Data,
199 AccessList,
201 AuthorizationList,
203 SignatureYParity,
205 SignatureR,
207 SignatureS,
209}
210
211pub fn decode_set_code_transaction<'a>(
213 input: &'a [u8],
214 limits: DecodeLimits,
215) -> Result<UnvalidatedSetCodeTransaction<'a>, SetCodeTransactionDecodeError> {
216 match decode_transaction_envelope(input, limits)
217 .map_err(SetCodeTransactionDecodeError::Envelope)?
218 {
219 TransactionEnvelope::Typed(typed)
220 if typed.transaction_type.get() == SET_CODE_TRANSACTION_TYPE =>
221 {
222 decode_set_code_payload(typed.payload, limits)
223 }
224 TransactionEnvelope::Typed(typed) => {
225 Err(SetCodeTransactionDecodeError::WrongTransactionType {
226 type_byte: typed.transaction_type.get(),
227 })
228 }
229 TransactionEnvelope::Legacy(_) => {
230 Err(SetCodeTransactionDecodeError::WrongTransactionType { type_byte: 0 })
231 }
232 }
233}
234
235fn decode_set_code_payload<'a>(
236 payload: &'a [u8],
237 limits: DecodeLimits,
238) -> Result<UnvalidatedSetCodeTransaction<'a>, SetCodeTransactionDecodeError> {
239 let list = eth_valkyoth_codec::decode_rlp_list(payload, limits)
240 .map_err(|source| field_error(SetCodeTransactionField::Payload, source))?;
241 if list.item_count() != SET_CODE_TRANSACTION_FIELD_COUNT {
242 return Err(SetCodeTransactionDecodeError::WrongFieldCount {
243 expected: SET_CODE_TRANSACTION_FIELD_COUNT,
244 found: list.item_count(),
245 });
246 }
247
248 let mut fields = list.items();
249 let chain_id =
250 decode_shared_chain_id(&mut fields, SetCodeTransactionField::ChainId, field_error)?;
251 let nonce = Nonce::new(decode_shared_u64_field(
252 &mut fields,
253 SetCodeTransactionField::Nonce,
254 field_error,
255 )?);
256 let max_priority_fee_per_gas = Wei::from_be_bytes(decode_shared_u256_field(
257 &mut fields,
258 SetCodeTransactionField::MaxPriorityFeePerGas,
259 field_error,
260 )?);
261 let max_fee_per_gas = Wei::from_be_bytes(decode_shared_u256_field(
262 &mut fields,
263 SetCodeTransactionField::MaxFeePerGas,
264 field_error,
265 )?);
266 let gas_limit = Gas::new(decode_shared_u64_field(
267 &mut fields,
268 SetCodeTransactionField::GasLimit,
269 field_error,
270 )?);
271 let to = decode_required_address(
272 next_shared_scalar(&mut fields, SetCodeTransactionField::To, field_error)?,
273 |found| SetCodeTransactionDecodeError::InvalidToLength { found },
274 )?;
275 let value = Wei::from_be_bytes(decode_shared_u256_field(
276 &mut fields,
277 SetCodeTransactionField::Value,
278 field_error,
279 )?);
280 let input =
281 next_shared_scalar(&mut fields, SetCodeTransactionField::Data, field_error)?.payload();
282 limits
283 .check_single_allocation_limit(input.len())
284 .map_err(|source| field_error(SetCodeTransactionField::Data, source))?;
285 let access_list = decode_access_list(next_shared_list(
286 &mut fields,
287 SetCodeTransactionField::AccessList,
288 field_error,
289 )?)
290 .map_err(map_access_list_error)?;
291 let authorization_list = decode_authorization_list(next_shared_list(
292 &mut fields,
293 SetCodeTransactionField::AuthorizationList,
294 field_error,
295 )?)
296 .map_err(map_authorization_error)?;
297 let y_parity = SignatureYParity::try_new(decode_shared_u64_field(
298 &mut fields,
299 SetCodeTransactionField::SignatureYParity,
300 field_error,
301 )?)
302 .map_err(|error| SetCodeTransactionDecodeError::InvalidYParity {
303 value: error.value(),
304 })?;
305 let r = decode_shared_u256_field(
306 &mut fields,
307 SetCodeTransactionField::SignatureR,
308 field_error,
309 )?;
310 let s = decode_shared_u256_field(
311 &mut fields,
312 SetCodeTransactionField::SignatureS,
313 field_error,
314 )?;
315
316 Ok(UnvalidatedSetCodeTransaction {
317 chain_id,
318 nonce,
319 max_priority_fee_per_gas,
320 max_fee_per_gas,
321 gas_limit,
322 to,
323 value,
324 input,
325 access_list,
326 authorization_list,
327 y_parity,
328 r,
329 s,
330 })
331}
332
333fn decode_authorization_list(
334 list: RlpList<'_>,
335) -> Result<SetCodeAuthorizationList<'_>, SetCodeAuthorizationDecodeError> {
336 for item in list.items() {
337 let _ = decode_authorization_item(item)?;
338 }
339 Ok(SetCodeAuthorizationList { list })
340}
341
342fn decode_authorization_item(
343 item: Result<RlpItem<'_>, DecodeError>,
344) -> Result<SetCodeAuthorization, SetCodeAuthorizationDecodeError> {
345 let item = item.map_err(SetCodeAuthorizationDecodeError::TupleDecode)?;
346 let RlpItem::List(list) = item else {
347 return Err(SetCodeAuthorizationDecodeError::TupleDecode(
348 DecodeError::UnexpectedScalar,
349 ));
350 };
351 if list.item_count() != SET_CODE_AUTHORIZATION_FIELD_COUNT {
352 return Err(
353 SetCodeAuthorizationDecodeError::InvalidAuthorizationFieldCount {
354 found: list.item_count(),
355 },
356 );
357 }
358
359 let mut fields = list.items();
360 let chain_id = decode_authorization_chain_id(next_shared_scalar(
361 &mut fields,
362 SetCodeTransactionField::AuthorizationList,
363 |_, source| auth_field_error(SetCodeAuthorizationField::ChainId, source),
364 )?)?;
365 let address = decode_authorization_address(next_shared_scalar(
366 &mut fields,
367 SetCodeTransactionField::AuthorizationList,
368 |_, source| auth_field_error(SetCodeAuthorizationField::Address, source),
369 )?)?;
370 let nonce = Nonce::new(decode_shared_u64_field(
371 &mut fields,
372 SetCodeTransactionField::AuthorizationList,
373 |_, source| auth_field_error(SetCodeAuthorizationField::Nonce, source),
374 )?);
375 let y_parity = SignatureYParity::try_new(decode_shared_u64_field(
376 &mut fields,
377 SetCodeTransactionField::AuthorizationList,
378 |_, source| auth_field_error(SetCodeAuthorizationField::YParity, source),
379 )?)
380 .map_err(
381 |error| SetCodeAuthorizationDecodeError::InvalidAuthorizationYParity {
382 value: error.value(),
383 },
384 )?;
385 let r = decode_shared_u256_field(
386 &mut fields,
387 SetCodeTransactionField::AuthorizationList,
388 |_, source| auth_field_error(SetCodeAuthorizationField::R, source),
389 )?;
390 let s = decode_shared_u256_field(
391 &mut fields,
392 SetCodeTransactionField::AuthorizationList,
393 |_, source| auth_field_error(SetCodeAuthorizationField::S, source),
394 )?;
395
396 Ok(SetCodeAuthorization {
397 chain_id,
398 address,
399 nonce,
400 y_parity,
401 r,
402 s,
403 })
404}
405
406fn decode_authorization_chain_id(
407 scalar: RlpScalar<'_>,
408) -> Result<SetCodeAuthorizationChainId, SetCodeAuthorizationDecodeError> {
409 let bytes = RlpInteger::try_from_scalar(scalar)
410 .and_then(RlpInteger::to_be_bytes32)
411 .map_err(|source| auth_field_error(SetCodeAuthorizationField::ChainId, source))?;
412 Ok(SetCodeAuthorizationChainId::from_be_bytes(bytes))
413}
414
415fn decode_authorization_address(
416 scalar: RlpScalar<'_>,
417) -> Result<Address, SetCodeAuthorizationDecodeError> {
418 let found = scalar.payload().len();
419 let bytes: [u8; ADDRESS_BYTES] = scalar.payload().try_into().map_err(|_| {
420 SetCodeAuthorizationDecodeError::InvalidAuthorizationAddressLength { found }
421 })?;
422 Ok(Address::from_bytes(bytes))
423}
424
425const fn auth_field_error(
426 field: SetCodeAuthorizationField,
427 source: DecodeError,
428) -> SetCodeAuthorizationDecodeError {
429 SetCodeAuthorizationDecodeError::FieldDecode { field, source }
430}
431
432const fn field_error(
433 field: SetCodeTransactionField,
434 source: DecodeError,
435) -> SetCodeTransactionDecodeError {
436 SetCodeTransactionDecodeError::FieldDecode { field, source }
437}
438
439const fn map_access_list_error(error: AccessListDecodeError) -> SetCodeTransactionDecodeError {
440 match error {
441 AccessListDecodeError::FieldDecode(source) => {
442 field_error(SetCodeTransactionField::AccessList, source)
443 }
444 AccessListDecodeError::InvalidAccessListEntryFieldCount { found } => {
445 SetCodeTransactionDecodeError::InvalidAccessListEntryFieldCount { found }
446 }
447 AccessListDecodeError::InvalidAccessListAddressLength { found } => {
448 SetCodeTransactionDecodeError::InvalidAccessListAddressLength { found }
449 }
450 AccessListDecodeError::InvalidStorageKeyLength { found } => {
451 SetCodeTransactionDecodeError::InvalidStorageKeyLength { found }
452 }
453 }
454}
455
456#[derive(Clone, Copy, Debug, Eq, PartialEq)]
457enum SetCodeAuthorizationDecodeError {
458 TupleDecode(DecodeError),
459 FieldDecode {
460 field: SetCodeAuthorizationField,
461 source: DecodeError,
462 },
463 InvalidAuthorizationFieldCount {
464 found: usize,
465 },
466 InvalidAuthorizationAddressLength {
467 found: usize,
468 },
469 InvalidAuthorizationYParity {
470 value: u64,
471 },
472}
473
474const fn map_authorization_error(
475 error: SetCodeAuthorizationDecodeError,
476) -> SetCodeTransactionDecodeError {
477 match error {
478 SetCodeAuthorizationDecodeError::TupleDecode(source) => {
479 field_error(SetCodeTransactionField::AuthorizationList, source)
480 }
481 SetCodeAuthorizationDecodeError::FieldDecode { field, source } => {
482 SetCodeTransactionDecodeError::AuthorizationFieldDecode { field, source }
483 }
484 SetCodeAuthorizationDecodeError::InvalidAuthorizationFieldCount { found } => {
485 SetCodeTransactionDecodeError::InvalidAuthorizationFieldCount { found }
486 }
487 SetCodeAuthorizationDecodeError::InvalidAuthorizationAddressLength { found } => {
488 SetCodeTransactionDecodeError::InvalidAuthorizationAddressLength { found }
489 }
490 SetCodeAuthorizationDecodeError::InvalidAuthorizationYParity { value } => {
491 SetCodeTransactionDecodeError::InvalidAuthorizationYParity { value }
492 }
493 }
494}