1use alloy_consensus::constants::KECCAK_EMPTY;
2use alloy_genesis::GenesisAccount;
3use alloy_primitives::{keccak256, Bytes, B256, U256};
4use derive_more::Deref;
5use revm_primitives::{AccountInfo, Bytecode as RevmBytecode, BytecodeDecodeError};
6
7#[cfg(any(test, feature = "reth-codec"))]
8pub mod compact_ids {
10 pub const LEGACY_RAW_BYTECODE_ID: u8 = 0;
12
13 pub const REMOVED_BYTECODE_ID: u8 = 1;
15
16 pub const LEGACY_ANALYZED_BYTECODE_ID: u8 = 2;
18
19 pub const EOF_BYTECODE_ID: u8 = 3;
21
22 pub const EIP7702_BYTECODE_ID: u8 = 4;
24}
25
26#[cfg_attr(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
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
66#[derive(Debug, Clone, Default, PartialEq, Eq, Deref)]
67pub struct Bytecode(pub RevmBytecode);
68
69impl Bytecode {
70 pub fn new_raw(bytes: Bytes) -> Self {
78 Self(RevmBytecode::new_raw(bytes))
79 }
80
81 #[inline]
85 pub fn new_raw_checked(bytecode: Bytes) -> Result<Self, BytecodeDecodeError> {
86 RevmBytecode::new_raw_checked(bytecode).map(Self)
87 }
88}
89
90#[cfg(any(test, feature = "reth-codec"))]
91impl reth_codecs::Compact for Bytecode {
92 fn to_compact<B>(&self, buf: &mut B) -> usize
93 where
94 B: bytes::BufMut + AsMut<[u8]>,
95 {
96 use compact_ids::{
97 EIP7702_BYTECODE_ID, EOF_BYTECODE_ID, LEGACY_ANALYZED_BYTECODE_ID,
98 LEGACY_RAW_BYTECODE_ID,
99 };
100
101 let bytecode = match &self.0 {
102 RevmBytecode::LegacyRaw(bytes) => bytes,
103 RevmBytecode::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
104 RevmBytecode::Eof(eof) => eof.raw(),
105 RevmBytecode::Eip7702(eip7702) => eip7702.raw(),
106 };
107 buf.put_u32(bytecode.len() as u32);
108 buf.put_slice(bytecode.as_ref());
109 let len = match &self.0 {
110 RevmBytecode::LegacyRaw(_) => {
111 buf.put_u8(LEGACY_RAW_BYTECODE_ID);
112 1
113 }
114 RevmBytecode::LegacyAnalyzed(analyzed) => {
116 buf.put_u8(LEGACY_ANALYZED_BYTECODE_ID);
117 buf.put_u64(analyzed.original_len() as u64);
118 let map = analyzed.jump_table().as_slice();
119 buf.put_slice(map);
120 1 + 8 + map.len()
121 }
122 RevmBytecode::Eof(_) => {
123 buf.put_u8(EOF_BYTECODE_ID);
124 1
125 }
126 RevmBytecode::Eip7702(_) => {
127 buf.put_u8(EIP7702_BYTECODE_ID);
128 1
129 }
130 };
131 len + bytecode.len() + 4
132 }
133
134 fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) {
139 use byteorder::ReadBytesExt;
140 use bytes::Buf;
141
142 use compact_ids::*;
143
144 let len = buf.read_u32::<byteorder::BigEndian>().expect("could not read bytecode length");
145 let bytes = Bytes::from(buf.copy_to_bytes(len as usize));
146 let variant = buf.read_u8().expect("could not read bytecode variant");
147 let decoded = match variant {
148 LEGACY_RAW_BYTECODE_ID => Self(RevmBytecode::new_raw(bytes)),
149 REMOVED_BYTECODE_ID => {
150 unreachable!("Junk data in database: checked Bytecode variant was removed")
151 }
152 LEGACY_ANALYZED_BYTECODE_ID => Self(unsafe {
153 RevmBytecode::new_analyzed(
154 bytes,
155 buf.read_u64::<byteorder::BigEndian>().unwrap() as usize,
156 revm_primitives::JumpTable::from_slice(buf),
157 )
158 }),
159 EOF_BYTECODE_ID | EIP7702_BYTECODE_ID => {
160 Self(RevmBytecode::new_raw(bytes))
162 }
163 _ => unreachable!("Junk data in database: unknown Bytecode variant"),
164 };
165 (decoded, &[])
166 }
167}
168
169impl From<&GenesisAccount> for Account {
170 fn from(value: &GenesisAccount) -> Self {
171 Self {
172 nonce: value.nonce.unwrap_or_default(),
173 balance: value.balance,
174 bytecode_hash: value.code.as_ref().map(keccak256),
175 }
176 }
177}
178
179impl From<AccountInfo> for Account {
180 fn from(revm_acc: AccountInfo) -> Self {
181 Self {
182 balance: revm_acc.balance,
183 nonce: revm_acc.nonce,
184 bytecode_hash: (!revm_acc.is_empty_code_hash()).then_some(revm_acc.code_hash),
185 }
186 }
187}
188
189impl From<&AccountInfo> for Account {
190 fn from(revm_acc: &AccountInfo) -> Self {
191 Self {
192 balance: revm_acc.balance,
193 nonce: revm_acc.nonce,
194 bytecode_hash: (!revm_acc.is_empty_code_hash()).then_some(revm_acc.code_hash),
195 }
196 }
197}
198
199impl From<Account> for AccountInfo {
200 fn from(reth_acc: Account) -> Self {
201 Self {
202 balance: reth_acc.balance,
203 nonce: reth_acc.nonce,
204 code_hash: reth_acc.bytecode_hash.unwrap_or(KECCAK_EMPTY),
205 code: None,
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use alloy_primitives::{hex_literal::hex, B256, U256};
213 use reth_codecs::Compact;
214 use revm_primitives::{JumpTable, LegacyAnalyzedBytecode};
215
216 use super::*;
217
218 #[test]
219 fn test_account() {
220 let mut buf = vec![];
221 let mut acc = Account::default();
222 let len = acc.to_compact(&mut buf);
223 assert_eq!(len, 2);
224
225 acc.balance = U256::from(2);
226 let len = acc.to_compact(&mut buf);
227 assert_eq!(len, 3);
228
229 acc.nonce = 2;
230 let len = acc.to_compact(&mut buf);
231 assert_eq!(len, 4);
232 }
233
234 #[test]
235 fn test_empty_account() {
236 let mut acc = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None };
237 assert!(acc.is_empty());
239
240 acc.bytecode_hash = Some(KECCAK_EMPTY);
241 assert!(acc.is_empty());
243
244 acc.balance = U256::from(2);
245 assert!(!acc.is_empty());
247
248 acc.balance = U256::ZERO;
249 acc.nonce = 10;
250 assert!(!acc.is_empty());
252
253 acc.nonce = 0;
254 acc.bytecode_hash = Some(B256::from(U256::ZERO));
255 assert!(!acc.is_empty());
257 }
258
259 #[test]
260 fn test_bytecode() {
261 let mut buf = vec![];
262 let bytecode = Bytecode::new_raw(Bytes::default());
263 let len = bytecode.to_compact(&mut buf);
264 assert_eq!(len, 5);
265
266 let mut buf = vec![];
267 let bytecode = Bytecode::new_raw(Bytes::from(&hex!("ffff")));
268 let len = bytecode.to_compact(&mut buf);
269 assert_eq!(len, 7);
270
271 let mut buf = vec![];
272 let bytecode = Bytecode(RevmBytecode::LegacyAnalyzed(LegacyAnalyzedBytecode::new(
273 Bytes::from(&hex!("ffff")),
274 2,
275 JumpTable::from_slice(&[0]),
276 )));
277 let len = bytecode.to_compact(&mut buf);
278 assert_eq!(len, 16);
279
280 let (decoded, remainder) = Bytecode::from_compact(&buf, len);
281 assert_eq!(decoded, bytecode);
282 assert!(remainder.is_empty());
283 }
284
285 #[test]
286 fn test_account_has_bytecode() {
287 let acc_no_bytecode = Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None };
289 assert!(!acc_no_bytecode.has_bytecode(), "Account should not have bytecode");
290
291 let acc_empty_bytecode =
293 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(KECCAK_EMPTY) };
294 assert!(acc_empty_bytecode.has_bytecode(), "Account should have bytecode");
295
296 let acc_with_bytecode = Account {
298 nonce: 1,
299 balance: U256::from(1000),
300 bytecode_hash: Some(B256::from_slice(&[0x11u8; 32])),
301 };
302 assert!(acc_with_bytecode.has_bytecode(), "Account should have bytecode");
303 }
304
305 #[test]
306 fn test_account_get_bytecode_hash() {
307 let acc_no_bytecode = Account { nonce: 0, balance: U256::ZERO, bytecode_hash: None };
309 assert_eq!(acc_no_bytecode.get_bytecode_hash(), KECCAK_EMPTY, "Should return KECCAK_EMPTY");
310
311 let acc_empty_bytecode =
313 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(KECCAK_EMPTY) };
314 assert_eq!(
315 acc_empty_bytecode.get_bytecode_hash(),
316 KECCAK_EMPTY,
317 "Should return KECCAK_EMPTY"
318 );
319
320 let bytecode_hash = B256::from_slice(&[0x11u8; 32]);
322 let acc_with_bytecode =
323 Account { nonce: 1, balance: U256::from(1000), bytecode_hash: Some(bytecode_hash) };
324 assert_eq!(
325 acc_with_bytecode.get_bytecode_hash(),
326 bytecode_hash,
327 "Should return the bytecode hash"
328 );
329 }
330}