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