|
| 1 | +use alloy_evm::Database; |
| 2 | +use alloy_primitives::{ |
| 3 | + Address, |
| 4 | + map::foldhash::{HashSet, HashSetExt}, |
| 5 | +}; |
| 6 | +use core::fmt::Debug; |
| 7 | +use op_revm::OpTransactionError; |
| 8 | +use reth_evm::{ConfigureEvm, Evm, eth::receipt_builder::ReceiptBuilderCtx}; |
| 9 | +use reth_node_api::PayloadBuilderError; |
1 | 10 | use reth_optimism_primitives::OpTransactionSigned; |
2 | 11 | use reth_primitives::Recovered; |
| 12 | +use reth_provider::{ProviderError, StateProvider}; |
| 13 | +use reth_revm::{ |
| 14 | + State, database::StateProviderDatabase, db::states::bundle_state::BundleRetention, |
| 15 | +}; |
| 16 | +use revm::{ |
| 17 | + DatabaseCommit, |
| 18 | + context::result::{EVMError, ResultAndState}, |
| 19 | +}; |
| 20 | +use tracing::warn; |
3 | 21 |
|
4 | | -use crate::tx_signer::Signer; |
| 22 | +use crate::{builders::context::OpPayloadBuilderCtx, primitives::reth::ExecutionInfo}; |
5 | 23 |
|
6 | | -pub trait BuilderTx { |
7 | | - fn estimated_builder_tx_gas(&self) -> u64; |
8 | | - fn estimated_builder_tx_da_size(&self) -> Option<u64>; |
9 | | - fn signed_builder_tx(&self) -> Result<Recovered<OpTransactionSigned>, secp256k1::Error>; |
| 24 | +#[derive(Debug, Clone)] |
| 25 | +pub struct BuilderTransactionCtx { |
| 26 | + pub gas_used: u64, |
| 27 | + pub da_size: u64, |
| 28 | + pub signed_tx: Recovered<OpTransactionSigned>, |
10 | 29 | } |
11 | 30 |
|
12 | | -// Scaffolding for how to construct the end of block builder transaction |
13 | | -// This will be the regular end of block transaction without the TEE key |
14 | | -#[derive(Clone)] |
15 | | -pub(super) struct StandardBuilderTx { |
16 | | - #[allow(dead_code)] |
17 | | - pub signer: Option<Signer>, |
| 31 | +/// Possible error variants during construction of builder txs. |
| 32 | +#[derive(Debug, thiserror::Error)] |
| 33 | +pub enum BuilderTransactionError { |
| 34 | + /// Builder account load fails to get builder nonce |
| 35 | + #[error("failed to load account {0}")] |
| 36 | + AccountLoadFailed(Address), |
| 37 | + /// Signature signing fails |
| 38 | + #[error("failed to sign transaction: {0}")] |
| 39 | + SigningError(secp256k1::Error), |
| 40 | + /// Unrecoverable error during evm execution. |
| 41 | + #[error("evm execution error {0}")] |
| 42 | + EvmExecutionError(Box<dyn core::error::Error + Send + Sync>), |
| 43 | + /// Any other builder transaction errors. |
| 44 | + #[error(transparent)] |
| 45 | + Other(Box<dyn core::error::Error + Send + Sync>), |
18 | 46 | } |
19 | 47 |
|
20 | | -impl BuilderTx for StandardBuilderTx { |
21 | | - fn estimated_builder_tx_gas(&self) -> u64 { |
22 | | - todo!() |
| 48 | +impl From<secp256k1::Error> for BuilderTransactionError { |
| 49 | + fn from(error: secp256k1::Error) -> Self { |
| 50 | + BuilderTransactionError::SigningError(error) |
23 | 51 | } |
| 52 | +} |
| 53 | + |
| 54 | +impl From<EVMError<ProviderError, OpTransactionError>> for BuilderTransactionError { |
| 55 | + fn from(error: EVMError<ProviderError, OpTransactionError>) -> Self { |
| 56 | + BuilderTransactionError::EvmExecutionError(Box::new(error)) |
| 57 | + } |
| 58 | +} |
24 | 59 |
|
25 | | - fn estimated_builder_tx_da_size(&self) -> Option<u64> { |
26 | | - todo!() |
| 60 | +impl From<BuilderTransactionError> for PayloadBuilderError { |
| 61 | + fn from(error: BuilderTransactionError) -> Self { |
| 62 | + match error { |
| 63 | + BuilderTransactionError::EvmExecutionError(e) => { |
| 64 | + PayloadBuilderError::EvmExecutionError(e) |
| 65 | + } |
| 66 | + _ => PayloadBuilderError::Other(Box::new(error)), |
| 67 | + } |
27 | 68 | } |
| 69 | +} |
| 70 | + |
| 71 | +pub trait BuilderTransactions<ExtraCtx: Debug + Default = ()>: Debug { |
| 72 | + fn simulate_builder_txs<Extra: Debug + Default>( |
| 73 | + &self, |
| 74 | + state_provider: impl StateProvider + Clone, |
| 75 | + info: &mut ExecutionInfo<Extra>, |
| 76 | + ctx: &OpPayloadBuilderCtx<ExtraCtx>, |
| 77 | + db: &mut State<impl Database>, |
| 78 | + ) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError>; |
| 79 | + |
| 80 | + fn add_builder_txs<Extra: Debug + Default>( |
| 81 | + &self, |
| 82 | + state_provider: impl StateProvider + Clone, |
| 83 | + info: &mut ExecutionInfo<Extra>, |
| 84 | + builder_ctx: &OpPayloadBuilderCtx<ExtraCtx>, |
| 85 | + db: &mut State<impl Database>, |
| 86 | + ) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError> { |
| 87 | + { |
| 88 | + let mut evm = builder_ctx |
| 89 | + .evm_config |
| 90 | + .evm_with_env(&mut *db, builder_ctx.evm_env.clone()); |
| 91 | + |
| 92 | + let mut invalid: HashSet<Address> = HashSet::new(); |
| 93 | + |
| 94 | + let builder_txs = |
| 95 | + self.simulate_builder_txs(state_provider, info, builder_ctx, evm.db_mut())?; |
| 96 | + for builder_tx in builder_txs.iter() { |
| 97 | + if invalid.contains(&builder_tx.signed_tx.signer()) { |
| 98 | + warn!(target: "payload_builder", tx_hash = ?builder_tx.signed_tx.tx_hash(), "builder signer invalid as previous builder tx reverted"); |
| 99 | + continue; |
| 100 | + } |
| 101 | + |
| 102 | + let ResultAndState { result, state } = evm |
| 103 | + .transact(&builder_tx.signed_tx) |
| 104 | + .map_err(|err| BuilderTransactionError::EvmExecutionError(Box::new(err)))?; |
| 105 | + |
| 106 | + if !result.is_success() { |
| 107 | + warn!(target: "payload_builder", tx_hash = ?builder_tx.signed_tx.tx_hash(), "builder tx reverted"); |
| 108 | + invalid.insert(builder_tx.signed_tx.signer()); |
| 109 | + continue; |
| 110 | + } |
| 111 | + |
| 112 | + // Add gas used by the transaction to cumulative gas used, before creating the receipt |
| 113 | + let gas_used = result.gas_used(); |
| 114 | + info.cumulative_gas_used += gas_used; |
| 115 | + |
| 116 | + let ctx = ReceiptBuilderCtx { |
| 117 | + tx: builder_tx.signed_tx.inner(), |
| 118 | + evm: &evm, |
| 119 | + result, |
| 120 | + state: &state, |
| 121 | + cumulative_gas_used: info.cumulative_gas_used, |
| 122 | + }; |
| 123 | + info.receipts.push(builder_ctx.build_receipt(ctx, None)); |
| 124 | + |
| 125 | + // Commit changes |
| 126 | + evm.db_mut().commit(state); |
| 127 | + |
| 128 | + // Append sender and transaction to the respective lists |
| 129 | + info.executed_senders.push(builder_tx.signed_tx.signer()); |
| 130 | + info.executed_transactions |
| 131 | + .push(builder_tx.signed_tx.clone().into_inner()); |
| 132 | + } |
| 133 | + |
| 134 | + // Release the db reference by dropping evm |
| 135 | + drop(evm); |
| 136 | + |
| 137 | + Ok(builder_txs) |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + fn simulate_builder_txs_state<Extra: Debug + Default>( |
| 142 | + &self, |
| 143 | + state_provider: impl StateProvider + Clone, |
| 144 | + builder_txs: Vec<&BuilderTransactionCtx>, |
| 145 | + ctx: &OpPayloadBuilderCtx<ExtraCtx>, |
| 146 | + db: &mut State<impl Database>, |
| 147 | + ) -> Result<State<StateProviderDatabase<impl StateProvider>>, BuilderTransactionError> { |
| 148 | + let state = StateProviderDatabase::new(state_provider.clone()); |
| 149 | + let mut simulation_state = State::builder() |
| 150 | + .with_database(state) |
| 151 | + .with_bundle_prestate(db.bundle_state.clone()) |
| 152 | + .with_bundle_update() |
| 153 | + .build(); |
| 154 | + let mut evm = ctx |
| 155 | + .evm_config |
| 156 | + .evm_with_env(&mut simulation_state, ctx.evm_env.clone()); |
| 157 | + |
| 158 | + for builder_tx in builder_txs { |
| 159 | + let ResultAndState { state, .. } = evm |
| 160 | + .transact(&builder_tx.signed_tx) |
| 161 | + .map_err(|err| BuilderTransactionError::EvmExecutionError(Box::new(err)))?; |
| 162 | + |
| 163 | + evm.db_mut().commit(state); |
| 164 | + evm.db_mut().merge_transitions(BundleRetention::Reverts); |
| 165 | + } |
28 | 166 |
|
29 | | - fn signed_builder_tx(&self) -> Result<Recovered<OpTransactionSigned>, secp256k1::Error> { |
30 | | - todo!() |
| 167 | + Ok(simulation_state) |
31 | 168 | } |
32 | 169 | } |
0 commit comments