Skip to content

Commit

Permalink
add scroll primitives and update provider
Browse files Browse the repository at this point in the history
Signed-off-by: Gregory Edison <gregory.edison1993@gmail.com>
  • Loading branch information
greged93 committed Nov 9, 2024
1 parent f53d82f commit 5b4f4d9
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 69 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ members = [
"crates/rpc/rpc-testing-util/",
"crates/rpc/rpc-types-compat/",
"crates/rpc/rpc/",
"crates/scroll/primitives",
"crates/scroll/revm",
"crates/scroll/storage",
"crates/stages/api/",
Expand Down Expand Up @@ -402,6 +403,7 @@ reth-rpc-eth-types = { path = "crates/rpc/rpc-eth-types", default-features = fal
reth-rpc-layer = { path = "crates/rpc/rpc-layer" }
reth-rpc-server-types = { path = "crates/rpc/rpc-server-types" }
reth-rpc-types-compat = { path = "crates/rpc/rpc-types-compat" }
reth-scroll-primitives = { path = "crates/scroll/primitives" }
reth-scroll-revm = { path = "crates/scroll/revm" }
reth-scroll-storage = { path = "crates/scroll/storage" }
reth-stages = { path = "crates/stages/stages" }
Expand Down
12 changes: 12 additions & 0 deletions crates/scroll/primitives/src/execution_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use revm::primitives::{map::HashMap, B256};

/// A Keccak code hash.
type KeccakHash = B256;
/// A Poseidon code hash.
type PoseidonHash = B256;
/// Size of a contract's code in bytes.
type CodeSize = u64;

/// Scroll post execution context maps a Keccak code hash of a contract's bytecode to its code size
/// and Poseidon code hash.
pub type ScrollPostExecutionContext = HashMap<KeccakHash, (CodeSize, PoseidonHash)>;
10 changes: 10 additions & 0 deletions crates/scroll/primitives/src/poseidon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use revm::primitives::{b256, B256};

/// The Poseidon hash of the empty string `""`.
pub const POSEIDON_EMPTY: B256 =
b256!("2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864");

/// Poseidon code hash
pub fn poseidon(code: &[u8]) -> B256 {
poseidon_bn254::hash_code(code).into()
}
15 changes: 13 additions & 2 deletions crates/scroll/revm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,16 @@ workspace = true
# revm
revm.workspace = true

# scroll
poseidon-bn254 = { git = "https://github.com/scroll-tech/poseidon-bn254", branch = "master", features = ["bn254"] }
#scroll
reth-scroll-primitives.workspace = true

[features]
default = ["std"]

arbitrary = ["revm/arbitrary"]

scroll = []

serde = ["revm/serde"]

std = ["revm/std"]
4 changes: 4 additions & 0 deletions crates/scroll/revm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@
pub mod states;

pub mod primitives;

#[cfg(feature = "scroll")]
pub use primitives::ScrollAccountInfo;
pub use revm::primitives::*;
34 changes: 6 additions & 28 deletions crates/scroll/revm/src/primitives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
//! Scroll `revm` primitives types redefinitions.

use revm::{
precompile::HashMap,
primitives::{b256, AccountInfo, Bytecode, B256, KECCAK_EMPTY, U256},
};

/// A Keccak code hash.
type KeccakHash = B256;
/// A Poseidon code hash.
type PoseidonHash = B256;
/// Size of a contract's code in bytes.
type CodeSize = usize;

/// Scroll post execution context maps a Keccak code hash of a contract's bytecode to its code size
/// and Poseidon code hash.
pub type ScrollPostExecutionContext = HashMap<KeccakHash, (CodeSize, PoseidonHash)>;

/// The Poseidon hash of the empty string `""`.
pub const POSEIDON_EMPTY: B256 =
b256!("2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864");

/// Poseidon code hash
pub fn poseidon(code: &[u8]) -> B256 {
poseidon_bn254::hash_code(code).into()
}
use reth_scroll_primitives::{poseidon, ScrollPostExecutionContext, POSEIDON_EMPTY};
use revm::primitives::{AccountInfo, Bytecode, B256, KECCAK_EMPTY, U256};

/// The Scroll account information. Code copy of [`AccountInfo`]. Provides additional `code_size`
/// and `poseidon_code_hash` fields needed in the state root computation.
Expand All @@ -39,7 +17,7 @@ pub struct ScrollAccountInfo {
/// inside `revm`.
pub code: Option<Bytecode>,
/// Account code size.
pub code_size: usize,
pub code_size: u64,
/// Account code Poseidon hash. [`POSEIDON_EMPTY`] if code is None or empty.
pub poseidon_code_hash: B256,
}
Expand Down Expand Up @@ -89,7 +67,7 @@ impl ScrollAccountInfo {
code: Bytecode,
poseidon_code_hash: B256,
) -> Self {
let code_size = code.len();
let code_size = code.len() as u64;
Self { balance, nonce, code: Some(code), code_hash, code_size, poseidon_code_hash }
}

Expand Down Expand Up @@ -122,7 +100,7 @@ impl ScrollAccountInfo {

/// Return bytecode hash associated with this account.
/// If account does not have code, it returns `KECCAK_EMPTY` hash.
pub fn code_hash(&self) -> B256 {
pub const fn code_hash(&self) -> B256 {
self.code_hash
}

Expand All @@ -146,7 +124,7 @@ impl ScrollAccountInfo {
/// Computes the Keccak and Poseidon hash of the provided bytecode.
pub fn from_bytecode(bytecode: Bytecode) -> Self {
let hash = bytecode.hash_slow();
let code_size = bytecode.len();
let code_size = bytecode.len() as u64;
let poseidon_code_hash = poseidon(bytecode.bytecode());

Self {
Expand Down
9 changes: 5 additions & 4 deletions crates/scroll/revm/src/states/bundle.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use super::bundle_account::ScrollBundleAccount;
use crate::{
primitives::{ScrollAccountInfo, ScrollPostExecutionContext},
primitives::ScrollAccountInfo,
states::{
changes::{ScrollPlainStateReverts, ScrollStateChangeset},
reverts::ScrollReverts,
},
};
use reth_scroll_primitives::ScrollPostExecutionContext;
use revm::{
db::{states::PlainStorageChangeset, BundleState, OriginalValuesKnown},
primitives::{map::HashMap, Address, Bytecode, B256, KECCAK_EMPTY},
Expand Down Expand Up @@ -36,7 +37,7 @@ impl From<(BundleState, ScrollPostExecutionContext)> for ScrollBundleState {
.iter()
.map(|reverts| {
reverts
.into_iter()
.iter()
.map(|(add, revert)| (*add, (revert.clone(), &context).into()))
.collect()
})
Expand Down Expand Up @@ -67,7 +68,7 @@ impl ScrollBundleState {
}

/// Return reference to the state.
pub fn state(&self) -> &HashMap<Address, ScrollBundleAccount> {
pub const fn state(&self) -> &HashMap<Address, ScrollBundleAccount> {
&self.state
}

Expand Down Expand Up @@ -157,7 +158,7 @@ impl ScrollBundleState {
(plain_state, reverts.into_plain_state_reverts())
}

/// Return and clear all reverts from [ScrollBundleState]
/// Return and clear all reverts from [`ScrollBundleState`]
pub fn take_all_reverts(&mut self) -> ScrollReverts {
self.reverts_size = 0;
core::mem::take(&mut self.reverts)
Expand Down
7 changes: 4 additions & 3 deletions crates/scroll/revm/src/states/bundle_account.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::primitives::{ScrollAccountInfo, ScrollPostExecutionContext};
use crate::primitives::ScrollAccountInfo;
use reth_scroll_primitives::ScrollPostExecutionContext;
use revm::{
db::{AccountStatus, BundleAccount, StorageWithOriginalValues},
interpreter::primitives::U256,
Expand All @@ -17,7 +18,7 @@ pub struct ScrollBundleAccount {
/// If it is different we add it to changeset.
///
/// If Account was destroyed we ignore original value and compare present state with
/// U256::ZERO.
/// [`U256::ZERO`].
pub storage: StorageWithOriginalValues,
/// Account status.
pub status: AccountStatus,
Expand All @@ -33,7 +34,7 @@ impl From<(BundleAccount, &ScrollPostExecutionContext)> for ScrollBundleAccount

impl ScrollBundleAccount {
/// Creates a [`ScrollBundleAccount`].
pub fn new(
pub const fn new(
original_info: Option<ScrollAccountInfo>,
present_info: Option<ScrollAccountInfo>,
storage: StorageWithOriginalValues,
Expand Down
4 changes: 2 additions & 2 deletions crates/scroll/revm/src/states/changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ pub struct ScrollStateChangeset {
pub struct ScrollPlainStateReverts {
/// Vector of account with removed contracts bytecode
///
/// Note: If AccountInfo is None means that account needs to be removed.
/// Note: If [`ScrollAccountInfo`] is None means that account needs to be removed.
pub accounts: Vec<Vec<(Address, Option<ScrollAccountInfo>)>>,
/// Vector of storage with its address.
pub storage: Vec<Vec<PlainStorageRevert>>,
}

impl ScrollPlainStateReverts {
/// Constructs new [ScrollPlainStateReverts] with pre-allocated capacity.
/// Constructs new [`ScrollPlainStateReverts`] with pre-allocated capacity.
pub fn with_capacity(capacity: usize) -> Self {
Self { accounts: Vec::with_capacity(capacity), storage: Vec::with_capacity(capacity) }
}
Expand Down
10 changes: 4 additions & 6 deletions crates/scroll/revm/src/states/reverts.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use crate::{
primitives::{ScrollAccountInfo, ScrollPostExecutionContext},
states::changes::ScrollPlainStateReverts,
};
use crate::{primitives::ScrollAccountInfo, states::changes::ScrollPlainStateReverts};
use reth_scroll_primitives::ScrollPostExecutionContext;
use revm::{
db::{
states::{reverts::AccountInfoRevert, PlainStorageRevert},
Expand All @@ -17,7 +15,7 @@ pub struct ScrollReverts(Vec<Vec<(Address, ScrollAccountRevert)>>);

impl ScrollReverts {
/// Create new reverts
pub fn new(reverts: Vec<Vec<(Address, ScrollAccountRevert)>>) -> Self {
pub const fn new(reverts: Vec<Vec<(Address, ScrollAccountRevert)>>) -> Self {
Self(reverts)
}

Expand All @@ -30,7 +28,7 @@ impl ScrollReverts {
// pessimistically pre-allocate assuming _all_ accounts changed.
let mut accounts = Vec::with_capacity(reverts.len());
let mut storage = Vec::with_capacity(reverts.len());
for (address, revert_account) in reverts.into_iter() {
for (address, revert_account) in reverts {
match revert_account.account {
ScrollAccountInfoRevert::RevertTo(acc) => accounts.push((address, Some(acc))),
ScrollAccountInfoRevert::DeleteIt => accounts.push((address, None)),
Expand Down
8 changes: 5 additions & 3 deletions crates/scroll/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ workspace = true
alloy-primitives.workspace = true

# reth
reth-primitives-traits.workspace = true
reth-revm.workspace = true
reth-primitives-traits = { workspace = true, features = ["scroll"] }
reth-revm = { workspace = true, features = ["scroll"] }
reth-storage-errors.workspace = true

# scroll
reth-scroll-primitives.workspace = true
reth-scroll-revm.workspace = true

[dev-dependencies]
eyre.workspace = true
reth-revm = { workspace = true, features = ["test-utils"] }
reth-primitives-traits.workspace = true
reth-revm = { workspace = true, features = ["test-utils"] }
reth-scroll-primitives.workspace = true
57 changes: 36 additions & 21 deletions crates/scroll/storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use reth_revm::{
primitives::{AccountInfo, Bytecode},
Database,
};
use reth_scroll_revm::primitives::{poseidon, ScrollPostExecutionContext, POSEIDON_EMPTY};
use reth_scroll_primitives::ScrollPostExecutionContext;
use reth_storage_errors::provider::ProviderError;

/// A similar construct as `StateProviderDatabase` which captures additional Scroll context derived
/// from bytecode during execution.
/// A similar construct as `StateProviderDatabase` which captures additional Scroll context for
/// touched accounts during execution.
#[derive(Clone, Debug)]
pub struct ScrollStateProviderDatabase<DB> {
/// Scroll post execution context.
Expand All @@ -35,18 +35,18 @@ impl<DB: EvmStateProvider> Database for ScrollStateProviderDatabase<DB> {
type Error = ProviderError;

/// Retrieves basic account information for a given address.
/// Caches the Scroll context for the touched account if it
/// has bytecode.
///
/// Returns `Ok` with `Some(AccountInfo)` if the account exists,
/// `None` if it doesn't, or an error if encountered.
fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
let Some(account) = self.db.basic_account(address)? else { return Ok(None) };
let Some(code_hash) = account.bytecode_hash else { return Ok(Some(account.into())) };

let bytecode_context = (account.code_size, account.poseidon_code_hash);
if let Entry::Vacant(entry) = self.post_execution_context.entry(code_hash) {
let code = self.db.bytecode_by_hash(code_hash)?.unwrap_or_default();
let poseidon_hash =
if code.is_empty() { POSEIDON_EMPTY } else { poseidon(code.bytecode()) };
entry.insert((code.len(), poseidon_hash));
entry.insert(bytecode_context);
}

Ok(Some(account.into()))
Expand Down Expand Up @@ -77,51 +77,66 @@ impl<DB: EvmStateProvider> Database for ScrollStateProviderDatabase<DB> {
#[cfg(test)]
mod tests {
use crate::ScrollStateProviderDatabase;
use alloy_primitives::{keccak256, Address, Bytes, U256};
use alloy_primitives::{keccak256, Address, Bytes, B256, U256};
use reth_primitives_traits::Account;
use reth_revm::{test_utils::StateProviderTest, Database};
use reth_scroll_revm::primitives::{poseidon, POSEIDON_EMPTY};
use reth_scroll_primitives::{poseidon, POSEIDON_EMPTY};

#[test]
fn test_scroll_post_execution_context() -> eyre::Result<()> {
let mut db = StateProviderTest::default();

// insert an eoa in the db
let eoa_address = Address::random();
let eoa = Account { nonce: 0, balance: U256::MAX, bytecode_hash: None };
let eoa = Account {
nonce: 0,
balance: U256::MAX,
bytecode_hash: None,
code_size: 0,
poseidon_code_hash: B256::ZERO,
};
db.insert_account(eoa_address, eoa, None, Default::default());

// insert a contract account in the db
let contract_address = Address::random();
let bytecode = Bytes::copy_from_slice(&[0x0, 0x1, 0x2, 0x3, 0x4, 0x5]);
let bytecode_hash = keccak256(&bytecode);
let contract = Account { nonce: 0, balance: U256::MAX, bytecode_hash: Some(bytecode_hash) };
let poseidon_code_hash = poseidon(&bytecode);
let contract = Account {
nonce: 0,
balance: U256::MAX,
bytecode_hash: Some(bytecode_hash),
code_size: bytecode.len() as u64,
poseidon_code_hash,
};
db.insert_account(contract_address, contract, Some(bytecode.clone()), Default::default());

// insert an empty contract account in the db
let empty_contract_address = Address::random();
let empty_bytecode = Bytes::copy_from_slice(&[]);
let empty_bytecode_hash = keccak256(&empty_bytecode);
let empty_contract = Account { nonce: 0, balance: U256::MAX, bytecode_hash: None };
let empty_contract = Account {
nonce: 0,
balance: U256::MAX,
bytecode_hash: None,
code_size: 0,
poseidon_code_hash: POSEIDON_EMPTY,
};
db.insert_account(
empty_contract_address,
contract,
empty_contract,
Some(empty_bytecode),
Default::default(),
);

// check eoa is in db
let mut provider = ScrollStateProviderDatabase::new(db);
let fetched_eoa = provider.basic(eoa_address)?.unwrap();
assert_eq!(Into::<Account>::into(fetched_eoa), eoa);

// check eoa is in db
let _ = provider.basic(eoa_address)?.unwrap();
// check contract is in db
let fetched_contract = provider.basic(contract_address)?.unwrap();
assert_eq!(Into::<Account>::into(fetched_contract), contract);

let _ = provider.basic(contract_address)?.unwrap();
// check empty contract is in db
let empty_fetched_contract = provider.basic(empty_contract_address)?.unwrap();
assert_eq!(Into::<Account>::into(empty_fetched_contract), empty_contract);
let _ = provider.basic(empty_contract_address)?.unwrap();

// check provider context contains only contract and empty contract
let post_execution_context = provider.post_execution_context();
Expand Down

0 comments on commit 5b4f4d9

Please sign in to comment.