Skip to content

Commit

Permalink
Offchain contract test (#1459)
Browse files Browse the repository at this point in the history
  • Loading branch information
rakanalh authored Nov 13, 2024
1 parent c45741c commit 17a677a
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 12 deletions.
1 change: 1 addition & 0 deletions bin/citrea/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ tracing-subscriber = { workspace = true }

[dev-dependencies]
citrea-evm = { path = "../../crates/evm", features = ["native"] }
citrea-primitives = { path = "../../crates/primitives", features = ["testing"] }
sov-mock-da = { path = "../../crates/sovereign-sdk/adapters/mock-da", default-features = false }
sov-prover-storage-manager = { path = "../../crates/sovereign-sdk/full-node/sov-prover-storage-manager", features = ["test-utils"] }
sov-rollup-interface = { path = "../../crates/sovereign-sdk/rollup-interface", features = ["fuzzing"] }
Expand Down
126 changes: 126 additions & 0 deletions bin/citrea/tests/e2e/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,129 @@ async fn execute_blocks(

Ok(())
}

/// Deploy pre-fork contract, activate a fork and then check fetching the contract's code
/// through RPC to make sure that the actual code is fetched properly pre and post fork.
#[tokio::test(flavor = "multi_thread")]
async fn test_offchain_contract_storage() {
// citrea::initialize_logging(tracing::Level::DEBUG);

let storage_dir = tempdir_with_children(&["DA", "sequencer", "prover", "full-node"]);
let da_db_dir = storage_dir.path().join("DA").to_path_buf();
let sequencer_db_dir = storage_dir.path().join("sequencer").to_path_buf();

let (seq_port_tx, seq_port_rx) = tokio::sync::oneshot::channel();

let sequencer_config = SequencerConfig::default();
let rollup_config =
create_default_rollup_config(true, &sequencer_db_dir, &da_db_dir, NodeMode::SequencerNode);
let seq_task = tokio::spawn(async {
start_rollup(
seq_port_tx,
GenesisPaths::from_dir(TEST_DATA_GENESIS_PATH),
None,
None,
rollup_config,
Some(sequencer_config),
)
.await;
});

let seq_port = seq_port_rx.await.unwrap();
let sequencer_client = make_test_client(seq_port).await.unwrap();

sequencer_client.send_publish_batch_request().await;
wait_for_l2_block(&sequencer_client, 1, None).await;

let (contract_address, contract, runtime_code) = {
let contract = SimpleStorageContract::default();
let runtime_code = sequencer_client
.deploy_contract_call(contract.byte_code(), None)
.await
.unwrap();
let deploy_contract_req = sequencer_client
.deploy_contract(contract.byte_code(), None)
.await
.unwrap();
sequencer_client.send_publish_batch_request().await;

let contract_address = deploy_contract_req
.get_receipt()
.await
.unwrap()
.contract_address
.unwrap();

(contract_address, contract, runtime_code)
};

{
let set_value_req = sequencer_client
.contract_transaction(contract_address, contract.set_call_data(42), None)
.await;
sequencer_client.send_publish_batch_request().await;
set_value_req.watch().await.unwrap();
}

let code = sequencer_client
.eth_get_code(contract_address, None)
.await
.unwrap();

assert_eq!(code.to_vec()[..runtime_code.len()], runtime_code.to_vec());

// reach the block at which the fork will be activated
for _ in 3..=100 {
sequencer_client.send_publish_batch_request().await;
}

// This should access the `code` and copy code over to `offchain_code` in EVM
let code = sequencer_client
.eth_get_code(contract_address, None)
.await
.unwrap();
assert_eq!(code.to_vec()[..runtime_code.len()], runtime_code.to_vec());

// Execute transaction on the contract living in `offchain_code`
{
let set_value_req = sequencer_client
.contract_transaction(contract_address, contract.set_call_data(50), None)
.await;
sequencer_client.send_publish_batch_request().await;
set_value_req.watch().await.unwrap();
}

let code = sequencer_client
.eth_get_code(contract_address, None)
.await
.unwrap();
assert_eq!(code.to_vec()[..runtime_code.len()], runtime_code.to_vec());

// Deploy a contract post-fork
let (contract_address, contract) = {
let contract = SimpleStorageContract::default();
let deploy_contract_req = sequencer_client
.deploy_contract(contract.byte_code(), None)
.await
.unwrap();
sequencer_client.send_publish_batch_request().await;

let contract_address = deploy_contract_req
.get_receipt()
.await
.unwrap()
.contract_address
.unwrap();

(contract_address, contract)
};

{
let set_value_req = sequencer_client
.contract_transaction(contract_address, contract.set_call_data(60), None)
.await;
sequencer_client.send_publish_batch_request().await;
set_value_req.watch().await.unwrap();
}
seq_task.abort();
}
1 change: 1 addition & 0 deletions crates/batch-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ tracing = { workspace = true }
sha2 = { workspace = true }
tempfile = { workspace = true }

citrea-primitives = { path = "../primitives", features = ["testing"] }
citrea-stf = { path = "../citrea-stf", features = ["native"] }
prover-services = { path = "../prover-services" }
sov-mock-da = { path = "../sovereign-sdk/adapters/mock-da", features = ["native"] }
Expand Down
1 change: 1 addition & 0 deletions crates/citrea-stf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ soft-confirmation-rule-enforcer = { path = "../soft-confirmation-rule-enforcer"
# sov-prover-storage-manager = { path = "../sovereign-sdk/full-node/sov-prover-storage-manager", features = [
# "test-utils",
# ] }
citrea-primitives = { path = "../primitives", features = ["testing"] }

[features]
default = []
Expand Down
1 change: 1 addition & 0 deletions crates/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ secp256k1 = { workspace = true, optional = true }
alloy = { workspace = true, features = ["consensus", "providers", "signers", "signer-local"] }
bcs = { workspace = true }
bytes = { workspace = true }
citrea-primitives = { path = "../primitives", features = ["testing"] }
lazy_static = "1.4.0"
rand = { workspace = true }
rayon = { workspace = true }
Expand Down
5 changes: 2 additions & 3 deletions crates/evm/src/evm/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
use std::collections::HashMap;

use reth_primitives::{keccak256, Address, B256};
use revm::primitives::SpecId::CANCUN;
use revm::primitives::{AccountInfo as ReVmAccountInfo, Bytecode, SpecId, U256};
use revm::Database;
use sov_modules_api::{StateMapAccessor, WorkingSet};
Expand Down Expand Up @@ -113,7 +112,7 @@ impl<'a, C: sov_modules_api::Context> Database for EvmDb<'a, C> {

// If CANCUN or later forks are activated, try to fetch code from offchain storage
// first. This is to prevent slower lookups in `code`.
if self.current_spec.is_enabled_in(CANCUN) {
if self.current_spec.is_enabled_in(SpecId::CANCUN) {
if let Some(code) = self
.offchain_code
.get(&code_hash, &mut self.working_set.offchain_state())
Expand All @@ -125,7 +124,7 @@ impl<'a, C: sov_modules_api::Context> Database for EvmDb<'a, C> {
let code = self.code.get(&code_hash, self.working_set);
if let Some(code) = code {
// Gradually migrate contract codes into the offchain code state map.
if self.current_spec.is_enabled_in(CANCUN) {
if self.current_spec.is_enabled_in(SpecId::CANCUN) {
self.offchain_code
.set(&code_hash, &code, &mut self.working_set.offchain_state());
}
Expand Down
5 changes: 2 additions & 3 deletions crates/evm/src/evm/db_commit.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::collections::BTreeMap;

use alloy_primitives::{Address, U256};
use revm::primitives::SpecId::CANCUN;
use revm::primitives::{Account, AccountInfo, HashMap};
use revm::primitives::{Account, AccountInfo, HashMap, SpecId};
use revm::DatabaseCommit;
use sov_modules_api::{StateMapAccessor, StateVecAccessor};

Expand Down Expand Up @@ -58,7 +57,7 @@ impl<'a, C: sov_modules_api::Context> DatabaseCommit for EvmDb<'a, C> {
.is_some();

if !exists_in_db {
if self.current_spec.is_enabled_in(CANCUN) {
if self.current_spec.is_enabled_in(SpecId::CANCUN) {
self.offchain_code.set(
&account_info.code_hash,
code,
Expand Down
5 changes: 2 additions & 3 deletions crates/evm/src/evm/db_init.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use reth_primitives::U256;
#[cfg(test)]
use revm::db::{CacheDB, EmptyDB};
use revm::primitives::SpecId::CANCUN;
use revm::primitives::{Address, Bytecode, B256};
use revm::primitives::{Address, Bytecode, SpecId, B256};
use sov_modules_api::StateMapAccessor;

use super::db::EvmDb;
Expand All @@ -21,7 +20,7 @@ impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {
}

fn insert_code(&mut self, code_hash: B256, code: Bytecode) {
if self.current_spec.is_enabled_in(CANCUN) {
if self.current_spec.is_enabled_in(SpecId::CANCUN) {
self.offchain_code
.set(&code_hash, &code, &mut self.working_set.offchain_state())
} else {
Expand Down
1 change: 0 additions & 1 deletion crates/evm/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ impl<C: sov_modules_api::Context> Evm<C> {
spec: vec![(0, SpecId::SHANGHAI)],
};

println!("Chain config: {:?}", chain_cfg);
self.cfg.set(&chain_cfg, working_set);

let header = reth_primitives::Header {
Expand Down
3 changes: 1 addition & 2 deletions crates/evm/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use reth_rpc_types::{
TransactionReceipt,
};
use reth_rpc_types_compat::block::from_primitive_with_hash;
use revm::primitives::SpecId::CANCUN;
use revm::primitives::{
BlobExcessGasAndPrice, BlockEnv, CfgEnvWithHandlerCfg, EVMError, ExecutionResult, HaltReason,
InvalidTransaction, SpecId, TransactTo,
Expand Down Expand Up @@ -375,7 +374,7 @@ impl<C: sov_modules_api::Context> Evm<C> {

let account = self.accounts.get(&address, working_set).unwrap_or_default();
let code = if let Some(code_hash) = account.code_hash {
if current_spec.is_enabled_in(CANCUN) {
if current_spec.is_enabled_in(SpecId::CANCUN) {
self.offchain_code
.get(&code_hash, &mut working_set.offchain_state())
.unwrap_or_else(|| self.code.get(&code_hash, working_set).unwrap_or_default())
Expand Down
1 change: 1 addition & 0 deletions crates/fullnode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ tracing = { workspace = true }
sha2 = { workspace = true }
tempfile = { workspace = true }

citrea-primitives = { path = "../primitives", features = ["testing"] }
sov-mock-da = { path = "../sovereign-sdk/adapters/mock-da", features = ["native"] }
sov-mock-zkvm = { path = "../sovereign-sdk/adapters/mock-zkvm" }
sov-prover-storage-manager = { path = "../sovereign-sdk/full-node/sov-prover-storage-manager", features = ["test-utils"] }
Expand Down
3 changes: 3 additions & 0 deletions crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ native = [
"dep:tracing",
"sov-rollup-interface/native",
]
testing = [
"native",
]
17 changes: 17 additions & 0 deletions crates/primitives/src/forks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use sov_rollup_interface::spec::SpecId;

/// This defines the list of forks which will be activated
/// at specific heights.
#[cfg(not(feature = "testing"))]
pub const FORKS: [Fork; 2] = [
Fork {
spec_id: SpecId::Genesis,
Expand All @@ -15,3 +16,19 @@ pub const FORKS: [Fork; 2] = [
// Examples of how we can define further forks
// Fork { spec_id: SpecId::Fork2, activation_height: 1000 },
];

#[cfg(feature = "testing")]
pub const FORKS: [Fork; 3] = [
Fork {
spec_id: SpecId::Genesis,
activation_height: 0,
},
Fork {
spec_id: SpecId::Fork1,
activation_height: 1000,
},
Fork {
spec_id: SpecId::Fork2,
activation_height: 2000,
},
];

0 comments on commit 17a677a

Please sign in to comment.