Implement EIP-1052 (EXTCODEHASH) and fix several issues in state account cache (#9234)
* Implement EIP-1052 and fix several issues related to account cache * Fix jsontests * Merge two matches together * Avoid making unnecessary Arc<Vec> * Address grumbles
This commit is contained in:
parent
f9814381a7
commit
29baccd857
@ -134,6 +134,8 @@ enum_with_from_u8! {
|
|||||||
RETURNDATASIZE = 0x3d,
|
RETURNDATASIZE = 0x3d,
|
||||||
#[doc = "copy return data buffer to memory"]
|
#[doc = "copy return data buffer to memory"]
|
||||||
RETURNDATACOPY = 0x3e,
|
RETURNDATACOPY = 0x3e,
|
||||||
|
#[doc = "return the keccak256 hash of contract code"]
|
||||||
|
EXTCODEHASH = 0x3f,
|
||||||
|
|
||||||
#[doc = "get hash of most recent complete block"]
|
#[doc = "get hash of most recent complete block"]
|
||||||
BLOCKHASH = 0x40,
|
BLOCKHASH = 0x40,
|
||||||
@ -492,6 +494,7 @@ lazy_static! {
|
|||||||
arr[CALLDATALOAD as usize] = Some(InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow));
|
arr[CALLDATALOAD as usize] = Some(InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow));
|
||||||
arr[CALLDATASIZE as usize] = Some(InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base));
|
arr[CALLDATASIZE as usize] = Some(InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base));
|
||||||
arr[CALLDATACOPY as usize] = Some(InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow));
|
arr[CALLDATACOPY as usize] = Some(InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow));
|
||||||
|
arr[EXTCODEHASH as usize] = Some(InstructionInfo::new("EXTCODEHASH", 1, 1, GasPriceTier::Special));
|
||||||
arr[CODESIZE as usize] = Some(InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base));
|
arr[CODESIZE as usize] = Some(InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base));
|
||||||
arr[CODECOPY as usize] = Some(InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow));
|
arr[CODECOPY as usize] = Some(InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow));
|
||||||
arr[GASPRICE as usize] = Some(InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base));
|
arr[GASPRICE as usize] = Some(InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base));
|
||||||
|
@ -143,6 +143,9 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
|||||||
instructions::EXTCODESIZE => {
|
instructions::EXTCODESIZE => {
|
||||||
Request::Gas(Gas::from(schedule.extcodesize_gas))
|
Request::Gas(Gas::from(schedule.extcodesize_gas))
|
||||||
},
|
},
|
||||||
|
instructions::EXTCODEHASH => {
|
||||||
|
Request::Gas(Gas::from(schedule.extcodehash_gas))
|
||||||
|
},
|
||||||
instructions::SUICIDE => {
|
instructions::SUICIDE => {
|
||||||
let mut gas = Gas::from(schedule.suicide_gas);
|
let mut gas = Gas::from(schedule.suicide_gas);
|
||||||
|
|
||||||
|
@ -230,8 +230,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
(instruction == instructions::STATICCALL && !schedule.have_static_call) ||
|
(instruction == instructions::STATICCALL && !schedule.have_static_call) ||
|
||||||
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
|
((instruction == instructions::RETURNDATACOPY || instruction == instructions::RETURNDATASIZE) && !schedule.have_return_data) ||
|
||||||
(instruction == instructions::REVERT && !schedule.have_revert) ||
|
(instruction == instructions::REVERT && !schedule.have_revert) ||
|
||||||
((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) {
|
((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) ||
|
||||||
|
(instruction == instructions::EXTCODEHASH && !schedule.have_extcodehash)
|
||||||
|
{
|
||||||
return Err(vm::Error::BadInstruction {
|
return Err(vm::Error::BadInstruction {
|
||||||
instruction: instruction as u8
|
instruction: instruction as u8
|
||||||
});
|
});
|
||||||
@ -568,9 +569,14 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
},
|
},
|
||||||
instructions::EXTCODESIZE => {
|
instructions::EXTCODESIZE => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let len = ext.extcodesize(&address)?;
|
let len = ext.extcodesize(&address)?.unwrap_or(0);
|
||||||
stack.push(U256::from(len));
|
stack.push(U256::from(len));
|
||||||
},
|
},
|
||||||
|
instructions::EXTCODEHASH => {
|
||||||
|
let address = u256_to_address(&stack.pop_back());
|
||||||
|
let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero);
|
||||||
|
stack.push(U256::from(hash));
|
||||||
|
},
|
||||||
instructions::CALLDATACOPY => {
|
instructions::CALLDATACOPY => {
|
||||||
Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
|
Self::copy_data_to_memory(&mut self.mem, stack, params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8]));
|
||||||
},
|
},
|
||||||
@ -591,7 +597,11 @@ impl<Cost: CostType> Interpreter<Cost> {
|
|||||||
instructions::EXTCODECOPY => {
|
instructions::EXTCODECOPY => {
|
||||||
let address = u256_to_address(&stack.pop_back());
|
let address = u256_to_address(&stack.pop_back());
|
||||||
let code = ext.extcode(&address)?;
|
let code = ext.extcode(&address)?;
|
||||||
Self::copy_data_to_memory(&mut self.mem, stack, &code);
|
Self::copy_data_to_memory(
|
||||||
|
&mut self.mem,
|
||||||
|
stack,
|
||||||
|
code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[])
|
||||||
|
);
|
||||||
},
|
},
|
||||||
instructions::GASPRICE => {
|
instructions::GASPRICE => {
|
||||||
stack.push(params.gas_price.clone());
|
stack.push(params.gas_price.clone());
|
||||||
|
@ -1339,7 +1339,7 @@ impl BlockInfo for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn code_hash(&self, address: &Address, id: BlockId) -> Option<H256> {
|
fn code_hash(&self, address: &Address, id: BlockId) -> Option<H256> {
|
||||||
self.state_at(id).and_then(|s| s.code_hash(address).ok())
|
self.state_at(id).and_then(|s| s.code_hash(address).unwrap_or(None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
|||||||
gas_price: t.gas_price,
|
gas_price: t.gas_price,
|
||||||
value: ActionValue::Transfer(t.value),
|
value: ActionValue::Transfer(t.value),
|
||||||
code: self.state.code(address)?,
|
code: self.state.code(address)?,
|
||||||
code_hash: Some(self.state.code_hash(address)?),
|
code_hash: self.state.code_hash(address)?,
|
||||||
data: Some(t.data.clone()),
|
data: Some(t.data.clone()),
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
params_type: vm::ParamsType::Separate,
|
params_type: vm::ParamsType::Separate,
|
||||||
|
@ -165,7 +165,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
|||||||
gas: self.machine.params().eip210_contract_gas,
|
gas: self.machine.params().eip210_contract_gas,
|
||||||
gas_price: 0.into(),
|
gas_price: 0.into(),
|
||||||
code: code,
|
code: code,
|
||||||
code_hash: Some(code_hash),
|
code_hash: code_hash,
|
||||||
data: Some(H256::from(number).to_vec()),
|
data: Some(H256::from(number).to_vec()),
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
params_type: vm::ParamsType::Separate,
|
params_type: vm::ParamsType::Separate,
|
||||||
@ -272,7 +272,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
|||||||
gas: *gas,
|
gas: *gas,
|
||||||
gas_price: self.origin_info.gas_price,
|
gas_price: self.origin_info.gas_price,
|
||||||
code: code,
|
code: code,
|
||||||
code_hash: Some(code_hash),
|
code_hash: code_hash,
|
||||||
data: Some(data.to_vec()),
|
data: Some(data.to_vec()),
|
||||||
call_type: call_type,
|
call_type: call_type,
|
||||||
params_type: vm::ParamsType::Separate,
|
params_type: vm::ParamsType::Separate,
|
||||||
@ -291,12 +291,16 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> vm::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> {
|
||||||
Ok(self.state.code(address)?.unwrap_or_else(|| Arc::new(vec![])))
|
Ok(self.state.code(address)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcodesize(&self, address: &Address) -> vm::Result<usize> {
|
fn extcodehash(&self, address: &Address) -> vm::Result<Option<H256>> {
|
||||||
Ok(self.state.code_size(address)?.unwrap_or(0))
|
Ok(self.state.code_hash(address)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extcodesize(&self, address: &Address) -> vm::Result<Option<usize>> {
|
||||||
|
Ok(self.state.code_size(address)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ret(mut self, gas: &U256, data: &ReturnData, apply_state: bool) -> vm::Result<U256>
|
fn ret(mut self, gas: &U256, data: &ReturnData, apply_state: bool) -> vm::Result<U256>
|
||||||
|
@ -166,14 +166,18 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
|
|||||||
MessageCallResult::Success(*gas, ReturnData::empty())
|
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> vm::Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> {
|
||||||
self.ext.extcode(address)
|
self.ext.extcode(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcodesize(&self, address: &Address) -> vm::Result<usize> {
|
fn extcodesize(&self, address: &Address) -> vm::Result<Option<usize>> {
|
||||||
self.ext.extcodesize(address)
|
self.ext.extcodesize(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extcodehash(&self, address: &Address) -> vm::Result<Option<H256>> {
|
||||||
|
self.ext.extcodehash(address)
|
||||||
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> vm::Result<()> {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> vm::Result<()> {
|
||||||
self.ext.log(topics, data)
|
self.ext.log(topics, data)
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ impl EthereumMachine {
|
|||||||
gas_price: 0.into(),
|
gas_price: 0.into(),
|
||||||
value: ActionValue::Transfer(0.into()),
|
value: ActionValue::Transfer(0.into()),
|
||||||
code: state.code(&contract_address)?,
|
code: state.code(&contract_address)?,
|
||||||
code_hash: Some(state.code_hash(&contract_address)?),
|
code_hash: state.code_hash(&contract_address)?,
|
||||||
data: data,
|
data: data,
|
||||||
call_type: CallType::Call,
|
call_type: CallType::Call,
|
||||||
params_type: ParamsType::Separate,
|
params_type: ParamsType::Separate,
|
||||||
|
@ -115,6 +115,8 @@ pub struct CommonParams {
|
|||||||
pub eip214_transition: BlockNumber,
|
pub eip214_transition: BlockNumber,
|
||||||
/// Number of first block where EIP-145 rules begin.
|
/// Number of first block where EIP-145 rules begin.
|
||||||
pub eip145_transition: BlockNumber,
|
pub eip145_transition: BlockNumber,
|
||||||
|
/// Number of first block where EIP-1052 rules begin.
|
||||||
|
pub eip1052_transition: BlockNumber,
|
||||||
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
|
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
|
||||||
pub dust_protection_transition: BlockNumber,
|
pub dust_protection_transition: BlockNumber,
|
||||||
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
|
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
|
||||||
@ -174,6 +176,7 @@ impl CommonParams {
|
|||||||
schedule.have_static_call = block_number >= self.eip214_transition;
|
schedule.have_static_call = block_number >= self.eip214_transition;
|
||||||
schedule.have_return_data = block_number >= self.eip211_transition;
|
schedule.have_return_data = block_number >= self.eip211_transition;
|
||||||
schedule.have_bitwise_shifting = block_number >= self.eip145_transition;
|
schedule.have_bitwise_shifting = block_number >= self.eip145_transition;
|
||||||
|
schedule.have_extcodehash = block_number >= self.eip1052_transition;
|
||||||
if block_number >= self.eip210_transition {
|
if block_number >= self.eip210_transition {
|
||||||
schedule.blockhash_gas = 800;
|
schedule.blockhash_gas = 800;
|
||||||
}
|
}
|
||||||
@ -270,6 +273,10 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
BlockNumber::max_value,
|
BlockNumber::max_value,
|
||||||
Into::into,
|
Into::into,
|
||||||
),
|
),
|
||||||
|
eip1052_transition: p.eip1052_transition.map_or_else(
|
||||||
|
BlockNumber::max_value,
|
||||||
|
Into::into,
|
||||||
|
),
|
||||||
dust_protection_transition: p.dust_protection_transition.map_or_else(
|
dust_protection_transition: p.dust_protection_transition.map_or_else(
|
||||||
BlockNumber::max_value,
|
BlockNumber::max_value,
|
||||||
Into::into,
|
Into::into,
|
||||||
|
@ -278,12 +278,13 @@ impl Account {
|
|||||||
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == KECCAK_EMPTY)
|
!self.code_cache.is_empty() || (self.code_cache.is_empty() && self.code_hash == KECCAK_EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code.
|
/// Provide a database to get `code_hash`. Should not be called if it is a contract without code. Returns the cached code, if successful.
|
||||||
|
#[must_use]
|
||||||
pub fn cache_code(&mut self, db: &HashDB<KeccakHasher>) -> Option<Arc<Bytes>> {
|
pub fn cache_code(&mut self, db: &HashDB<KeccakHasher>) -> Option<Arc<Bytes>> {
|
||||||
// TODO: fill out self.code_cache;
|
// TODO: fill out self.code_cache;
|
||||||
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||||
|
|
||||||
if self.is_cached() { return Some(self.code_cache.clone()) }
|
if self.is_cached() { return Some(self.code_cache.clone()); }
|
||||||
|
|
||||||
match db.get(&self.code_hash) {
|
match db.get(&self.code_hash) {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
@ -298,8 +299,7 @@ impl Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide code to cache. For correctness, should be the correct code for the
|
/// Provide code to cache. For correctness, should be the correct code for the account.
|
||||||
/// account.
|
|
||||||
pub fn cache_given_code(&mut self, code: Arc<Bytes>) {
|
pub fn cache_given_code(&mut self, code: Arc<Bytes>) {
|
||||||
trace!("Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
trace!("Account::cache_given_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||||
|
|
||||||
@ -307,7 +307,9 @@ impl Account {
|
|||||||
self.code_cache = code;
|
self.code_cache = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a database to get `code_size`. Should not be called if it is a contract without code.
|
/// Provide a database to get `code_size`. Should not be called if it is a contract without code. Returns whether
|
||||||
|
/// the cache succeeds.
|
||||||
|
#[must_use]
|
||||||
pub fn cache_code_size(&mut self, db: &HashDB<KeccakHasher>) -> bool {
|
pub fn cache_code_size(&mut self, db: &HashDB<KeccakHasher>) -> bool {
|
||||||
// TODO: fill out self.code_cache;
|
// TODO: fill out self.code_cache;
|
||||||
trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
|
||||||
@ -324,7 +326,9 @@ impl Account {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
// If the code hash is empty hash, then the code size is zero.
|
||||||
|
self.code_size = Some(0);
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,9 +608,9 @@ impl<B: Backend> State<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an account's code hash.
|
/// Get an account's code hash.
|
||||||
pub fn code_hash(&self, a: &Address) -> TrieResult<H256> {
|
pub fn code_hash(&self, a: &Address) -> TrieResult<Option<H256>> {
|
||||||
self.ensure_cached(a, RequireCache::None, true,
|
self.ensure_cached(a, RequireCache::None, true,
|
||||||
|a| a.as_ref().map_or(KECCAK_EMPTY, |a| a.code_hash()))
|
|a| a.as_ref().map(|a| a.code_hash()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get accounts' code size.
|
/// Get accounts' code size.
|
||||||
@ -911,31 +911,38 @@ impl<B: Backend> State<B> {
|
|||||||
Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post))
|
Ok(pod_state::diff_pod(&pod_state_pre, &pod_state_post))
|
||||||
}
|
}
|
||||||
|
|
||||||
// load required account data from the databases.
|
/// Load required account data from the databases. Returns whether the cache succeeds.
|
||||||
fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB<KeccakHasher>) {
|
#[must_use]
|
||||||
|
fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB<KeccakHasher>) -> bool {
|
||||||
if let RequireCache::None = require {
|
if let RequireCache::None = require {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.is_cached() {
|
if account.is_cached() {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there's already code in the global cache, always cache it localy
|
// if there's already code in the global cache, always cache it localy
|
||||||
let hash = account.code_hash();
|
let hash = account.code_hash();
|
||||||
match state_db.get_cached_code(&hash) {
|
match state_db.get_cached_code(&hash) {
|
||||||
Some(code) => account.cache_given_code(code),
|
Some(code) => {
|
||||||
|
account.cache_given_code(code);
|
||||||
|
true
|
||||||
|
},
|
||||||
None => match require {
|
None => match require {
|
||||||
RequireCache::None => {},
|
RequireCache::None => true,
|
||||||
RequireCache::Code => {
|
RequireCache::Code => {
|
||||||
if let Some(code) = account.cache_code(db) {
|
if let Some(code) = account.cache_code(db) {
|
||||||
// propagate code loaded from the database to
|
// propagate code loaded from the database to
|
||||||
// the global code cache.
|
// the global code cache.
|
||||||
state_db.cache_code(hash, code)
|
state_db.cache_code(hash, code);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RequireCache::CodeSize => {
|
RequireCache::CodeSize => {
|
||||||
account.cache_code_size(db);
|
account.cache_code_size(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -950,8 +957,11 @@ impl<B: Backend> State<B> {
|
|||||||
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) {
|
||||||
if let Some(ref mut account) = maybe_acc.account {
|
if let Some(ref mut account) = maybe_acc.account {
|
||||||
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a));
|
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a));
|
||||||
Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb());
|
if Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()) {
|
||||||
return Ok(f(Some(account)));
|
return Ok(f(Some(account)));
|
||||||
|
} else {
|
||||||
|
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Ok(f(None));
|
return Ok(f(None));
|
||||||
}
|
}
|
||||||
@ -959,12 +969,14 @@ impl<B: Backend> State<B> {
|
|||||||
let result = self.db.get_cached(a, |mut acc| {
|
let result = self.db.get_cached(a, |mut acc| {
|
||||||
if let Some(ref mut account) = acc {
|
if let Some(ref mut account) = acc {
|
||||||
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a));
|
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a));
|
||||||
Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb());
|
if !Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()) {
|
||||||
|
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a))));
|
||||||
}
|
}
|
||||||
f(acc.map(|a| &*a))
|
}
|
||||||
|
Ok(f(acc.map(|a| &*a)))
|
||||||
});
|
});
|
||||||
match result {
|
match result {
|
||||||
Some(r) => Ok(r),
|
Some(r) => Ok(r?),
|
||||||
None => {
|
None => {
|
||||||
// first check if it is not in database for sure
|
// first check if it is not in database for sure
|
||||||
if check_null && self.db.is_known_null(a) { return Ok(f(None)); }
|
if check_null && self.db.is_known_null(a) { return Ok(f(None)); }
|
||||||
@ -975,7 +987,9 @@ impl<B: Backend> State<B> {
|
|||||||
let mut maybe_acc = db.get_with(a, from_rlp)?;
|
let mut maybe_acc = db.get_with(a, from_rlp)?;
|
||||||
if let Some(ref mut account) = maybe_acc.as_mut() {
|
if let Some(ref mut account) = maybe_acc.as_mut() {
|
||||||
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a));
|
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), account.address_hash(a));
|
||||||
Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb());
|
if !Self::update_account_cache(require, account, &self.db, accountdb.as_hashdb()) {
|
||||||
|
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let r = f(maybe_acc.as_ref());
|
let r = f(maybe_acc.as_ref());
|
||||||
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
|
self.insert_cache(a, AccountEntry::new_clean(maybe_acc));
|
||||||
|
@ -106,10 +106,13 @@ pub trait Ext {
|
|||||||
) -> MessageCallResult;
|
) -> MessageCallResult;
|
||||||
|
|
||||||
/// Returns code at given address
|
/// Returns code at given address
|
||||||
fn extcode(&self, address: &Address) -> Result<Arc<Bytes>>;
|
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>>;
|
||||||
|
|
||||||
|
/// Returns code hash at given address
|
||||||
|
fn extcodehash(&self, address: &Address) -> Result<Option<H256>>;
|
||||||
|
|
||||||
/// Returns code size at given address
|
/// Returns code size at given address
|
||||||
fn extcodesize(&self, address: &Address) -> Result<usize>;
|
fn extcodesize(&self, address: &Address) -> Result<Option<usize>>;
|
||||||
|
|
||||||
/// Creates log entry with given topics and data
|
/// Creates log entry with given topics and data
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()>;
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()>;
|
||||||
|
@ -26,6 +26,8 @@ pub struct Schedule {
|
|||||||
pub have_create2: bool,
|
pub have_create2: bool,
|
||||||
/// Does it have a REVERT instruction
|
/// Does it have a REVERT instruction
|
||||||
pub have_revert: bool,
|
pub have_revert: bool,
|
||||||
|
/// Does it have a EXTCODEHASH instruction
|
||||||
|
pub have_extcodehash: bool,
|
||||||
/// VM stack limit
|
/// VM stack limit
|
||||||
pub stack_limit: usize,
|
pub stack_limit: usize,
|
||||||
/// Max number of nested calls/creates
|
/// Max number of nested calls/creates
|
||||||
@ -92,6 +94,8 @@ pub struct Schedule {
|
|||||||
pub extcodecopy_base_gas: usize,
|
pub extcodecopy_base_gas: usize,
|
||||||
/// Price of BALANCE
|
/// Price of BALANCE
|
||||||
pub balance_gas: usize,
|
pub balance_gas: usize,
|
||||||
|
/// Price of EXTCODEHASH
|
||||||
|
pub extcodehash_gas: usize,
|
||||||
/// Price of SUICIDE
|
/// Price of SUICIDE
|
||||||
pub suicide_gas: usize,
|
pub suicide_gas: usize,
|
||||||
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
|
/// Amount of additional gas to pay when SUICIDE credits a non-existant account
|
||||||
@ -197,6 +201,7 @@ impl Schedule {
|
|||||||
have_revert: false,
|
have_revert: false,
|
||||||
have_return_data: false,
|
have_return_data: false,
|
||||||
have_bitwise_shifting: false,
|
have_bitwise_shifting: false,
|
||||||
|
have_extcodehash: false,
|
||||||
stack_limit: 1024,
|
stack_limit: 1024,
|
||||||
max_depth: 1024,
|
max_depth: 1024,
|
||||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||||
@ -229,6 +234,7 @@ impl Schedule {
|
|||||||
copy_gas: 3,
|
copy_gas: 3,
|
||||||
extcodesize_gas: 700,
|
extcodesize_gas: 700,
|
||||||
extcodecopy_base_gas: 700,
|
extcodecopy_base_gas: 700,
|
||||||
|
extcodehash_gas: 400,
|
||||||
balance_gas: 400,
|
balance_gas: 400,
|
||||||
suicide_gas: 5000,
|
suicide_gas: 5000,
|
||||||
suicide_to_new_account_cost: 25000,
|
suicide_to_new_account_cost: 25000,
|
||||||
@ -268,6 +274,7 @@ impl Schedule {
|
|||||||
have_revert: false,
|
have_revert: false,
|
||||||
have_return_data: false,
|
have_return_data: false,
|
||||||
have_bitwise_shifting: false,
|
have_bitwise_shifting: false,
|
||||||
|
have_extcodehash: false,
|
||||||
stack_limit: 1024,
|
stack_limit: 1024,
|
||||||
max_depth: 1024,
|
max_depth: 1024,
|
||||||
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
tier_step_gas: [0, 2, 3, 5, 8, 10, 20, 0],
|
||||||
@ -300,6 +307,7 @@ impl Schedule {
|
|||||||
copy_gas: 3,
|
copy_gas: 3,
|
||||||
extcodesize_gas: 20,
|
extcodesize_gas: 20,
|
||||||
extcodecopy_base_gas: 20,
|
extcodecopy_base_gas: 20,
|
||||||
|
extcodehash_gas: 400,
|
||||||
balance_gas: 20,
|
balance_gas: 20,
|
||||||
suicide_gas: 0,
|
suicide_gas: 0,
|
||||||
suicide_to_new_account_cost: 0,
|
suicide_to_new_account_cost: 0,
|
||||||
|
@ -24,6 +24,7 @@ use {
|
|||||||
ReturnData, Ext, ContractCreateResult, MessageCallResult,
|
ReturnData, Ext, ContractCreateResult, MessageCallResult,
|
||||||
CreateContractAddress, Result, GasLeft,
|
CreateContractAddress, Result, GasLeft,
|
||||||
};
|
};
|
||||||
|
use hash::keccak;
|
||||||
|
|
||||||
pub struct FakeLogEntry {
|
pub struct FakeLogEntry {
|
||||||
pub topics: Vec<H256>,
|
pub topics: Vec<H256>,
|
||||||
@ -168,12 +169,16 @@ impl Ext for FakeExt {
|
|||||||
MessageCallResult::Success(*gas, ReturnData::empty())
|
MessageCallResult::Success(*gas, ReturnData::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcode(&self, address: &Address) -> Result<Arc<Bytes>> {
|
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>> {
|
||||||
Ok(self.codes.get(address).unwrap_or(&Arc::new(Bytes::new())).clone())
|
Ok(self.codes.get(address).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extcodesize(&self, address: &Address) -> Result<usize> {
|
fn extcodesize(&self, address: &Address) -> Result<Option<usize>> {
|
||||||
Ok(self.codes.get(address).map_or(0, |c| c.len()))
|
Ok(self.codes.get(address).map(|c| c.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extcodehash(&self, address: &Address) -> Result<Option<H256>> {
|
||||||
|
Ok(self.codes.get(address).map(|c| keccak(c.as_ref())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()> {
|
fn log(&mut self, topics: Vec<H256>, data: &[u8]) -> Result<()> {
|
||||||
|
@ -109,6 +109,9 @@ pub struct Params {
|
|||||||
#[serde(rename="eip658Transition")]
|
#[serde(rename="eip658Transition")]
|
||||||
pub eip658_transition: Option<Uint>,
|
pub eip658_transition: Option<Uint>,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
|
#[serde(rename="eip1052Transition")]
|
||||||
|
pub eip1052_transition: Option<Uint>,
|
||||||
|
/// See `CommonParams` docs.
|
||||||
#[serde(rename="dustProtectionTransition")]
|
#[serde(rename="dustProtectionTransition")]
|
||||||
pub dust_protection_transition: Option<Uint>,
|
pub dust_protection_transition: Option<Uint>,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
|
Loading…
Reference in New Issue
Block a user