EIP-1702: Generalized Account Versioning Scheme (#10771)

* EIP-1702: Generalized Account Versioning Scheme

* Fix pWASM's `create` and `create2` contract creation version

* Update ethcore/src/snapshot/account.rs

Co-Authored-By: Andronik Ordian <write@reusable.software>

* Update ethcore/src/factory.rs

Co-Authored-By: Andronik Ordian <write@reusable.software>

* Add pWasm frontend config

* Add snapshot testing with version

* Fix merge conflict
This commit is contained in:
Wei Tang
2019-07-08 12:03:27 +02:00
committed by David
parent fe7bc545bf
commit 141f6a047e
26 changed files with 305 additions and 94 deletions

View File

@@ -309,13 +309,13 @@ impl<'a> CallCreateExecutive<'a> {
}
fn transfer_exec_balance_and_init_contract<B: 'a + StateBackend>(params: &ActionParams, schedule: &Schedule, state: &mut State<B>, substate: &mut Substate) -> vm::Result<()> {
let nonce_offset = if schedule.no_empty {1} else {0}.into();
let nonce_offset = if schedule.no_empty { 1 } else { 0 }.into();
let prev_bal = state.balance(&params.address)?;
if let ActionValue::Transfer(val) = params.value {
state.sub_balance(&params.sender, &val, &mut substate.to_cleanup_mode(&schedule))?;
state.new_contract(&params.address, val.saturating_add(prev_bal), nonce_offset)?;
state.new_contract(&params.address, val.saturating_add(prev_bal), nonce_offset, params.code_version)?;
} else {
state.new_contract(&params.address, prev_bal, nonce_offset)?;
state.new_contract(&params.address, prev_bal, nonce_offset, params.code_version)?;
}
Ok(())
@@ -451,12 +451,15 @@ impl<'a> CallCreateExecutive<'a> {
let origin_info = OriginInfo::from(&params);
let exec = self.factory.create(params, self.schedule, self.depth);
let out = {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
let out = match exec {
Some(exec) => {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
},
None => Ok(Err(vm::Error::OutOfGas)),
};
let res = match out {
@@ -499,12 +502,15 @@ impl<'a> CallCreateExecutive<'a> {
let origin_info = OriginInfo::from(&params);
let exec = self.factory.create(params, self.schedule, self.depth);
let out = {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
let out = match exec {
Some(exec) => {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
},
None => Ok(Err(vm::Error::OutOfGas)),
};
let res = match out {
@@ -874,6 +880,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
gas_price: t.gas_price,
value: ActionValue::Transfer(t.value),
code: Some(Arc::new(t.data.clone())),
code_version: schedule.latest_version,
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
@@ -896,6 +903,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
value: ActionValue::Transfer(t.value),
code: self.state.code(address)?,
code_hash: self.state.code_hash(address)?,
code_version: self.state.code_version(address)?,
data: Some(t.data.clone()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
@@ -2093,13 +2101,13 @@ mod tests {
let k = H256::zero();
let mut state = get_temp_state_with_factory(factory.clone());
state.new_contract(&x1, U256::zero(), U256::from(1)).unwrap();
state.new_contract(&x1, U256::zero(), U256::from(1), U256::zero()).unwrap();
state.init_code(&x1, "600160005560006000556001600055".from_hex().unwrap()).unwrap();
state.new_contract(&x2, U256::zero(), U256::from(1)).unwrap();
state.new_contract(&x2, U256::zero(), U256::from(1), U256::zero()).unwrap();
state.init_code(&x2, "600060005560016000556000600055".from_hex().unwrap()).unwrap();
state.new_contract(&y1, U256::zero(), U256::from(1)).unwrap();
state.new_contract(&y1, U256::zero(), U256::from(1), U256::zero()).unwrap();
state.init_code(&y1, "600060006000600061100062fffffff4".from_hex().unwrap()).unwrap();
state.new_contract(&y2, U256::zero(), U256::from(1)).unwrap();
state.new_contract(&y2, U256::zero(), U256::from(1), U256::zero()).unwrap();
state.init_code(&y2, "600060006000600061100162fffffff4".from_hex().unwrap()).unwrap();
let info = EnvInfo::default();

View File

@@ -160,10 +160,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
if self.env_info.number + 256 >= self.machine.params().eip210_transition {
let blockhash_contract_address = self.machine.params().eip210_contract_address;
let code_res = self.state.code(&blockhash_contract_address)
.and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash)));
.and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash)))
.and_then(|(code, hash)| self.state.code_version(&blockhash_contract_address).map(|version| (code, hash, version)));
let (code, code_hash) = match code_res {
Ok((code, hash)) => (code, hash),
let (code, code_hash, code_version) = match code_res {
Ok((code, hash, version)) => (code, hash, version),
Err(_) => return H256::zero(),
};
let data: H256 = BigEndianHash::from_uint(number);
@@ -178,6 +179,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
gas_price: 0.into(),
code: code,
code_hash: code_hash,
code_version: code_version,
data: Some(data.as_bytes().to_vec()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
@@ -214,6 +216,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
gas: &U256,
value: &U256,
code: &[u8],
parent_version: &U256,
address_scheme: CreateContractAddress,
trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
@@ -237,6 +240,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
value: ActionValue::Transfer(*value),
code: Some(Arc::new(code.to_vec())),
code_hash: code_hash,
code_version: *parent_version,
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
@@ -275,10 +279,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
trace!(target: "externalities", "call");
let code_res = self.state.code(code_address)
.and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash)));
.and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash)))
.and_then(|(code, hash)| self.state.code_version(code_address).map(|version| (code, hash, version)));
let (code, code_hash) = match code_res {
Ok((code, hash)) => (code, hash),
let (code, code_hash, code_version) = match code_res {
Ok((code, hash, version)) => (code, hash, version),
Err(_) => return Ok(MessageCallResult::Failed),
};
@@ -292,6 +297,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
gas_price: self.origin_info.gas_price,
code: code,
code_hash: code_hash,
code_version: code_version,
data: Some(data.to_vec()),
call_type: call_type,
params_type: vm::ParamsType::Separate,
@@ -611,7 +617,7 @@ mod tests {
let address = {
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce, false) {
match ext.create(&U256::max_value(), &U256::zero(), &[], &U256::zero(), CreateContractAddress::FromSenderAndNonce, false) {
Ok(ContractCreateResult::Created(address, _)) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."),
}
@@ -633,7 +639,7 @@ mod tests {
let address = {
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::zero()), false) {
match ext.create(&U256::max_value(), &U256::zero(), &[], &U256::zero(), CreateContractAddress::FromSenderSaltAndCodeHash(H256::zero()), false) {
Ok(ContractCreateResult::Created(address, _)) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."),
}

View File

@@ -17,8 +17,9 @@
use trie::TrieFactory;
use ethtrie::RlpCodec;
use account_db::Factory as AccountFactory;
use ethereum_types::U256;
use evm::{Factory as EvmFactory, VMType};
use vm::{Exec, ActionParams, Schedule};
use vm::{Exec, ActionParams, VersionedSchedule, Schedule};
use wasm::WasmInterpreter;
use keccak_hasher::KeccakHasher;
@@ -31,11 +32,22 @@ pub struct VmFactory {
}
impl VmFactory {
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<dyn Exec> {
if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
Box::new(WasmInterpreter::new(params))
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Option<Box<dyn Exec>> {
if params.code_version.is_zero() {
Some(if schedule.wasm.is_some() && schedule.versions.is_empty() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
Box::new(WasmInterpreter::new(params))
} else {
self.evm.create(params, schedule, depth)
})
} else {
self.evm.create(params, schedule, depth)
let version_config = schedule.versions.get(&params.code_version);
match version_config {
Some(VersionedSchedule::PWasm) => {
Some(Box::new(WasmInterpreter::new(params)))
},
None => None,
}
}
}

View File

@@ -146,6 +146,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
gas: &U256,
value: &U256,
code: &[u8],
_code_version: &U256,
address: CreateContractAddress,
_trap: bool
) -> Result<ContractCreateResult, vm::TrapKind> {
@@ -298,7 +299,7 @@ fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8]
&mut tracer,
&mut vm_tracer,
));
let mut evm = vm_factory.create(params, &schedule, 0);
let mut evm = vm_factory.create(params, &schedule, 0).expect("Current tests are all of version 0; factory always return Some; qed");
let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed");
// a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone();

View File

@@ -176,6 +176,7 @@ impl Machine {
value: value.unwrap_or_else(|| ActionValue::Transfer(0.into())),
code,
code_hash,
code_version: 0.into(),
data,
call_type: call_type.unwrap_or(CallType::Call),
params_type: ParamsType::Separate,

View File

@@ -89,6 +89,7 @@ mod test {
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
}
]);
assert_eq!(super::diff_pod(&a, &PodState::default()), StateDiff { raw: map![
@@ -99,7 +100,7 @@ mod test {
storage: map![],
}
]});
assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff{ raw: map![
assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff { raw: map![
Address::from_low_u64_be(1) => AccountDiff{
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
@@ -117,6 +118,7 @@ mod test {
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
}
]);
let b = PodState::from(map![
@@ -125,16 +127,18 @@ mod test {
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
},
Address::from_low_u64_be(2) => PodAccount {
balance: 69.into(),
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
}
]);
assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
Address::from_low_u64_be(2) => AccountDiff{
Address::from_low_u64_be(2) => AccountDiff {
balance: Diff::Born(69.into()),
nonce: Diff::Born(0.into()),
code: Diff::Born(vec![]),
@@ -142,7 +146,7 @@ mod test {
}
]});
assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![
Address::from_low_u64_be(2) => AccountDiff{
Address::from_low_u64_be(2) => AccountDiff {
balance: Diff::Died(69.into()),
nonce: Diff::Died(0.into()),
code: Diff::Died(vec![]),
@@ -159,12 +163,14 @@ mod test {
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
},
Address::from_low_u64_be(2) => PodAccount {
balance: 69.into(),
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
}
]);
let b = PodState::from(map![
@@ -173,16 +179,18 @@ mod test {
nonce: 1.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
},
Address::from_low_u64_be(2) => PodAccount {
balance: 69.into(),
nonce: 0.into(),
code: Some(Vec::new()),
storage: map![],
version: 0.into(),
}
]);
assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![
Address::from_low_u64_be(1) => AccountDiff{
Address::from_low_u64_be(1) => AccountDiff {
balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()),
code: Diff::Same,

View File

@@ -35,6 +35,7 @@ const ACC_EMPTY: BasicAccount = BasicAccount {
balance: U256([0, 0, 0, 0]),
storage_root: KECCAK_NULL_RLP,
code_hash: KECCAK_EMPTY,
code_version: U256([0, 0, 0, 0]),
};
// whether an encoded account has code and how it is referred to.
@@ -84,10 +85,14 @@ pub fn to_fat_rlps(
let mut leftover: Option<Vec<u8>> = None;
loop {
account_stream.append(account_hash);
account_stream.begin_list(5);
let use_short_version = acc.code_version.is_zero();
match use_short_version {
true => { account_stream.begin_list(5); },
false => { account_stream.begin_list(6); },
}
account_stream.append(&acc.nonce)
.append(&acc.balance);
.append(&acc.balance);
// [has_code, code_hash].
if acc.code_hash == KECCAK_EMPTY {
@@ -107,6 +112,10 @@ pub fn to_fat_rlps(
}
}
if !use_short_version {
account_stream.append(&acc.code_version);
}
account_stream.begin_unbounded_list();
if account_stream.len() > target_chunk_size {
// account does not fit, push an empty record to mark a new chunk
@@ -170,6 +179,12 @@ pub fn from_fat_rlp(
return Ok((ACC_EMPTY, None));
}
let use_short_version = match rlp.item_count()? {
5 => true,
6 => false,
_ => return Err(rlp::DecoderError::RlpIncorrectListLen.into()),
};
let nonce = rlp.val_at(0)?;
let balance = rlp.val_at(1)?;
let code_state: CodeState = {
@@ -193,13 +208,19 @@ pub fn from_fat_rlp(
}
};
let code_version = if use_short_version {
U256::zero()
} else {
rlp.val_at(4)?
};
{
let mut storage_trie = if storage_root.is_zero() {
TrieDBMut::new(acct_db, &mut storage_root)
} else {
TrieDBMut::from_existing(acct_db, &mut storage_root)?
};
let pairs = rlp.at(4)?;
let pairs = rlp.at(if use_short_version { 4 } else { 5 })?;
for pair_rlp in pairs.iter() {
let k: Bytes = pair_rlp.val_at(0)?;
let v: Bytes = pair_rlp.val_at(1)?;
@@ -209,10 +230,11 @@ pub fn from_fat_rlp(
}
let acc = BasicAccount {
nonce: nonce,
balance: balance,
storage_root: storage_root,
code_hash: code_hash,
nonce,
balance,
storage_root,
code_hash,
code_version,
};
Ok((acc, new_code))
@@ -246,6 +268,28 @@ mod tests {
balance: 123456789.into(),
storage_root: KECCAK_NULL_RLP,
code_hash: KECCAK_EMPTY,
code_version: 0.into(),
};
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);
let p = Progress::default();
let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap();
let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap();
assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account);
}
#[test]
fn encoding_version() {
let mut db = get_temp_state_db();
let addr = Address::random();
let account = BasicAccount {
nonce: 50.into(),
balance: 123456789.into(),
storage_root: KECCAK_NULL_RLP,
code_hash: KECCAK_EMPTY,
code_version: 1.into(),
};
let thin_rlp = ::rlp::encode(&account);
@@ -270,6 +314,7 @@ mod tests {
balance: 987654321.into(),
storage_root: root,
code_hash: KECCAK_EMPTY,
code_version: 0.into(),
}
};
@@ -297,6 +342,7 @@ mod tests {
balance: 987654321.into(),
storage_root: root,
code_hash: KECCAK_EMPTY,
code_version: 0.into(),
}
};
@@ -337,6 +383,7 @@ mod tests {
balance: 123456789.into(),
storage_root: KECCAK_NULL_RLP,
code_hash,
code_version: 0.into(),
};
let account2 = BasicAccount {
@@ -344,6 +391,7 @@ mod tests {
balance: 98765432123456789usize.into(),
storage_root: KECCAK_NULL_RLP,
code_hash,
code_version: 0.into(),
};
let mut used_code = HashSet::new();

View File

@@ -31,7 +31,7 @@ use rustc_hex::{FromHex, ToHex};
use types::BlockNumber;
use types::encoded;
use types::header::Header;
use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType};
use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType, VersionedSchedule};
use builtin::Builtin;
use engines::{
@@ -131,6 +131,9 @@ pub struct CommonParams {
pub remove_dust_contracts: bool,
/// Wasm activation blocknumber, if any disabled initially.
pub wasm_activation_transition: BlockNumber,
/// Wasm account version, activated after `wasm_activation_transition`. If this field is defined, do not use code
/// prefix to determine VM to execute.
pub wasm_version: Option<U256>,
/// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated.
pub kip4_transition: BlockNumber,
/// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated.
@@ -208,6 +211,9 @@ impl CommonParams {
wasm.have_gasleft = true;
}
schedule.wasm = Some(wasm);
if let Some(version) = self.wasm_version {
schedule.versions.insert(version, VersionedSchedule::PWasm);
}
}
}
@@ -327,6 +333,7 @@ impl From<ethjson::spec::Params> for CommonParams {
BlockNumber::max_value,
Into::into
),
wasm_version: p.wasm_version.map(Into::into),
kip4_transition: p.kip4_transition.map_or_else(
BlockNumber::max_value,
Into::into
@@ -665,6 +672,7 @@ impl Spec {
let params = ActionParams {
code_address: address.clone(),
code_hash: Some(keccak(constructor)),
code_version: U256::zero(),
address: address.clone(),
sender: from.clone(),
origin: from.clone(),

View File

@@ -497,14 +497,14 @@ impl<B: Backend> State<B> {
/// Create a new contract at address `contract`. If there is already an account at the address
/// it will have its code reset, ready for `init_code()`.
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> {
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> {
let original_storage_root = self.original_storage_root(contract)?;
let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset);
if overflow {
return Err(Box::new(TrieError::DecoderError(H256::from(*contract),
rlp::DecoderError::Custom("Nonce overflow".into()))));
}
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root))));
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root))));
Ok(())
}
@@ -732,6 +732,12 @@ impl<B: Backend> State<B> {
|a| a.as_ref().map(|a| a.code_hash()))
}
/// Get an account's code version.
pub fn code_version(&self, a: &Address) -> TrieResult<U256> {
self.ensure_cached(a, RequireCache::None, true,
|a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero()))
}
/// Get accounts' code size.
pub fn code_size(&self, a: &Address) -> TrieResult<Option<usize>> {
self.ensure_cached(a, RequireCache::CodeSize, true,
@@ -790,13 +796,13 @@ impl<B: Backend> State<B> {
/// Initialise the code of account `a` so that it is `code`.
/// NOTE: Account should have been created with `new_contract`.
pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code);
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.init_code(code);
Ok(())
}
/// Reset the code of account `a` so that it is `code`.
pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code);
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code);
Ok(())
}
@@ -1069,11 +1075,11 @@ impl<B: Backend> State<B> {
};
// Storage must be fetched after ensure_cached to avoid borrow problem.
(*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()))
(*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version())
})
})?;
if let Some((balance, nonce, storage_keys, code)) = account {
if let Some((balance, nonce, storage_keys, code, version)) = account {
let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| {
let mut s = s?;
@@ -1082,7 +1088,7 @@ impl<B: Backend> State<B> {
})?;
m.insert(address, PodAccount {
balance, nonce, storage, code
balance, nonce, storage, code, version
});
}
@@ -1270,6 +1276,7 @@ impl<B: Backend> State<B> {
nonce: self.account_start_nonce,
code_hash: KECCAK_EMPTY,
storage_root: KECCAK_NULL_RLP,
code_version: 0.into(),
});
Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account))
@@ -2190,7 +2197,7 @@ mod tests {
let a = Address::zero();
let (root, db) = {
let mut state = get_temp_state();
state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap();
state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap();
state.init_code(&a, vec![1, 2, 3]).unwrap();
assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3])));
state.commit().unwrap();
@@ -2426,7 +2433,7 @@ mod tests {
state.clear();
let c0 = state.checkpoint();
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
let c1 = state.checkpoint();
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap();
let c2 = state.checkpoint();
@@ -2486,7 +2493,7 @@ mod tests {
let cm1 = state.checkpoint();
let c0 = state.checkpoint();
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
let c1 = state.checkpoint();
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap();
let c2 = state.checkpoint();
@@ -2558,7 +2565,7 @@ mod tests {
let a = Address::from_low_u64_be(1000);
state.checkpoint(); // c1
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap();
state.checkpoint(); // c2
state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap();
@@ -2585,7 +2592,7 @@ mod tests {
state.clear();
state.checkpoint(); // c1
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
state.checkpoint(); // c2
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap();
state.revert_to_checkpoint(); // revert to c2
@@ -2633,7 +2640,7 @@ mod tests {
state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account
state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account
state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account
state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account
state.new_contract(&e, 100.into(), 1.into(), 0.into()).unwrap(); // create a contract account
state.init_code(&e, vec![0x00]).unwrap();
state.commit().unwrap();
state.drop()
@@ -2685,7 +2692,8 @@ mod tests {
balance: U256::from(100),
nonce: U256::zero(),
code: Some(Default::default()),
storage: Default::default()
storage: Default::default(),
version: U256::zero(),
}), None).as_ref());
}
@@ -2718,12 +2726,14 @@ mod tests {
code: Some(Default::default()),
storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))]
.into_iter().collect(),
version: U256::zero(),
}), Some(&PodAccount {
balance: U256::zero(),
nonce: U256::zero(),
code: Some(Default::default()),
storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))]
.into_iter().collect(),
version: U256::zero(),
})).as_ref());
}

View File

@@ -57,6 +57,7 @@ fn test_blockhash_eip210(factory: Factory) {
value: ActionValue::Transfer(0.into()),
code: Some(blockhash_contract_code.clone()),
code_hash: Some(blockhash_contract_code_hash),
code_version: 0.into(),
data: Some(H256::from_low_u64_be(i - 1).as_bytes().to_vec()),
call_type: CallType::Call,
params_type: ParamsType::Separate,
@@ -80,6 +81,7 @@ fn test_blockhash_eip210(factory: Factory) {
value: ActionValue::Transfer(0.into()),
code: Some(get_prev_hash_code),
code_hash: Some(get_prev_hash_code_hash),
code_version: 0.into(),
data: None,
call_type: CallType::Call,
params_type: ParamsType::Separate,