Skip to content

Commit

Permalink
Implement basic transact_call (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorpaas authored Nov 8, 2023
1 parent 7d133f2 commit b15f4b7
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 2 deletions.
114 changes: 114 additions & 0 deletions src/standard/gasometer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,117 @@ impl MemoryCost {
Ok(Some(costs::memory_gas(new)?))
}
}

/// Transaction cost.
#[derive(Debug, Clone, Copy)]
pub enum TransactionCost {
/// Call transaction cost.
Call {
/// Length of zeros in transaction data.
zero_data_len: usize,
/// Length of non-zeros in transaction data.
non_zero_data_len: usize,
/// Number of addresses in transaction access list (see EIP-2930)
access_list_address_len: usize,
/// Total number of storage keys in transaction access list (see EIP-2930)
access_list_storage_len: usize,
},
/// Create transaction cost.
Create {
/// Length of zeros in transaction data.
zero_data_len: usize,
/// Length of non-zeros in transaction data.
non_zero_data_len: usize,
/// Number of addresses in transaction access list (see EIP-2930)
access_list_address_len: usize,
/// Total number of storage keys in transaction access list (see EIP-2930)
access_list_storage_len: usize,
/// Cost of initcode = 2 * ceil(len(initcode) / 32) (see EIP-3860)
initcode_cost: u64,
},
}

impl TransactionCost {
pub fn call(data: &[u8], access_list: &[(H160, Vec<H256>)]) -> TransactionCost {
let zero_data_len = data.iter().filter(|v| **v == 0).count();
let non_zero_data_len = data.len() - zero_data_len;
let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);

TransactionCost::Call {
zero_data_len,
non_zero_data_len,
access_list_address_len,
access_list_storage_len,
}
}

pub fn create(data: &[u8], access_list: &[(H160, Vec<H256>)]) -> TransactionCost {
let zero_data_len = data.iter().filter(|v| **v == 0).count();
let non_zero_data_len = data.len() - zero_data_len;
let (access_list_address_len, access_list_storage_len) = count_access_list(access_list);
let initcode_cost = init_code_cost(data);

TransactionCost::Create {
zero_data_len,
non_zero_data_len,
access_list_address_len,
access_list_storage_len,
initcode_cost,
}
}

pub fn cost(&self, config: &Config) -> u64 {
let gas_cost = match self {
TransactionCost::Call {
zero_data_len,
non_zero_data_len,
access_list_address_len,
access_list_storage_len,
} => {
#[deny(clippy::let_and_return)]
let cost = config.gas_transaction_call
+ *zero_data_len as u64 * config.gas_transaction_zero_data
+ *non_zero_data_len as u64 * config.gas_transaction_non_zero_data
+ *access_list_address_len as u64 * config.gas_access_list_address
+ *access_list_storage_len as u64 * config.gas_access_list_storage_key;

cost
}
TransactionCost::Create {
zero_data_len,
non_zero_data_len,
access_list_address_len,
access_list_storage_len,
initcode_cost,
} => {
let mut cost = config.gas_transaction_create
+ *zero_data_len as u64 * config.gas_transaction_zero_data
+ *non_zero_data_len as u64 * config.gas_transaction_non_zero_data
+ *access_list_address_len as u64 * config.gas_access_list_address
+ *access_list_storage_len as u64 * config.gas_access_list_storage_key;
if config.max_initcode_size.is_some() {
cost += initcode_cost;
}

cost
}
};

gas_cost
}
}

/// Counts the number of addresses and storage keys in the access list
fn count_access_list(access_list: &[(H160, Vec<H256>)]) -> (usize, usize) {
let access_list_address_len = access_list.len();
let access_list_storage_len = access_list.iter().map(|(_, keys)| keys.len()).sum();

(access_list_address_len, access_list_storage_len)
}

pub fn init_code_cost(data: &[u8]) -> u64 {
// As per EIP-3860:
// > We define initcode_cost(initcode) to equal INITCODE_WORD_COST * ceil(len(initcode) / 32).
// where INITCODE_WORD_COST is 2.
2 * ((data.len() as u64 + 31) / 32)
}
97 changes: 95 additions & 2 deletions src/standard/invoker.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{Config, GasedMachine, Gasometer, Machine};
use super::{gasometer::TransactionCost, Config, Etable, GasedMachine, Gasometer, Machine};
use crate::call_create::{CallCreateTrapData, CallTrapData, CreateTrapData};
use crate::{
Capture, Context, ExitError, ExitException, ExitResult, Gasometer as GasometerT,
Expand All @@ -8,7 +8,7 @@ use crate::{
use alloc::rc::Rc;
use core::cmp::min;
use core::convert::Infallible;
use primitive_types::{H160, U256};
use primitive_types::{H160, H256, U256};

pub enum CallCreateTrapPrepareData {
Call {
Expand All @@ -28,10 +28,103 @@ pub enum CallCreateTrapEnterData {
Create { trap: CreateTrapData, address: H160 },
}

const DEFAULT_HEAP_DEPTH: Option<usize> = Some(4);

pub struct Invoker<'config> {
config: &'config Config,
}

impl<'config> Invoker<'config> {
pub fn transact_call<H>(
&self,
caller: H160,
address: H160,
value: U256,
data: Vec<u8>,
gas_limit: U256,
access_list: Vec<(H160, Vec<H256>)>,
handler: &mut H,
etable: &Etable<H>,
) -> ExitResult
where
H: RuntimeFullBackend + TransactionalBackend,
{
let gas_limit = if gas_limit > U256::from(u64::MAX) {
return Err(ExitException::OutOfGas.into());
} else {
gas_limit.as_u64()
};

let context = Context {
caller,
address,
apparent_value: value,
};
let code = handler.code(address);

let transaction_cost = TransactionCost::call(&data, &access_list).cost(self.config);

let machine = Machine::new(
Rc::new(code),
Rc::new(data),
self.config.stack_limit,
self.config.memory_limit,
RuntimeState {
context,
retbuf: Vec::new(),
gas: U256::zero(),
},
);
let mut gasometer = Gasometer::new(gas_limit, &machine, self.config);

gasometer.record_cost(transaction_cost)?;

handler.push_substate();

let work = || -> ExitResult {
if self.config.increase_state_access_gas {
if self.config.warm_coinbase_address {
let coinbase = handler.block_coinbase();
handler.mark_hot(coinbase, None)?;
}
handler.mark_hot(caller, None)?;
handler.mark_hot(address, None)?;
}

handler.inc_nonce(caller)?;

let transfer = Transfer {
source: caller,
target: address,
value,
};
handler.transfer(transfer)?;

let machine = GasedMachine {
machine,
gasometer,
is_static: false,
};

let (_machine, result) =
crate::execute(machine, handler, 0, DEFAULT_HEAP_DEPTH, self, etable);

result
};

match work() {
Ok(exit) => {
handler.pop_substate(TransactionalMergeStrategy::Commit);
Ok(exit)
}
Err(err) => {
handler.pop_substate(TransactionalMergeStrategy::Discard);
Err(err)
}
}
}
}

impl<'config, H> InvokerT<RuntimeState, Gasometer<'config>, H, Opcode> for Invoker<'config>
where
H: RuntimeFullBackend + TransactionalBackend,
Expand Down

0 comments on commit b15f4b7

Please sign in to comment.