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