mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: BlockExecutionStrategy::execute_transaction API (#14586)
This commit is contained in:
@ -92,6 +92,8 @@ pub struct EthExecutionStrategy<'a, Evm, EvmConfig> {
|
|||||||
evm: Evm,
|
evm: Evm,
|
||||||
/// Receipts of executed transactions.
|
/// Receipts of executed transactions.
|
||||||
receipts: Vec<Receipt>,
|
receipts: Vec<Receipt>,
|
||||||
|
/// Total gas used by transactions in this block.
|
||||||
|
gas_used: u64,
|
||||||
/// Utility to call system smart contracts.
|
/// Utility to call system smart contracts.
|
||||||
system_caller: SystemCaller<&'a ChainSpec>,
|
system_caller: SystemCaller<&'a ChainSpec>,
|
||||||
}
|
}
|
||||||
@ -108,6 +110,7 @@ impl<'a, Evm, EvmConfig> EthExecutionStrategy<'a, Evm, EvmConfig> {
|
|||||||
factory,
|
factory,
|
||||||
block,
|
block,
|
||||||
receipts: Vec::new(),
|
receipts: Vec::new(),
|
||||||
|
gas_used: 0,
|
||||||
system_caller: SystemCaller::new(&factory.chain_spec),
|
system_caller: SystemCaller::new(&factory.chain_spec),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,49 +135,47 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_transactions<'a>(
|
fn execute_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
transactions: impl IntoIterator<Item = Recovered<&'a TransactionSigned>>,
|
tx: Recovered<&TransactionSigned>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let mut cumulative_gas_used = 0;
|
// The sum of the transaction's gas limit, Tg, and the gas utilized in this block prior,
|
||||||
for (tx_index, tx) in transactions.into_iter().enumerate() {
|
// must be no greater than the block's gasLimit.
|
||||||
// The sum of the transaction's gas limit, Tg, and the gas utilized in this block prior,
|
let block_available_gas = self.block.gas_limit() - self.gas_used;
|
||||||
// must be no greater than the block's gasLimit.
|
if tx.gas_limit() > block_available_gas {
|
||||||
let block_available_gas = self.block.gas_limit() - cumulative_gas_used;
|
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||||
if tx.gas_limit() > block_available_gas {
|
transaction_gas_limit: tx.gas_limit(),
|
||||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
block_available_gas,
|
||||||
transaction_gas_limit: tx.gas_limit(),
|
|
||||||
block_available_gas,
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
|
.into())
|
||||||
let tx_env = self.evm_config.tx_env(tx.clone());
|
|
||||||
let hash = tx.hash();
|
|
||||||
|
|
||||||
// Execute transaction.
|
|
||||||
let result_and_state = self.evm.transact(tx_env).map_err(move |err| {
|
|
||||||
// Ensure hash is calculated for error log, if not already done
|
|
||||||
BlockValidationError::EVM { hash: *hash, error: Box::new(err) }
|
|
||||||
})?;
|
|
||||||
self.system_caller
|
|
||||||
.on_state(StateChangeSource::Transaction(tx_index), &result_and_state.state);
|
|
||||||
let ResultAndState { result, state } = result_and_state;
|
|
||||||
self.evm.db_mut().commit(state);
|
|
||||||
|
|
||||||
// append gas used
|
|
||||||
cumulative_gas_used += result.gas_used();
|
|
||||||
|
|
||||||
// Push transaction changeset and calculate header bloom filter for receipt.
|
|
||||||
self.receipts.push(Receipt {
|
|
||||||
tx_type: tx.tx_type(),
|
|
||||||
// Success flag was added in `EIP-658: Embedding transaction status code in
|
|
||||||
// receipts`.
|
|
||||||
success: result.is_success(),
|
|
||||||
cumulative_gas_used,
|
|
||||||
logs: result.into_logs(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let tx_env = self.evm_config.tx_env(tx.clone());
|
||||||
|
let hash = tx.hash();
|
||||||
|
|
||||||
|
// Execute transaction.
|
||||||
|
let result_and_state = self.evm.transact(tx_env).map_err(move |err| {
|
||||||
|
// Ensure hash is calculated for error log, if not already done
|
||||||
|
BlockValidationError::EVM { hash: *hash, error: Box::new(err) }
|
||||||
|
})?;
|
||||||
|
self.system_caller
|
||||||
|
.on_state(StateChangeSource::Transaction(self.receipts.len()), &result_and_state.state);
|
||||||
|
let ResultAndState { result, state } = result_and_state;
|
||||||
|
self.evm.db_mut().commit(state);
|
||||||
|
|
||||||
|
// append gas used
|
||||||
|
self.gas_used += result.gas_used();
|
||||||
|
|
||||||
|
// Push transaction changeset and calculate header bloom filter for receipt.
|
||||||
|
self.receipts.push(Receipt {
|
||||||
|
tx_type: tx.tx_type(),
|
||||||
|
// Success flag was added in `EIP-658: Embedding transaction status code in
|
||||||
|
// receipts`.
|
||||||
|
success: result.is_success(),
|
||||||
|
cumulative_gas_used: self.gas_used,
|
||||||
|
logs: result.into_logs(),
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -173,12 +173,10 @@ pub trait BlockExecutionStrategy {
|
|||||||
/// Applies any necessary changes before executing the block's transactions.
|
/// Applies any necessary changes before executing the block's transactions.
|
||||||
fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error>;
|
fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
/// Executes all transactions in the block.
|
/// Executes a single transaction and applies execution result to internal state.
|
||||||
fn execute_transactions<'a>(
|
fn execute_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
transactions: impl IntoIterator<
|
tx: Recovered<&<Self::Primitives as NodePrimitives>::SignedTx>,
|
||||||
Item = Recovered<&'a <Self::Primitives as NodePrimitives>::SignedTx>,
|
|
||||||
>,
|
|
||||||
) -> Result<(), Self::Error>;
|
) -> Result<(), Self::Error>;
|
||||||
|
|
||||||
/// Applies any necessary changes after executing the block's transactions.
|
/// Applies any necessary changes after executing the block's transactions.
|
||||||
@ -278,7 +276,9 @@ where
|
|||||||
let mut strategy = self.strategy_factory.create_strategy(&mut self.db, block);
|
let mut strategy = self.strategy_factory.create_strategy(&mut self.db, block);
|
||||||
|
|
||||||
strategy.apply_pre_execution_changes()?;
|
strategy.apply_pre_execution_changes()?;
|
||||||
strategy.execute_transactions(block.transactions_recovered())?;
|
for tx in block.transactions_recovered() {
|
||||||
|
strategy.execute_transaction(tx)?;
|
||||||
|
}
|
||||||
let result = strategy.apply_post_execution_changes()?;
|
let result = strategy.apply_post_execution_changes()?;
|
||||||
|
|
||||||
self.db.merge_transitions(BundleRetention::Reverts);
|
self.db.merge_transitions(BundleRetention::Reverts);
|
||||||
@ -298,7 +298,9 @@ where
|
|||||||
strategy.with_state_hook(Some(Box::new(state_hook)));
|
strategy.with_state_hook(Some(Box::new(state_hook)));
|
||||||
|
|
||||||
strategy.apply_pre_execution_changes()?;
|
strategy.apply_pre_execution_changes()?;
|
||||||
strategy.execute_transactions(block.transactions_recovered())?;
|
for tx in block.transactions_recovered() {
|
||||||
|
strategy.execute_transaction(tx)?;
|
||||||
|
}
|
||||||
let result = strategy.apply_post_execution_changes()?;
|
let result = strategy.apply_post_execution_changes()?;
|
||||||
|
|
||||||
self.db.merge_transitions(BundleRetention::Reverts);
|
self.db.merge_transitions(BundleRetention::Reverts);
|
||||||
@ -444,9 +446,9 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_transactions<'a>(
|
fn execute_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
_transactions: impl IntoIterator<Item = Recovered<&'a TransactionSigned>>,
|
_tx: Recovered<&TransactionSigned>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,6 +106,10 @@ pub struct OpExecutionStrategy<'a, Evm, N: NodePrimitives, ChainSpec, EvmConfig:
|
|||||||
evm: Evm,
|
evm: Evm,
|
||||||
/// Receipts of executed transactions.
|
/// Receipts of executed transactions.
|
||||||
receipts: Vec<N::Receipt>,
|
receipts: Vec<N::Receipt>,
|
||||||
|
/// Total gas used by executed transactions.
|
||||||
|
gas_used: u64,
|
||||||
|
/// Whether Regolith hardfork is active.
|
||||||
|
is_regolith: bool,
|
||||||
/// Utility to call system smart contracts.
|
/// Utility to call system smart contracts.
|
||||||
system_caller: SystemCaller<&'a ChainSpec>,
|
system_caller: SystemCaller<&'a ChainSpec>,
|
||||||
}
|
}
|
||||||
@ -127,6 +131,8 @@ where
|
|||||||
factory,
|
factory,
|
||||||
block,
|
block,
|
||||||
receipts: Vec::new(),
|
receipts: Vec::new(),
|
||||||
|
gas_used: 0,
|
||||||
|
is_regolith: factory.chain_spec.is_regolith_active_at_timestamp(block.timestamp()),
|
||||||
system_caller: SystemCaller::new(&factory.chain_spec),
|
system_caller: SystemCaller::new(&factory.chain_spec),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,96 +171,88 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_transactions<'a>(
|
fn execute_transaction(&mut self, tx: Recovered<&N::SignedTx>) -> Result<(), Self::Error> {
|
||||||
&mut self,
|
// The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior,
|
||||||
transactions: impl IntoIterator<Item = Recovered<&'a N::SignedTx>>,
|
// must be no greater than the block’s gasLimit.
|
||||||
) -> Result<(), Self::Error> {
|
let block_available_gas = self.block.gas_limit() - self.gas_used;
|
||||||
let is_regolith = self.chain_spec.is_regolith_active_at_timestamp(self.block.timestamp());
|
if tx.gas_limit() > block_available_gas && (self.is_regolith || !tx.is_deposit()) {
|
||||||
|
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||||
let mut cumulative_gas_used = 0;
|
transaction_gas_limit: tx.gas_limit(),
|
||||||
for (tx_index, tx) in transactions.into_iter().enumerate() {
|
block_available_gas,
|
||||||
// The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior,
|
|
||||||
// must be no greater than the block’s gasLimit.
|
|
||||||
let block_available_gas = self.block.gas_limit() - cumulative_gas_used;
|
|
||||||
if tx.gas_limit() > block_available_gas && (is_regolith || !tx.is_deposit()) {
|
|
||||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
|
||||||
transaction_gas_limit: tx.gas_limit(),
|
|
||||||
block_available_gas,
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
|
.into())
|
||||||
// Cache the depositor account prior to the state transition for the deposit nonce.
|
|
||||||
//
|
|
||||||
// Note that this *only* needs to be done post-regolith hardfork, as deposit nonces
|
|
||||||
// were not introduced in Bedrock. In addition, regular transactions don't have deposit
|
|
||||||
// nonces, so we don't need to touch the DB for those.
|
|
||||||
let depositor = (is_regolith && tx.is_deposit())
|
|
||||||
.then(|| {
|
|
||||||
self.evm
|
|
||||||
.db_mut()
|
|
||||||
.load_cache_account(tx.signer())
|
|
||||||
.map(|acc| acc.account_info().unwrap_or_default())
|
|
||||||
})
|
|
||||||
.transpose()
|
|
||||||
.map_err(|_| OpBlockExecutionError::AccountLoadFailed(tx.signer()))?;
|
|
||||||
|
|
||||||
let tx_env = self.evm_config.tx_env(&tx);
|
|
||||||
let hash = tx.tx_hash();
|
|
||||||
|
|
||||||
// Execute transaction.
|
|
||||||
let result_and_state = self.evm.transact(tx_env).map_err(move |err| {
|
|
||||||
// Ensure hash is calculated for error log, if not already done
|
|
||||||
BlockValidationError::EVM { hash: *hash, error: Box::new(err) }
|
|
||||||
})?;
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
target: "evm",
|
|
||||||
?tx,
|
|
||||||
"Executed transaction"
|
|
||||||
);
|
|
||||||
self.system_caller
|
|
||||||
.on_state(StateChangeSource::Transaction(tx_index), &result_and_state.state);
|
|
||||||
let ResultAndState { result, state } = result_and_state;
|
|
||||||
self.evm.db_mut().commit(state);
|
|
||||||
|
|
||||||
// append gas used
|
|
||||||
cumulative_gas_used += result.gas_used();
|
|
||||||
|
|
||||||
self.receipts.push(
|
|
||||||
match self.receipt_builder.build_receipt(ReceiptBuilderCtx {
|
|
||||||
tx: tx.tx(),
|
|
||||||
result,
|
|
||||||
cumulative_gas_used,
|
|
||||||
}) {
|
|
||||||
Ok(receipt) => receipt,
|
|
||||||
Err(ctx) => {
|
|
||||||
let receipt = Receipt {
|
|
||||||
// Success flag was added in `EIP-658: Embedding transaction status code
|
|
||||||
// in receipts`.
|
|
||||||
status: Eip658Value::Eip658(ctx.result.is_success()),
|
|
||||||
cumulative_gas_used,
|
|
||||||
logs: ctx.result.into_logs(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.receipt_builder.build_deposit_receipt(OpDepositReceipt {
|
|
||||||
inner: receipt,
|
|
||||||
deposit_nonce: depositor.map(|account| account.nonce),
|
|
||||||
// The deposit receipt version was introduced in Canyon to indicate an
|
|
||||||
// update to how receipt hashes should be computed
|
|
||||||
// when set. The state transition process ensures
|
|
||||||
// this is only set for post-Canyon deposit
|
|
||||||
// transactions.
|
|
||||||
deposit_receipt_version: (tx.is_deposit() &&
|
|
||||||
self.chain_spec
|
|
||||||
.is_canyon_active_at_timestamp(self.block.timestamp()))
|
|
||||||
.then_some(1),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache the depositor account prior to the state transition for the deposit nonce.
|
||||||
|
//
|
||||||
|
// Note that this *only* needs to be done post-regolith hardfork, as deposit nonces
|
||||||
|
// were not introduced in Bedrock. In addition, regular transactions don't have deposit
|
||||||
|
// nonces, so we don't need to touch the DB for those.
|
||||||
|
let depositor = (self.is_regolith && tx.is_deposit())
|
||||||
|
.then(|| {
|
||||||
|
self.evm
|
||||||
|
.db_mut()
|
||||||
|
.load_cache_account(tx.signer())
|
||||||
|
.map(|acc| acc.account_info().unwrap_or_default())
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
.map_err(|_| OpBlockExecutionError::AccountLoadFailed(tx.signer()))?;
|
||||||
|
|
||||||
|
let tx_env = self.evm_config.tx_env(&tx);
|
||||||
|
let hash = tx.tx_hash();
|
||||||
|
|
||||||
|
// Execute transaction.
|
||||||
|
let result_and_state = self.evm.transact(tx_env).map_err(move |err| {
|
||||||
|
// Ensure hash is calculated for error log, if not already done
|
||||||
|
BlockValidationError::EVM { hash: *hash, error: Box::new(err) }
|
||||||
|
})?;
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
target: "evm",
|
||||||
|
?tx,
|
||||||
|
"Executed transaction"
|
||||||
|
);
|
||||||
|
self.system_caller
|
||||||
|
.on_state(StateChangeSource::Transaction(self.receipts.len()), &result_and_state.state);
|
||||||
|
let ResultAndState { result, state } = result_and_state;
|
||||||
|
self.evm.db_mut().commit(state);
|
||||||
|
|
||||||
|
// append gas used
|
||||||
|
self.gas_used += result.gas_used();
|
||||||
|
|
||||||
|
self.receipts.push(
|
||||||
|
match self.receipt_builder.build_receipt(ReceiptBuilderCtx {
|
||||||
|
tx: tx.tx(),
|
||||||
|
result,
|
||||||
|
cumulative_gas_used: self.gas_used,
|
||||||
|
}) {
|
||||||
|
Ok(receipt) => receipt,
|
||||||
|
Err(ctx) => {
|
||||||
|
let receipt = Receipt {
|
||||||
|
// Success flag was added in `EIP-658: Embedding transaction status code
|
||||||
|
// in receipts`.
|
||||||
|
status: Eip658Value::Eip658(ctx.result.is_success()),
|
||||||
|
cumulative_gas_used: self.gas_used,
|
||||||
|
logs: ctx.result.into_logs(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.receipt_builder.build_deposit_receipt(OpDepositReceipt {
|
||||||
|
inner: receipt,
|
||||||
|
deposit_nonce: depositor.map(|account| account.nonce),
|
||||||
|
// The deposit receipt version was introduced in Canyon to indicate an
|
||||||
|
// update to how receipt hashes should be computed
|
||||||
|
// when set. The state transition process ensures
|
||||||
|
// this is only set for post-Canyon deposit
|
||||||
|
// transactions.
|
||||||
|
deposit_receipt_version: (tx.is_deposit() &&
|
||||||
|
self.chain_spec
|
||||||
|
.is_canyon_active_at_timestamp(self.block.timestamp()))
|
||||||
|
.then_some(1),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -133,9 +133,9 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_transactions<'a>(
|
fn execute_transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
_transactions: impl IntoIterator<Item = Recovered<&'a TransactionSigned>>,
|
_tx: Recovered<&TransactionSigned>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user