Skip to content

Commit

Permalink
refactor payload builder code into high-level pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
ralexstokes committed Sep 7, 2023
1 parent 553c515 commit 75b87a5
Showing 1 changed file with 107 additions and 71 deletions.
178 changes: 107 additions & 71 deletions mev-build-rs/src/reth_builder/payload_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use ethers::{
};
use reth_primitives::{
constants::{BEACON_NONCE, EMPTY_OMMER_ROOT},
proofs, Block, Bytes, ChainSpec, Header, IntoRecoveredTransaction, Receipt, TransactionSigned,
TransactionSignedEcRecovered, Withdrawal, H256, U256,
proofs, Block, Bytes, ChainSpec, Header, IntoRecoveredTransaction, Receipt, SealedBlock,
TransactionSigned, TransactionSignedEcRecovered, Withdrawal, H256, U256,
};
use reth_provider::PostState;
use reth_provider::{PostState, StateProviderBox};
use reth_revm::{
database::{State, SubState},
env::tx_env_with_recovered,
Expand Down Expand Up @@ -54,30 +54,20 @@ pub enum BuildOutcome {
Worse { threshold: U256, provided: U256 },
}

pub fn build_payload<
Provider: reth_provider::StateProviderFactory,
Pool: reth_transaction_pool::TransactionPool,
>(
fn assemble_txs_from_pool<'a, Pool: reth_transaction_pool::TransactionPool>(
context: &BuildContext,
threshold_value: U256,
provider: &Provider,
pool: &Pool,
) -> Result<BuildOutcome, Error> {
let parent_hash = context.parent_hash;

let state = State::new(provider.state_by_block_hash(parent_hash)?);
// TODO: add `CacheDB` for reads
let mut db = SubState::new(state);
let mut post_state = PostState::default();

mut db: CacheDB<State<StateProviderBox<'a>>>,
post_state: &mut PostState,
) -> Result<(Vec<TransactionSigned>, U256, u64, CacheDB<State<StateProviderBox<'a>>>), Error> {
let base_fee = context.base_fee();
let block_number = context.number();
let block_gas_limit = context.gas_limit();

let mut cumulative_gas_used = 0;
let mut total_fees = U256::ZERO;

let mut executed_txs = vec![];
let mut executed_txs: Vec<TransactionSigned> = vec![];
let mut best_txs = pool.best_transactions_with_base_fee(base_fee);

while let Some(pool_tx) = best_txs.next() {
Expand Down Expand Up @@ -112,7 +102,7 @@ pub fn build_payload<
},
};

commit_state_changes(&mut db, &mut post_state, block_number, state, true);
commit_state_changes(&mut db, post_state, block_number, state, true);

let gas_used = result.gas_used();
cumulative_gas_used += gas_used;
Expand All @@ -132,18 +122,78 @@ pub fn build_payload<

executed_txs.push(tx.into_signed());
}
Ok((executed_txs, total_fees, cumulative_gas_used, db))
}

if total_fees < threshold_value {
return Ok(BuildOutcome::Worse { threshold: threshold_value, provided: total_fees })
}
fn assemble_payload_from_txs(
context: &BuildContext,
mut db: CacheDB<State<StateProviderBox>>,
mut post_state: PostState,
cumulative_gas_used: u64,
executed_txs: Vec<TransactionSigned>,
) -> Result<SealedBlock, Error> {
let base_fee = context.base_fee();
let block_number = context.number();
let block_gas_limit = context.gas_limit();

let withdrawals_root = process_withdrawals(
&context.withdrawals,
&context.chain_spec,
&mut db,
&mut post_state,
context.timestamp,
block_number,
)?;

// NOTE: next chunk of code implements end-of-block proposer payments
// TODO: refactor/encapsulate this lol
let receipts_root = post_state.receipts_root(block_number);
let logs_bloom = post_state.logs_bloom(block_number);
let state_root = db.db.0.state_root(post_state)?;
let transactions_root = proofs::calculate_transaction_root(&executed_txs);

let header = Header {
parent_hash: context.parent_hash,
ommers_hash: EMPTY_OMMER_ROOT,
beneficiary: context.block_env.coinbase,
state_root,
transactions_root,
withdrawals_root: Some(withdrawals_root),
receipts_root,
logs_bloom,
timestamp: context.timestamp,
mix_hash: H256::from_slice(context.prev_randao.as_ref()),
nonce: BEACON_NONCE,
base_fee_per_gas: Some(base_fee),
number: block_number,
gas_limit: block_gas_limit,
difficulty: U256::ZERO,
gas_used: cumulative_gas_used,
extra_data: context.extra_data.clone(),
blob_gas_used: None,
excess_blob_gas: None,
};

let payload = Block {
header,
body: executed_txs,
ommers: vec![],
withdrawals: Some(context.withdrawals.clone()),
};
Ok(payload.seal_slow())
}

fn determine_payment(context: &BuildContext, total_fees: &U256) -> (U256, U256) {
let integral_percent = (context.bid_percent * 100.0) as u64;
let payment = total_fees * U256::from(integral_percent) / U256::from(100);
let revenue = total_fees - payment;
let total_payment = context.subsidy + payment;
(revenue, total_payment)
}

fn construct_payment_tx(
context: &BuildContext,
value: U256,
db: &mut CacheDB<State<StateProviderBox>>,
) -> Result<TransactionSignedEcRecovered, Error> {
let sender = context.builder_wallet.address();
let signer_account = db.load_account(sender.into())?;
let nonce = signer_account.info.nonce;
Expand All @@ -152,10 +202,11 @@ pub fn build_payload<
let tx = Eip1559TransactionRequest::new()
.from(sender)
.to(fee_recipient)
// TODO: support smart contract payments
.gas(21000)
.max_fee_per_gas(base_fee)
.max_fee_per_gas(context.base_fee())
.max_priority_fee_per_gas(0)
.value(total_payment)
.value(value)
.data(ethers::types::Bytes::default())
.access_list(ethers::types::transaction::eip2930::AccessList::default())
.nonce(nonce)
Expand All @@ -168,9 +219,34 @@ pub fn build_payload<
let payment_tx = TransactionSigned::decode_enveloped(Bytes::from(tx_encoded.as_ref()))
.expect("can decode valid txn");

let payment_tx =
TransactionSignedEcRecovered::from_signed_transaction(payment_tx, sender.into());
Ok(TransactionSignedEcRecovered::from_signed_transaction(payment_tx, sender.into()))
}

pub fn build_payload<
Provider: reth_provider::StateProviderFactory,
Pool: reth_transaction_pool::TransactionPool,
>(
context: &BuildContext,
threshold_value: U256,
provider: &Provider,
pool: &Pool,
) -> Result<BuildOutcome, Error> {
let state = State::new(provider.state_by_block_hash(context.parent_hash)?);
let db = SubState::new(state);
let mut post_state = PostState::default();

let (mut executed_txs, total_fees, mut cumulative_gas_used, mut db) =
assemble_txs_from_pool(context, pool, db, &mut post_state)?;

if total_fees < threshold_value {
return Ok(BuildOutcome::Worse { threshold: threshold_value, provided: total_fees })
}

let (revenue, total_payment) = determine_payment(context, &total_fees);

let payment_tx = construct_payment_tx(context, total_payment, &mut db)?;

// NOTE: extend `executed_txs` with the `payment_tx`
let env = Env {
cfg: context.cfg_env.clone(),
block: context.block_env.clone(),
Expand All @@ -182,6 +258,7 @@ pub fn build_payload<

let ResultAndState { result, state } = evm.transact().map_err(Error::Execution)?;

let block_number = context.number();
commit_state_changes(&mut db, &mut post_state, block_number, state, true);

let gas_used = result.gas_used();
Expand All @@ -199,49 +276,8 @@ pub fn build_payload<

executed_txs.push(payment_tx.into_signed());

let withdrawals_root = process_withdrawals(
&context.withdrawals,
&context.chain_spec,
&mut db,
&mut post_state,
context.timestamp,
block_number,
)?;

let receipts_root = post_state.receipts_root(block_number);
let logs_bloom = post_state.logs_bloom(block_number);
let state_root = db.db.0.state_root(post_state)?;
let transactions_root = proofs::calculate_transaction_root(&executed_txs);

let header = Header {
parent_hash,
ommers_hash: EMPTY_OMMER_ROOT,
beneficiary: context.block_env.coinbase,
state_root,
transactions_root,
withdrawals_root: Some(withdrawals_root),
receipts_root,
logs_bloom,
timestamp: context.timestamp,
mix_hash: H256::from_slice(context.prev_randao.as_ref()),
nonce: BEACON_NONCE,
base_fee_per_gas: Some(base_fee),
number: block_number,
gas_limit: block_gas_limit,
difficulty: U256::ZERO,
gas_used: cumulative_gas_used,
extra_data: context.extra_data.clone(),
blob_gas_used: None,
excess_blob_gas: None,
};

let payload = Block {
header,
body: executed_txs,
ommers: vec![],
withdrawals: Some(context.withdrawals.clone()),
};
let payload = payload.seal_slow();
let payload =
assemble_payload_from_txs(context, db, post_state, cumulative_gas_used, executed_txs)?;

Ok(BuildOutcome::BetterOrEqual(PayloadWithPayments {
payload: Some(payload),
Expand Down

0 comments on commit 75b87a5

Please sign in to comment.