feat: Stateless validation function receives public keys corresponding to each transaction (#17841)

Co-authored-by: Wolfgang Welz <welzwo@gmail.com>
This commit is contained in:
kevaundray
2025-10-16 11:21:15 +01:00
committed by GitHub
parent 5beeaedfae
commit be648d950c
8 changed files with 193 additions and 10 deletions

View File

@@ -28,7 +28,7 @@ reth-evm.workspace = true
reth-evm-ethereum.workspace = true
reth-ethereum-consensus.workspace = true
reth-revm = { workspace = true, features = ["std", "witness"] }
reth-stateless = { workspace = true }
reth-stateless = { workspace = true, features = ["secp256k1"] }
reth-tracing.workspace = true
reth-trie.workspace = true
reth-trie-db.workspace = true

View File

@@ -10,17 +10,20 @@ use reth_chainspec::ChainSpec;
use reth_consensus::{Consensus, HeaderValidator};
use reth_db_common::init::{insert_genesis_hashes, insert_genesis_history, insert_genesis_state};
use reth_ethereum_consensus::{validate_block_post_execution, EthBeaconConsensus};
use reth_ethereum_primitives::Block;
use reth_ethereum_primitives::{Block, TransactionSigned};
use reth_evm::{execute::Executor, ConfigureEvm};
use reth_evm_ethereum::EthEvmConfig;
use reth_primitives_traits::{RecoveredBlock, SealedBlock};
use reth_primitives_traits::{Block as BlockTrait, RecoveredBlock, SealedBlock};
use reth_provider::{
test_utils::create_test_provider_factory_with_chain_spec, BlockWriter, DatabaseProviderFactory,
ExecutionOutcome, HeaderProvider, HistoryWriter, OriginalValuesKnown, StateProofProvider,
StateWriter, StaticFileProviderFactory, StaticFileSegment, StaticFileWriter,
};
use reth_revm::{database::StateProviderDatabase, witness::ExecutionWitnessRecord, State};
use reth_stateless::{validation::stateless_validation, ExecutionWitness};
use reth_stateless::{
trie::StatelessSparseTrie, validation::stateless_validation_with_trie, ExecutionWitness,
UncompressedPublicKey,
};
use reth_trie::{HashedPostState, KeccakKeyHasher, StateRoot};
use reth_trie_db::DatabaseStateRoot;
use std::{
@@ -356,9 +359,16 @@ fn run_case(
}
// Now validate using the stateless client if everything else passes
for (block, execution_witness) in &program_inputs {
stateless_validation(
block.clone(),
for (recovered_block, execution_witness) in &program_inputs {
let block = recovered_block.clone().into_block();
// Recover the actual public keys from the transaction signatures
let public_keys = recover_signers(block.body().transactions())
.expect("Failed to recover public keys from transaction signatures");
stateless_validation_with_trie::<StatelessSparseTrie, _, _>(
block,
public_keys,
execution_witness.clone(),
chain_spec.clone(),
EthEvmConfig::new(chain_spec.clone()),
@@ -413,6 +423,22 @@ fn pre_execution_checks(
Ok(())
}
/// Recover public keys from transaction signatures.
fn recover_signers<'a, I>(txs: I) -> Result<Vec<UncompressedPublicKey>, Box<dyn std::error::Error>>
where
I: IntoIterator<Item = &'a TransactionSigned>,
{
txs.into_iter()
.enumerate()
.map(|(i, tx)| {
tx.signature()
.recover_from_prehash(&tx.signature_hash())
.map(|keys| keys.to_encoded_point(false).as_bytes().try_into().unwrap())
.map_err(|e| format!("failed to recover signature for tx #{i}: {e}").into())
})
.collect::<Result<Vec<UncompressedPublicKey>, _>>()
}
/// Returns whether the test at the given path should be skipped.
///
/// Some tests are edge cases that cannot happen on mainnet, while others are skipped for