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(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(feature = "arbitrary", derive(arbitrary::Arbitrary))]
30#[cfg_attr(feature = "reth-codec", derive(reth_codecs::Compact))]
31#[cfg_attr(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 {
95 balance: value.balance,
96 nonce: value.nonce,
97 bytecode_hash: (value.code_hash != KECCAK_EMPTY).then_some(value.code_hash),
98 }
99 }
100}
101
102impl InMemorySize for Account {
103 fn size(&self) -> usize {
104 size_of::<Self>()
105 }
106}
107
108#[cfg(feature = "reth-codec")]
109reth_codecs::impl_compression_for_compact!(Account);
110
111#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115#[derive(Debug, Clone, Default, PartialEq, Eq, Deref)]
116pub struct Bytecode(pub RevmBytecode);
117
118impl Bytecode {
119 pub fn new_raw(bytes: Bytes) -> Self {
127 Self(RevmBytecode::new_raw(bytes))
128 }
129
130 #[inline]
134 pub fn new_raw_checked(bytecode: Bytes) -> Result<Self, BytecodeDecodeError> {
135 RevmBytecode::new_raw_checked(bytecode).map(Self)
136 }
137}
138
139#[cfg(feature = "reth-codec")]
140impl reth_codecs::Compact for Bytecode {
141 fn to_compact<B>(&self, buf: &mut B) -> usize
142 where
143 B: bytes::BufMut + AsMut<[u8]>,
144 {
145 use compact_ids::{EIP7702_BYTECODE_ID, LEGACY_ANALYZED_BYTECODE_ID};
146
147 let bytecode = self.0.bytes_ref();
148 buf.put_u32(bytecode.len() as u32);
149 buf.put_slice(bytecode.as_ref());
150 let len = if self.0.is_legacy() {
151 if let Some(jump_table) = self.0.legacy_jump_table() {
153 buf.put_u8(LEGACY_ANALYZED_BYTECODE_ID);
154 buf.put_u64(self.0.len() as u64);
155 let map = jump_table.as_slice();
156 buf.put_slice(map);
157 1 + 8 + map.len()
158 } else {
159 unreachable!("legacy bytecode must contain a jump table")
160 }
161 } else {
162 buf.put_u8(EIP7702_BYTECODE_ID);
163 1
164 };
165 len + bytecode.len() + 4
166 }
167
168 fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) {
173 use byteorder::ReadBytesExt;
174 use bytes::Buf;
175
176 use compact_ids::*;
177
178 let len = buf.read_u32::<byteorder::BigEndian>().expect("could not read bytecode length")
179 as usize;
180 let bytes = Bytes::from(buf.copy_to_bytes(len));
181 let variant = buf.read_u8().expect("could not read bytecode variant");
182 let decoded = match variant {
183 LEGACY_RAW_BYTECODE_ID => Self(RevmBytecode::new_raw(bytes)),
184 REMOVED_BYTECODE_ID => {
185 unreachable!("Junk data in database: checked Bytecode variant was removed")
186 }
187 LEGACY_ANALYZED_BYTECODE_ID => {
188 let original_len = buf.read_u64::<byteorder::BigEndian>().unwrap() as usize;
189 let jump_table_len = if buf.len() * 8 >= bytes.len() {
193 bytes.len()
195 } else {
196 original_len
198 };
199 Self(RevmBytecode::new_analyzed(
200 bytes,
201 original_len,
202 revm_bytecode::JumpTable::from_slice(buf, jump_table_len),
203 ))
204 }
205 EIP7702_BYTECODE_ID => {
206 Self(RevmBytecode::new_raw(bytes))
208 }
209 _ => unreachable!("Junk data in database: unknown Bytecode variant"),
210 };
211 (decoded, &[])
212 }
213}
214
215#[cfg(feature = "reth-codec")]
216reth_codecs::impl_compression_for_compact!(Bytecode);
217
218impl From<&GenesisAccount> for Account {
219 fn from(value: &GenesisAccount) -> Self {
220 Self {
221 nonce: value.nonce.unwrap_or_default(),
222 balance: value.balance,
223 bytecode_hash: value.code.as_ref().map(keccak256),
224 }
225 }
226}
227
228impl From<AccountInfo> for Account {
229 fn from(revm_acc: AccountInfo) -> Self {
230 Self {
231 balance: revm_acc.balance,
232 nonce: revm_acc.nonce,
233 bytecode_hash: (!revm_acc.is_empty_code_hash()).then_some(revm_acc.code_hash),
234 }
235 }
236}
237
238impl From<&AccountInfo> for Account {
239 fn from(revm_acc: &AccountInfo) -> Self {
240 Self {
241 balance: revm_acc.balance,
242 nonce: revm_acc.nonce,
243 bytecode_hash: (!revm_acc.is_empty_code_hash()).then_some(revm_acc.code_hash),
244 }
245 }
246}
247
248impl From<Account> for AccountInfo {
249 fn from(reth_acc: Account) -> Self {
250 Self {
251 balance: reth_acc.balance,
252 nonce: reth_acc.nonce,
253 code_hash: reth_acc.bytecode_hash.unwrap_or(KECCAK_EMPTY),
254 code: None,
255 account_id: None,
256 }
257 }
258}
259
260#[cfg(all(test, feature = "std", feature = "reth-codec"))]
261mod tests {
262 use super::*;
263 use alloy_primitives::{hex_literal::hex, B256, U256};
264 use reth_codecs::Compact;
265 use revm_bytecode::JumpTable;
266
267 #[test]
268 fn test_account() {
269 let mut buf = vec![];
270 let mut acc = Account::default();
271 let len = acc.to_compact(&mut buf);
272 assert_eq!(len, 2);
273
274 acc.balance = U256::from(2);
275 let len = acc.to_compact(&mut buf);
276 assert_eq!(len, 3);
277
278 acc.nonce = 2;
279 let len = acc.to_compact(&mut buf);
280 assert_eq!(len, 4);
281 }
282
283 #[test]
284 fn test_empty_account() {
285 let mut acc = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None };
286 assert!(acc.is_empty());
288
289 acc.bytecode_hash = Some(KECCAK_EMPTY);
290 assert!(acc.is_empty());
292
293 acc.balance = U256::from(2);
294 assert!(!acc.is_empty());
296
297 acc.balance = U256::ZERO;
298 acc.nonce = 10;
299 assert!(!acc.is_empty());
301
302 acc.nonce = 0;
303 acc.bytecode_hash = Some(B256::from(U256::ZERO));
304 assert!(!acc.is_empty());
306 }
307
308 #[test]
309 #[ignore]
310 fn test_bytecode() {
311 let mut buf = vec![];
312 let bytecode = Bytecode::new_raw(Bytes::default());
313 let len = bytecode.to_compact(&mut buf);
314 assert_eq!(len, 14);
315
316 let mut buf = vec![];
317 let bytecode = Bytecode::new_raw(Bytes::from(&hex!("ffff")));
318 let len = bytecode.to_compact(&mut buf);
319 assert_eq!(len, 17);
320
321 let mut buf = vec![];
322 let bytecode = Bytecode(RevmBytecode::new_analyzed(
323 Bytes::from(&hex!("ff00")),
324 2,
325 JumpTable::from_slice(&[0], 2),
326 ));
327 let len = bytecode.to_compact(&mut buf);
328 assert_eq!(len, 16);
329
330 let (decoded, remainder) = Bytecode::from_compact(&buf, len);
331 assert_eq!(decoded, bytecode);
332 assert!(remainder.is_empty());
333 }
334
335 #[test]
336 fn test_account_has_bytecode() {
337 let acc_no_bytecode = Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None };
339 assert!(!acc_no_bytecode.has_bytecode(), "Account should not have bytecode");
340
341 let acc_empty_bytecode =
343 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(KECCAK_EMPTY) };
344 assert!(acc_empty_bytecode.has_bytecode(), "Account should have bytecode");
345
346 let acc_with_bytecode = Account {
348 nonce: 1,
349 balance: U256::from(1000),
350 bytecode_hash: Some(B256::from_slice(&[0x11u8; 32])),
351 };
352 assert!(acc_with_bytecode.has_bytecode(), "Account should have bytecode");
353 }
354
355 #[test]
356 fn test_account_get_bytecode_hash() {
357 let acc_no_bytecode = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None };
359 assert_eq!(acc_no_bytecode.get_bytecode_hash(), KECCAK_EMPTY, "Should return KECCAK_EMPTY");
360
361 let acc_empty_bytecode =
363 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(KECCAK_EMPTY) };
364 assert_eq!(
365 acc_empty_bytecode.get_bytecode_hash(),
366 KECCAK_EMPTY,
367 "Should return KECCAK_EMPTY"
368 );
369
370 let bytecode_hash = B256::from_slice(&[0x11u8; 32]);
372 let acc_with_bytecode =
373 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(bytecode_hash) };
374 assert_eq!(
375 acc_with_bytecode.get_bytecode_hash(),
376 bytecode_hash,
377 "Should return the bytecode hash"
378 );
379 }
380}