revme/cmd/statetest/
merkle_trie.rs

1use std::convert::Infallible;
2
3use alloy_rlp::{RlpEncodable, RlpMaxEncodedLen};
4use hash_db::Hasher;
5use plain_hasher::PlainHasher;
6use revm::{
7    context::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction},
8    database::{bal::EvmDatabaseError, EmptyDB, PlainAccount, State},
9    primitives::{keccak256, Address, Log, B256, U256},
10};
11use triehash::sec_trie_root;
12
13pub struct TestValidationResult {
14    pub logs_root: B256,
15    pub state_root: B256,
16}
17
18pub fn compute_test_roots(
19    exec_result: &Result<
20        ExecutionResult<HaltReason>,
21        EVMError<EvmDatabaseError<Infallible>, InvalidTransaction>,
22    >,
23    db: &State<EmptyDB>,
24) -> TestValidationResult {
25    TestValidationResult {
26        logs_root: log_rlp_hash(exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()),
27        state_root: state_merkle_trie_root(db.cache.trie_account()),
28    }
29}
30
31pub fn log_rlp_hash(logs: &[Log]) -> B256 {
32    let mut out = Vec::with_capacity(alloy_rlp::list_length(logs));
33    alloy_rlp::encode_list(logs, &mut out);
34    keccak256(&out)
35}
36
37pub fn state_merkle_trie_root<'a>(
38    accounts: impl IntoIterator<Item = (Address, &'a PlainAccount)>,
39) -> B256 {
40    trie_root(accounts.into_iter().map(|(address, acc)| {
41        (
42            address,
43            alloy_rlp::encode_fixed_size(&TrieAccount::new(acc)),
44        )
45    }))
46}
47
48#[derive(RlpEncodable, RlpMaxEncodedLen)]
49struct TrieAccount {
50    nonce: u64,
51    balance: U256,
52    root_hash: B256,
53    code_hash: B256,
54}
55
56impl TrieAccount {
57    fn new(acc: &PlainAccount) -> Self {
58        Self {
59            nonce: acc.info.nonce,
60            balance: acc.info.balance,
61            root_hash: sec_trie_root::<KeccakHasher, _, _, _>(
62                acc.storage
63                    .iter()
64                    .filter(|(_k, &v)| !v.is_zero())
65                    .map(|(k, v)| (k.to_be_bytes::<32>(), alloy_rlp::encode_fixed_size(v))),
66            ),
67            code_hash: acc.info.code_hash,
68        }
69    }
70}
71
72#[inline]
73pub fn trie_root<I, A, B>(input: I) -> B256
74where
75    I: IntoIterator<Item = (A, B)>,
76    A: AsRef<[u8]>,
77    B: AsRef<[u8]>,
78{
79    sec_trie_root::<KeccakHasher, _, _, _>(input)
80}
81
82#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
83pub struct KeccakHasher;
84
85impl Hasher for KeccakHasher {
86    type Out = B256;
87    type StdHasher = PlainHasher;
88    const LENGTH: usize = 32;
89
90    #[inline]
91    fn hash(x: &[u8]) -> Self::Out {
92        keccak256(x)
93    }
94}