1use alloy_consensus::constants::KECCAK_EMPTY;
2use alloy_genesis::GenesisAccount;
3use alloy_primitives::{keccak256, Bytes, B256, U256};
4use alloy_trie::TrieAccount;
5use derive_more::Deref;
6use revm_bytecode::{Bytecode as RevmBytecode, BytecodeDecodeError};
7use revm_state::AccountInfo;
8
9#[cfg(any(test, feature = "reth-codec"))]
10pub mod compact_ids {
12 pub const LEGACY_RAW_BYTECODE_ID: u8 = 0;
14
15 pub const REMOVED_BYTECODE_ID: u8 = 1;
17
18 pub const LEGACY_ANALYZED_BYTECODE_ID: u8 = 2;
20
21 pub const EIP7702_BYTECODE_ID: u8 = 4;
23}
24
25#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
28#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
29#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))]
30#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
31pub struct Account {
32 pub nonce: u64,
34 pub balance: U256,
36 pub bytecode_hash: Option<B256>,
38}
39
40impl Account {
41 pub const fn has_bytecode(&self) -> bool {
43 self.bytecode_hash.is_some()
44 }
45
46 pub fn is_empty(&self) -> bool {
49 self.nonce == 0 &&
50 self.balance.is_zero() &&
51 self.bytecode_hash.is_none_or(|hash| hash == KECCAK_EMPTY)
52 }
53
54 pub fn get_bytecode_hash(&self) -> B256 {
57 self.bytecode_hash.unwrap_or(KECCAK_EMPTY)
58 }
59
60 pub fn into_trie_account(self, storage_root: B256) -> TrieAccount {
62 let Self { nonce, balance, bytecode_hash } = self;
63 TrieAccount {
64 nonce,
65 balance,
66 storage_root,
67 code_hash: bytecode_hash.unwrap_or(KECCAK_EMPTY),
68 }
69 }
70
71 pub fn from_revm_account(revm_account: &revm_state::Account) -> Self {
73 Self {
74 balance: revm_account.info.balance,
75 nonce: revm_account.info.nonce,
76 bytecode_hash: if revm_account.info.code_hash == revm_primitives::KECCAK_EMPTY {
77 None
78 } else {
79 Some(revm_account.info.code_hash)
80 },
81 }
82 }
83}
84
85impl From<revm_state::Account> for Account {
86 fn from(value: revm_state::Account) -> Self {
87 Self::from_revm_account(&value)
88 }
89}
90
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95#[derive(Debug, Clone, Default, PartialEq, Eq, Deref)]
96pub struct Bytecode(pub RevmBytecode);
97
98impl Bytecode {
99 pub fn new_raw(bytes: Bytes) -> Self {
107 Self(RevmBytecode::new_raw(bytes))
108 }
109
110 #[inline]
114 pub fn new_raw_checked(bytecode: Bytes) -> Result<Self, BytecodeDecodeError> {
115 RevmBytecode::new_raw_checked(bytecode).map(Self)
116 }
117}
118
119#[cfg(any(test, feature = "reth-codec"))]
120impl reth_codecs::Compact for Bytecode {
121 fn to_compact<B>(&self, buf: &mut B) -> usize
122 where
123 B: bytes::BufMut + AsMut<[u8]>,
124 {
125 use compact_ids::{EIP7702_BYTECODE_ID, LEGACY_ANALYZED_BYTECODE_ID};
126
127 let bytecode = match &self.0 {
128 RevmBytecode::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
129 RevmBytecode::Eip7702(eip7702) => eip7702.raw(),
130 };
131 buf.put_u32(bytecode.len() as u32);
132 buf.put_slice(bytecode.as_ref());
133 let len = match &self.0 {
134 RevmBytecode::LegacyAnalyzed(analyzed) => {
136 buf.put_u8(LEGACY_ANALYZED_BYTECODE_ID);
137 buf.put_u64(analyzed.original_len() as u64);
138 let map = analyzed.jump_table().as_slice();
139 buf.put_slice(map);
140 1 + 8 + map.len()
141 }
142 RevmBytecode::Eip7702(_) => {
143 buf.put_u8(EIP7702_BYTECODE_ID);
144 1
145 }
146 };
147 len + bytecode.len() + 4
148 }
149
150 fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) {
155 use byteorder::ReadBytesExt;
156 use bytes::Buf;
157
158 use compact_ids::*;
159
160 let len = buf.read_u32::<byteorder::BigEndian>().expect("could not read bytecode length")
161 as usize;
162 let bytes = Bytes::from(buf.copy_to_bytes(len));
163 let variant = buf.read_u8().expect("could not read bytecode variant");
164 let decoded = match variant {
165 LEGACY_RAW_BYTECODE_ID => Self(RevmBytecode::new_raw(bytes)),
166 REMOVED_BYTECODE_ID => {
167 unreachable!("Junk data in database: checked Bytecode variant was removed")
168 }
169 LEGACY_ANALYZED_BYTECODE_ID => {
170 let original_len = buf.read_u64::<byteorder::BigEndian>().unwrap() as usize;
171 let jump_table_len = if buf.len() * 8 >= bytes.len() {
175 bytes.len()
177 } else {
178 original_len
180 };
181 Self(RevmBytecode::new_analyzed(
182 bytes,
183 original_len,
184 revm_bytecode::JumpTable::from_slice(buf, jump_table_len),
185 ))
186 }
187 EIP7702_BYTECODE_ID => {
188 Self(RevmBytecode::new_raw(bytes))
190 }
191 _ => unreachable!("Junk data in database: unknown Bytecode variant"),
192 };
193 (decoded, &[])
194 }
195}
196
197impl From<&GenesisAccount> for Account {
198 fn from(value: &GenesisAccount) -> Self {
199 Self {
200 nonce: value.nonce.unwrap_or_default(),
201 balance: value.balance,
202 bytecode_hash: value.code.as_ref().map(keccak256),
203 }
204 }
205}
206
207impl From<AccountInfo> for Account {
208 fn from(revm_acc: AccountInfo) -> Self {
209 Self {
210 balance: revm_acc.balance,
211 nonce: revm_acc.nonce,
212 bytecode_hash: (!revm_acc.is_empty_code_hash()).then_some(revm_acc.code_hash),
213 }
214 }
215}
216
217impl From<&AccountInfo> for Account {
218 fn from(revm_acc: &AccountInfo) -> Self {
219 Self {
220 balance: revm_acc.balance,
221 nonce: revm_acc.nonce,
222 bytecode_hash: (!revm_acc.is_empty_code_hash()).then_some(revm_acc.code_hash),
223 }
224 }
225}
226
227impl From<Account> for AccountInfo {
228 fn from(reth_acc: Account) -> Self {
229 Self {
230 balance: reth_acc.balance,
231 nonce: reth_acc.nonce,
232 code_hash: reth_acc.bytecode_hash.unwrap_or(KECCAK_EMPTY),
233 code: None,
234 }
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use alloy_primitives::{hex_literal::hex, B256, U256};
242 use reth_codecs::Compact;
243 use revm_bytecode::{JumpTable, LegacyAnalyzedBytecode};
244
245 #[test]
246 fn test_account() {
247 let mut buf = vec![];
248 let mut acc = Account::default();
249 let len = acc.to_compact(&mut buf);
250 assert_eq!(len, 2);
251
252 acc.balance = U256::from(2);
253 let len = acc.to_compact(&mut buf);
254 assert_eq!(len, 3);
255
256 acc.nonce = 2;
257 let len = acc.to_compact(&mut buf);
258 assert_eq!(len, 4);
259 }
260
261 #[test]
262 fn test_empty_account() {
263 let mut acc = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None };
264 assert!(acc.is_empty());
266
267 acc.bytecode_hash = Some(KECCAK_EMPTY);
268 assert!(acc.is_empty());
270
271 acc.balance = U256::from(2);
272 assert!(!acc.is_empty());
274
275 acc.balance = U256::ZERO;
276 acc.nonce = 10;
277 assert!(!acc.is_empty());
279
280 acc.nonce = 0;
281 acc.bytecode_hash = Some(B256::from(U256::ZERO));
282 assert!(!acc.is_empty());
284 }
285
286 #[test]
287 #[ignore]
288 fn test_bytecode() {
289 let mut buf = vec![];
290 let bytecode = Bytecode::new_raw(Bytes::default());
291 let len = bytecode.to_compact(&mut buf);
292 assert_eq!(len, 14);
293
294 let mut buf = vec![];
295 let bytecode = Bytecode::new_raw(Bytes::from(&hex!("ffff")));
296 let len = bytecode.to_compact(&mut buf);
297 assert_eq!(len, 17);
298
299 let mut buf = vec![];
300 let bytecode = Bytecode(RevmBytecode::LegacyAnalyzed(LegacyAnalyzedBytecode::new(
301 Bytes::from(&hex!("ff00")),
302 2,
303 JumpTable::from_slice(&[0], 2),
304 )));
305 let len = bytecode.to_compact(&mut buf);
306 assert_eq!(len, 16);
307
308 let (decoded, remainder) = Bytecode::from_compact(&buf, len);
309 assert_eq!(decoded, bytecode);
310 assert!(remainder.is_empty());
311 }
312
313 #[test]
314 fn test_account_has_bytecode() {
315 let acc_no_bytecode = Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None };
317 assert!(!acc_no_bytecode.has_bytecode(), "Account should not have bytecode");
318
319 let acc_empty_bytecode =
321 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(KECCAK_EMPTY) };
322 assert!(acc_empty_bytecode.has_bytecode(), "Account should have bytecode");
323
324 let acc_with_bytecode = Account {
326 nonce: 1,
327 balance: U256::from(1000),
328 bytecode_hash: Some(B256::from_slice(&[0x11u8; 32])),
329 };
330 assert!(acc_with_bytecode.has_bytecode(), "Account should have bytecode");
331 }
332
333 #[test]
334 fn test_account_get_bytecode_hash() {
335 let acc_no_bytecode = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None };
337 assert_eq!(acc_no_bytecode.get_bytecode_hash(), KECCAK_EMPTY, "Should return KECCAK_EMPTY");
338
339 let acc_empty_bytecode =
341 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(KECCAK_EMPTY) };
342 assert_eq!(
343 acc_empty_bytecode.get_bytecode_hash(),
344 KECCAK_EMPTY,
345 "Should return KECCAK_EMPTY"
346 );
347
348 let bytecode_hash = B256::from_slice(&[0x11u8; 32]);
350 let acc_with_bytecode =
351 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(bytecode_hash) };
352 assert_eq!(
353 acc_with_bytecode.get_bytecode_hash(),
354 bytecode_hash,
355 "Should return the bytecode hash"
356 );
357 }
358}