Skip to content

#222 - Support Empty Account #223

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ impl<'config> Gasometer<'config> {
Ok(())
}

#[inline]
/// Check gas limit before `CREATE` code deposit. (See EIP-2)
pub fn can_deposit(&self, len: usize) -> bool {
let cost = len as u64 * consts::G_CODEDEPOSIT;
let all_gas_cost = self.total_used_gas() + cost;
self.gas_limit >= all_gas_cost
}

#[inline]
/// Record `CREATE` code deposit.
pub fn record_deposit(&mut self, len: usize) -> Result<(), ExitError> {
Expand Down
105 changes: 105 additions & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ pub struct Config {
pub call_l64_after_gas: bool,
/// Whether empty account is considered exists.
pub empty_considered_exists: bool,
/// Create empty contract if there's no gas for paying code deposit.
pub can_create_empty_contract: bool,
/// Whether create transactions and create opcode increases nonce by one.
pub create_increase_nonce: bool,
/// Stack limit.
Expand All @@ -278,6 +280,8 @@ pub struct Config {
pub has_bitwise_shifting: bool,
/// Has chain ID.
pub has_chain_id: bool,
/// Has static_call. See [EIP-214](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-214.md)
pub has_static_call: bool,
/// Has self balance.
pub has_self_balance: bool,
/// Has ext code hash.
Expand Down Expand Up @@ -323,6 +327,7 @@ impl Config {
warm_coinbase_address: false,
err_on_call_with_more_gas: true,
empty_considered_exists: true,
can_create_empty_contract: true,
create_increase_nonce: false,
call_l64_after_gas: false,
stack_limit: 1024,
Expand All @@ -337,6 +342,7 @@ impl Config {
has_return_data: false,
has_bitwise_shifting: false,
has_chain_id: false,
has_static_call: false,
has_self_balance: false,
has_ext_code_hash: false,
has_base_fee: false,
Expand All @@ -345,6 +351,101 @@ impl Config {
}
}

/// Homestead hard fork configuration.
/// mainnet block number: 1,150,000
pub const fn homestead() -> Config {
let mut config = Self::frontier();
// makes edits to contract creation process
// see [EIP-2](https://eips.ethereum.org/EIPS/eip-2)
config.gas_transaction_create = 53_000;
config.can_create_empty_contract = false;
config.empty_considered_exists = true;

// Enable delegate call opcode
// see [EIP-7](https://eips.ethereum.org/EIPS/eip-7)
config.has_delegate_call = true;
config
}

/// Tangerine whistle hard fork configuration.
pub const fn tangerine_whistle() -> Config {
let mut config = Self::homestead();
// Gas cost changes for IO-heavy operations
// see [EIP-150](https://eips.ethereum.org/EIPS/eip-150)
config.gas_ext_code = 700;
config.gas_balance = 400;
config.gas_sload = 200;
config.gas_call = 700;
config.gas_suicide = 5_000;
config.gas_suicide_new_account = 25_000;
config.err_on_call_with_more_gas = false;
config.call_l64_after_gas = true;
config
}

/// Spurious Dragon hard fork configuration.
pub const fn spurious_dragon() -> Config {
let mut config = Self::tangerine_whistle();
// EXP cost increase
// see [EIP-160](https://eips.ethereum.org/EIPS/eip-160)
config.gas_expbyte = 50;

// State trie clearing
// - An account is considered empty when it has no code and zero nonce and zero balance. It should be noted
// that very few state changes can ultimately result in accounts that are empty following the execution of the transaction:
// - an empty account has zero value transferred to it through CALL;
// - an empty account has zero value transferred to it through SUICIDE;
// - an empty account has zero value transferred to it through a message-call transaction;
// - an empty account has zero value transferred to it through a zero-gas-price fees transfer.
// - empty account deletions are reverted when the state is reverted.
// - An account is considered dead when either it is non-existent or it is empty.
// see [EIP-161](https://eips.ethereum.org/EIPS/eip-161)
config.create_increase_nonce = true;
config.empty_considered_exists = false;

// Contract code size limit
// see [EIP-170](https://eips.ethereum.org/EIPS/eip-170)
config.create_contract_limit = Some(0x6000);
config
}

/// Byzantium hard fork configuration.
pub const fn byzantium() -> Config {
let mut config = Self::spurious_dragon();

// adds REVERT opcode
// see [EIP-140](https://eips.ethereum.org/EIPS/eip-140)
config.has_revert = true;

// adds support for variable length return values
// see [EIP-211](https://eips.ethereum.org/EIPS/eip-211)
config.has_return_data = true;

// adds STATICCALL opcode, allowing non-state-changing calls to other contracts.
// see [EIP-214](https://eips.ethereum.org/EIPS/eip-214)
config.has_static_call = true;
config
}

/// Constantinople hard fork configuration.
pub const fn constantinople() -> Config {
let mut config = Self::byzantium();

// adds Bitwise shifting instructions
// see [EIP-145](https://eips.ethereum.org/EIPS/eip-145)
config.has_bitwise_shifting = true;

// allows you to interact with addresses that have yet to be created.
// see [EIP-1014](https://eips.ethereum.org/EIPS/eip-1014)
config.has_create2 = true;

// adds EXTCODEHASH opcode
// see [EIP-1052](https://eips.ethereum.org/EIPS/eip-1052)
config.has_ext_code_hash = true;
config.gas_ext_code_hash = 400;
config
}

/// Istanbul hard fork configuration.
pub const fn istanbul() -> Config {
Config {
Expand Down Expand Up @@ -377,6 +478,7 @@ impl Config {
warm_coinbase_address: false,
err_on_call_with_more_gas: false,
empty_considered_exists: false,
can_create_empty_contract: false,
create_increase_nonce: true,
call_l64_after_gas: true,
stack_limit: 1024,
Expand All @@ -391,6 +493,7 @@ impl Config {
has_return_data: true,
has_bitwise_shifting: true,
has_chain_id: true,
has_static_call: true,
has_self_balance: true,
has_ext_code_hash: true,
has_base_fee: false,
Expand Down Expand Up @@ -474,6 +577,7 @@ impl Config {
warm_coinbase_address,
err_on_call_with_more_gas: false,
empty_considered_exists: false,
can_create_empty_contract: false,
create_increase_nonce: true,
call_l64_after_gas: true,
stack_limit: 1024,
Expand All @@ -488,6 +592,7 @@ impl Config {
has_return_data: true,
has_bitwise_shifting: true,
has_chain_id: true,
has_static_call: true,
has_self_balance: true,
has_ext_code_hash: true,
has_base_fee,
Expand Down
32 changes: 24 additions & 8 deletions src/executor/stack/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,20 +1002,35 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
}
}

match self
.state
.metadata_mut()
.gasometer
.record_deposit(out.len())
// Before EIP-2 is possible to create empty contract
let bytecode = if self.config.can_create_empty_contract
&& !self.state.metadata().gasometer.can_deposit(out.len())
{
Ok(()) => {
Ok(None)
} else {
self.state
.metadata_mut()
.gasometer
.record_deposit(out.len())
.map(|_| Some(out))
};

match bytecode {
Ok(Some(bytecode)) => {
let exit_result = self.exit_substate(StackExitKind::Succeeded);
if let Err(e) = self.record_external_operation(
crate::ExternalOperation::Write(U256::from(out.len())),
crate::ExternalOperation::Write(U256::from(bytecode.len())),
) {
return (e.into(), None, Vec::new());
}
self.state.set_code(address, out);
self.state.set_code(address, bytecode);
if let Err(e) = exit_result {
return (e.into(), None, Vec::new());
}
(ExitReason::Succeed(s), Some(address), Vec::new())
}
Ok(None) => {
let exit_result = self.exit_substate(StackExitKind::Succeeded);
if let Err(e) = exit_result {
return (e.into(), None, Vec::new());
}
Expand Down Expand Up @@ -1320,6 +1335,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler
stack: &Stack,
) -> Result<(), ExitError> {
// log::trace!(target: "evm", "Running opcode: {:?}, Pre gas-left: {:?}", opcode, gasometer.gas());
// log::trace!(target: "evm", "Running opcode: {:?}, Pre gas-left: {:?}", opcode, self.state.metadata().gasometer.gas());

if let Some(cost) = gasometer::static_opcode_cost(opcode) {
self.state.metadata_mut().gasometer.record_cost(cost)?;
Expand Down