From 786a0ba8f91a1cf6b060ab6a33fa80bfd9e8ac18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?= Date: Mon, 16 Dec 2024 09:54:27 +0100 Subject: [PATCH 1/3] chore(bolt-sidecar): PooledTransactionsElement -> PooledTransaction --- bolt-sidecar/src/builder/template.rs | 4 +- bolt-sidecar/src/common/transactions.rs | 10 ++-- bolt-sidecar/src/primitives/commitment.rs | 2 +- bolt-sidecar/src/primitives/transaction.rs | 67 ++++++++++++---------- bolt-sidecar/src/state/execution.rs | 21 +++---- 5 files changed, 53 insertions(+), 51 deletions(-) diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index 19e7bf04..edde1e09 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -1,5 +1,4 @@ -use std::collections::HashMap; - +use alloy::consensus::transaction::PooledTransaction; use alloy::{ consensus::Transaction, primitives::{Address, TxHash, U256}, @@ -9,6 +8,7 @@ use ethereum_consensus::{ deneb::mainnet::{Blob, BlobsBundle}, }; use reth_primitives::TransactionSigned; +use std::collections::HashMap; use tracing::warn; use crate::{ diff --git a/bolt-sidecar/src/common/transactions.rs b/bolt-sidecar/src/common/transactions.rs index 42961c88..812e538a 100644 --- a/bolt-sidecar/src/common/transactions.rs +++ b/bolt-sidecar/src/common/transactions.rs @@ -1,5 +1,7 @@ -use alloy::{consensus::Transaction, primitives::U256}; -use reth_primitives::PooledTransactionsElement; +use alloy::{ + consensus::{transaction::PooledTransaction, Transaction}, + primitives::U256, +}; use crate::{primitives::AccountState, state::ValidationError}; @@ -34,7 +36,7 @@ pub fn calculate_max_basefee(current: u128, block_diff: u64) -> Option { /// - For legacy transactions: `gas_price * gas_limit + tx_value`. /// - For EIP-4844 blob transactions: `max_fee_per_gas * gas_limit + tx_value + max_blob_fee_per_gas /// * blob_gas_used`. -pub fn max_transaction_cost(transaction: &PooledTransactionsElement) -> U256 { +pub fn max_transaction_cost(transaction: &PooledTransaction) -> U256 { let gas_limit = transaction.gas_limit() as u128; let mut fee_cap = transaction.max_fee_per_gas(); @@ -53,7 +55,7 @@ pub fn max_transaction_cost(transaction: &PooledTransactionsElement) -> U256 { /// 2. The balance of the account must be higher than the transaction's max cost. pub fn validate_transaction( account_state: &AccountState, - transaction: &PooledTransactionsElement, + transaction: &PooledTransaction, ) -> Result<(), ValidationError> { // Check if the nonce is correct (should be the same as the transaction count) if transaction.nonce() < account_state.transaction_count { diff --git a/bolt-sidecar/src/primitives/commitment.rs b/bolt-sidecar/src/primitives/commitment.rs index 21cdd22e..1789e1f9 100644 --- a/bolt-sidecar/src/primitives/commitment.rs +++ b/bolt-sidecar/src/primitives/commitment.rs @@ -202,7 +202,7 @@ impl InclusionRequest { /// Recovers the signer of all transactions in the request. pub fn recover_signers(&mut self) -> Result<(), SignatureError> { for tx in &mut self.txs { - let signer = tx.recover_signer().ok_or(SignatureError)?; + let signer = tx.recover_signer().map_err(|_| SignatureError)?; tx.sender = Some(signer); } diff --git a/bolt-sidecar/src/primitives/transaction.rs b/bolt-sidecar/src/primitives/transaction.rs index 5115c436..3d93023e 100644 --- a/bolt-sidecar/src/primitives/transaction.rs +++ b/bolt-sidecar/src/primitives/transaction.rs @@ -1,16 +1,16 @@ -use std::{borrow::Cow, fmt}; - use alloy::{ - consensus::{BlobTransactionSidecar, Transaction, TxType}, + consensus::{ + transaction::PooledTransaction, BlobTransactionSidecar, Transaction, TxType, Typed2718, + }, eips::eip2718::{Decodable2718, Encodable2718}, hex, primitives::{Address, U256}, }; -use reth_primitives::PooledTransactionsElement; use serde::{de, ser::SerializeSeq}; +use std::{borrow::Cow, fmt}; /// Trait that exposes additional information on transaction types that don't already do it -/// by themselves (e.g. [`PooledTransactionsElement`]). +/// by themselves (e.g. [`PooledTransaction`]). pub trait TransactionExt { /// Returns the value of the transaction. fn value(&self) -> U256; @@ -22,20 +22,20 @@ pub trait TransactionExt { fn size(&self) -> usize; } -impl TransactionExt for PooledTransactionsElement { +impl TransactionExt for PooledTransaction { fn value(&self) -> U256 { match self { Self::Legacy(transaction) => transaction.tx().value, Self::Eip2930(transaction) => transaction.tx().value, Self::Eip1559(transaction) => transaction.tx().value, - Self::BlobTransaction(blob_tx) => blob_tx.tx().tx.value, + Self::Eip4844(transaction) => transaction.tx().tx().value, _ => unimplemented!(), } } fn blob_sidecar(&self) -> Option<&BlobTransactionSidecar> { match self { - Self::BlobTransaction(blob_tx) => Some(&blob_tx.tx().sidecar), + Self::Eip4844(transaction) => Some(&transaction.tx().sidecar), _ => None, } } @@ -45,7 +45,7 @@ impl TransactionExt for PooledTransactionsElement { Self::Legacy(transaction) => transaction.tx().size(), Self::Eip2930(transaction) => transaction.tx().size(), Self::Eip1559(transaction) => transaction.tx().size(), - Self::BlobTransaction(blob_tx) => blob_tx.tx().tx.size(), + Self::Eip4844(blob_tx) => blob_tx.tx().tx().size(), _ => unimplemented!(), } } @@ -66,13 +66,13 @@ pub const fn tx_type_str(tx_type: TxType) -> &'static str { #[derive(Clone, PartialEq, Eq)] pub struct FullTransaction { /// The transaction itself. - pub tx: PooledTransactionsElement, + pub tx: PooledTransaction, /// The sender of the transaction, if recovered. pub sender: Option
, } -impl From for FullTransaction { - fn from(tx: PooledTransactionsElement) -> Self { +impl From for FullTransaction { + fn from(tx: PooledTransaction) -> Self { Self { tx, sender: None } } } @@ -81,33 +81,38 @@ impl fmt::Debug for FullTransaction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut debug_struct = f.debug_struct("FullTransaction"); - match &self.tx { - PooledTransactionsElement::BlobTransaction(blob_tx) => { + if self.tx.is_eip4844() { + if let Some(tx) = self.tx.as_eip4844_with_sidecar() { + let sidecar = tx.sidecar(); + let shortened_blobs: Vec = // Use alternative `Display` to print trimmed blob - blob_tx.tx().sidecar.blobs.iter().map(|blob| format!("{blob:#}")).collect(); - - debug_struct.field("tx", &"BlobTransaction"); - debug_struct.field("hash", &blob_tx.hash()); - debug_struct.field("transaction", &blob_tx.tx()); - debug_struct.field("signature", &blob_tx.signature()); - - debug_struct.field("sidecar_blobs", &shortened_blobs); - debug_struct.field("sidecar_commitments", &blob_tx.tx().sidecar().commitments); - debug_struct.field("sidecar_proofs", &blob_tx.tx().sidecar.proofs); - } - other => { - debug_struct.field("tx", other); + sidecar.blobs.iter().map(|blob| format!("{blob:#}")).collect(); + + debug_struct + .field("tx", &"BlobTransaction") + .field("hash", self.tx.hash()) + .field("transaction", &tx.tx()) + .field("signature", &self.tx.signature()) + .field("sidecar_blobs", &shortened_blobs) + .field("sidecar_commitments", &sidecar.commitments) + .field("sidecar_proofs", &sidecar.proofs); + } else { + // Blob transaction without sidecar + debug_struct + .field("tx", &"EIP-4844 Transaction (no sidecar)") + .field("hash", self.tx.hash()); } } + // Non-blob transactions, just for debug debug_struct.field("sender", &self.sender); debug_struct.finish() } } impl std::ops::Deref for FullTransaction { - type Target = PooledTransactionsElement; + type Target = PooledTransaction; fn deref(&self) -> &Self::Target { &self.tx @@ -123,12 +128,12 @@ impl std::ops::DerefMut for FullTransaction { impl FullTransaction { /// Convenience method to parse a raw transaction into a `FullTransaction`. pub fn decode_enveloped(data: impl AsRef<[u8]>) -> eyre::Result { - let tx = PooledTransactionsElement::decode_2718(&mut data.as_ref())?; + let tx = PooledTransaction::decode_2718(&mut data.as_ref())?; Ok(Self { tx, sender: None }) } /// Returns the inner transaction. - pub fn into_inner(self) -> PooledTransactionsElement { + pub fn into_inner(self) -> PooledTransaction { self.tx } @@ -184,7 +189,7 @@ where for s in hex_strings { let data = hex::decode(s.trim_start_matches("0x")).map_err(de::Error::custom)?; - let tx = PooledTransactionsElement::decode_2718(&mut data.as_slice()) + let tx = PooledTransaction::decode_2718(&mut data.as_slice()) .map_err(de::Error::custom) .map(|tx| FullTransaction { tx, sender: None })?; txs.push(tx); diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 0229caac..d5ff85b5 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -1,11 +1,12 @@ +use alloy::consensus::Transaction; use alloy::{ - consensus::{BlobTransactionValidationError, EnvKzgSettings, Transaction}, + consensus::{BlobTransactionValidationError, EnvKzgSettings}, eips::eip4844::MAX_BLOBS_PER_BLOCK, - primitives::{Address, U256}, + primitives::Address, + primitives::U256, transports::TransportError, }; -use reth_primitives::PooledTransactionsElement; -use std::{collections::HashMap, ops::Deref}; +use std::collections::HashMap; use thiserror::Error; use tracing::{debug, error, trace, warn}; @@ -382,23 +383,18 @@ impl ExecutionState { validate_transaction(&account_state_with_diffs, tx)?; // Check EIP-4844-specific limits - if let Some(transaction) = tx.as_eip4844() { + if let Some(transaction) = tx.as_eip4844_with_sidecar() { if let Some(template) = self.block_templates.get(&target_slot) { if template.blob_count() >= MAX_BLOBS_PER_BLOCK { return Err(ValidationError::Eip4844Limit); } } - let PooledTransactionsElement::BlobTransaction(ref blob_transaction) = tx.deref() - else { - unreachable!("EIP-4844 transaction should be a blob transaction") - }; - // Calculate max possible increase in blob basefee let max_blob_basefee = calculate_max_basefee(self.blob_basefee, slot_diff) .ok_or(ValidationError::MaxBaseFeeCalcOverflow)?; - let blob_basefee = blob_transaction.0.tx().max_fee_per_blob_gas().unwrap_or(0); + let blob_basefee = transaction.max_fee_per_blob_gas().unwrap_or(0); debug!(%max_blob_basefee, %blob_basefee, "Validating blob basefee"); if blob_basefee < max_blob_basefee { @@ -406,8 +402,7 @@ impl ExecutionState { } // Validate blob against KZG settings - let sidecar = blob_transaction.0.tx().sidecar(); - transaction.validate_blob(sidecar, self.kzg_settings.get())?; + transaction.validate_blob(self.kzg_settings.get())?; } // Increase the bundle nonce and balance diffs for this sender for the next iteration From 19737e43690a9b4395fff6a21f96e54e8b2993e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?= Date: Wed, 18 Dec 2024 19:13:23 +0100 Subject: [PATCH 2/3] chore(bolt-sidecar): fix PooledTransaction conversion --- bolt-sidecar/src/builder/template.rs | 5 +---- bolt-sidecar/src/primitives/transaction.rs | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index edde1e09..36d227da 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -1,4 +1,3 @@ -use alloy::consensus::transaction::PooledTransaction; use alloy::{ consensus::Transaction, primitives::{Address, TxHash, U256}, @@ -51,9 +50,7 @@ impl BlockTemplate { pub fn as_signed_transactions(&self) -> Vec { self.signed_constraints_list .iter() - .flat_map(|sc| { - sc.message.transactions.iter().map(|c| c.clone().into_inner().into_transaction()) - }) + .flat_map(|sc| sc.message.transactions.iter().map(|c| c.clone().into_signed())) .collect() } diff --git a/bolt-sidecar/src/primitives/transaction.rs b/bolt-sidecar/src/primitives/transaction.rs index 3d93023e..1028a749 100644 --- a/bolt-sidecar/src/primitives/transaction.rs +++ b/bolt-sidecar/src/primitives/transaction.rs @@ -1,11 +1,13 @@ use alloy::{ consensus::{ - transaction::PooledTransaction, BlobTransactionSidecar, Transaction, TxType, Typed2718, + transaction::PooledTransaction, BlobTransactionSidecar, Signed, Transaction, TxType, + Typed2718, }, eips::eip2718::{Decodable2718, Encodable2718}, hex, primitives::{Address, U256}, }; +use reth_primitives::TransactionSigned; use serde::{de, ser::SerializeSeq}; use std::{borrow::Cow, fmt}; @@ -137,6 +139,22 @@ impl FullTransaction { self.tx } + /// Returns the signed transaction. + pub fn into_signed(self) -> TransactionSigned { + match self.tx { + PooledTransaction::Legacy(tx) => tx.into(), + PooledTransaction::Eip2930(tx) => tx.into(), + PooledTransaction::Eip1559(tx) => tx.into(), + PooledTransaction::Eip4844(tx) => { + let sig = *tx.signature(); + let hash = *tx.hash(); + let inner_tx = tx.into_parts().0.into_parts().0; + Signed::new_unchecked(inner_tx, sig, hash).into() + } + _ => unimplemented!(), + } + } + /// Returns the sender of the transaction, if recovered. pub fn sender(&self) -> Option<&Address> { self.sender.as_ref() From 3a4f6e544beb8060143343b4f3c362b191894c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?= Date: Wed, 18 Dec 2024 21:17:30 +0100 Subject: [PATCH 3/3] fix(bolt-sidecar): avoid unimplemented txs --- bolt-sidecar/src/primitives/transaction.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bolt-sidecar/src/primitives/transaction.rs b/bolt-sidecar/src/primitives/transaction.rs index 1028a749..a32f6f62 100644 --- a/bolt-sidecar/src/primitives/transaction.rs +++ b/bolt-sidecar/src/primitives/transaction.rs @@ -28,10 +28,10 @@ impl TransactionExt for PooledTransaction { fn value(&self) -> U256 { match self { Self::Legacy(transaction) => transaction.tx().value, - Self::Eip2930(transaction) => transaction.tx().value, Self::Eip1559(transaction) => transaction.tx().value, + Self::Eip2930(transaction) => transaction.tx().value, Self::Eip4844(transaction) => transaction.tx().tx().value, - _ => unimplemented!(), + Self::Eip7702(transaction) => transaction.tx().value, } } @@ -45,10 +45,10 @@ impl TransactionExt for PooledTransaction { fn size(&self) -> usize { match self { Self::Legacy(transaction) => transaction.tx().size(), - Self::Eip2930(transaction) => transaction.tx().size(), Self::Eip1559(transaction) => transaction.tx().size(), + Self::Eip2930(transaction) => transaction.tx().size(), Self::Eip4844(blob_tx) => blob_tx.tx().tx().size(), - _ => unimplemented!(), + Self::Eip7702(transaction) => transaction.tx().size(), } } } @@ -143,15 +143,15 @@ impl FullTransaction { pub fn into_signed(self) -> TransactionSigned { match self.tx { PooledTransaction::Legacy(tx) => tx.into(), - PooledTransaction::Eip2930(tx) => tx.into(), PooledTransaction::Eip1559(tx) => tx.into(), + PooledTransaction::Eip2930(tx) => tx.into(), PooledTransaction::Eip4844(tx) => { let sig = *tx.signature(); let hash = *tx.hash(); let inner_tx = tx.into_parts().0.into_parts().0; Signed::new_unchecked(inner_tx, sig, hash).into() } - _ => unimplemented!(), + PooledTransaction::Eip7702(tx) => tx.into(), } }