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

Implement call method for the EVM module #449

Merged
merged 13 commits into from
Jul 3, 2023
33 changes: 32 additions & 1 deletion module-system/module-implementations/sov-evm/src/call.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
use crate::{
evm::{
db::EvmDb,
executor::{self},
transaction::EvmTransaction,
},
Evm,
};
use anyhow::Result;
use revm::primitives::CfgEnv;
use sov_modules_api::CallResponse;
use sov_state::WorkingSet;

#[cfg_attr(
feature = "native",
derive(serde::Serialize),
derive(serde::Deserialize)
)]
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)]
pub struct CallMessage {}
pub struct CallMessage {
pub tx: EvmTransaction,
}

impl<C: sov_modules_api::Context> Evm<C> {
pub(crate) fn execute_call(
&self,
tx: EvmTransaction,
_context: &C,
working_set: &mut WorkingSet<C::Storage>,
) -> Result<CallResponse> {
let cfg_env = CfgEnv::default();
let block_env = self.block_env.get(working_set).unwrap_or_default();
let evm_db: EvmDb<'_, C> = self.get_db(working_set);

executor::execute_tx(evm_db, block_env, tx, cfg_env).unwrap();
Ok(CallResponse::default())
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use bytes::Bytes;

use super::AccountInfo;
use revm::primitives::{AccountInfo as ReVmAccountInfo, U256};
use revm::primitives::{Bytecode, B256};
use super::{
transaction::{AccessListItem, BlockEnv, EvmTransaction},
AccountInfo,
};
use revm::primitives::{
AccountInfo as ReVmAccountInfo, BlockEnv as ReVmBlockEnv, Bytecode, CreateScheme, TransactTo,
TxEnv, B160, B256, U256,
};

impl From<AccountInfo> for ReVmAccountInfo {
fn from(info: AccountInfo) -> Self {
Expand All @@ -25,3 +30,59 @@ impl From<ReVmAccountInfo> for AccountInfo {
}
}
}

impl From<BlockEnv> for ReVmBlockEnv {
fn from(block_env: BlockEnv) -> Self {
Self {
number: U256::from_le_bytes(block_env.number),
coinbase: B160::from_slice(&block_env.coinbase),
timestamp: U256::from_le_bytes(block_env.timestamp),
// TODO: handle difficulty
difficulty: U256::ZERO,
prevrandao: block_env.prevrandao.map(|r| B256::from_slice(&r)),
basefee: U256::from_le_bytes(block_env.basefee),
gas_limit: U256::from_le_bytes(block_env.gas_limit),
}
}
}

impl From<AccessListItem> for (B160, Vec<U256>) {
citizen-stig marked this conversation as resolved.
Show resolved Hide resolved
fn from(item: AccessListItem) -> Self {
(
B160::from_slice(&item.address),
item.storage_keys
.into_iter()
.map(U256::from_le_bytes)
.collect(),
)
}
}

impl From<EvmTransaction> for TxEnv {
fn from(tx: EvmTransaction) -> Self {
let to = match tx.to {
Some(addr) => TransactTo::Call(B160::from_slice(&addr)),
None => TransactTo::Create(CreateScheme::Create),
};

let access_list = tx
.access_lists
.into_iter()
.map(|item| item.into())
.collect();

Self {
caller: B160::from_slice(&tx.caller),
data: Bytes::from(tx.data),
gas_limit: tx.gas_limit,
gas_price: tx.gas_price.map(U256::from_le_bytes).unwrap_or_default(),
gas_priority_fee: tx.max_priority_fee_per_gas.map(U256::from_le_bytes),
transact_to: to,
value: U256::from_le_bytes(tx.value),
nonce: Some(tx.nonce),
//TODO: handle chain_id
chain_id: None,
access_list,
}
}
}
7 changes: 4 additions & 3 deletions module-system/module-implementations/sov-evm/src/evm/db.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{Address, DbAccount};
use super::{DbAccount, EthAddress};

use revm::{
primitives::{AccountInfo as ReVmAccountInfo, Bytecode, B160, B256, U256},
Database,
Expand All @@ -7,13 +8,13 @@ use sov_state::WorkingSet;
use std::convert::Infallible;

pub(crate) struct EvmDb<'a, C: sov_modules_api::Context> {
pub(crate) accounts: sov_state::StateMap<Address, DbAccount>,
pub(crate) accounts: sov_state::StateMap<EthAddress, DbAccount>,
pub(crate) working_set: &'a mut WorkingSet<C::Storage>,
}

impl<'a, C: sov_modules_api::Context> EvmDb<'a, C> {
pub(crate) fn new(
accounts: sov_state::StateMap<Address, DbAccount>,
accounts: sov_state::StateMap<EthAddress, DbAccount>,
working_set: &'a mut WorkingSet<C::Storage>,
) -> Self {
Self {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{db::EvmDb, AccountInfo, Address, DbAccount};
use super::{db::EvmDb, AccountInfo, DbAccount, EthAddress};
#[cfg(test)]
use revm::{
db::{CacheDB, EmptyDB},
Expand All @@ -7,11 +7,11 @@ use revm::{

/// Initializes database with a predefined account.
pub(crate) trait InitEvmDb {
fn insert_account_info(&mut self, address: Address, acc: AccountInfo);
fn insert_account_info(&mut self, address: EthAddress, acc: AccountInfo);
}

impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {
fn insert_account_info(&mut self, sender: Address, info: AccountInfo) {
fn insert_account_info(&mut self, sender: EthAddress, info: AccountInfo) {
let parent_prefix = self.accounts.prefix();
let db_account = DbAccount::new_with_info(parent_prefix, sender, info);

Expand All @@ -21,7 +21,7 @@ impl<'a, C: sov_modules_api::Context> InitEvmDb for EvmDb<'a, C> {

#[cfg(test)]
impl InitEvmDb for CacheDB<EmptyDB> {
fn insert_account_info(&mut self, sender: Address, acc: AccountInfo) {
fn insert_account_info(&mut self, sender: EthAddress, acc: AccountInfo) {
self.insert_account_info(B160::from_slice(&sender), acc.into());
}
}
16 changes: 13 additions & 3 deletions module-system/module-implementations/sov-evm/src/evm/executor.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
use super::transaction::{BlockEnv, EvmTransaction};
use revm::{
self,
primitives::{EVMError, ExecutionResult, TxEnv},
primitives::{CfgEnv, EVMError, Env, ExecutionResult},
Database, DatabaseCommit,
};
use std::convert::Infallible;

#[allow(dead_code)]
pub(crate) fn execute_tx<DB: Database<Error = Infallible> + DatabaseCommit>(
db: DB,
tx_env: TxEnv,
block_env: BlockEnv,
tx: EvmTransaction,
config_env: CfgEnv,
) -> Result<ExecutionResult, EVMError<Infallible>> {
let mut evm = revm::new();
evm.env.tx = tx_env;

let env = Env {
cfg: config_env,
block: block_env.into(),
tx: tx.into(),
};

evm.env = env;
evm.database(db);
evm.transact_commit()
}
23 changes: 13 additions & 10 deletions module-system/module-implementations/sov-evm/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ use sov_state::Prefix;
mod conversions;
pub(crate) mod db;
mod db_commit;
mod db_init;
mod executor;
pub(crate) mod db_init;
pub(crate) mod executor;
#[cfg(test)]
pub(crate) mod test_helpers;
#[cfg(test)]
mod tests;
pub(crate) mod transaction;

pub(crate) type Address = [u8; 20];
pub(crate) type SovU256 = [u8; 32];
pub(crate) type EthAddress = [u8; 20];
pub(crate) type Bytes32 = [u8; 32];

// Stores information about an EVM account
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone, Default)]
pub(crate) struct AccountInfo {
pub(crate) balance: SovU256,
pub(crate) code_hash: SovU256,
pub(crate) balance: Bytes32,
pub(crate) code_hash: Bytes32,
// TODO: `code` can be a huge chunk of data. We can use `StateValue` and lazy load it only when needed.
// https://github.com/Sovereign-Labs/sovereign-sdk/issues/425
pub(crate) code: Vec<u8>,
Expand All @@ -26,27 +29,27 @@ pub(crate) struct AccountInfo {
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)]
pub(crate) struct DbAccount {
pub(crate) info: AccountInfo,
pub(crate) storage: sov_state::StateMap<SovU256, SovU256>,
pub(crate) storage: sov_state::StateMap<Bytes32, Bytes32>,
}

impl DbAccount {
fn new(parent_prefix: &Prefix, address: Address) -> Self {
fn new(parent_prefix: &Prefix, address: EthAddress) -> Self {
let prefix = Self::create_storage_prefix(parent_prefix, address);
Self {
info: Default::default(),
storage: sov_state::StateMap::new(prefix),
}
}

fn new_with_info(parent_prefix: &Prefix, address: Address, info: AccountInfo) -> Self {
fn new_with_info(parent_prefix: &Prefix, address: EthAddress, info: AccountInfo) -> Self {
let prefix = Self::create_storage_prefix(parent_prefix, address);
Self {
info,
storage: sov_state::StateMap::new(prefix),
}
}

fn create_storage_prefix(parent_prefix: &Prefix, address: Address) -> Prefix {
fn create_storage_prefix(parent_prefix: &Prefix, address: EthAddress) -> Prefix {
let mut prefix = parent_prefix.as_aligned_vec().clone().into_inner();
prefix.extend_from_slice(&address);
Prefix::new(prefix)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use bytes::Bytes;
use ethers_contract::BaseContract;
use ethers_core::abi::Abi;
use revm::primitives::{ExecutionResult, Output, B160};
use std::path::PathBuf;

pub(crate) fn output(result: ExecutionResult) -> Bytes {
match result {
ExecutionResult::Success { output, .. } => match output {
Output::Call(out) => out,
Output::Create(out, _) => out,
},
_ => panic!("Expected successful ExecutionResult"),
citizen-stig marked this conversation as resolved.
Show resolved Hide resolved
}
}

pub(crate) fn contract_address(result: ExecutionResult) -> B160 {
match result {
ExecutionResult::Success {
output: Output::Create(_, Some(addr)),
..
} => addr,
_ => panic!("Expected successful contract creation"),
}
}

pub(crate) fn test_data_path() -> PathBuf {
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("src");
path.push("evm");
path.push("test_data");
path
}

pub(crate) fn make_contract_from_abi(path: PathBuf) -> BaseContract {
let abi_json = std::fs::read_to_string(path).unwrap();
let abi: Abi = serde_json::from_str(&abi_json).unwrap();
BaseContract::from(abi)
}
Loading