Skip to content

Commit 57594a7

Browse files
committed
introduce Scroll revm crate
Signed-off-by: Gregory Edison <[email protected]>
1 parent 53df6f0 commit 57594a7

File tree

10 files changed

+643
-0
lines changed

10 files changed

+643
-0
lines changed

Cargo.lock

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ members = [
9999
"crates/rpc/rpc-testing-util/",
100100
"crates/rpc/rpc-types-compat/",
101101
"crates/rpc/rpc/",
102+
"crates/scroll/revm",
102103
"crates/stages/api/",
103104
"crates/stages/stages/",
104105
"crates/stages/types/",
@@ -400,6 +401,7 @@ reth-rpc-eth-types = { path = "crates/rpc/rpc-eth-types", default-features = fal
400401
reth-rpc-layer = { path = "crates/rpc/rpc-layer" }
401402
reth-rpc-server-types = { path = "crates/rpc/rpc-server-types" }
402403
reth-rpc-types-compat = { path = "crates/rpc/rpc-types-compat" }
404+
reth-scroll-revm = { path = "crates/scroll/revm" }
403405
reth-stages = { path = "crates/stages/stages" }
404406
reth-stages-api = { path = "crates/stages/api" }
405407
reth-stages-types = { path = "crates/stages/types" }

crates/scroll/revm/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[package]
2+
name = "reth-scroll-revm"
3+
version.workspace = true
4+
edition.workspace = true
5+
rust-version.workspace = true
6+
license.workspace = true
7+
homepage.workspace = true
8+
repository.workspace = true
9+
exclude.workspace = true
10+
11+
[lints]
12+
workspace = true
13+
14+
[dependencies]
15+
# revm
16+
revm.workspace = true
17+
18+
# scroll
19+
poseidon-bn254 = { git = "https://github.com/scroll-tech/poseidon-bn254", branch = "master", features = ["bn254"] }

crates/scroll/revm/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! Scroll `revm` types redefinitions. Account types are redefined with two additional fields
2+
//! `code_size` and `poseidon_code_hash`, which are used during computation of the state root.
3+
4+
pub mod states;
5+
6+
pub mod primitives;
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! Scroll `revm` primitives types redefinitions.
2+
3+
use revm::{
4+
precompile::HashMap,
5+
primitives::{b256, AccountInfo, Bytecode, B256, KECCAK_EMPTY, U256},
6+
};
7+
8+
/// A Keccak code hash.
9+
type KeccakHash = B256;
10+
/// A Poseidon code hash.
11+
type PoseidonHash = B256;
12+
/// Size of a contract's code in bytes.
13+
type CodeSize = usize;
14+
15+
/// Scroll post execution context maps a Keccak code hash of a contract's bytecode to its code size
16+
/// and Poseidon code hash.
17+
pub type ScrollPostExecutionContext = HashMap<KeccakHash, (CodeSize, PoseidonHash)>;
18+
19+
/// The Poseidon hash of the empty string `""`.
20+
pub const POSEIDON_EMPTY: B256 =
21+
b256!("2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864");
22+
23+
/// Poseidon code hash
24+
pub fn poseidon(code: &[u8]) -> B256 {
25+
poseidon_bn254::hash_code(code).into()
26+
}
27+
28+
/// The Scroll account information. Code copy of [`AccountInfo`]. Provides additional `code_size`
29+
/// and `poseidon_code_hash` fields needed in the state root computation.
30+
#[derive(Clone, Debug, Eq)]
31+
pub struct ScrollAccountInfo {
32+
/// Account balance.
33+
pub balance: U256,
34+
/// Account nonce.
35+
pub nonce: u64,
36+
/// Account code keccak hash.
37+
pub code_hash: B256,
38+
/// code: if None, `code_by_hash` will be used to fetch it if code needs to be loaded from
39+
/// inside `revm`.
40+
pub code: Option<Bytecode>,
41+
/// Account code size.
42+
pub code_size: usize,
43+
/// Account code Poseidon hash. [`POSEIDON_EMPTY`] if code is None or empty.
44+
pub poseidon_code_hash: B256,
45+
}
46+
47+
impl From<(AccountInfo, &ScrollPostExecutionContext)> for ScrollAccountInfo {
48+
fn from((info, context): (AccountInfo, &ScrollPostExecutionContext)) -> Self {
49+
let (code_size, poseidon_code_hash) =
50+
context.get(&info.code_hash).copied().unwrap_or((0, POSEIDON_EMPTY));
51+
Self {
52+
balance: info.balance,
53+
nonce: info.nonce,
54+
code_hash: info.code_hash,
55+
code: info.code,
56+
code_size,
57+
poseidon_code_hash,
58+
}
59+
}
60+
}
61+
62+
impl Default for ScrollAccountInfo {
63+
fn default() -> Self {
64+
Self {
65+
balance: U256::ZERO,
66+
code_hash: KECCAK_EMPTY,
67+
code: Some(Bytecode::default()),
68+
nonce: 0,
69+
code_size: 0,
70+
poseidon_code_hash: POSEIDON_EMPTY,
71+
}
72+
}
73+
}
74+
75+
impl PartialEq for ScrollAccountInfo {
76+
fn eq(&self, other: &Self) -> bool {
77+
self.balance == other.balance &&
78+
self.nonce == other.nonce &&
79+
self.code_hash == other.code_hash
80+
}
81+
}
82+
83+
impl ScrollAccountInfo {
84+
/// Creates a new [`ScrollAccountInfo`] with the given fields.
85+
pub fn new(
86+
balance: U256,
87+
nonce: u64,
88+
code_hash: B256,
89+
code: Bytecode,
90+
poseidon_code_hash: B256,
91+
) -> Self {
92+
let code_size = code.len();
93+
Self { balance, nonce, code: Some(code), code_hash, code_size, poseidon_code_hash }
94+
}
95+
96+
/// Returns account info without the code.
97+
pub fn without_code(mut self) -> Self {
98+
self.take_bytecode();
99+
self
100+
}
101+
102+
/// Returns if an account is empty.
103+
///
104+
/// An account is empty if the following conditions are met.
105+
/// - code hash is zero or set to the Keccak256 hash of the empty string `""`
106+
/// - balance is zero
107+
/// - nonce is zero
108+
pub fn is_empty(&self) -> bool {
109+
let code_empty = self.is_empty_code_hash() || self.code_hash.is_zero();
110+
code_empty && self.balance.is_zero() && self.nonce == 0
111+
}
112+
113+
/// Returns `true` if the account is not empty.
114+
pub fn exists(&self) -> bool {
115+
!self.is_empty()
116+
}
117+
118+
/// Returns `true` if account has no nonce and code.
119+
pub fn has_no_code_and_nonce(&self) -> bool {
120+
self.is_empty_code_hash() && self.nonce == 0
121+
}
122+
123+
/// Return bytecode hash associated with this account.
124+
/// If account does not have code, it returns `KECCAK_EMPTY` hash.
125+
pub fn code_hash(&self) -> B256 {
126+
self.code_hash
127+
}
128+
129+
/// Returns true if the code hash is the Keccak256 hash of the empty string `""`.
130+
#[inline]
131+
pub fn is_empty_code_hash(&self) -> bool {
132+
self.code_hash == KECCAK_EMPTY
133+
}
134+
135+
/// Take bytecode from account. Code will be set to None.
136+
pub fn take_bytecode(&mut self) -> Option<Bytecode> {
137+
self.code.take()
138+
}
139+
140+
/// Returns a [`ScrollAccountInfo`] with only balance.
141+
pub fn from_balance(balance: U256) -> Self {
142+
Self { balance, ..Default::default() }
143+
}
144+
145+
/// Returns a [`ScrollAccountInfo`] with defaults for balance and nonce.
146+
/// Computes the Keccak and Poseidon hash of the provided bytecode.
147+
pub fn from_bytecode(bytecode: Bytecode) -> Self {
148+
let hash = bytecode.hash_slow();
149+
let code_size = bytecode.len();
150+
let poseidon_code_hash = poseidon(bytecode.bytecode());
151+
152+
Self {
153+
balance: U256::ZERO,
154+
nonce: 1,
155+
code: Some(bytecode),
156+
code_hash: hash,
157+
code_size,
158+
poseidon_code_hash,
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)