Skip to content

Commit fe1aeed

Browse files
authored
feat: add EIP4844 transactions (#140)
This PR is based on #138 (as they modify the same code), please merge it first **Motivation** Add support for EIP4844 transactions <!-- Why does this pull request exist? What are its goals? --> **Description** Add `EIP4844` transactions + needed trait and methods and integrate it into existing transaction-related code <!-- A clear and concise general description of the changes this PR introduces --> <!-- Link to issues: Resolves #111, Resolves #222 --> Closes #26 (evm already performs validations before executing so adding the transaction itself will sufice)
1 parent 6e0c299 commit fe1aeed

File tree

4 files changed

+152
-7
lines changed

4 files changed

+152
-7
lines changed

crates/core/src/evm/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use super::{
99
use revm::{
1010
inspector_handle_register,
1111
inspectors::TracerEip3155,
12-
primitives::{BlockEnv, Bytecode, TxEnv, U256},
12+
primitives::{BlockEnv, Bytecode, TxEnv, B256, U256},
1313
CacheState, Evm,
1414
};
1515
use std::collections::HashMap;
@@ -101,7 +101,12 @@ fn tx_env(tx: &Transaction) -> TxEnv {
101101
})
102102
.collect(),
103103
gas_priority_fee: tx.max_priority_fee().map(U256::from),
104-
..Default::default()
104+
blob_hashes: tx
105+
.blob_versioned_hashes()
106+
.into_iter()
107+
.map(|hash| B256::from(hash.0))
108+
.collect(),
109+
max_fee_per_blob_gas: tx.max_fee_per_blob_gas().map(|x| U256::from(x)),
105110
}
106111
}
107112

crates/core/src/types/engine/payload.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{rlp::error::RLPDecodeError, serde_utils};
88

99
use crate::types::{
1010
compute_withdrawals_root, BlockBody, BlockHeader, EIP1559Transaction, EIP2930Transaction,
11-
LegacyTransaction, Transaction, Withdrawal, DEFAULT_OMMERS_HASH,
11+
EIP4844Transaction, LegacyTransaction, Transaction, Withdrawal, DEFAULT_OMMERS_HASH,
1212
};
1313

1414
#[allow(unused)]
@@ -81,7 +81,13 @@ impl EncodedTransaction {
8181
0x2 => {
8282
EIP1559Transaction::decode(tx_bytes).map(Transaction::EIP1559Transaction)
8383
}
84-
_ => unimplemented!("We don't know this tx type yet"),
84+
// EIP4844
85+
0x3 => {
86+
EIP4844Transaction::decode(tx_bytes).map(Transaction::EIP4844Transaction)
87+
}
88+
ty => Err(RLPDecodeError::Custom(format!(
89+
"Invalid transaction type: {ty}"
90+
))),
8591
}
8692
}
8793
// LegacyTransaction

crates/core/src/types/transaction.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub enum Transaction {
1616
LegacyTransaction(LegacyTransaction),
1717
EIP2930Transaction(EIP2930Transaction),
1818
EIP1559Transaction(EIP1559Transaction),
19+
EIP4844Transaction(EIP4844Transaction),
1920
}
2021

2122
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -64,6 +65,24 @@ pub struct EIP1559Transaction {
6465
pub signature_s: U256,
6566
}
6667

68+
#[derive(Clone, Debug, PartialEq, Eq)]
69+
pub struct EIP4844Transaction {
70+
pub chain_id: u64,
71+
pub nonce: u64,
72+
pub max_priority_fee_per_gas: u64,
73+
pub max_fee_per_gas: u64,
74+
pub gas: u64,
75+
pub to: Address,
76+
pub value: U256,
77+
pub data: Bytes,
78+
pub access_list: Vec<(Address, Vec<H256>)>,
79+
pub max_fee_per_blob_gas: u64,
80+
pub blob_versioned_hashes: Vec<H256>,
81+
pub signature_y_parity: bool,
82+
pub signature_r: U256,
83+
pub signature_s: U256,
84+
}
85+
6786
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6887
pub enum TxType {
6988
Legacy = 0x00,
@@ -90,6 +109,7 @@ impl Transaction {
90109
Transaction::LegacyTransaction(_) => TxType::Legacy,
91110
Transaction::EIP2930Transaction(_) => TxType::EIP2930,
92111
Transaction::EIP1559Transaction(_) => TxType::EIP1559,
112+
Transaction::EIP4844Transaction(_) => TxType::EIP4844,
93113
}
94114
}
95115
}
@@ -100,6 +120,7 @@ impl RLPEncode for Transaction {
100120
Transaction::LegacyTransaction(t) => t.encode(buf),
101121
Transaction::EIP2930Transaction(t) => t.encode(buf),
102122
Transaction::EIP1559Transaction(t) => t.encode(buf),
123+
Transaction::EIP4844Transaction(t) => t.encode(buf),
103124
};
104125
}
105126
}
@@ -183,6 +204,27 @@ impl RLPEncode for EIP1559Transaction {
183204
}
184205
}
185206

207+
impl RLPEncode for EIP4844Transaction {
208+
fn encode(&self, buf: &mut dyn bytes::BufMut) {
209+
Encoder::new(buf)
210+
.encode_field(&self.chain_id)
211+
.encode_field(&self.nonce)
212+
.encode_field(&self.max_priority_fee_per_gas)
213+
.encode_field(&self.max_fee_per_gas)
214+
.encode_field(&self.gas)
215+
.encode_field(&self.to)
216+
.encode_field(&self.value)
217+
.encode_field(&self.data)
218+
.encode_field(&self.access_list)
219+
.encode_field(&self.max_fee_per_blob_gas)
220+
.encode_field(&self.blob_versioned_hashes)
221+
.encode_field(&self.signature_y_parity)
222+
.encode_field(&self.signature_r)
223+
.encode_field(&self.signature_s)
224+
.finish();
225+
}
226+
}
227+
186228
impl RLPDecode for LegacyTransaction {
187229
fn decode_unfinished(rlp: &[u8]) -> Result<(LegacyTransaction, &[u8]), RLPDecodeError> {
188230
let decoder = Decoder::new(rlp)?;
@@ -278,6 +320,45 @@ impl RLPDecode for EIP1559Transaction {
278320
}
279321
}
280322

323+
impl RLPDecode for EIP4844Transaction {
324+
fn decode_unfinished(rlp: &[u8]) -> Result<(EIP4844Transaction, &[u8]), RLPDecodeError> {
325+
let decoder = Decoder::new(rlp)?;
326+
let (chain_id, decoder) = decoder.decode_field("chain_id")?;
327+
let (nonce, decoder) = decoder.decode_field("nonce")?;
328+
let (max_priority_fee_per_gas, decoder) =
329+
decoder.decode_field("max_priority_fee_per_gas")?;
330+
let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
331+
let (gas, decoder) = decoder.decode_field("gas")?;
332+
let (to, decoder) = decoder.decode_field("to")?;
333+
let (value, decoder) = decoder.decode_field("value")?;
334+
let (data, decoder) = decoder.decode_field("data")?;
335+
let (access_list, decoder) = decoder.decode_field("access_list")?;
336+
let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
337+
let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
338+
let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
339+
let (signature_r, decoder) = decoder.decode_field("signature_r")?;
340+
let (signature_s, decoder) = decoder.decode_field("signature_s")?;
341+
342+
let tx = EIP4844Transaction {
343+
chain_id,
344+
nonce,
345+
max_priority_fee_per_gas,
346+
max_fee_per_gas,
347+
gas,
348+
to,
349+
value,
350+
data,
351+
access_list,
352+
max_fee_per_blob_gas,
353+
blob_versioned_hashes,
354+
signature_y_parity,
355+
signature_r,
356+
signature_s,
357+
};
358+
Ok((tx, decoder.finish()?))
359+
}
360+
}
361+
281362
impl Transaction {
282363
pub fn sender(&self) -> Address {
283364
match self {
@@ -338,6 +419,28 @@ impl Transaction {
338419
&Bytes::from(buf),
339420
)
340421
}
422+
Transaction::EIP4844Transaction(tx) => {
423+
let mut buf = vec![self.tx_type() as u8];
424+
Encoder::new(&mut buf)
425+
.encode_field(&tx.chain_id)
426+
.encode_field(&tx.nonce)
427+
.encode_field(&tx.max_priority_fee_per_gas)
428+
.encode_field(&tx.max_fee_per_gas)
429+
.encode_field(&tx.gas)
430+
.encode_field(&tx.to)
431+
.encode_field(&tx.value)
432+
.encode_field(&tx.data)
433+
.encode_field(&tx.access_list)
434+
.encode_field(&tx.max_fee_per_blob_gas)
435+
.encode_field(&tx.blob_versioned_hashes)
436+
.finish();
437+
recover_address(
438+
&tx.signature_r,
439+
&tx.signature_s,
440+
tx.signature_y_parity,
441+
&Bytes::from(buf),
442+
)
443+
}
341444
}
342445
}
343446

@@ -346,6 +449,7 @@ impl Transaction {
346449
Transaction::LegacyTransaction(tx) => tx.gas,
347450
Transaction::EIP2930Transaction(tx) => tx.gas_limit,
348451
Transaction::EIP1559Transaction(tx) => tx.gas_limit,
452+
Transaction::EIP4844Transaction(tx) => tx.gas,
349453
}
350454
}
351455

@@ -354,6 +458,7 @@ impl Transaction {
354458
Transaction::LegacyTransaction(tx) => tx.gas_price,
355459
Transaction::EIP2930Transaction(tx) => tx.gas_price,
356460
Transaction::EIP1559Transaction(tx) => tx.max_fee_per_gas,
461+
Transaction::EIP4844Transaction(tx) => tx.max_fee_per_gas,
357462
}
358463
}
359464

@@ -362,6 +467,7 @@ impl Transaction {
362467
Transaction::LegacyTransaction(tx) => tx.to.clone(),
363468
Transaction::EIP2930Transaction(tx) => tx.to.clone(),
364469
Transaction::EIP1559Transaction(tx) => TxKind::Call(tx.destination),
470+
Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
365471
}
366472
}
367473

@@ -370,6 +476,7 @@ impl Transaction {
370476
Transaction::LegacyTransaction(tx) => tx.value,
371477
Transaction::EIP2930Transaction(tx) => tx.value,
372478
Transaction::EIP1559Transaction(tx) => tx.amount,
479+
Transaction::EIP4844Transaction(tx) => tx.value,
373480
}
374481
}
375482

@@ -378,6 +485,7 @@ impl Transaction {
378485
Transaction::LegacyTransaction(_tx) => None,
379486
Transaction::EIP2930Transaction(_tx) => None,
380487
Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
488+
Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
381489
}
382490
}
383491

@@ -386,6 +494,7 @@ impl Transaction {
386494
Transaction::LegacyTransaction(_tx) => None,
387495
Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
388496
Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
497+
Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
389498
}
390499
}
391500

@@ -394,6 +503,7 @@ impl Transaction {
394503
Transaction::LegacyTransaction(_tx) => Vec::new(),
395504
Transaction::EIP2930Transaction(tx) => tx.access_list.clone(),
396505
Transaction::EIP1559Transaction(tx) => tx.access_list.clone(),
506+
Transaction::EIP4844Transaction(tx) => tx.access_list.clone(),
397507
}
398508
}
399509

@@ -402,6 +512,7 @@ impl Transaction {
402512
Transaction::LegacyTransaction(tx) => tx.nonce,
403513
Transaction::EIP2930Transaction(tx) => tx.nonce,
404514
Transaction::EIP1559Transaction(tx) => tx.signer_nonce,
515+
Transaction::EIP4844Transaction(tx) => tx.nonce,
405516
}
406517
}
407518

@@ -410,6 +521,25 @@ impl Transaction {
410521
Transaction::LegacyTransaction(tx) => &tx.data,
411522
Transaction::EIP2930Transaction(tx) => &tx.data,
412523
Transaction::EIP1559Transaction(tx) => &tx.payload,
524+
Transaction::EIP4844Transaction(tx) => &tx.data,
525+
}
526+
}
527+
528+
pub fn blob_versioned_hashes(&self) -> Vec<H256> {
529+
match self {
530+
Transaction::LegacyTransaction(_tx) => Vec::new(),
531+
Transaction::EIP2930Transaction(_tx) => Vec::new(),
532+
Transaction::EIP1559Transaction(_tx) => Vec::new(),
533+
Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
534+
}
535+
}
536+
537+
pub fn max_fee_per_blob_gas(&self) -> Option<u64> {
538+
match self {
539+
Transaction::LegacyTransaction(_tx) => None,
540+
Transaction::EIP2930Transaction(_tx) => None,
541+
Transaction::EIP1559Transaction(_tx) => None,
542+
Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
413543
}
414544
}
415545
}

crates/rpc/src/engine/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub fn new_payload_v3(request: NewPayloadV3Request) -> Result<PayloadStatus, Rpc
3535

3636
info!("Received new payload with block hash: {}", block_hash);
3737

38-
let (block_header, _block_body) =
38+
let (block_header, block_body) =
3939
match request.payload.into_block(request.parent_beacon_block_root) {
4040
Ok(block) => block,
4141
Err(error) => {
@@ -66,8 +66,12 @@ pub fn new_payload_v3(request: NewPayloadV3Request) -> Result<PayloadStatus, Rpc
6666
info!("Block hash {} is valid", block_hash);
6767
// Concatenate blob versioned hashes lists (tx.blob_versioned_hashes) of each blob transaction included in the payload, respecting the order of inclusion
6868
// and check that the resulting array matches expected_blob_versioned_hashes
69-
// As we don't curretly handle blob txs, we just check that it is empty
70-
if !request.expected_blob_versioned_hashes.is_empty() {
69+
let blob_versioned_hashes: Vec<H256> = block_body
70+
.transactions
71+
.iter()
72+
.flat_map(|tx| tx.blob_versioned_hashes())
73+
.collect();
74+
if request.expected_blob_versioned_hashes != blob_versioned_hashes {
7175
return Ok(PayloadStatus {
7276
status: PayloadValidationStatus::Invalid,
7377
latest_valid_hash: None,

0 commit comments

Comments
 (0)