reth_trie_common/
account.rs

1use crate::root::storage_root_unhashed;
2use alloy_consensus::constants::KECCAK_EMPTY;
3use alloy_genesis::GenesisAccount;
4use alloy_primitives::{keccak256, B256, U256};
5use alloy_rlp::{RlpDecodable, RlpEncodable};
6use alloy_trie::EMPTY_ROOT_HASH;
7use reth_primitives_traits::Account;
8use revm_primitives::{AccountInfo, FlaggedStorage};
9
10/// An Ethereum account as represented in the trie.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)]
12pub struct TrieAccount {
13    /// Account nonce.
14    pub nonce: u64,
15    /// Account balance.
16    pub balance: U256,
17    /// Account's storage root.
18    pub storage_root: B256,
19    /// Hash of the account's bytecode.
20    pub code_hash: B256,
21}
22
23impl TrieAccount {
24    /// Get account's storage root.
25    pub const fn storage_root(&self) -> B256 {
26        self.storage_root
27    }
28}
29
30impl From<GenesisAccount> for TrieAccount {
31    fn from(account: GenesisAccount) -> Self {
32        let storage_root = account
33            .storage
34            .map(|storage| {
35                storage_root_unhashed(
36                    storage.into_iter().filter(|(_, value)| !value.is_zero()).map(
37                        |(slot, value)| {
38                            (slot, FlaggedStorage::new_from_value(U256::from_be_bytes(*value)))
39                        },
40                    ),
41                )
42            })
43            .unwrap_or(EMPTY_ROOT_HASH);
44
45        Self {
46            nonce: account.nonce.unwrap_or_default(),
47            balance: account.balance,
48            storage_root,
49            code_hash: account.code.map_or(KECCAK_EMPTY, keccak256),
50        }
51    }
52}
53
54impl From<(Account, B256)> for TrieAccount {
55    fn from((account, storage_root): (Account, B256)) -> Self {
56        Self {
57            nonce: account.nonce,
58            balance: account.balance,
59            storage_root,
60            code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY),
61        }
62    }
63}
64
65impl From<(AccountInfo, B256)> for TrieAccount {
66    fn from((account, storage_root): (AccountInfo, B256)) -> Self {
67        Self {
68            nonce: account.nonce,
69            balance: account.balance,
70            storage_root,
71            code_hash: account.code_hash,
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use alloy_primitives::Bytes;
80    use std::collections::BTreeMap;
81
82    #[test]
83    fn test_from_genesis_account_with_default_values() {
84        let genesis_account = GenesisAccount::default();
85
86        // Convert the GenesisAccount to a TrieAccount
87        let trie_account: TrieAccount = genesis_account.into();
88
89        // Check the fields are properly set.
90        assert_eq!(trie_account.nonce, 0);
91        assert_eq!(trie_account.balance, U256::default());
92        assert_eq!(trie_account.storage_root(), EMPTY_ROOT_HASH);
93        assert_eq!(trie_account.code_hash, KECCAK_EMPTY);
94
95        // Check that the default Account converts to the same TrieAccount
96        assert_eq!(TrieAccount::from((Account::default(), EMPTY_ROOT_HASH)), trie_account);
97
98        // Check that the default AccountInfo converts to the same TrieAccount
99        assert_eq!(TrieAccount::from((AccountInfo::default(), EMPTY_ROOT_HASH)), trie_account);
100    }
101
102    #[test]
103    fn test_from_genesis_account_with_values() {
104        // Create a GenesisAccount with specific values
105        let mut storage = BTreeMap::new();
106        storage.insert(B256::from([0x01; 32]), B256::from([0x02; 32]));
107
108        let genesis_account = GenesisAccount {
109            nonce: Some(10),
110            balance: U256::from(1000),
111            code: Some(Bytes::from(vec![0x60, 0x61])),
112            storage: Some(storage),
113            private_key: None,
114        };
115
116        // Convert the GenesisAccount to a TrieAccount
117        let trie_account: TrieAccount = genesis_account.into();
118
119        let expected_storage_root = storage_root_unhashed(BTreeMap::from([(
120            B256::from([0x01; 32]),
121            FlaggedStorage::from(U256::from_be_bytes(*B256::from([0x02; 32]))),
122        )]));
123
124        // Check that the fields are properly set.
125        assert_eq!(trie_account.nonce, 10);
126        assert_eq!(trie_account.balance, U256::from(1000));
127        assert_eq!(trie_account.storage_root(), expected_storage_root);
128        assert_eq!(trie_account.code_hash, keccak256([0x60, 0x61]));
129
130        // Check that the Account converts to the same TrieAccount
131        assert_eq!(
132            TrieAccount::from((
133                Account {
134                    nonce: 10,
135                    balance: U256::from(1000),
136                    bytecode_hash: Some(keccak256([0x60, 0x61]))
137                },
138                expected_storage_root
139            )),
140            trie_account
141        );
142
143        // Check that the AccountInfo converts to the same TrieAccount
144        assert_eq!(
145            TrieAccount::from((
146                AccountInfo {
147                    nonce: 10,
148                    balance: U256::from(1000),
149                    code_hash: keccak256([0x60, 0x61]),
150                    ..Default::default()
151                },
152                expected_storage_root
153            )),
154            trie_account
155        );
156    }
157
158    #[test]
159    fn test_from_genesis_account_with_zeroed_storage_values() {
160        // Create a GenesisAccount with storage containing zero values
161        let storage = BTreeMap::from([(B256::from([0x01; 32]), B256::from([0x00; 32]))]);
162
163        let genesis_account = GenesisAccount {
164            nonce: Some(3),
165            balance: U256::from(300),
166            code: None,
167            storage: Some(storage),
168            private_key: None,
169        };
170
171        // Convert the GenesisAccount to a TrieAccount
172        let trie_account: TrieAccount = genesis_account.into();
173
174        // Check the fields are properly set.
175        assert_eq!(trie_account.nonce, 3);
176        assert_eq!(trie_account.balance, U256::from(300));
177        // Zero values in storage should result in EMPTY_ROOT_HASH
178        assert_eq!(trie_account.storage_root(), EMPTY_ROOT_HASH);
179        // No code provided, so code hash should be KECCAK_EMPTY
180        assert_eq!(trie_account.code_hash, KECCAK_EMPTY);
181    }
182}