mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
Initial reth port
This commit is contained in:
@ -3,8 +3,8 @@
|
||||
use alloy_consensus::{
|
||||
constants::MAXIMUM_EXTRA_DATA_SIZE, BlockHeader as _, EMPTY_OMMER_ROOT_HASH,
|
||||
};
|
||||
use alloy_eips::{calc_next_block_base_fee, eip4844::DATA_GAS_PER_BLOB, eip7840::BlobParams};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks};
|
||||
use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, eip7840::BlobParams};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_consensus::ConsensusError;
|
||||
use reth_primitives_traits::{
|
||||
Block, BlockBody, BlockHeader, GotExpected, SealedBlock, SealedHeader,
|
||||
@ -17,7 +17,7 @@ pub fn validate_header_gas<H: BlockHeader>(header: &H) -> Result<(), ConsensusEr
|
||||
return Err(ConsensusError::HeaderGasUsedExceedsGasLimit {
|
||||
gas_used: header.gas_used(),
|
||||
gas_limit: header.gas_limit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -30,7 +30,7 @@ pub fn validate_header_base_fee<H: BlockHeader, ChainSpec: EthereumHardforks>(
|
||||
) -> Result<(), ConsensusError> {
|
||||
if chain_spec.is_london_active_at_block(header.number()) && header.base_fee_per_gas().is_none()
|
||||
{
|
||||
return Err(ConsensusError::BaseFeeMissing)
|
||||
return Err(ConsensusError::BaseFeeMissing);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -95,14 +95,14 @@ where
|
||||
expected: header.ommers_hash(),
|
||||
}
|
||||
.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
let tx_root = body.calculate_tx_root();
|
||||
if header.transactions_root() != tx_root {
|
||||
return Err(ConsensusError::BodyTransactionRootDiff(
|
||||
GotExpected { got: tx_root, expected: header.transactions_root() }.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
match (header.withdrawals_root(), body.calculate_withdrawals_root()) {
|
||||
@ -110,7 +110,7 @@ where
|
||||
if withdrawals_root != header_withdrawals_root {
|
||||
return Err(ConsensusError::BodyWithdrawalsRootDiff(
|
||||
GotExpected { got: withdrawals_root, expected: header_withdrawals_root }.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
(None, None) => {
|
||||
@ -145,12 +145,12 @@ where
|
||||
expected: block.ommers_hash(),
|
||||
}
|
||||
.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
// Check transaction root
|
||||
if let Err(error) = block.ensure_transaction_root_valid() {
|
||||
return Err(ConsensusError::BodyTransactionRootDiff(error.into()))
|
||||
return Err(ConsensusError::BodyTransactionRootDiff(error.into()));
|
||||
}
|
||||
|
||||
// EIP-4895: Beacon chain push withdrawals as operations
|
||||
@ -179,14 +179,14 @@ pub fn validate_4844_header_standalone<H: BlockHeader>(header: &H) -> Result<(),
|
||||
let excess_blob_gas = header.excess_blob_gas().ok_or(ConsensusError::ExcessBlobGasMissing)?;
|
||||
|
||||
if header.parent_beacon_block_root().is_none() {
|
||||
return Err(ConsensusError::ParentBeaconBlockRootMissing)
|
||||
return Err(ConsensusError::ParentBeaconBlockRootMissing);
|
||||
}
|
||||
|
||||
if blob_gas_used % DATA_GAS_PER_BLOB != 0 {
|
||||
return Err(ConsensusError::BlobGasUsedNotMultipleOfBlobGasPerBlob {
|
||||
blob_gas_used,
|
||||
blob_gas_per_blob: DATA_GAS_PER_BLOB,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// `excess_blob_gas` must also be a multiple of `DATA_GAS_PER_BLOB`. This will be checked later
|
||||
@ -195,7 +195,7 @@ pub fn validate_4844_header_standalone<H: BlockHeader>(header: &H) -> Result<(),
|
||||
return Err(ConsensusError::ExcessBlobGasNotMultipleOfBlobGasPerBlob {
|
||||
excess_blob_gas,
|
||||
blob_gas_per_blob: DATA_GAS_PER_BLOB,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -229,13 +229,13 @@ pub fn validate_against_parent_hash_number<H: BlockHeader>(
|
||||
return Err(ConsensusError::ParentBlockNumberMismatch {
|
||||
parent_block_number: parent.number(),
|
||||
block_number: header.number(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if parent.hash() != header.parent_hash() {
|
||||
return Err(ConsensusError::ParentHashMismatch(
|
||||
GotExpected { got: header.parent_hash(), expected: parent.hash() }.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -247,37 +247,10 @@ pub fn validate_against_parent_eip1559_base_fee<
|
||||
H: BlockHeader,
|
||||
ChainSpec: EthChainSpec + EthereumHardforks,
|
||||
>(
|
||||
header: &H,
|
||||
parent: &H,
|
||||
chain_spec: &ChainSpec,
|
||||
_header: &H,
|
||||
_parent: &H,
|
||||
_chain_spec: &ChainSpec,
|
||||
) -> Result<(), ConsensusError> {
|
||||
if chain_spec.is_london_active_at_block(header.number()) {
|
||||
let base_fee = header.base_fee_per_gas().ok_or(ConsensusError::BaseFeeMissing)?;
|
||||
|
||||
let expected_base_fee = if chain_spec
|
||||
.ethereum_fork_activation(EthereumHardfork::London)
|
||||
.transitions_at_block(header.number())
|
||||
{
|
||||
alloy_eips::eip1559::INITIAL_BASE_FEE
|
||||
} else {
|
||||
// This BaseFeeMissing will not happen as previous blocks are checked to have
|
||||
// them.
|
||||
let base_fee = parent.base_fee_per_gas().ok_or(ConsensusError::BaseFeeMissing)?;
|
||||
calc_next_block_base_fee(
|
||||
parent.gas_used(),
|
||||
parent.gas_limit(),
|
||||
base_fee,
|
||||
chain_spec.base_fee_params_at_timestamp(header.timestamp()),
|
||||
)
|
||||
};
|
||||
if expected_base_fee != base_fee {
|
||||
return Err(ConsensusError::BaseFeeDiff(GotExpected {
|
||||
expected: expected_base_fee,
|
||||
got: base_fee,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -287,11 +260,11 @@ pub fn validate_against_parent_timestamp<H: BlockHeader>(
|
||||
header: &H,
|
||||
parent: &H,
|
||||
) -> Result<(), ConsensusError> {
|
||||
if header.timestamp() <= parent.timestamp() {
|
||||
if header.timestamp() < parent.timestamp() {
|
||||
return Err(ConsensusError::TimestampIsInPast {
|
||||
parent_timestamp: parent.timestamp(),
|
||||
timestamp: header.timestamp(),
|
||||
})
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -315,7 +288,7 @@ pub fn validate_against_parent_4844<H: BlockHeader>(
|
||||
let parent_excess_blob_gas = parent.excess_blob_gas().unwrap_or(0);
|
||||
|
||||
if header.blob_gas_used().is_none() {
|
||||
return Err(ConsensusError::BlobGasUsedMissing)
|
||||
return Err(ConsensusError::BlobGasUsedMissing);
|
||||
}
|
||||
let excess_blob_gas = header.excess_blob_gas().ok_or(ConsensusError::ExcessBlobGasMissing)?;
|
||||
|
||||
@ -326,7 +299,7 @@ pub fn validate_against_parent_4844<H: BlockHeader>(
|
||||
diff: GotExpected { got: excess_blob_gas, expected: expected_excess_blob_gas },
|
||||
parent_excess_blob_gas,
|
||||
parent_blob_gas_used,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -185,9 +185,11 @@ where
|
||||
|
||||
// Calculate the state root and trie updates after re-execution. They should match
|
||||
// the original ones.
|
||||
let (re_executed_root, trie_output) =
|
||||
let (_re_executed_root, trie_output) =
|
||||
state_provider.state_root_with_updates(hashed_state)?;
|
||||
if let Some((original_updates, original_root)) = trie_updates {
|
||||
let re_executed_root = B256::ZERO;
|
||||
if let Some((original_updates, _original_root)) = trie_updates {
|
||||
let original_root = B256::ZERO;
|
||||
if re_executed_root != original_root {
|
||||
let filename = format!("{}_{}.state_root.diff", block.number(), block.hash());
|
||||
let diff_path = self.save_diff(filename, &re_executed_root, &original_root)?;
|
||||
|
||||
@ -2892,6 +2892,7 @@ where
|
||||
task_elapsed = ?time_from_last_update,
|
||||
"State root task finished"
|
||||
);
|
||||
let task_state_root = B256::ZERO;
|
||||
|
||||
if task_state_root != sealed_block.header().state_root() ||
|
||||
self.config.always_compare_trie_updates()
|
||||
|
||||
@ -14,9 +14,14 @@ workspace = true
|
||||
# reth
|
||||
reth-cli.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
|
||||
# misc
|
||||
eyre.workspace = true
|
||||
once_cell.workspace = true
|
||||
alloy-chains.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
clap.workspace = true
|
||||
|
||||
@ -1,17 +1,97 @@
|
||||
use reth_chainspec::{ChainSpec, DEV, HOLESKY, MAINNET, SEPOLIA};
|
||||
extern crate alloc;
|
||||
|
||||
use alloy_primitives::{b256, Address, Bytes, B256, B64, U256};
|
||||
use once_cell::sync::Lazy;
|
||||
use reth_chainspec::{ChainSpec, DEV, DEV_HARDFORKS, HOLESKY, SEPOLIA};
|
||||
use reth_cli::chainspec::{parse_genesis, ChainSpecParser};
|
||||
use reth_primitives::{Header, SealedHeader};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Chains supported by reth. First value should be used as the default.
|
||||
pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "holesky", "dev"];
|
||||
|
||||
static GENESIS_HASH: B256 =
|
||||
b256!("d8fcc13b6a195b88b7b2da3722ff6cad767b13a8c1e9ffb1c73aa9d216d895f0");
|
||||
|
||||
/// The Hyperliqiud Mainnet spec
|
||||
pub static HL_MAINNET: Lazy<alloc::sync::Arc<ChainSpec>> = Lazy::new(|| {
|
||||
ChainSpec {
|
||||
chain: alloy_chains::Chain::from_id(999),
|
||||
// genesis contains empty alloc field because state at first bedrock block is imported
|
||||
// manually from trusted source
|
||||
genesis: serde_json::from_str(r#"{
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x6490fdd2",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x1c9c380",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494",
|
||||
"alloc": {
|
||||
"0x2222222222222222222222222222222222222222": {
|
||||
"nonce": 0,
|
||||
"balance": "0x33b2e3c9fd0803ce8000000",
|
||||
"code": "0x608060405236603f5760405134815233907f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258749060200160405180910390a2005b600080fdfea2646970667358221220ca425db50898ac19f9e4676e86e8ebed9853baa048942f6306fe8a86b8d4abb964736f6c63430008090033",
|
||||
"storage": {}
|
||||
},
|
||||
"0x5555555555555555555555555555555555555555": {
|
||||
"nonce": 0,
|
||||
"balance": "0x0",
|
||||
"code": "0x6080604052600436106100bc5760003560e01c8063313ce56711610074578063a9059cbb1161004e578063a9059cbb146102cb578063d0e30db0146100bc578063dd62ed3e14610311576100bc565b8063313ce5671461024b57806370a082311461027657806395d89b41146102b6576100bc565b806318160ddd116100a557806318160ddd146101aa57806323b872dd146101d15780632e1a7d4d14610221576100bc565b806306fdde03146100c6578063095ea7b314610150575b6100c4610359565b005b3480156100d257600080fd5b506100db6103a8565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101155781810151838201526020016100fd565b50505050905090810190601f1680156101425780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015c57600080fd5b506101966004803603604081101561017357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610454565b604080519115158252519081900360200190f35b3480156101b657600080fd5b506101bf6104c7565b60408051918252519081900360200190f35b3480156101dd57600080fd5b50610196600480360360608110156101f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602081013590911690604001356104cb565b34801561022d57600080fd5b506100c46004803603602081101561024457600080fd5b503561066b565b34801561025757600080fd5b50610260610700565b6040805160ff9092168252519081900360200190f35b34801561028257600080fd5b506101bf6004803603602081101561029957600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610709565b3480156102c257600080fd5b506100db61071b565b3480156102d757600080fd5b50610196600480360360408110156102ee57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610793565b34801561031d57600080fd5b506101bf6004803603604081101561033457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166107a7565b33600081815260036020908152604091829020805434908101909155825190815291517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9281900390910190a2565b6000805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b820191906000526020600020905b81548152906001019060200180831161042f57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b4790565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600360205260408120548211156104fd57600080fd5b73ffffffffffffffffffffffffffffffffffffffff84163314801590610573575073ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14155b156105ed5773ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020548211156105b557600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020805483900390555b73ffffffffffffffffffffffffffffffffffffffff808516600081815260036020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060019392505050565b3360009081526003602052604090205481111561068757600080fd5b33600081815260036020526040808220805485900390555183156108fc0291849190818181858888f193505050501580156106c6573d6000803e3d6000fd5b5060408051828152905133917f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65919081900360200190a250565b60025460ff1681565b60036020526000908152604090205481565b60018054604080516020600284861615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b60006107a03384846104cb565b9392505050565b60046020908152600092835260408084209091529082529020548156fea265627a7a72315820e87684b404839c5657b1e7820bfa5ac4539ac8c83c21e28ec1086123db902cfe64736f6c63430005110032",
|
||||
"storage": {
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x5772617070656420485950450000000000000000000000000000000000000018",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x574859504500000000000000000000000000000000000000000000000000000a",
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000012"
|
||||
}
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}"#)
|
||||
.expect("Can't deserialize Hyperliquid Mainnet genesis json"),
|
||||
genesis_header: SealedHeader::new(
|
||||
Header {
|
||||
parent_hash: B256::ZERO,
|
||||
number: 0,
|
||||
timestamp: 0,
|
||||
transactions_root: B256::ZERO,
|
||||
receipts_root: B256::ZERO,
|
||||
state_root: B256::ZERO,
|
||||
gas_used: 0,
|
||||
gas_limit: 0x1c9c380,
|
||||
difficulty: U256::ZERO,
|
||||
mix_hash: B256::ZERO,
|
||||
extra_data: Bytes::new(),
|
||||
nonce: B64::ZERO,
|
||||
ommers_hash: B256::ZERO,
|
||||
beneficiary: Address::ZERO,
|
||||
logs_bloom: Default::default(),
|
||||
base_fee_per_gas: Some(0),
|
||||
withdrawals_root: Some(B256::ZERO),
|
||||
blob_gas_used: Some(0),
|
||||
excess_blob_gas: Some(0),
|
||||
parent_beacon_block_root: Some(B256::ZERO),
|
||||
requests_hash: Some(B256::ZERO),
|
||||
},
|
||||
GENESIS_HASH,
|
||||
),
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
hardforks: DEV_HARDFORKS.clone(),
|
||||
prune_delete_limit: 10000,
|
||||
..Default::default()
|
||||
}.into()
|
||||
});
|
||||
|
||||
/// Clap value parser for [`ChainSpec`]s.
|
||||
///
|
||||
/// The value parser matches either a known chain, the path
|
||||
/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct.
|
||||
pub fn chain_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(match s {
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"mainnet" => HL_MAINNET.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
"holesky" => HOLESKY.clone(),
|
||||
"dev" => DEV.clone(),
|
||||
|
||||
@ -21,10 +21,7 @@ use reth_consensus_common::validation::{
|
||||
};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives::{NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader};
|
||||
use reth_primitives_traits::{
|
||||
constants::{GAS_LIMIT_BOUND_DIVISOR, MINIMUM_GAS_LIMIT},
|
||||
Block, BlockHeader,
|
||||
};
|
||||
use reth_primitives_traits::{constants::MINIMUM_GAS_LIMIT, Block, BlockHeader};
|
||||
use std::{fmt::Debug, sync::Arc, time::SystemTime};
|
||||
|
||||
mod validation;
|
||||
@ -52,43 +49,13 @@ impl<ChainSpec: EthChainSpec + EthereumHardforks> EthBeaconConsensus<ChainSpec>
|
||||
fn validate_against_parent_gas_limit<H: BlockHeader>(
|
||||
&self,
|
||||
header: &SealedHeader<H>,
|
||||
parent: &SealedHeader<H>,
|
||||
_parent: &SealedHeader<H>,
|
||||
) -> Result<(), ConsensusError> {
|
||||
// Determine the parent gas limit, considering elasticity multiplier on the London fork.
|
||||
let parent_gas_limit = if !self.chain_spec.is_london_active_at_block(parent.number()) &&
|
||||
self.chain_spec.is_london_active_at_block(header.number())
|
||||
{
|
||||
parent.gas_limit() *
|
||||
self.chain_spec
|
||||
.base_fee_params_at_timestamp(header.timestamp())
|
||||
.elasticity_multiplier as u64
|
||||
} else {
|
||||
parent.gas_limit()
|
||||
};
|
||||
|
||||
// Check for an increase in gas limit beyond the allowed threshold.
|
||||
if header.gas_limit() > parent_gas_limit {
|
||||
if header.gas_limit() - parent_gas_limit >= parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR {
|
||||
return Err(ConsensusError::GasLimitInvalidIncrease {
|
||||
parent_gas_limit,
|
||||
child_gas_limit: header.gas_limit(),
|
||||
})
|
||||
}
|
||||
}
|
||||
// Check for a decrease in gas limit beyond the allowed threshold.
|
||||
else if parent_gas_limit - header.gas_limit() >=
|
||||
parent_gas_limit / GAS_LIMIT_BOUND_DIVISOR
|
||||
{
|
||||
return Err(ConsensusError::GasLimitInvalidDecrease {
|
||||
parent_gas_limit,
|
||||
child_gas_limit: header.gas_limit(),
|
||||
})
|
||||
}
|
||||
// Check if the self gas limit is below the minimum required limit.
|
||||
else if header.gas_limit() < MINIMUM_GAS_LIMIT {
|
||||
if header.gas_limit() < MINIMUM_GAS_LIMIT {
|
||||
return Err(ConsensusError::GasLimitInvalidMinimum {
|
||||
child_gas_limit: header.gas_limit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -139,33 +106,33 @@ where
|
||||
validate_header_base_fee(header.header(), &self.chain_spec)?;
|
||||
|
||||
// EIP-4895: Beacon chain push withdrawals as operations
|
||||
if self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) &&
|
||||
header.withdrawals_root().is_none()
|
||||
if self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp())
|
||||
&& header.withdrawals_root().is_none()
|
||||
{
|
||||
return Err(ConsensusError::WithdrawalsRootMissing)
|
||||
} else if !self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp()) &&
|
||||
header.withdrawals_root().is_some()
|
||||
return Err(ConsensusError::WithdrawalsRootMissing);
|
||||
} else if !self.chain_spec.is_shanghai_active_at_timestamp(header.timestamp())
|
||||
&& header.withdrawals_root().is_some()
|
||||
{
|
||||
return Err(ConsensusError::WithdrawalsRootUnexpected)
|
||||
return Err(ConsensusError::WithdrawalsRootUnexpected);
|
||||
}
|
||||
|
||||
// Ensures that EIP-4844 fields are valid once cancun is active.
|
||||
if self.chain_spec.is_cancun_active_at_timestamp(header.timestamp()) {
|
||||
validate_4844_header_standalone(header.header())?;
|
||||
} else if header.blob_gas_used().is_some() {
|
||||
return Err(ConsensusError::BlobGasUsedUnexpected)
|
||||
return Err(ConsensusError::BlobGasUsedUnexpected);
|
||||
} else if header.excess_blob_gas().is_some() {
|
||||
return Err(ConsensusError::ExcessBlobGasUnexpected)
|
||||
return Err(ConsensusError::ExcessBlobGasUnexpected);
|
||||
} else if header.parent_beacon_block_root().is_some() {
|
||||
return Err(ConsensusError::ParentBeaconBlockRootUnexpected)
|
||||
return Err(ConsensusError::ParentBeaconBlockRootUnexpected);
|
||||
}
|
||||
|
||||
if self.chain_spec.is_prague_active_at_timestamp(header.timestamp()) {
|
||||
if header.requests_hash().is_none() {
|
||||
return Err(ConsensusError::RequestsHashMissing)
|
||||
return Err(ConsensusError::RequestsHashMissing);
|
||||
}
|
||||
} else if header.requests_hash().is_some() {
|
||||
return Err(ConsensusError::RequestsHashUnexpected)
|
||||
return Err(ConsensusError::RequestsHashUnexpected);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -208,15 +175,15 @@ where
|
||||
|
||||
if is_post_merge {
|
||||
if !header.difficulty().is_zero() {
|
||||
return Err(ConsensusError::TheMergeDifficultyIsNotZero)
|
||||
return Err(ConsensusError::TheMergeDifficultyIsNotZero);
|
||||
}
|
||||
|
||||
if !header.nonce().is_some_and(|nonce| nonce.is_zero()) {
|
||||
return Err(ConsensusError::TheMergeNonceIsNotZero)
|
||||
return Err(ConsensusError::TheMergeNonceIsNotZero);
|
||||
}
|
||||
|
||||
if header.ommers_hash() != EMPTY_OMMER_ROOT_HASH {
|
||||
return Err(ConsensusError::TheMergeOmmerRootIsNotEmpty)
|
||||
return Err(ConsensusError::TheMergeOmmerRootIsNotEmpty);
|
||||
}
|
||||
|
||||
// Post-merge, the consensus layer is expected to perform checks such that the block
|
||||
@ -245,7 +212,7 @@ where
|
||||
return Err(ConsensusError::TimestampIsInFuture {
|
||||
timestamp: header.timestamp(),
|
||||
present_timestamp,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
validate_header_extra_data(header)?;
|
||||
|
||||
@ -28,7 +28,7 @@ where
|
||||
return Err(ConsensusError::BlockGasUsed {
|
||||
gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() },
|
||||
gas_spent_by_tx: gas_spent_by_transactions(receipts),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Before Byzantium, receipts contained state root that would mean that expensive
|
||||
@ -36,24 +36,29 @@ where
|
||||
// transaction This was replaced with is_success flag.
|
||||
// See more about EIP here: https://eips.ethereum.org/EIPS/eip-658
|
||||
if chain_spec.is_byzantium_active_at_block(block.header().number()) {
|
||||
if let Err(error) =
|
||||
verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts)
|
||||
{
|
||||
// Filter out system tx receipts
|
||||
let receipts: Vec<R> =
|
||||
receipts.iter().filter(|r| r.cumulative_gas_used() != 0).cloned().collect::<Vec<_>>();
|
||||
if let Err(error) = verify_receipts(
|
||||
block.header().receipts_root(),
|
||||
block.header().logs_bloom(),
|
||||
receipts.as_slice(),
|
||||
) {
|
||||
tracing::debug!(%error, ?receipts, "receipts verification failed");
|
||||
return Err(error)
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that the header requests hash matches the calculated requests hash
|
||||
if chain_spec.is_prague_active_at_timestamp(block.header().timestamp()) {
|
||||
let Some(header_requests_hash) = block.header().requests_hash() else {
|
||||
return Err(ConsensusError::RequestsHashMissing)
|
||||
return Err(ConsensusError::RequestsHashMissing);
|
||||
};
|
||||
let requests_hash = requests.requests_hash();
|
||||
if requests_hash != header_requests_hash {
|
||||
return Err(ConsensusError::BodyRequestsHashDiff(
|
||||
GotExpected::new(requests_hash, header_requests_hash).into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,13 +100,13 @@ fn compare_receipts_root_and_logs_bloom(
|
||||
if calculated_receipts_root != expected_receipts_root {
|
||||
return Err(ConsensusError::BodyReceiptRootDiff(
|
||||
GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
if calculated_logs_bloom != expected_logs_bloom {
|
||||
return Err(ConsensusError::BodyBloomLogDiff(
|
||||
GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(),
|
||||
))
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -8,7 +8,7 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloy_consensus::{Header, Transaction};
|
||||
use alloy_eips::{eip4895::Withdrawals, eip6110, eip7685::Requests};
|
||||
use alloy_evm::FromRecoveredTx;
|
||||
use alloy_primitives::{Address, B256};
|
||||
use alloy_primitives::{address, Address, B256};
|
||||
use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks, MAINNET};
|
||||
use reth_evm::{
|
||||
execute::{
|
||||
@ -187,10 +187,13 @@ where
|
||||
transaction_gas_limit: tx.gas_limit(),
|
||||
block_available_gas,
|
||||
}
|
||||
.into())
|
||||
.into());
|
||||
}
|
||||
|
||||
const HL_SYSETM_TX_FROM_ADDR: Address = address!("2222222222222222222222222222222222222222");
|
||||
|
||||
let hash = tx.hash();
|
||||
let is_system_transaction = tx.signer() == HL_SYSETM_TX_FROM_ADDR;
|
||||
|
||||
// Execute transaction.
|
||||
let result_and_state =
|
||||
@ -203,7 +206,9 @@ where
|
||||
let gas_used = result.gas_used();
|
||||
|
||||
// append gas used
|
||||
self.gas_used += gas_used;
|
||||
if !is_system_transaction {
|
||||
self.gas_used += gas_used;
|
||||
}
|
||||
|
||||
// Push transaction changeset and calculate header bloom filter for receipt.
|
||||
self.receipts.push(Receipt {
|
||||
|
||||
@ -80,7 +80,10 @@ impl ConfigureEvmEnv for EthEvmConfig {
|
||||
let spec = config::revm_spec(self.chain_spec(), header);
|
||||
|
||||
// configure evm env based on parent block
|
||||
let cfg_env = CfgEnv::new().with_chain_id(self.chain_spec.chain().id()).with_spec(spec);
|
||||
let mut cfg_env = CfgEnv::new().with_chain_id(self.chain_spec.chain().id()).with_spec(spec);
|
||||
// this one is effective; todo: disable after system transaction
|
||||
cfg_env.disable_base_fee = true;
|
||||
cfg_env.disable_eip3607 = true;
|
||||
|
||||
let block_env = BlockEnv {
|
||||
number: header.number(),
|
||||
|
||||
@ -12,7 +12,8 @@ use alloy_eips::{
|
||||
};
|
||||
use alloy_evm::FromRecoveredTx;
|
||||
use alloy_primitives::{
|
||||
keccak256, Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256,
|
||||
address, keccak256, Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind,
|
||||
B256, U256,
|
||||
};
|
||||
use alloy_rlp::{Decodable, Encodable};
|
||||
use core::hash::{Hash, Hasher};
|
||||
@ -329,9 +330,9 @@ impl Hash for TransactionSigned {
|
||||
|
||||
impl PartialEq for TransactionSigned {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signature == other.signature &&
|
||||
self.transaction == other.transaction &&
|
||||
self.tx_hash() == other.tx_hash()
|
||||
self.signature == other.signature
|
||||
&& self.transaction == other.transaction
|
||||
&& self.tx_hash() == other.tx_hash()
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,13 +580,13 @@ impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(Self { transaction, signature, hash: Default::default() })
|
||||
Ok(Self { transaction, signature, ..Default::default() })
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for TransactionSigned {
|
||||
fn size(&self) -> usize {
|
||||
let Self { hash: _, signature, transaction } = self;
|
||||
let Self { hash: _, signature, transaction, .. } = self;
|
||||
self.tx_hash().size() + signature.size() + transaction.size()
|
||||
}
|
||||
}
|
||||
@ -614,42 +615,26 @@ impl Decodable2718 for TransactionSigned {
|
||||
TxType::Legacy => Err(Eip2718Error::UnexpectedType(0)),
|
||||
TxType::Eip2930 => {
|
||||
let (tx, signature) = TxEip2930::rlp_decode_with_signature(buf)?;
|
||||
Ok(Self {
|
||||
transaction: Transaction::Eip2930(tx),
|
||||
signature,
|
||||
hash: Default::default(),
|
||||
})
|
||||
Ok(Self { transaction: Transaction::Eip2930(tx), signature, ..Default::default() })
|
||||
}
|
||||
TxType::Eip1559 => {
|
||||
let (tx, signature) = TxEip1559::rlp_decode_with_signature(buf)?;
|
||||
Ok(Self {
|
||||
transaction: Transaction::Eip1559(tx),
|
||||
signature,
|
||||
hash: Default::default(),
|
||||
})
|
||||
Ok(Self { transaction: Transaction::Eip1559(tx), signature, ..Default::default() })
|
||||
}
|
||||
TxType::Eip4844 => {
|
||||
let (tx, signature) = TxEip4844::rlp_decode_with_signature(buf)?;
|
||||
Ok(Self {
|
||||
transaction: Transaction::Eip4844(tx),
|
||||
signature,
|
||||
hash: Default::default(),
|
||||
})
|
||||
Ok(Self { transaction: Transaction::Eip4844(tx), signature, ..Default::default() })
|
||||
}
|
||||
TxType::Eip7702 => {
|
||||
let (tx, signature) = TxEip7702::rlp_decode_with_signature(buf)?;
|
||||
Ok(Self {
|
||||
transaction: Transaction::Eip7702(tx),
|
||||
signature,
|
||||
hash: Default::default(),
|
||||
})
|
||||
Ok(Self { transaction: Transaction::Eip7702(tx), signature, ..Default::default() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
|
||||
let (tx, signature) = TxLegacy::rlp_decode_with_signature(buf)?;
|
||||
Ok(Self { transaction: Transaction::Legacy(tx), signature, hash: Default::default() })
|
||||
Ok(Self { transaction: Transaction::Legacy(tx), signature, ..Default::default() })
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,6 +833,13 @@ impl SignedTransaction for TransactionSigned {
|
||||
}
|
||||
|
||||
fn recover_signer(&self) -> Result<Address, RecoveryError> {
|
||||
const HL_SYSTEM_TX_FROM_ADDR: Address =
|
||||
address!("2222222222222222222222222222222222222222");
|
||||
let signature = self.signature();
|
||||
if signature.r() == U256::from(1) && signature.s() == U256::from(1) && signature.v() == true
|
||||
{
|
||||
return Ok(HL_SYSTEM_TX_FROM_ADDR);
|
||||
}
|
||||
let signature_hash = self.signature_hash();
|
||||
recover_signer(&self.signature, signature_hash)
|
||||
}
|
||||
|
||||
@ -20,3 +20,5 @@ reth-primitives-traits.workspace = true
|
||||
# alloy
|
||||
alloy-rpc-types-engine.workspace = true
|
||||
alloy-consensus.workspace = true
|
||||
alloy-eips.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
@ -12,6 +12,7 @@ pub mod cancun;
|
||||
pub mod prague;
|
||||
pub mod shanghai;
|
||||
|
||||
use alloy_primitives::{address, Address};
|
||||
use alloy_rpc_types_engine::{ExecutionData, PayloadError};
|
||||
use reth_chainspec::EthereumHardforks;
|
||||
use reth_primitives::SealedBlock;
|
||||
@ -84,19 +85,41 @@ impl<ChainSpec: EthereumHardforks> ExecutionPayloadValidator<ChainSpec> {
|
||||
&self,
|
||||
payload: ExecutionData,
|
||||
) -> Result<SealedBlock<reth_primitives::Block<T>>, PayloadError> {
|
||||
let ExecutionData { payload, sidecar } = payload;
|
||||
let ExecutionData { mut payload, sidecar } = payload;
|
||||
|
||||
let expected_hash = payload.block_hash();
|
||||
|
||||
// First parse the block
|
||||
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
|
||||
const HL_SYSTEM_TX_FROM_ADDR: Address =
|
||||
address!("2222222222222222222222222222222222222222");
|
||||
let transactions = payload.as_v1().transactions.clone();
|
||||
let (normal, system) = transactions.into_iter().partition(|tx| {
|
||||
let tx = T::decode_2718(&mut tx.iter().as_slice());
|
||||
match tx {
|
||||
Ok(tx) => {
|
||||
!matches!(tx.recover_signer(), Ok(address) if HL_SYSTEM_TX_FROM_ADDR == address)
|
||||
}
|
||||
Err(_) => true,
|
||||
}
|
||||
});
|
||||
payload.as_v1_mut().transactions = normal;
|
||||
let mut block = payload.try_into_block_with_sidecar(&sidecar)?;
|
||||
block.body.transactions = system
|
||||
.iter()
|
||||
.map(|tx| {
|
||||
T::decode_2718(&mut tx.iter().as_slice())
|
||||
.expect("transaction should be valid")
|
||||
})
|
||||
.chain(block.body.transactions)
|
||||
.collect();
|
||||
let sealed_block = block.seal_slow();
|
||||
|
||||
// Ensure the hash included in the payload matches the block hash
|
||||
if expected_hash != sealed_block.hash() {
|
||||
return Err(PayloadError::BlockHash {
|
||||
execution: sealed_block.hash(),
|
||||
consensus: expected_hash,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
shanghai::ensure_well_formed_fields(
|
||||
|
||||
@ -8,6 +8,7 @@ use alloc::{fmt, vec::Vec};
|
||||
use alloy_consensus::{Transaction, Typed2718};
|
||||
use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals};
|
||||
use alloy_primitives::{Address, Bytes, B256};
|
||||
use revm_primitives::address;
|
||||
|
||||
/// Helper trait that unifies all behaviour required by transaction to support full node operations.
|
||||
pub trait FullBlockBody: BlockBody<Transaction: FullSignedTx> + MaybeSerdeBincodeCompat {}
|
||||
@ -81,7 +82,15 @@ pub trait BlockBody:
|
||||
|
||||
/// Calculate the transaction root for the block body.
|
||||
fn calculate_tx_root(&self) -> B256 {
|
||||
alloy_consensus::proofs::calculate_transaction_root(self.transactions())
|
||||
const HL_SYSETM_TX_FROM_ADDR: Address =
|
||||
address!("2222222222222222222222222222222222222222");
|
||||
let transactions: Vec<Self::Transaction> = self
|
||||
.transactions()
|
||||
.into_iter()
|
||||
.filter(|tx| !matches!(tx.recover_signer(), Ok(address) if HL_SYSETM_TX_FROM_ADDR == address))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
alloy_consensus::proofs::calculate_transaction_root(transactions.as_slice())
|
||||
}
|
||||
|
||||
/// Returns block withdrawals if any.
|
||||
|
||||
@ -12,6 +12,7 @@ use alloy_consensus::{
|
||||
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
|
||||
use alloy_primitives::{keccak256, Address, PrimitiveSignature as Signature, TxHash, B256};
|
||||
use core::hash::Hash;
|
||||
use revm_primitives::{address, U256};
|
||||
|
||||
/// Helper trait that unifies all behaviour required by block to support full node operations.
|
||||
pub trait FullSignedTx: SignedTransaction + MaybeCompact + MaybeSerdeBincodeCompat {}
|
||||
@ -166,6 +167,11 @@ impl SignedTransaction for PooledTransaction {
|
||||
&self,
|
||||
buf: &mut Vec<u8>,
|
||||
) -> Result<Address, RecoveryError> {
|
||||
let signature = self.signature();
|
||||
if signature.r() == U256::from(1) && signature.s() == U256::from(1) && signature.v() == true
|
||||
{
|
||||
return Ok(address!("2222222222222222222222222222222222222222"));
|
||||
}
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().encode_for_signing(buf),
|
||||
Self::Eip2930(tx) => tx.tx().encode_for_signing(buf),
|
||||
@ -174,7 +180,7 @@ impl SignedTransaction for PooledTransaction {
|
||||
Self::Eip4844(tx) => tx.tx().encode_for_signing(buf),
|
||||
}
|
||||
let signature_hash = keccak256(buf);
|
||||
recover_signer_unchecked(self.signature(), signature_hash)
|
||||
recover_signer_unchecked(signature, signature_hash)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::primitives::alloy_primitives::{BlockNumber, StorageKey, StorageValue};
|
||||
use alloy_primitives::{Address, B256, U256};
|
||||
use alloy_primitives::{keccak256, Address, B256, U256};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use reth_primitives_traits::Account;
|
||||
use reth_storage_api::{AccountReader, BlockHashReader, StateProvider};
|
||||
@ -159,7 +159,11 @@ impl<DB: EvmStateProvider> DatabaseRef for StateProviderDatabase<DB> {
|
||||
///
|
||||
/// Returns `Ok` with the block hash if found, or the default hash otherwise.
|
||||
fn block_hash_ref(&self, number: u64) -> Result<B256, Self::Error> {
|
||||
// Get the block hash or default hash with an attempt to convert U256 block number to u64
|
||||
Ok(self.0.block_hash(number)?.unwrap_or_default())
|
||||
if number >= 270000 {
|
||||
// Get the block hash or default hash with an attempt to convert U256 block number to u64
|
||||
Ok(self.0.block_hash(number)?.unwrap_or_default())
|
||||
} else {
|
||||
Ok(keccak256(number.to_string().as_bytes()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ use alloy_primitives::PrimitiveSignature as Signature;
|
||||
use alloy_rpc_types::TransactionRequest;
|
||||
use alloy_rpc_types_eth::{Transaction, TransactionInfo};
|
||||
use reth_primitives::{Recovered, TransactionSigned};
|
||||
use reth_primitives_traits::SignedTransaction;
|
||||
use reth_rpc_eth_api::EthApiTypes;
|
||||
use reth_rpc_eth_types::EthApiError;
|
||||
use reth_rpc_types_compat::TransactionCompat;
|
||||
@ -43,6 +44,7 @@ where
|
||||
tx_info: TransactionInfo,
|
||||
) -> Result<Self::Transaction, Self::Error> {
|
||||
let (tx, from) = tx.into_parts();
|
||||
let from = tx.recover_signer().unwrap_or(from);
|
||||
let inner: TxEnvelope = tx.into();
|
||||
|
||||
let TransactionInfo {
|
||||
@ -70,7 +72,7 @@ where
|
||||
request: TransactionRequest,
|
||||
) -> Result<TransactionSigned, Self::Error> {
|
||||
let Ok(tx) = request.build_typed_tx() else {
|
||||
return Err(EthApiError::TransactionConversionError)
|
||||
return Err(EthApiError::TransactionConversionError);
|
||||
};
|
||||
|
||||
// Create an empty signature for the transaction.
|
||||
|
||||
@ -3067,7 +3067,7 @@ impl<TX: DbTxMut + DbTx + 'static, N: NodeTypesForProvider + 'static> BlockWrite
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let first_number = blocks.first().unwrap().number();
|
||||
let first_number: u64 = blocks.first().unwrap().number();
|
||||
|
||||
let last = blocks.last().unwrap();
|
||||
let last_block_number = last.number();
|
||||
|
||||
Reference in New Issue
Block a user