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