Skip to content

Commit

Permalink
feat: add EIP4844 transactions (lambdaclass#140)
Browse files Browse the repository at this point in the history
This PR is based on lambdaclass#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 lambdaclass#111, Resolves lambdaclass#222 -->

Closes lambdaclass#26 (evm already performs validations before executing so adding
the transaction itself will sufice)
  • Loading branch information
fmoletta authored Jul 12, 2024
1 parent 7f8f7da commit f0c69f3
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 7 deletions.
9 changes: 7 additions & 2 deletions crates/core/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::{
use revm::{
inspector_handle_register,
inspectors::TracerEip3155,
primitives::{BlockEnv, Bytecode, TxEnv, U256},
primitives::{BlockEnv, Bytecode, TxEnv, B256, U256},
CacheState, Evm,
};
use std::collections::HashMap;
Expand Down Expand Up @@ -101,7 +101,12 @@ fn tx_env(tx: &Transaction) -> TxEnv {
})
.collect(),
gas_priority_fee: tx.max_priority_fee().map(U256::from),
..Default::default()
blob_hashes: tx
.blob_versioned_hashes()
.into_iter()
.map(|hash| B256::from(hash.0))
.collect(),
max_fee_per_blob_gas: tx.max_fee_per_blob_gas().map(|x| U256::from(x)),
}
}

Expand Down
10 changes: 8 additions & 2 deletions crates/core/src/types/engine/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{rlp::error::RLPDecodeError, serde_utils};

use crate::types::{
compute_withdrawals_root, BlockBody, BlockHeader, EIP1559Transaction, EIP2930Transaction,
LegacyTransaction, Transaction, Withdrawal, DEFAULT_OMMERS_HASH,
EIP4844Transaction, LegacyTransaction, Transaction, Withdrawal, DEFAULT_OMMERS_HASH,
};

#[allow(unused)]
Expand Down Expand Up @@ -81,7 +81,13 @@ impl EncodedTransaction {
0x2 => {
EIP1559Transaction::decode(tx_bytes).map(Transaction::EIP1559Transaction)
}
_ => unimplemented!("We don't know this tx type yet"),
// EIP4844
0x3 => {
EIP4844Transaction::decode(tx_bytes).map(Transaction::EIP4844Transaction)
}
ty => Err(RLPDecodeError::Custom(format!(
"Invalid transaction type: {ty}"
))),
}
}
// LegacyTransaction
Expand Down
130 changes: 130 additions & 0 deletions crates/core/src/types/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum Transaction {
LegacyTransaction(LegacyTransaction),
EIP2930Transaction(EIP2930Transaction),
EIP1559Transaction(EIP1559Transaction),
EIP4844Transaction(EIP4844Transaction),
}

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -64,6 +65,24 @@ pub struct EIP1559Transaction {
pub signature_s: U256,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EIP4844Transaction {
pub chain_id: u64,
pub nonce: u64,
pub max_priority_fee_per_gas: u64,
pub max_fee_per_gas: u64,
pub gas: u64,
pub to: Address,
pub value: U256,
pub data: Bytes,
pub access_list: Vec<(Address, Vec<H256>)>,
pub max_fee_per_blob_gas: u64,
pub blob_versioned_hashes: Vec<H256>,
pub signature_y_parity: bool,
pub signature_r: U256,
pub signature_s: U256,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TxType {
Legacy = 0x00,
Expand All @@ -90,6 +109,7 @@ impl Transaction {
Transaction::LegacyTransaction(_) => TxType::Legacy,
Transaction::EIP2930Transaction(_) => TxType::EIP2930,
Transaction::EIP1559Transaction(_) => TxType::EIP1559,
Transaction::EIP4844Transaction(_) => TxType::EIP4844,
}
}
}
Expand All @@ -100,6 +120,7 @@ impl RLPEncode for Transaction {
Transaction::LegacyTransaction(t) => t.encode(buf),
Transaction::EIP2930Transaction(t) => t.encode(buf),
Transaction::EIP1559Transaction(t) => t.encode(buf),
Transaction::EIP4844Transaction(t) => t.encode(buf),
};
}
}
Expand Down Expand Up @@ -183,6 +204,27 @@ impl RLPEncode for EIP1559Transaction {
}
}

impl RLPEncode for EIP4844Transaction {
fn encode(&self, buf: &mut dyn bytes::BufMut) {
Encoder::new(buf)
.encode_field(&self.chain_id)
.encode_field(&self.nonce)
.encode_field(&self.max_priority_fee_per_gas)
.encode_field(&self.max_fee_per_gas)
.encode_field(&self.gas)
.encode_field(&self.to)
.encode_field(&self.value)
.encode_field(&self.data)
.encode_field(&self.access_list)
.encode_field(&self.max_fee_per_blob_gas)
.encode_field(&self.blob_versioned_hashes)
.encode_field(&self.signature_y_parity)
.encode_field(&self.signature_r)
.encode_field(&self.signature_s)
.finish();
}
}

impl RLPDecode for LegacyTransaction {
fn decode_unfinished(rlp: &[u8]) -> Result<(LegacyTransaction, &[u8]), RLPDecodeError> {
let decoder = Decoder::new(rlp)?;
Expand Down Expand Up @@ -278,6 +320,45 @@ impl RLPDecode for EIP1559Transaction {
}
}

impl RLPDecode for EIP4844Transaction {
fn decode_unfinished(rlp: &[u8]) -> Result<(EIP4844Transaction, &[u8]), RLPDecodeError> {
let decoder = Decoder::new(rlp)?;
let (chain_id, decoder) = decoder.decode_field("chain_id")?;
let (nonce, decoder) = decoder.decode_field("nonce")?;
let (max_priority_fee_per_gas, decoder) =
decoder.decode_field("max_priority_fee_per_gas")?;
let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
let (gas, decoder) = decoder.decode_field("gas")?;
let (to, decoder) = decoder.decode_field("to")?;
let (value, decoder) = decoder.decode_field("value")?;
let (data, decoder) = decoder.decode_field("data")?;
let (access_list, decoder) = decoder.decode_field("access_list")?;
let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
let (signature_r, decoder) = decoder.decode_field("signature_r")?;
let (signature_s, decoder) = decoder.decode_field("signature_s")?;

let tx = EIP4844Transaction {
chain_id,
nonce,
max_priority_fee_per_gas,
max_fee_per_gas,
gas,
to,
value,
data,
access_list,
max_fee_per_blob_gas,
blob_versioned_hashes,
signature_y_parity,
signature_r,
signature_s,
};
Ok((tx, decoder.finish()?))
}
}

impl Transaction {
pub fn sender(&self) -> Address {
match self {
Expand Down Expand Up @@ -338,6 +419,28 @@ impl Transaction {
&Bytes::from(buf),
)
}
Transaction::EIP4844Transaction(tx) => {
let mut buf = vec![self.tx_type() as u8];
Encoder::new(&mut buf)
.encode_field(&tx.chain_id)
.encode_field(&tx.nonce)
.encode_field(&tx.max_priority_fee_per_gas)
.encode_field(&tx.max_fee_per_gas)
.encode_field(&tx.gas)
.encode_field(&tx.to)
.encode_field(&tx.value)
.encode_field(&tx.data)
.encode_field(&tx.access_list)
.encode_field(&tx.max_fee_per_blob_gas)
.encode_field(&tx.blob_versioned_hashes)
.finish();
recover_address(
&tx.signature_r,
&tx.signature_s,
tx.signature_y_parity,
&Bytes::from(buf),
)
}
}
}

Expand All @@ -346,6 +449,7 @@ impl Transaction {
Transaction::LegacyTransaction(tx) => tx.gas,
Transaction::EIP2930Transaction(tx) => tx.gas_limit,
Transaction::EIP1559Transaction(tx) => tx.gas_limit,
Transaction::EIP4844Transaction(tx) => tx.gas,
}
}

Expand All @@ -354,6 +458,7 @@ impl Transaction {
Transaction::LegacyTransaction(tx) => tx.gas_price,
Transaction::EIP2930Transaction(tx) => tx.gas_price,
Transaction::EIP1559Transaction(tx) => tx.max_fee_per_gas,
Transaction::EIP4844Transaction(tx) => tx.max_fee_per_gas,
}
}

Expand All @@ -362,6 +467,7 @@ impl Transaction {
Transaction::LegacyTransaction(tx) => tx.to.clone(),
Transaction::EIP2930Transaction(tx) => tx.to.clone(),
Transaction::EIP1559Transaction(tx) => TxKind::Call(tx.destination),
Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
}
}

Expand All @@ -370,6 +476,7 @@ impl Transaction {
Transaction::LegacyTransaction(tx) => tx.value,
Transaction::EIP2930Transaction(tx) => tx.value,
Transaction::EIP1559Transaction(tx) => tx.amount,
Transaction::EIP4844Transaction(tx) => tx.value,
}
}

Expand All @@ -378,6 +485,7 @@ impl Transaction {
Transaction::LegacyTransaction(_tx) => None,
Transaction::EIP2930Transaction(_tx) => None,
Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
}
}

Expand All @@ -386,6 +494,7 @@ impl Transaction {
Transaction::LegacyTransaction(_tx) => None,
Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
}
}

Expand All @@ -394,6 +503,7 @@ impl Transaction {
Transaction::LegacyTransaction(_tx) => Vec::new(),
Transaction::EIP2930Transaction(tx) => tx.access_list.clone(),
Transaction::EIP1559Transaction(tx) => tx.access_list.clone(),
Transaction::EIP4844Transaction(tx) => tx.access_list.clone(),
}
}

Expand All @@ -402,6 +512,7 @@ impl Transaction {
Transaction::LegacyTransaction(tx) => tx.nonce,
Transaction::EIP2930Transaction(tx) => tx.nonce,
Transaction::EIP1559Transaction(tx) => tx.signer_nonce,
Transaction::EIP4844Transaction(tx) => tx.nonce,
}
}

Expand All @@ -410,6 +521,25 @@ impl Transaction {
Transaction::LegacyTransaction(tx) => &tx.data,
Transaction::EIP2930Transaction(tx) => &tx.data,
Transaction::EIP1559Transaction(tx) => &tx.payload,
Transaction::EIP4844Transaction(tx) => &tx.data,
}
}

pub fn blob_versioned_hashes(&self) -> Vec<H256> {
match self {
Transaction::LegacyTransaction(_tx) => Vec::new(),
Transaction::EIP2930Transaction(_tx) => Vec::new(),
Transaction::EIP1559Transaction(_tx) => Vec::new(),
Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
}
}

pub fn max_fee_per_blob_gas(&self) -> Option<u64> {
match self {
Transaction::LegacyTransaction(_tx) => None,
Transaction::EIP2930Transaction(_tx) => None,
Transaction::EIP1559Transaction(_tx) => None,
Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions crates/rpc/src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn new_payload_v3(request: NewPayloadV3Request) -> Result<PayloadStatus, Rpc

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

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

0 comments on commit f0c69f3

Please sign in to comment.