Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre_state_root in evm hook #986

Merged
merged 15 commits into from
Oct 9, 2023
572 changes: 135 additions & 437 deletions examples/demo-rollup/tests/evm/mod.rs

Large diffs are not rendered by default.

333 changes: 333 additions & 0 deletions examples/demo-rollup/tests/evm/test_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
use ethereum_types::H160;
use ethers_core::abi::Address;
use ethers_core::k256::ecdsa::SigningKey;
use ethers_core::types::transaction::eip2718::TypedTransaction;
use ethers_core::types::{
Block, Eip1559TransactionRequest, Transaction, TransactionRequest, TxHash,
};
use ethers_middleware::SignerMiddleware;
use ethers_providers::{Http, Middleware, PendingTransaction, Provider};
use ethers_signers::Wallet;
use jsonrpsee::core::client::ClientT;
use jsonrpsee::http_client::{HttpClient, HttpClientBuilder};
use jsonrpsee::rpc_params;
use reth_primitives::Bytes;
use sov_evm::SimpleStorageContract;

const MAX_FEE_PER_GAS: u64 = 100000001;
const GAS: u64 = 900000u64;

pub(crate) struct TestClient {
chain_id: u64,
pub(crate) from_addr: Address,
contract: SimpleStorageContract,
client: SignerMiddleware<Provider<Http>, Wallet<SigningKey>>,
http_client: HttpClient,
}

impl TestClient {
#[allow(dead_code)]
pub(crate) async fn new(
chain_id: u64,
key: Wallet<SigningKey>,
from_addr: Address,
contract: SimpleStorageContract,
rpc_addr: std::net::SocketAddr,
) -> Self {
let host = format!("http://localhost:{}", rpc_addr.port());

let provider = Provider::try_from(&host).unwrap();
let client = SignerMiddleware::new_with_provider_chain(provider, key)
.await
.unwrap();

let http_client = HttpClientBuilder::default().build(host).unwrap();

Self {
chain_id,
from_addr,
contract,
client,
http_client,
}
}

pub(crate) async fn send_publish_batch_request(&self) {
let _: String = self
.http_client
.request("eth_publishBatch", rpc_params![])
.await
.unwrap();
}

pub(crate) async fn deploy_contract(
&self,
) -> Result<PendingTransaction<'_, Http>, Box<dyn std::error::Error>> {
let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.chain_id(self.chain_id)
.nonce(0u64)
.max_priority_fee_per_gas(10u64)
.max_fee_per_gas(MAX_FEE_PER_GAS)
.gas(GAS)
.data(self.contract.byte_code());

let typed_transaction = TypedTransaction::Eip1559(req);

let receipt_req = self
.client
.send_transaction(typed_transaction, None)
.await?;

Ok(receipt_req)
}

pub(crate) async fn deploy_contract_call(&self) -> Result<Bytes, Box<dyn std::error::Error>> {
let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.chain_id(self.chain_id)
.nonce(0u64)
.max_priority_fee_per_gas(10u64)
.max_fee_per_gas(MAX_FEE_PER_GAS)
.gas(GAS)
.data(self.contract.byte_code());

let typed_transaction = TypedTransaction::Eip1559(req);

let receipt_req = self.eth_call(typed_transaction, None).await?;

Ok(receipt_req)
}

pub(crate) async fn set_value_unsigned(
&self,
contract_address: H160,
set_arg: u32,
) -> PendingTransaction<'_, Http> {
// Tx without gas_limit should estimate and include it in send_transaction endpoint
// Tx without nonce should fetch and include it in send_transaction endpoint
let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.to(contract_address)
.chain_id(self.chain_id)
.data(self.contract.set_call_data(set_arg))
.max_priority_fee_per_gas(10u64)
.max_fee_per_gas(MAX_FEE_PER_GAS);

let typed_transaction = TypedTransaction::Eip1559(req);

self.eth_send_transaction(typed_transaction).await
}

pub(crate) async fn set_value(
&self,
contract_address: H160,
set_arg: u32,
) -> PendingTransaction<'_, Http> {
let nonce = self.eth_get_transaction_count(self.from_addr).await;

let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.to(contract_address)
.chain_id(self.chain_id)
.nonce(nonce)
.data(self.contract.set_call_data(set_arg))
.max_priority_fee_per_gas(10u64)
.max_fee_per_gas(MAX_FEE_PER_GAS)
.gas(GAS);

let typed_transaction = TypedTransaction::Eip1559(req);

self.client
.send_transaction(typed_transaction, None)
.await
.unwrap()
}

pub(crate) async fn set_value_call(
&self,
contract_address: H160,
set_arg: u32,
) -> Result<Bytes, Box<dyn std::error::Error>> {
let nonce = self.eth_get_transaction_count(self.from_addr).await;

// Any type of transaction can be used for eth_call
let req = TransactionRequest::new()
.from(self.from_addr)
.to(contract_address)
.chain_id(self.chain_id)
.nonce(nonce)
.data(self.contract.set_call_data(set_arg))
.gas_price(10u64);

let typed_transaction = TypedTransaction::Legacy(req.clone());

// Estimate gas on rpc
let gas = self
.eth_estimate_gas(typed_transaction, Some("latest".to_owned()))
.await;

// Call with the estimated gas
let req = req.gas(gas);
let typed_transaction = TypedTransaction::Legacy(req);

let response = self
.eth_call(typed_transaction, Some("latest".to_owned()))
.await?;

Ok(response)
}

pub(crate) async fn failing_call(
&self,
contract_address: H160,
) -> Result<Bytes, Box<dyn std::error::Error>> {
let nonce = self.eth_get_transaction_count(self.from_addr).await;

// Any type of transaction can be used for eth_call
let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.to(contract_address)
.chain_id(self.chain_id)
.nonce(nonce)
.data(self.contract.failing_function_call_data())
.max_priority_fee_per_gas(10u64)
.max_fee_per_gas(MAX_FEE_PER_GAS)
.gas(GAS);

let typed_transaction = TypedTransaction::Eip1559(req);

self.eth_call(typed_transaction, Some("latest".to_owned()))
.await
}

pub(crate) async fn query_contract(
&self,
contract_address: H160,
) -> Result<ethereum_types::U256, Box<dyn std::error::Error>> {
let nonce = self.eth_get_transaction_count(self.from_addr).await;

let req = Eip1559TransactionRequest::new()
.from(self.from_addr)
.to(contract_address)
.chain_id(self.chain_id)
.nonce(nonce)
.data(self.contract.get_call_data())
.gas(GAS);

let typed_transaction = TypedTransaction::Eip1559(req);

let response = self.client.call(&typed_transaction, None).await?;

let resp_array: [u8; 32] = response.to_vec().try_into().unwrap();
Ok(ethereum_types::U256::from(resp_array))
}

pub(crate) async fn eth_accounts(&self) -> Vec<Address> {
self.http_client
.request("eth_accounts", rpc_params![])
.await
.unwrap()
}

pub(crate) async fn eth_send_transaction(
&self,
tx: TypedTransaction,
) -> PendingTransaction<'_, Http> {
self.client
.provider()
.send_transaction(tx, None)
.await
.unwrap()
}

pub(crate) async fn eth_chain_id(&self) -> u64 {
let chain_id: ethereum_types::U64 = self
.http_client
.request("eth_chainId", rpc_params![])
.await
.unwrap();

chain_id.as_u64()
}

pub(crate) async fn eth_get_balance(&self, address: Address) -> ethereum_types::U256 {
self.http_client
.request("eth_getBalance", rpc_params![address, "latest"])
.await
.unwrap()
}

pub(crate) async fn eth_get_storage_at(
&self,
address: Address,
index: ethereum_types::U256,
) -> ethereum_types::U256 {
self.http_client
.request("eth_getStorageAt", rpc_params![address, index, "latest"])
.await
.unwrap()
}

pub(crate) async fn eth_get_code(&self, address: Address) -> Bytes {
self.http_client
.request("eth_getCode", rpc_params![address, "latest"])
.await
.unwrap()
}

pub(crate) async fn eth_get_transaction_count(&self, address: Address) -> u64 {
let count: ethereum_types::U64 = self
.http_client
.request("eth_getTransactionCount", rpc_params![address, "latest"])
.await
.unwrap();

count.as_u64()
}

pub(crate) async fn eth_get_block_by_number(
&self,
block_number: Option<String>,
) -> Block<TxHash> {
self.http_client
.request("eth_getBlockByNumber", rpc_params![block_number, false])
.await
.unwrap()
}

pub(crate) async fn eth_get_block_by_number_with_detail(
&self,
block_number: Option<String>,
) -> Block<Transaction> {
self.http_client
.request("eth_getBlockByNumber", rpc_params![block_number, true])
.await
.unwrap()
}

pub(crate) async fn eth_call(
&self,
tx: TypedTransaction,
block_number: Option<String>,
) -> Result<Bytes, Box<dyn std::error::Error>> {
self.http_client
.request("eth_call", rpc_params![tx, block_number])
.await
.map_err(|e| e.into())
}

pub(crate) async fn eth_estimate_gas(
&self,
tx: TypedTransaction,
block_number: Option<String>,
) -> u64 {
let gas: ethereum_types::U64 = self
.http_client
.request("eth_estimateGas", rpc_params![tx, block_number])
.await
.unwrap();

gas.as_u64()
}
}
2 changes: 1 addition & 1 deletion examples/demo-stf/src/hooks_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl<C: Context, Da: DaSpec> SlotHooks<Da> for Runtime<C, Da> {
) {
#[cfg(feature = "experimental")]
self.evm
.begin_slot_hook(slot_header.hash().into(), working_set);
.begin_slot_hook(slot_header.hash().into(), pre_state_root, working_set);

self.chain_state.begin_slot_hook(
slot_header,
Expand Down
29 changes: 11 additions & 18 deletions module-system/module-implementations/sov-evm/src/evm/tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::convert::Infallible;

use reth_primitives::TransactionKind;
use revm::db::CacheDB;
use revm::precompile::B160;
use revm::primitives::{CfgEnv, ExecutionResult, Output, SpecId, KECCAK_EMPTY, U256};
use revm::{Database, DatabaseCommit};
Expand All @@ -14,20 +13,10 @@ use super::executor;
use crate::evm::primitive_types::BlockEnv;
use crate::evm::AccountInfo;
use crate::smart_contracts::SimpleStorageContract;
use crate::tests::dev_signer::TestSigner;
use crate::tests::test_signer::TestSigner;
use crate::Evm;
type C = sov_modules_api::default_context::DefaultContext;

pub(crate) fn output(result: ExecutionResult) -> bytes::Bytes {
match result {
ExecutionResult::Success { output, .. } => match output {
Output::Call(out) => out,
Output::Create(out, _) => out,
},
_ => panic!("Expected successful ExecutionResult"),
}
}

#[test]
fn simple_contract_execution_sov_state() {
let tmpdir = tempfile::tempdir().unwrap();
Expand All @@ -40,12 +29,6 @@ fn simple_contract_execution_sov_state() {
simple_contract_execution(evm_db);
}

#[test]
fn simple_contract_execution_in_memory_state() {
let db = CacheDB::default();
simple_contract_execution(db);
}

fn simple_contract_execution<DB: Database<Error = Infallible> + DatabaseCommit + InitEvmDb>(
mut evm_db: DB,
) {
Expand Down Expand Up @@ -148,3 +131,13 @@ fn contract_address(result: &ExecutionResult) -> Option<B160> {
_ => None,
}
}

fn output(result: ExecutionResult) -> bytes::Bytes {
match result {
ExecutionResult::Success { output, .. } => match output {
Output::Call(out) => out,
Output::Create(out, _) => out,
},
_ => panic!("Expected successful ExecutionResult"),
}
}
Loading