Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promote create_reorg to component trait method #12750

Open
Tracked by #12578
emhane opened this issue Nov 21, 2024 · 0 comments
Open
Tracked by #12578

Promote create_reorg to component trait method #12750

emhane opened this issue Nov 21, 2024 · 0 comments
Labels
A-sdk Related to reth's use as a library C-debt Refactor of code section that is hard to understand or maintain D-complex Quite challenging from either a design or technical perspective Ask for help! S-needs-design This issue requires design work to think about how it would best be accomplished

Comments

@emhane
Copy link
Member

emhane commented Nov 21, 2024

Describe the feature

In order to make it possible to have use generic data primitives in consensus, the function create_reorg_head must be moved to a trait method of consensus engine component, so it can be implemented separately for op and l1, since the function constructs a block type and receipt type.

fn create_reorg_head<Provider, Evm, Spec>(
provider: &Provider,
evm_config: &Evm,
payload_validator: &ExecutionPayloadValidator<Spec>,
mut depth: usize,
next_payload: ExecutionPayload,
next_sidecar: ExecutionPayloadSidecar,
) -> RethResult<(ExecutionPayload, ExecutionPayloadSidecar)>
where
Provider: BlockReader + StateProviderFactory,
Evm: ConfigureEvm<Header = Header>,
Spec: EthereumHardforks,
{
let chain_spec = payload_validator.chain_spec();
// Ensure next payload is valid.
let next_block = payload_validator
.ensure_well_formed_payload(next_payload, next_sidecar)
.map_err(RethError::msg)?;
// Fetch reorg target block depending on its depth and its parent.
let mut previous_hash = next_block.parent_hash;
let mut candidate_transactions = next_block.body.transactions;
let reorg_target = 'target: {
loop {
let reorg_target = provider
.block_by_hash(previous_hash)?
.ok_or_else(|| ProviderError::HeaderNotFound(previous_hash.into()))?;
if depth == 0 {
break 'target reorg_target
}
depth -= 1;
previous_hash = reorg_target.parent_hash;
candidate_transactions = reorg_target.body.transactions;
}
};
let reorg_target_parent = provider
.block_by_hash(reorg_target.parent_hash)?
.ok_or_else(|| ProviderError::HeaderNotFound(reorg_target.parent_hash.into()))?;
debug!(target: "engine::stream::reorg", number = reorg_target.number, hash = %previous_hash, "Selected reorg target");
// Configure state
let state_provider = provider.state_by_block_hash(reorg_target.parent_hash)?;
let mut state = State::builder()
.with_database_ref(StateProviderDatabase::new(&state_provider))
.with_bundle_update()
.build();
// Configure environments
let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default());
let mut block_env = BlockEnv::default();
evm_config.fill_cfg_and_block_env(&mut cfg, &mut block_env, &reorg_target.header, U256::MAX);
let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default());
let mut evm = evm_config.evm_with_env(&mut state, env);
// apply eip-4788 pre block contract call
let mut system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone());
system_caller.apply_beacon_root_contract_call(
reorg_target.timestamp,
reorg_target.number,
reorg_target.parent_beacon_block_root,
&mut evm,
)?;
let mut cumulative_gas_used = 0;
let mut sum_blob_gas_used = 0;
let mut transactions = Vec::new();
let mut receipts = Vec::new();
let mut versioned_hashes = Vec::new();
for tx in candidate_transactions {
// ensure we still have capacity for this transaction
if cumulative_gas_used + tx.gas_limit() > reorg_target.gas_limit {
continue
}
// Configure the environment for the block.
let tx_recovered = tx.clone().try_into_ecrecovered().map_err(|_| {
BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError)
})?;
evm_config.fill_tx_env(evm.tx_mut(), &tx_recovered, tx_recovered.signer());
let exec_result = match evm.transact() {
Ok(result) => result,
error @ Err(EVMError::Transaction(_) | EVMError::Header(_)) => {
trace!(target: "engine::stream::reorg", hash = %tx.hash(), ?error, "Error executing transaction from next block");
continue
}
// Treat error as fatal
Err(error) => {
return Err(RethError::Execution(BlockExecutionError::Validation(
BlockValidationError::EVM { hash: tx.hash, error: Box::new(error) },
)))
}
};
evm.db_mut().commit(exec_result.state);
if let Some(blob_tx) = tx.transaction.as_eip4844() {
sum_blob_gas_used += blob_tx.blob_gas();
versioned_hashes.extend(blob_tx.blob_versioned_hashes.clone());
}
cumulative_gas_used += exec_result.result.gas_used();
#[allow(clippy::needless_update)] // side-effect of optimism fields
receipts.push(Some(Receipt {
tx_type: tx.tx_type(),
success: exec_result.result.is_success(),
cumulative_gas_used,
logs: exec_result.result.into_logs().into_iter().map(Into::into).collect(),
..Default::default()
}));
// append transaction to the list of executed transactions
transactions.push(tx);
}
drop(evm);
if let Some(withdrawals) = &reorg_target.body.withdrawals {
state.increment_balances(post_block_withdrawals_balance_increments(
chain_spec,
reorg_target.timestamp,
withdrawals,
))?;
}
// merge all transitions into bundle state, this would apply the withdrawal balance changes
// and 4788 contract call
state.merge_transitions(BundleRetention::PlainState);
let outcome: ExecutionOutcome = ExecutionOutcome::new(
state.take_bundle(),
Receipts::from(vec![receipts]),
reorg_target.number,
Default::default(),
);
let hashed_state = HashedPostState::from_bundle_state(&outcome.state().state);
let (blob_gas_used, excess_blob_gas) =
if chain_spec.is_cancun_active_at_timestamp(reorg_target.timestamp) {
(
Some(sum_blob_gas_used),
Some(calc_excess_blob_gas(
reorg_target_parent.excess_blob_gas.unwrap_or_default(),
reorg_target_parent.blob_gas_used.unwrap_or_default(),
)),
)
} else {
(None, None)
};
let reorg_block = Block {
header: Header {
// Set same fields as the reorg target
parent_hash: reorg_target.header.parent_hash,
ommers_hash: reorg_target.header.ommers_hash,
beneficiary: reorg_target.header.beneficiary,
difficulty: reorg_target.header.difficulty,
number: reorg_target.header.number,
gas_limit: reorg_target.header.gas_limit,
timestamp: reorg_target.header.timestamp,
extra_data: reorg_target.header.extra_data,
mix_hash: reorg_target.header.mix_hash,
nonce: reorg_target.header.nonce,
base_fee_per_gas: reorg_target.header.base_fee_per_gas,
parent_beacon_block_root: reorg_target.header.parent_beacon_block_root,
withdrawals_root: reorg_target.header.withdrawals_root,
// Compute or add new fields
transactions_root: proofs::calculate_transaction_root(&transactions),
receipts_root: outcome.receipts_root_slow(reorg_target.header.number).unwrap(),
logs_bloom: outcome.block_logs_bloom(reorg_target.header.number).unwrap(),
requests_hash: None, // TODO(prague)
gas_used: cumulative_gas_used,
blob_gas_used: blob_gas_used.map(Into::into),
excess_blob_gas: excess_blob_gas.map(Into::into),
state_root: state_provider.state_root(hashed_state)?,
},
body: BlockBody {
transactions,
ommers: reorg_target.body.ommers,
withdrawals: reorg_target.body.withdrawals,
},
}
.seal_slow();
Ok((
block_to_payload(reorg_block),
// todo(onbjerg): how do we support execution requests?
reorg_target
.header
.parent_beacon_block_root
.map(|root| {
ExecutionPayloadSidecar::v3(CancunPayloadFields {
parent_beacon_block_root: root,
versioned_hashes,
})
})
.unwrap_or_else(ExecutionPayloadSidecar::none),
))
}

Additional context

No response

@emhane emhane added A-sdk Related to reth's use as a library C-debt Refactor of code section that is hard to understand or maintain D-complex Quite challenging from either a design or technical perspective Ask for help! S-needs-design This issue requires design work to think about how it would best be accomplished labels Nov 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-sdk Related to reth's use as a library C-debt Refactor of code section that is hard to understand or maintain D-complex Quite challenging from either a design or technical perspective Ask for help! S-needs-design This issue requires design work to think about how it would best be accomplished
Projects
Status: Todo
Development

No branches or pull requests

1 participant