mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: refactor autoseal execution into method (#3844)
This commit is contained in:
@ -21,16 +21,26 @@
|
||||
//! be mined.
|
||||
|
||||
use reth_beacon_consensus::BeaconEngineMessage;
|
||||
use reth_interfaces::consensus::{Consensus, ConsensusError};
|
||||
use reth_primitives::{
|
||||
BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Header, SealedBlock,
|
||||
SealedHeader, H256, U256,
|
||||
use reth_interfaces::{
|
||||
consensus::{Consensus, ConsensusError},
|
||||
executor::{BlockExecutionError, BlockValidationError},
|
||||
};
|
||||
use reth_provider::{BlockReaderIdExt, CanonStateNotificationSender};
|
||||
use reth_primitives::{
|
||||
constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT},
|
||||
proofs, Address, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec,
|
||||
Header, ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned, EMPTY_OMMER_ROOT, H256,
|
||||
U256,
|
||||
};
|
||||
use reth_provider::{BlockReaderIdExt, CanonStateNotificationSender, PostState, StateProvider};
|
||||
use reth_revm::executor::Executor;
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use tokio::sync::{mpsc::UnboundedSender, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use tracing::trace;
|
||||
use tracing::{trace, warn};
|
||||
|
||||
mod client;
|
||||
mod mode;
|
||||
@ -182,6 +192,7 @@ impl Storage {
|
||||
}
|
||||
}
|
||||
|
||||
/// In-memory storage for the chain the auto seal engine is building.
|
||||
#[derive(Default, Debug)]
|
||||
pub(crate) struct StorageInner {
|
||||
/// Headers buffered for download.
|
||||
@ -232,4 +243,127 @@ impl StorageInner {
|
||||
self.bodies.insert(self.best_hash, body);
|
||||
self.hash_to_number.insert(self.best_hash, self.best_block);
|
||||
}
|
||||
|
||||
/// Fills in pre-execution header fields based on the current best block and given
|
||||
/// transactions.
|
||||
pub(crate) fn build_header_template(&self, transactions: &Vec<TransactionSigned>) -> Header {
|
||||
// check previous block for base fee
|
||||
let base_fee_per_gas =
|
||||
self.headers.get(&self.best_block).and_then(|parent| parent.next_block_base_fee());
|
||||
|
||||
let mut header = Header {
|
||||
parent_hash: self.best_hash,
|
||||
ommers_hash: EMPTY_OMMER_ROOT,
|
||||
beneficiary: Default::default(),
|
||||
state_root: Default::default(),
|
||||
transactions_root: Default::default(),
|
||||
receipts_root: Default::default(),
|
||||
withdrawals_root: None,
|
||||
logs_bloom: Default::default(),
|
||||
difficulty: U256::from(2),
|
||||
number: self.best_block + 1,
|
||||
gas_limit: ETHEREUM_BLOCK_GAS_LIMIT,
|
||||
gas_used: 0,
|
||||
timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(),
|
||||
mix_hash: Default::default(),
|
||||
nonce: 0,
|
||||
base_fee_per_gas,
|
||||
extra_data: Default::default(),
|
||||
};
|
||||
|
||||
header.transactions_root = if transactions.is_empty() {
|
||||
EMPTY_TRANSACTIONS
|
||||
} else {
|
||||
proofs::calculate_transaction_root(transactions)
|
||||
};
|
||||
|
||||
header
|
||||
}
|
||||
|
||||
/// Executes the block with the given block and senders, on the provided [Executor].
|
||||
///
|
||||
/// This returns the poststate from execution and post-block changes, as well as the gas used.
|
||||
pub(crate) fn execute<DB: StateProvider>(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
executor: &mut Executor<DB>,
|
||||
senders: Vec<Address>,
|
||||
) -> Result<(PostState, u64), BlockExecutionError> {
|
||||
trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions");
|
||||
|
||||
let (post_state, gas_used) =
|
||||
executor.execute_transactions(block, U256::ZERO, Some(senders.clone()))?;
|
||||
|
||||
// apply post block changes
|
||||
let post_state = executor.apply_post_block_changes(block, U256::ZERO, post_state)?;
|
||||
|
||||
Ok((post_state, gas_used))
|
||||
}
|
||||
|
||||
/// Fills in the post-execution header fields based on the given PostState and gas used.
|
||||
/// In doing this, the state root is calculated and the final header is returned.
|
||||
pub(crate) fn complete_header<DB: StateProvider>(
|
||||
&self,
|
||||
mut header: Header,
|
||||
post_state: &PostState,
|
||||
executor: &mut Executor<DB>,
|
||||
gas_used: u64,
|
||||
) -> Header {
|
||||
let receipts = post_state.receipts(header.number);
|
||||
header.receipts_root = if receipts.is_empty() {
|
||||
EMPTY_RECEIPTS
|
||||
} else {
|
||||
let receipts_with_bloom =
|
||||
receipts.iter().map(|r| r.clone().into()).collect::<Vec<ReceiptWithBloom>>();
|
||||
proofs::calculate_receipt_root(&receipts_with_bloom)
|
||||
};
|
||||
|
||||
header.gas_used = gas_used;
|
||||
|
||||
// calculate the state root
|
||||
let state_root = executor.db().db.0.state_root(post_state.clone()).unwrap();
|
||||
header.state_root = state_root;
|
||||
header
|
||||
}
|
||||
|
||||
/// Builds and executes a new block with the given transactions, on the provided [Executor].
|
||||
///
|
||||
/// This returns the header of the executed block, as well as the poststate from execution.
|
||||
pub(crate) fn build_and_execute<DB: StateProvider>(
|
||||
&mut self,
|
||||
transactions: Vec<TransactionSigned>,
|
||||
executor: &mut Executor<DB>,
|
||||
) -> Result<(SealedHeader, PostState), BlockExecutionError> {
|
||||
let header = self.build_header_template(&transactions);
|
||||
|
||||
let block = Block { header, body: transactions, ommers: vec![], withdrawals: None };
|
||||
|
||||
let senders =
|
||||
block.body.iter().map(|tx| tx.recover_signer()).collect::<Option<Vec<_>>>().ok_or(
|
||||
BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError),
|
||||
)?;
|
||||
|
||||
trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions");
|
||||
|
||||
// now execute the block
|
||||
let (post_state, gas_used) = self.execute(&block, executor, senders)?;
|
||||
|
||||
let Block { header, body, .. } = block;
|
||||
let body = BlockBody { transactions: body, ommers: vec![], withdrawals: None };
|
||||
|
||||
trace!(target: "consensus::auto", ?post_state, ?header, ?body, "executed block, calculating state root and completing header");
|
||||
|
||||
// fill in the rest of the fields
|
||||
let header = self.complete_header(header, &post_state, executor, gas_used);
|
||||
|
||||
trace!(target: "consensus::auto", root=?header.state_root, ?body, "calculated root");
|
||||
|
||||
// finally insert into storage
|
||||
self.insert_new_block(header.clone(), body);
|
||||
|
||||
// set new header with hash that should have been updated by insert_new_block
|
||||
let new_header = header.seal(self.best_hash);
|
||||
|
||||
Ok((new_header, post_state))
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,7 @@ use crate::{mode::MiningMode, Storage};
|
||||
use futures_util::{future::BoxFuture, FutureExt};
|
||||
use reth_beacon_consensus::{BeaconEngineMessage, ForkchoiceStatus};
|
||||
use reth_interfaces::consensus::ForkchoiceState;
|
||||
use reth_primitives::{
|
||||
constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT},
|
||||
proofs, Block, BlockBody, ChainSpec, Header, IntoRecoveredTransaction, ReceiptWithBloom,
|
||||
SealedBlockWithSenders, EMPTY_OMMER_ROOT, U256,
|
||||
};
|
||||
use reth_primitives::{Block, ChainSpec, IntoRecoveredTransaction, SealedBlockWithSenders};
|
||||
use reth_provider::{CanonChainTracker, CanonStateNotificationSender, Chain, StateProviderFactory};
|
||||
use reth_revm::{
|
||||
database::{State, SubState},
|
||||
@ -20,11 +16,10 @@ use std::{
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use tokio::sync::{mpsc::UnboundedSender, oneshot};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
/// A Future that listens for new ready transactions and puts new blocks into storage
|
||||
pub struct MiningTask<Client, Pool: TransactionPool> {
|
||||
@ -123,104 +118,28 @@ where
|
||||
this.insert_task = Some(Box::pin(async move {
|
||||
let mut storage = storage.write().await;
|
||||
|
||||
// check previous block for base fee
|
||||
let base_fee_per_gas = storage
|
||||
.headers
|
||||
.get(&storage.best_block)
|
||||
.and_then(|parent| parent.next_block_base_fee());
|
||||
|
||||
let mut header = Header {
|
||||
parent_hash: storage.best_hash,
|
||||
ommers_hash: EMPTY_OMMER_ROOT,
|
||||
beneficiary: Default::default(),
|
||||
state_root: Default::default(),
|
||||
transactions_root: Default::default(),
|
||||
receipts_root: Default::default(),
|
||||
withdrawals_root: None,
|
||||
logs_bloom: Default::default(),
|
||||
difficulty: U256::from(2),
|
||||
number: storage.best_block + 1,
|
||||
gas_limit: ETHEREUM_BLOCK_GAS_LIMIT,
|
||||
gas_used: 0,
|
||||
timestamp: SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs(),
|
||||
mix_hash: Default::default(),
|
||||
nonce: 0,
|
||||
base_fee_per_gas,
|
||||
extra_data: Default::default(),
|
||||
};
|
||||
|
||||
let transactions = transactions
|
||||
let (transactions, senders): (Vec<_>, Vec<_>) = transactions
|
||||
.into_iter()
|
||||
.map(|tx| tx.to_recovered_transaction().into_signed())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
header.transactions_root = if transactions.is_empty() {
|
||||
EMPTY_TRANSACTIONS
|
||||
} else {
|
||||
proofs::calculate_transaction_root(&transactions)
|
||||
};
|
||||
|
||||
let block =
|
||||
Block { header, body: transactions, ommers: vec![], withdrawals: None };
|
||||
.map(|tx| {
|
||||
let recovered = tx.to_recovered_transaction();
|
||||
let signer = recovered.signer();
|
||||
(recovered.into_signed(), signer)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
// execute the new block
|
||||
let substate = SubState::new(State::new(client.latest().unwrap()));
|
||||
let mut executor = Executor::new(chain_spec, substate);
|
||||
|
||||
trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions");
|
||||
|
||||
let senders = block
|
||||
.body
|
||||
.iter()
|
||||
.map(|tx| tx.recover_signer())
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
|
||||
match executor.execute_transactions(&block, U256::ZERO, Some(senders.clone())) {
|
||||
Ok((post_state, gas_used)) => {
|
||||
// apply post block changes
|
||||
let post_state = executor
|
||||
.apply_post_block_changes(&block, U256::ZERO, post_state)
|
||||
.unwrap();
|
||||
|
||||
let Block { mut header, body, .. } = block;
|
||||
|
||||
match storage.build_and_execute(transactions.clone(), &mut executor) {
|
||||
Ok((new_header, post_state)) => {
|
||||
// clear all transactions from pool
|
||||
pool.remove_transactions(body.iter().map(|tx| tx.hash()));
|
||||
pool.remove_transactions(transactions.iter().map(|tx| tx.hash()));
|
||||
|
||||
let receipts = post_state.receipts(header.number);
|
||||
header.receipts_root = if receipts.is_empty() {
|
||||
EMPTY_RECEIPTS
|
||||
} else {
|
||||
let receipts_with_bloom = receipts
|
||||
.iter()
|
||||
.map(|r| r.clone().into())
|
||||
.collect::<Vec<ReceiptWithBloom>>();
|
||||
proofs::calculate_receipt_root(&receipts_with_bloom)
|
||||
};
|
||||
let transactions = body.clone();
|
||||
let body =
|
||||
BlockBody { transactions: body, ommers: vec![], withdrawals: None };
|
||||
header.gas_used = gas_used;
|
||||
|
||||
trace!(target: "consensus::auto", ?post_state, ?header, ?body, "executed block, calculating root");
|
||||
|
||||
// calculate the state root
|
||||
let state_root =
|
||||
executor.db().db.0.state_root(post_state.clone()).unwrap();
|
||||
header.state_root = state_root;
|
||||
|
||||
trace!(target: "consensus::auto", root=?header.state_root, ?body, "calculated root");
|
||||
|
||||
storage.insert_new_block(header.clone(), body);
|
||||
|
||||
let new_hash = storage.best_hash;
|
||||
let state = ForkchoiceState {
|
||||
head_block_hash: new_hash,
|
||||
finalized_block_hash: new_hash,
|
||||
safe_block_hash: new_hash,
|
||||
head_block_hash: new_header.hash,
|
||||
finalized_block_hash: new_header.hash,
|
||||
safe_block_hash: new_header.hash,
|
||||
};
|
||||
drop(storage);
|
||||
|
||||
@ -261,7 +180,7 @@ where
|
||||
|
||||
// seal the block
|
||||
let block = Block {
|
||||
header: header.clone(),
|
||||
header: new_header.clone().unseal(),
|
||||
body: transactions,
|
||||
ommers: vec![],
|
||||
withdrawals: None,
|
||||
@ -273,9 +192,9 @@ where
|
||||
.expect("senders are valid");
|
||||
|
||||
// update canon chain for rpc
|
||||
client.set_canonical_head(header.clone().seal(new_hash));
|
||||
client.set_safe(header.clone().seal(new_hash));
|
||||
client.set_finalized(header.clone().seal(new_hash));
|
||||
client.set_canonical_head(new_header.clone());
|
||||
client.set_safe(new_header.clone());
|
||||
client.set_finalized(new_header.clone());
|
||||
|
||||
debug!(target: "consensus::auto", header=?sealed_block_with_senders.hash(), "sending block notification");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user