Skip to content

Commit

Permalink
Merge pull request #752 from chainbound/nico/chore/minimize-reth-deps
Browse files Browse the repository at this point in the history
chore: remove all reth types
  • Loading branch information
merklefruit authored Jan 30, 2025
2 parents dd46194 + ab429ce commit 8f349ee
Showing 5 changed files with 49 additions and 78 deletions.
58 changes: 26 additions & 32 deletions bolt-sidecar/src/builder/compat.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy::{
consensus::BlockHeader,
consensus::{transaction::PooledTransaction, Block, BlockHeader},
eips::{eip2718::Encodable2718, eip4895::Withdrawal},
primitives::{Address, Bloom, B256, U256},
rpc::types::Withdrawals,
@@ -19,14 +19,12 @@ use ethereum_consensus::{
ssz::prelude::{ssz_rs, ByteList, ByteVector, HashTreeRoot, List},
types::mainnet::ExecutionPayload as ConsensusExecutionPayload,
};
use reth_primitives::{SealedBlock, TransactionSigned};
use reth_primitives_traits::BlockBody;

/// Compatibility: convert a sealed header into an ethereum-consensus execution payload header.
/// This requires recalculating the withdrals and transactions roots as SSZ instead of MPT roots.
pub(crate) fn to_execution_payload_header(
sealed_block: &SealedBlock,
transactions: Vec<TransactionSigned>,
sealed_block: &Block<PooledTransaction>,
transactions: Vec<PooledTransaction>,
) -> ConsensusExecutionPayloadHeader {
// Transactions and withdrawals are treated as opaque byte arrays in consensus types
let transactions_bytes = transactions.iter().map(|t| t.encoded_2718()).collect::<Vec<_>>();
@@ -42,15 +40,15 @@ pub(crate) fn to_execution_payload_header(
let mut withdrawals_ssz: List<ConsensusWithdrawal, MAX_WITHDRAWALS_PER_PAYLOAD> =
List::default();

if let Some(withdrawals) = sealed_block.body().withdrawals.as_ref() {
if let Some(withdrawals) = sealed_block.body.withdrawals.as_ref() {
for w in withdrawals {
withdrawals_ssz.push(to_consensus_withdrawal(w));
}
}

let withdrawals_root = withdrawals_ssz.hash_tree_root().expect("valid withdrawals root");

let header = sealed_block.header();
let header = &sealed_block.header;

ConsensusExecutionPayloadHeader {
parent_hash: to_bytes32(header.parent_hash),
@@ -75,37 +73,31 @@ pub(crate) fn to_execution_payload_header(

/// Compatibility: convert a sealed block into an Alloy execution payload
pub(crate) fn to_alloy_execution_payload(
block: &SealedBlock,
block: &Block<PooledTransaction>,
block_hash: B256,
) -> ExecutionPayloadV3 {
let alloy_withdrawals = block
.body()
.withdrawals
.as_ref()
.map(|withdrawals| {
withdrawals
.iter()
.map(|w| Withdrawal {
index: w.index,
validator_index: w.validator_index,
address: w.address,
amount: w.amount,
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
let alloy_withdrawals =
block.body.withdrawals.clone().map(|w| w.into_inner()).unwrap_or_default();

let transactions = block
.body
.transactions
.iter()
.map(|tx| tx.encoded_2718())
.map(Into::into)
.collect::<Vec<_>>();

ExecutionPayloadV3 {
blob_gas_used: block.blob_gas_used().unwrap_or_default(),
excess_blob_gas: block.excess_blob_gas.unwrap_or_default(),
payload_inner: ExecutionPayloadV2 {
payload_inner: ExecutionPayloadV1 {
base_fee_per_gas: U256::from(block.base_fee_per_gas.unwrap_or_default()),
block_hash,
transactions,
base_fee_per_gas: U256::from(block.base_fee_per_gas.unwrap_or_default()),
block_number: block.number,
extra_data: block.extra_data.clone(),
transactions: block.body().encoded_2718_transactions(),
fee_recipient: block.header().beneficiary,
fee_recipient: block.header.beneficiary,
gas_limit: block.gas_limit,
gas_used: block.gas_used,
logs_bloom: block.logs_bloom,
@@ -121,11 +113,13 @@ pub(crate) fn to_alloy_execution_payload(
}

/// Compatibility: convert a sealed block into an ethereum-consensus execution payload
pub(crate) fn to_consensus_execution_payload(value: &SealedBlock) -> ConsensusExecutionPayload {
let hash = value.hash();
let header = value.header();
let transactions = &value.body().transactions;
let withdrawals = &value.body().withdrawals;
pub(crate) fn to_consensus_execution_payload(
value: &Block<PooledTransaction>,
) -> ConsensusExecutionPayload {
let hash = value.hash_slow();
let header = &value.header;
let transactions = &value.body.transactions;
let withdrawals = &value.body.withdrawals;
let transactions = transactions
.iter()
.map(|t| spec::Transaction::try_from(t.encoded_2718().as_ref()).unwrap())
25 changes: 11 additions & 14 deletions bolt-sidecar/src/builder/fallback/engine_hinter.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::ops::Deref;

use alloy::{
consensus::{Header, EMPTY_OMMER_ROOT_HASH},
consensus::{transaction::PooledTransaction, Block, BlockBody, Header, EMPTY_OMMER_ROOT_HASH},
primitives::{Address, Bloom, Bytes, B256, B64, U256},
rpc::types::{Block, Withdrawal, Withdrawals},
rpc::types::{Block as RpcBlock, Withdrawal, Withdrawals},
};
use alloy_provider::ext::EngineApi;
use alloy_rpc_types_engine::{ClientCode, ExecutionPayloadV3, JwtSecret, PayloadStatusEnum};
use reqwest::Url;
use reth_primitives::{BlockBody, SealedBlock, SealedHeader, TransactionSigned};
use tracing::{debug, error};

use crate::{
@@ -52,7 +51,7 @@ impl EngineHinter {
pub async fn fetch_payload_from_hints(
&self,
mut ctx: EngineHinterContext,
) -> Result<SealedBlock, BuilderError> {
) -> Result<Block<PooledTransaction>, BuilderError> {
// The block body can be the same for all iterations, since it only contains
// the transactions and withdrawals from the context.
let body = ctx.build_block_body();
@@ -66,20 +65,18 @@ impl EngineHinter {
// Build a new block header using the hints from the context
let header = ctx.build_block_header_with_hints();

let sealed_hash = header.hash_slow();
let sealed_header = SealedHeader::new(header, sealed_hash);
let sealed_block = SealedBlock::new(sealed_header, body.clone());
let block_hash = ctx.hints.block_hash.unwrap_or(sealed_block.hash());
let block = Block { header, body: body.clone() };
let block_hash = ctx.hints.block_hash.unwrap_or(block.hash_slow());

// build the new execution payload from the block header
let exec_payload = to_alloy_execution_payload(&sealed_block, block_hash);
let exec_payload = to_alloy_execution_payload(&block, block_hash);

// attempt to fetch the next hint from the engine API payload response
let hint = self.next_hint(exec_payload, &ctx).await?;
debug!(?hint, "Received hint from engine API");

if matches!(hint, EngineApiHint::ValidPayload) {
return Ok(sealed_block);
return Ok(block);
}

// Populate the new hint in the context and continue the loop
@@ -206,17 +203,17 @@ pub struct EngineHinterContext {
pub parent_beacon_block_root: B256,
pub blob_versioned_hashes: Vec<B256>,
pub block_timestamp: u64,
pub transactions: Vec<TransactionSigned>,
pub transactions: Vec<PooledTransaction>,
pub withdrawals: Vec<Withdrawal>,
pub head_block: Block,
pub head_block: RpcBlock,
pub hints: Hints,
pub el_client_code: ClientCode,
}

impl EngineHinterContext {
/// Build a block body using the transactions and withdrawals from the context.
pub fn build_block_body(&self) -> BlockBody {
BlockBody {
pub fn build_block_body(&self) -> BlockBody<PooledTransaction> {
BlockBody::<PooledTransaction> {
ommers: Vec::new(),
transactions: self.transactions.clone(),
withdrawals: Some(Withdrawals::new(self.withdrawals.clone())),
14 changes: 6 additions & 8 deletions bolt-sidecar/src/builder/fallback/payload_builder.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use alloy::{
consensus::{proofs, Transaction},
consensus::{proofs, transaction::PooledTransaction, Block, Transaction},
eips::{calc_excess_blob_gas, calc_next_block_base_fee, eip1559::BaseFeeParams},
primitives::{Address, Bytes},
};
use reth_primitives::{SealedBlock, TransactionSigned};
use tracing::debug;

use super::{
@@ -62,8 +61,8 @@ impl FallbackPayloadBuilder {
pub async fn build_fallback_payload(
&self,
target_slot: u64,
transactions: &[TransactionSigned],
) -> Result<SealedBlock, BuilderError> {
transactions: &[PooledTransaction],
) -> Result<Block<PooledTransaction>, BuilderError> {
// Fetch the latest block to get the necessary parent values for the new block.
// For the timestamp, we must use the one expected by the beacon chain instead, to
// prevent edge cases where the proposer before us has missed their slot and therefore
@@ -145,15 +144,14 @@ mod tests {
use std::time::{SystemTime, UNIX_EPOCH};

use alloy::{
consensus::{constants, proofs},
consensus::{constants, proofs, transaction::PooledTransaction},
eips::eip2718::{Decodable2718, Encodable2718},
network::{EthereumWallet, TransactionBuilder},
primitives::{hex, Address},
providers::{Provider, ProviderBuilder},
signers::{k256::ecdsa::SigningKey, local::PrivateKeySigner},
};
use beacon_api_client::mainnet::Client as BeaconClient;
use reth_primitives::TransactionSigned;
use tracing::warn;

use crate::{
@@ -192,15 +190,15 @@ mod tests {
let tx = default_test_transaction(addy, Some(nonce)).with_chain_id(17000);
let tx_signed = tx.build(&wallet).await?;
let raw_encoded = tx_signed.encoded_2718();
let tx_signed_reth = TransactionSigned::decode_2718(&mut raw_encoded.as_slice())?;
let tx_signed_reth = PooledTransaction::decode_2718(&mut raw_encoded.as_slice())?;

let slot = genesis_time +
(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() / cfg.chain.slot_time()) +
1;

let block = builder.build_fallback_payload(slot, &[tx_signed_reth]).await?;

assert_eq!(block.body().transactions.len(), 1);
assert_eq!(block.body.transactions.len(), 1);

Ok(())
}
7 changes: 3 additions & 4 deletions bolt-sidecar/src/builder/template.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use alloy::{
consensus::Transaction,
consensus::{transaction::PooledTransaction, Transaction},
primitives::{Address, TxHash, U256},
};
use ethereum_consensus::{
crypto::{KzgCommitment, KzgProof},
deneb::mainnet::{Blob, BlobsBundle},
};
use reth_primitives::TransactionSigned;
use std::collections::HashMap;
use tracing::warn;

@@ -47,10 +46,10 @@ impl BlockTemplate {
/// Converts the list of signed constraints into a list of signed transactions. Use this when
/// building a local execution payload.
#[inline]
pub fn as_signed_transactions(&self) -> Vec<TransactionSigned> {
pub fn as_signed_transactions(&self) -> Vec<PooledTransaction> {
self.signed_constraints_list
.iter()
.flat_map(|sc| sc.message.transactions.iter().map(|c| c.clone().into_signed()))
.flat_map(|sc| sc.message.transactions.iter().map(|c| c.clone().into_inner()))
.collect()
}

23 changes: 3 additions & 20 deletions bolt-sidecar/src/primitives/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::{borrow::Cow, fmt};

use alloy::{
consensus::{
transaction::PooledTransaction, BlobTransactionSidecar, Signed, Transaction, TxType,
Typed2718,
transaction::PooledTransaction, BlobTransactionSidecar, 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};

/// Trait that exposes additional information on transaction types that don't already do it
/// by themselves (e.g. [`PooledTransaction`]).
@@ -139,22 +138,6 @@ impl FullTransaction {
self.tx
}

/// Returns the signed transaction.
pub fn into_signed(self) -> TransactionSigned {
match self.tx {
PooledTransaction::Legacy(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()
}
PooledTransaction::Eip7702(tx) => tx.into(),
}
}

/// Returns the sender of the transaction, if recovered.
pub fn sender(&self) -> Option<&Address> {
self.sender.as_ref()

0 comments on commit 8f349ee

Please sign in to comment.