Skip to content

Commit

Permalink
Esad/evm genesis storage (#286)
Browse files Browse the repository at this point in the history
* Add dockerfile and dockerignore for sequencer

* Add dockerized sequencer config

* Add specific genesis config for docker

* Add docker publish block script

* Update docker config chain id for hive tests

* Store hive dockerfile in another folder

* Update hive genesis folder location

* Update hive config

* add storage as genesis option

* update hive genesis

* update docker rust version

* Erce/fix evm genesis storage (#321)

* Add eth_getBlockTransactionCountByHash rpc and test

* Add hive contract and WIP test

* Add hive contract and WIP test

* Fix predeploy contract bug

* Add more tests related to genesis contract deployment

* update rust version (#317)

* Esad/more links in readme (#318)

* update rust version

* add links in warning

* Don't burn base fee (#302)

* Don't burn base fee

* small change on estimate gas test

---------

Co-authored-by: eyusufatik <[email protected]>

* update reth remove alloy-rpc-types and use reth-rpc-types

* Update cargo.lock

* Remove print

---------

Co-authored-by: Esad Yusuf Atik <[email protected]>
Co-authored-by: Roman <[email protected]>
Co-authored-by: eyusufatik <[email protected]>

* fix horrible nightly merges

* Restore removed Cargo.lock files

* use HashMap::is_empty instead of Option for skip_deserialize in EVM Genesis config

* Add storage slots w/o slot len check

---------

Co-authored-by: Erce Can Bektüre <[email protected]>
Co-authored-by: Erce Can Bektüre <[email protected]>
Co-authored-by: Roman <[email protected]>
  • Loading branch information
4 people authored Apr 2, 2024
1 parent 72fb8d6 commit efa5f8f
Show file tree
Hide file tree
Showing 18 changed files with 622 additions and 138 deletions.
71 changes: 70 additions & 1 deletion bin/citrea/tests/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ use std::net::SocketAddr;
use std::str::FromStr;

// use citrea::initialize_logging;
use citrea_evm::smart_contracts::{LogsContract, SimpleStorageContract, TestContract};
use citrea_evm::smart_contracts::{
HiveContract, LogsContract, SimpleStorageContract, TestContract,
};
use citrea_stf::genesis_config::GenesisPaths;
use ethers_core::abi::Address;
use ethers_core::types::{BlockId, Bytes, U256};
use ethers_signers::{LocalWallet, Signer};
use reth_primitives::BlockNumberOrTag;
// use sov_demo_rollup::initialize_logging;
use sov_modules_stf_blueprint::kernels::basic::BasicKernelGenesisPaths;
use sov_stf_runner::RollupProverConfig;

Expand Down Expand Up @@ -132,6 +135,72 @@ async fn test_eth_get_logs() -> Result<(), anyhow::Error> {
Ok(())
}

#[tokio::test]
async fn test_genesis_contract_call() -> Result<(), Box<dyn std::error::Error>> {
let (seq_port_tx, seq_port_rx) = tokio::sync::oneshot::channel();

let seq_task = tokio::spawn(async move {
start_rollup(
seq_port_tx,
GenesisPaths::from_dir("../../hive/genesis"),
BasicKernelGenesisPaths {
chain_state: "../test-data/genesis/integration-tests/chain_state.json".into(),
},
RollupProverConfig::Execute,
NodeMode::SequencerNode,
None,
123456,
true,
)
.await;
});

let seq_port = seq_port_rx.await.unwrap();
let seq_test_client = make_test_client(seq_port).await;
// call the contract with address 0x0000000000000000000000000000000000000314
let contract_address = Address::from_str("0x0000000000000000000000000000000000000314").unwrap();

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

let expected_code = "60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029";
assert_eq!(code.to_vec(), hex::decode(expected_code).unwrap());

let hive_contract = HiveContract::new();

let res: String = seq_test_client
.contract_call(
contract_address,
hive_contract.call_const_func(1, 2, 4),
None,
)
.await
.unwrap();
let expected_res = "0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004";
assert_eq!(res, expected_res);

let storage_value = seq_test_client
.eth_get_storage_at(contract_address, U256::zero(), None)
.await
.unwrap();
assert_eq!(storage_value, 4660.into());

let storage_value = seq_test_client
.eth_get_storage_at(
contract_address,
U256::from_str("0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9")
.unwrap(),
None,
)
.await
.unwrap();
assert_eq!(storage_value, 1.into());
seq_task.abort();
Ok(())
}

#[allow(clippy::borrowed_box)]
async fn test_getlogs(client: &Box<TestClient>) -> Result<(), Box<dyn std::error::Error>> {
let (contract_address, contract) = {
Expand Down
15 changes: 14 additions & 1 deletion crates/evm/src/evm/db_init.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use reth_primitives::Bytes;
use reth_primitives::{Bytes, U256};
#[cfg(test)]
use revm::db::{CacheDB, EmptyDB};
use revm::primitives::{Address, B256};
Expand All @@ -11,6 +11,7 @@ use super::{AccountInfo, DbAccount};
pub(crate) trait InitEvmDb {
fn insert_account_info(&mut self, address: Address, acc: AccountInfo);
fn insert_code(&mut self, code_hash: B256, code: Bytes);
fn insert_storage(&mut self, address: Address, index: U256, value: U256);
}

impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {
Expand All @@ -24,6 +25,14 @@ impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {
fn insert_code(&mut self, code_hash: B256, code: Bytes) {
self.code.set(&code_hash, &code, self.working_set)
}

fn insert_storage(&mut self, address: Address, index: U256, value: U256) {
self.accounts
.get(&address, self.working_set)
.expect("Account should already be inserted")
.storage
.set(&index, &value, self.working_set);
}
}

#[cfg(test)]
Expand All @@ -36,4 +45,8 @@ impl InitEvmDb for CacheDB<EmptyDB> {
self.contracts
.insert(code_hash, revm::primitives::Bytecode::new_raw(code));
}

fn insert_storage(&mut self, address: Address, index: U256, value: U256) {
self.insert_account_storage(address, index, value).unwrap();
}
}
1 change: 1 addition & 0 deletions crates/evm/src/evm/test_data/HiveContract.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"constant":true,"inputs":[],"name":"ui","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getFromMap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"value","type":"uint256"}],"name":"addToMap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"ui_","type":"uint256"},{"name":"addr_","type":"address"}],"name":"events","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"},{"name":"c","type":"uint256"}],"name":"constFunc","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"ui_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[],"name":"E0","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"","type":"uint256"}],"name":"E1","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"","type":"uint256"}],"name":"E2","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"","type":"address"}],"name":"E3","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"","type":"address"}],"name":"E4","type":"event"},{"anonymous":true,"inputs":[{"indexed":false,"name":"","type":"uint256"},{"indexed":false,"name":"","type":"address"}],"name":"E5","type":"event"}]
1 change: 1 addition & 0 deletions crates/evm/src/evm/test_data/HiveContract.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
608060405234801561001057600080fd5b506040516020806104dd833981018060405281019080805190602001909291905050508060008190555080600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505061044f8061008e6000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d14610072578063abd1a0cf1461009d578063abfced1d146100f4578063e05c914a14610141578063e6768b451461018e575b600080fd5b34801561007e57600080fd5b506100876101f1565b6040518082815260200191505060405180910390f35b3480156100a957600080fd5b506100de600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506101f7565b6040518082815260200191505060405180910390f35b34801561010057600080fd5b5061013f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610240565b005b34801561014d57600080fd5b5061018c60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610288565b005b34801561019a57600080fd5b506101cd60048036038101908080359060200190929190803590602001909291908035906020019092919050505061040c565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405160405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405160405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405160405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05050565b6000806000858585925092509250935093509390505600a165627a7a72305820fe919e6847ca1cdcd1515740e9d079a4632ffc0e13562b71878a2ca15753361a0029
43 changes: 43 additions & 0 deletions crates/evm/src/evm/test_data/HiveContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pragma solidity ^0.4.6;

contract Test {
event E0();
event E1(uint);
event E2(uint indexed);
event E3(address);
event E4(address indexed);
event E5(uint, address) anonymous;

uint public ui;
mapping(address => uint) map;

function Test(uint ui_) {
ui = ui_;
map[msg.sender] = ui_;
}

function events(uint ui_, address addr_) {
E0();
E1(ui_);
E2(ui_);
E3(addr_);
E4(addr_);
E5(ui_, addr_);
}

function constFunc(
uint a,
uint b,
uint c
) constant returns (uint, uint, uint) {
return (a, b, c);
}

function getFromMap(address addr) constant returns (uint) {
return map[addr];
}

function addToMap(address addr, uint value) {
map[addr] = value;
}
}
78 changes: 77 additions & 1 deletion crates/evm/src/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ pub struct AccountData {
pub code_hash: B256,
/// Smart contract code.
pub code: Bytes,
#[serde(
default = "Default::default",
skip_serializing_if = "HashMap::is_empty"
)]
/// Smart contract storage
pub storage: HashMap<U256, U256>,
/// Account nonce.
pub nonce: u64,
}
Expand Down Expand Up @@ -104,6 +110,10 @@ impl<C: sov_modules_api::Context> Evm<C> {

if acc.code.len() > 0 {
evm_db.insert_code(acc.code_hash, acc.code.clone());

for (k, v) in acc.storage.iter() {
evm_db.insert_storage(acc.address, *k, *v);
}
}
}

Expand Down Expand Up @@ -184,15 +194,18 @@ impl<C: sov_modules_api::Context> Evm<C> {

#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::str::FromStr;

use hex::FromHex;
use reth_primitives::Bytes;
use revm::primitives::{Address, SpecId};

use super::U256;
use crate::{AccountData, EvmConfig};

#[test]
fn test_config_serialization() {
fn test_config_deserialization() {
let address = Address::from_str("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266").unwrap();
let config = EvmConfig {
data: vec![AccountData {
Expand All @@ -201,6 +214,7 @@ mod tests {
code_hash: AccountData::empty_code(),
code: Bytes::default(),
nonce: 0,
storage: Default::default(),
}],
chain_id: 1,
limit_contract_code_size: None,
Expand Down Expand Up @@ -235,6 +249,68 @@ mod tests {
}
}"#;

let parsed_config: EvmConfig = serde_json::from_str(data).unwrap();
assert_eq!(config, parsed_config);

let mut storage = HashMap::new();
storage.insert(U256::from(0), U256::from(0x1234));
storage.insert(
U256::from_be_slice(
&hex::decode("6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9")
.unwrap(),
),
U256::from(1),
);

let address = Address::from_str("0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266").unwrap();
let config = EvmConfig {
data: vec![AccountData {
address,
balance: AccountData::balance(u64::MAX),
code_hash: AccountData::empty_code(),
code: Bytes::from_hex("0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063").unwrap(),
nonce: 0,
storage,
}],
chain_id: 1,
limit_contract_code_size: None,
spec: vec![(0, SpecId::SHANGHAI)].into_iter().collect(),
block_timestamp_delta: 1u64,
..Default::default()
};

// code and hash are invalid
// just to test that serialization works
let data = r#"
{
"data":[
{
"address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"balance":"0xffffffffffffffff",
"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"code":"0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
},
"nonce":0
}],
"chain_id":1,
"limit_contract_code_size":null,
"spec":{
"0":"SHANGHAI"
},
"coinbase":"0x0000000000000000000000000000000000000000",
"starting_base_fee":1000000000,
"block_gas_limit":30000000,
"genesis_timestamp":0,
"block_timestamp_delta":1,
"base_fee_params":{
"max_change_denominator":8,
"elasticity_multiplier":2
}
}"#;

let parsed_config: EvmConfig = serde_json::from_str(data).unwrap();
assert_eq!(config, parsed_config)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ where
self.block_env.set(&new_pending_env, working_set);
self.l1_fee_rate.set(&l1_fee_rate, working_set);

// if hight > 256, start removing the oldest block
// if height > 256, start removing the oldest block
// keeping only 256 most recent blocks
// this first happens on txs in block 257
// remove block 0, keep blocks 1-256
Expand Down
17 changes: 17 additions & 0 deletions crates/evm/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,23 @@ impl<C: sov_modules_api::Context> Evm<C> {
self.estimate_gas_with_env(request, block_env, cfg_env, &mut tx_env, working_set)
}

/// Handler for: `eth_getBlockTransactionCountByHash`
// https://github.com/paradigmxyz/reth/blob/main/crates/rpc/rpc/src/eth/api/call.rs#L172
#[rpc_method(name = "eth_getBlockTransactionCountByHash")]
pub fn eth_get_block_transaction_count_by_hash(
&self,
block_hash: reth_primitives::B256,
working_set: &mut WorkingSet<C>,
) -> RpcResult<Option<reth_primitives::U256>> {
info!("evm module: eth_getBlockTransactionCountByHash");
// Get the number of transactions in a block given blockhash
let block = self.get_block_by_hash(block_hash, None, working_set)?;
match block {
Some(block) => Ok(Some(U256::from(block.transactions.len()))),
None => Ok(None),
}
}

/// Inner gas estimator
pub(crate) fn estimate_gas_with_env(
&self,
Expand Down
53 changes: 53 additions & 0 deletions crates/evm/src/smart_contracts/hive_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use ethers_contract::BaseContract;
use ethers_core::types::Bytes;

use super::{make_contract_from_abi, test_data_path};

/// CallerContract wrapper.
pub struct HiveContract {
bytecode: Bytes,
base_contract: BaseContract,
}

impl Default for HiveContract {
fn default() -> Self {
Self::new()
}
}

impl HiveContract {
/// Create a new instance of HiveContract.
pub fn new() -> Self {
let contract_data = {
let mut path = test_data_path();
path.push("HiveContract.bin");

let contract_data = std::fs::read_to_string(path).unwrap();
hex::decode(contract_data).unwrap()
};

let base_contract = {
let mut path = test_data_path();
path.push("HiveContract.abi");

make_contract_from_abi(path)
};
Self {
bytecode: Bytes::from(contract_data),
base_contract,
}
}
/// Calls ConstFunc of Hive Contract
pub fn call_const_func(&self, a: u32, b: u32, c: u32) -> Bytes {
let arg_a = ethereum_types::U256::from(a);
let arg_b = ethereum_types::U256::from(b);
let arg_c = ethereum_types::U256::from(c);
let args = (arg_a, arg_b, arg_c);
self.base_contract.encode("constFunc", args).unwrap()
}

/// Bytecode of the Hive Contract.
pub fn byte_code(&self) -> Bytes {
self.bytecode.clone()
}
}
Loading

0 comments on commit efa5f8f

Please sign in to comment.