From 141f6a047ece33cf54bea130707553a075d152b2 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Mon, 8 Jul 2019 12:03:27 +0200 Subject: [PATCH] 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 * Update ethcore/src/factory.rs Co-Authored-By: Andronik Ordian * Add pWasm frontend config * Add snapshot testing with version * Fix merge conflict --- ethcore/evm/src/interpreter/mod.rs | 5 ++- ethcore/light/src/on_demand/request.rs | 10 +---- ethcore/pod-account/src/lib.rs | 29 +++++++++--- ethcore/src/executive.rs | 46 +++++++++++-------- ethcore/src/externalities.rs | 22 +++++---- ethcore/src/factory.rs | 22 ++++++--- ethcore/src/json_tests/executive.rs | 3 +- ethcore/src/machine.rs | 1 + ethcore/src/pod_state.rs | 16 +++++-- ethcore/src/snapshot/account.rs | 62 +++++++++++++++++++++++--- ethcore/src/spec/spec.rs | 10 ++++- ethcore/src/state/mod.rs | 38 ++++++++++------ ethcore/src/tests/evm.rs | 2 + ethcore/state-account/src/lib.rs | 48 +++++++++++++------- ethcore/types/src/basic_account.rs | 46 ++++++++++++++++++- ethcore/vm/src/action_params.rs | 4 ++ ethcore/vm/src/ext.rs | 1 + ethcore/vm/src/lib.rs | 2 +- ethcore/vm/src/schedule.rs | 15 +++++++ ethcore/vm/src/tests.rs | 1 + ethcore/wasm/src/lib.rs | 1 + ethcore/wasm/src/runtime.rs | 3 +- json/src/blockchain/account.rs | 3 ++ json/src/spec/account.rs | 2 + json/src/spec/params.rs | 4 +- json/src/vm/transaction.rs | 3 ++ 26 files changed, 305 insertions(+), 94 deletions(-) diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index d81891d6e..07e0bf32f 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -118,6 +118,8 @@ struct InterpreterParams { pub code_address: Address, /// Hash of currently executed code. pub code_hash: Option, + /// Code version. + pub code_version: U256, /// Receive address. Usually equal to code_address, /// except when called using CALLCODE. pub address: Address, @@ -144,6 +146,7 @@ impl From for InterpreterParams { InterpreterParams { code_address: params.code_address, code_hash: params.code_hash, + code_version: params.code_version, address: params.address, sender: params.sender, origin: params.origin, @@ -531,7 +534,7 @@ impl Interpreter { let contract_code = self.mem.read_slice(init_off, init_size); - let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme, true); + let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, &self.params.code_version, address_scheme, true); return match create_result { Ok(ContractCreateResult::Created(address, gas_left)) => { self.stack.push(address_to_u256(address)); diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index b2bf39951..1efed6e33 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -33,7 +33,7 @@ use hash_db::HashDB; use kvdb::DBValue; use parking_lot::Mutex; use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field}; -use rlp::{RlpStream, Rlp}; +use rlp::RlpStream; use trie::Trie; use vm::EnvInfo; @@ -984,13 +984,7 @@ impl Account { match TrieDB::new(&db, &state_root).and_then(|t| t.get(keccak(&self.address).as_bytes()))? { Some(val) => { - let rlp = Rlp::new(&val); - Ok(Some(BasicAccount { - nonce: rlp.val_at(0)?, - balance: rlp.val_at(1)?, - storage_root: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - })) + Ok(Some(rlp::decode::(&val)?)) }, None => { trace!(target: "on_demand", "Account {:?} not found", self.address); diff --git a/ethcore/pod-account/src/lib.rs b/ethcore/pod-account/src/lib.rs index ea004f017..4d5b0143f 100644 --- a/ethcore/pod-account/src/lib.rs +++ b/ethcore/pod-account/src/lib.rs @@ -46,6 +46,9 @@ pub struct PodAccount { pub code: Option, /// The storage of the account. pub storage: BTreeMap, + /// The version of the account. + #[serde(default)] + pub version: U256, } fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result @@ -93,6 +96,7 @@ impl From for PodAccount { let value: U256 = value.into(); (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) }).collect(), + version: a.version.into(), } } } @@ -108,6 +112,7 @@ impl From for PodAccount { let value: U256 = value.into(); (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) }).collect()), + version: a.version.map_or_else(U256::zero, Into::into), } } } @@ -165,7 +170,9 @@ mod test { #[test] fn existence() { - let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; + let a = PodAccount { + balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![], version: 0.into(), + }; assert_eq!(diff_pod(Some(&a), Some(&a)), None); assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{ balance: Diff::Born(69.into()), @@ -177,8 +184,12 @@ mod test { #[test] fn basic() { - let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; - let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]}; + let a = PodAccount { + balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![], version: 0.into(), + }; + let b = PodAccount { + balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![], version: 0.into(), + }; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Changed(69.into(), 42.into()), nonce: Diff::Changed(0.into(), 1.into()), @@ -189,8 +200,12 @@ mod test { #[test] fn code() { - let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; - let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]}; + let a = PodAccount { + balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![], version: 0.into(), + }; + let b = PodAccount { + balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![], version: 0.into(), + }; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Same, nonce: Diff::Changed(0.into(), 1.into()), @@ -214,6 +229,7 @@ mod test { H256::from_low_u64_be(6) => H256::from_low_u64_be(0), H256::from_low_u64_be(7) => H256::from_low_u64_be(0) ], + version: 0.into(), }; let b = PodAccount { balance: 0.into(), @@ -227,7 +243,8 @@ mod test { H256::from_low_u64_be(7) => H256::from_low_u64_be(7), H256::from_low_u64_be(8) => H256::from_low_u64_be(0), H256::from_low_u64_be(9) => H256::from_low_u64_be(9) - ] + ], + version: 0.into(), }; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Same, diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index bb11580b0..29e0dc217 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -309,13 +309,13 @@ impl<'a> CallCreateExecutive<'a> { } fn transfer_exec_balance_and_init_contract(params: &ActionParams, schedule: &Schedule, state: &mut State, 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(¶ms.address)?; if let ActionValue::Transfer(val) = params.value { state.sub_balance(¶ms.sender, &val, &mut substate.to_cleanup_mode(&schedule))?; - state.new_contract(¶ms.address, val.saturating_add(prev_bal), nonce_offset)?; + state.new_contract(¶ms.address, val.saturating_add(prev_bal), nonce_offset, params.code_version)?; } else { - state.new_contract(¶ms.address, prev_bal, nonce_offset)?; + state.new_contract(¶ms.address, prev_bal, nonce_offset, params.code_version)?; } Ok(()) @@ -451,12 +451,15 @@ impl<'a> CallCreateExecutive<'a> { let origin_info = OriginInfo::from(¶ms); 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(¶ms); 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(); diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index d5ffb5dbc..bf98b86d6 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -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 { @@ -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."), } diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs index 574cbeeb3..670900ca0 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/src/factory.rs @@ -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 { - 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> { + 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(¶ms.code_version); + + match version_config { + Some(VersionedSchedule::PWasm) => { + Some(Box::new(WasmInterpreter::new(params))) + }, + None => None, + } } } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 3c2792740..6083b6c82 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -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 { @@ -298,7 +299,7 @@ fn do_json_test_for(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(); diff --git a/ethcore/src/machine.rs b/ethcore/src/machine.rs index 51d4e3f82..510442565 100644 --- a/ethcore/src/machine.rs +++ b/ethcore/src/machine.rs @@ -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, diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index 406bcc05d..3cc3921ac 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -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, diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 72bf2b66a..7bf2f4829 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -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> = 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::(&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(); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 7a409f521..117561a72 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -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, /// 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 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(), diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index e085a28a4..402f9394b 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -497,14 +497,14 @@ impl State { /// 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 State { |a| a.as_ref().map(|a| a.code_hash())) } + /// Get an account's code version. + pub fn code_version(&self, a: &Address) -> TrieResult { + 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> { self.ensure_cached(a, RequireCache::CodeSize, true, @@ -790,13 +796,13 @@ impl State { /// 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 State { }; // 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 State { })?; m.insert(address, PodAccount { - balance, nonce, storage, code + balance, nonce, storage, code, version }); } @@ -1270,6 +1276,7 @@ impl State { 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()); } diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index de1c14b27..9758ee0e7 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -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, diff --git a/ethcore/state-account/src/lib.rs b/ethcore/state-account/src/lib.rs index d2f773c3c..db3ad4147 100644 --- a/ethcore/state-account/src/lib.rs +++ b/ethcore/state-account/src/lib.rs @@ -71,6 +71,8 @@ pub struct Account { code_size: Option, // Code cache of the account. code_cache: Arc, + // Version of the account. + code_version: U256, // Account code new or has been modified. code_filth: Filth, // Cached address hash. @@ -89,6 +91,7 @@ impl From for Account { code_hash: basic.code_hash, code_size: None, code_cache: Arc::new(vec![]), + code_version: basic.code_version, code_filth: Filth::Clean, address_hash: Cell::new(None), } @@ -98,7 +101,7 @@ impl From for Account { impl Account { #[cfg(test)] /// General constructor. - pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { + pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes, version: U256) -> Account { Account { balance: balance, nonce: nonce, @@ -109,6 +112,7 @@ impl Account { code_hash: keccak(&code), code_size: Some(code.len()), code_cache: Arc::new(code), + code_version: version, code_filth: Filth::Dirty, address_hash: Cell::new(None), } @@ -131,6 +135,7 @@ impl Account { code_filth: Filth::Dirty, code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), + code_version: pod.version, address_hash: Cell::new(None), } } @@ -146,6 +151,7 @@ impl Account { m }), code: self.code().map(|x| x.to_vec()), + version: self.code_version, } } @@ -161,6 +167,7 @@ impl Account { code_hash: KECCAK_EMPTY, code_cache: Arc::new(vec![]), code_size: Some(0), + code_version: U256::zero(), code_filth: Filth::Clean, address_hash: Cell::new(None), } @@ -174,7 +181,7 @@ impl Account { /// Create a new contract account. /// NOTE: make sure you use `init_code` on this before `commit`ing. - pub fn new_contract(balance: U256, nonce: U256, original_storage_root: H256) -> Account { + pub fn new_contract(balance: U256, nonce: U256, version: U256, original_storage_root: H256) -> Account { Account { balance: balance, nonce: nonce, @@ -189,6 +196,7 @@ impl Account { code_hash: KECCAK_EMPTY, code_cache: Arc::new(vec![]), code_size: None, + code_version: version, code_filth: Filth::Clean, address_hash: Cell::new(None), } @@ -319,6 +327,9 @@ impl Account { /// return the nonce associated with this account. pub fn nonce(&self) -> &U256 { &self.nonce } + /// return the code version associated with this account. + pub fn code_version(&self) -> &U256 { &self.code_version } + /// return the code hash associated with this account. pub fn code_hash(&self) -> H256 { self.code_hash.clone() @@ -529,12 +540,15 @@ impl Account { /// Export to RLP. pub fn rlp(&self) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&self.storage_root); - stream.append(&self.code_hash); - stream.out() + let basic = BasicAccount { + nonce: self.nonce, + balance: self.balance, + storage_root: self.storage_root, + code_hash: self.code_hash, + code_version: self.code_version, + }; + + rlp::encode(&basic) } /// Clone basic account data @@ -549,6 +563,7 @@ impl Account { code_hash: self.code_hash.clone(), code_size: self.code_size.clone(), code_cache: self.code_cache.clone(), + code_version: self.code_version, code_filth: self.code_filth, address_hash: self.address_hash.clone(), } @@ -579,6 +594,7 @@ impl Account { self.code_filth = other.code_filth; self.code_cache = other.code_cache; self.code_size = other.code_size; + self.code_version = other.code_version; self.address_hash = other.address_hash; if self.storage_root == other.storage_root { let mut cache = self.storage_cache.borrow_mut(); @@ -649,7 +665,7 @@ mod tests { let mut db = new_memory_db(); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); a.set_storage(H256::zero(), H256::from_low_u64_be(0x1234)); a.commit_storage(&Default::default(), &mut db).unwrap(); a.init_code(vec![]); @@ -669,7 +685,7 @@ mod tests { let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); a.init_code(vec![0x55, 0x44, 0xffu8]); a.commit_code(&mut db); a.rlp() @@ -684,7 +700,7 @@ mod tests { #[test] fn commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); @@ -695,7 +711,7 @@ mod tests { #[test] fn commit_remove_commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); @@ -709,7 +725,7 @@ mod tests { #[test] fn commit_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.init_code(vec![0x55, 0x44, 0xffu8]); @@ -721,7 +737,7 @@ mod tests { #[test] fn reset_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.init_code(vec![0x55, 0x44, 0xffu8]); @@ -737,7 +753,7 @@ mod tests { #[test] fn rlpio() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new(), 0.into()); let b = Account::from_rlp(&a.rlp()).unwrap(); assert_eq!(a.balance(), b.balance()); assert_eq!(a.nonce(), b.nonce()); @@ -747,7 +763,7 @@ mod tests { #[test] fn new_account() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new(), 0.into()); assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); assert_eq!(*a.balance(), 69u8.into()); assert_eq!(*a.nonce(), 0u8.into()); diff --git a/ethcore/types/src/basic_account.rs b/ethcore/types/src/basic_account.rs index 039dbac94..3cdde9162 100644 --- a/ethcore/types/src/basic_account.rs +++ b/ethcore/types/src/basic_account.rs @@ -19,7 +19,7 @@ use ethereum_types::{U256, H256}; /// Basic account type. -#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct BasicAccount { /// Nonce of the account. pub nonce: U256, @@ -29,4 +29,48 @@ pub struct BasicAccount { pub storage_root: H256, /// Code hash of the account. pub code_hash: H256, + /// Code version of the account. + pub code_version: U256, +} + +impl rlp::Encodable for BasicAccount { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let use_short_version = self.code_version == U256::zero(); + + match use_short_version { + true => { stream.begin_list(4); } + false => { stream.begin_list(5); } + } + + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&self.storage_root); + stream.append(&self.code_hash); + + if !use_short_version { + stream.append(&self.code_version); + } + } +} + +impl rlp::Decodable for BasicAccount { + fn decode(rlp: &rlp::Rlp) -> Result { + let use_short_version = match rlp.item_count()? { + 4 => true, + 5 => false, + _ => return Err(rlp::DecoderError::RlpIncorrectListLen), + }; + + Ok(BasicAccount { + nonce: rlp.val_at(0)?, + balance: rlp.val_at(1)?, + storage_root: rlp.val_at(2)?, + code_hash: rlp.val_at(3)?, + code_version: if use_short_version { + U256::zero() + } else { + rlp.val_at(4)? + }, + }) + } } diff --git a/ethcore/vm/src/action_params.rs b/ethcore/vm/src/action_params.rs index 3b974310c..6d0ca2a0f 100644 --- a/ethcore/vm/src/action_params.rs +++ b/ethcore/vm/src/action_params.rs @@ -84,6 +84,8 @@ pub struct ActionParams { pub value: ActionValue, /// Code being executed. pub code: Option>, + /// Code version being executed. + pub code_version: U256, /// Input data. pub data: Option, /// Type of call @@ -105,6 +107,7 @@ impl Default for ActionParams { gas_price: U256::zero(), value: ActionValue::Transfer(U256::zero()), code: None, + code_version: U256::zero(), data: None, call_type: CallType::None, params_type: ParamsType::Separate, @@ -122,6 +125,7 @@ impl From for ActionParams { sender: t.sender.into(), origin: t.origin.into(), code: Some(Arc::new(t.code.into())), + code_version: t.code_version.into(), data: Some(t.data.into()), gas: t.gas.into(), gas_price: t.gas_price.into(), diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 9b499fcab..ba6849589 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -97,6 +97,7 @@ pub trait Ext { gas: &U256, value: &U256, code: &[u8], + parent_version: &U256, address: CreateContractAddress, trap: bool, ) -> ::std::result::Result; diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index 4204ff92d..725a1ce8a 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -36,7 +36,7 @@ pub mod tests; pub use action_params::{ActionParams, ActionValue, ParamsType}; pub use call_type::CallType; pub use env_info::{EnvInfo, LastHashes}; -pub use schedule::{Schedule, CleanDustMode, WasmCosts}; +pub use schedule::{Schedule, VersionedSchedule, CleanDustMode, WasmCosts}; pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress}; pub use return_data::{ReturnData, GasLeft}; pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError}; diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index d1dc3b5aa..dec689ca2 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -15,6 +15,13 @@ // along with Parity Ethereum. If not, see . //! Cost schedule and other parameterisations for the EVM. +use std::collections::HashMap; +use ethereum_types::U256; + +/// Definition of schedules that can be applied to a version. +pub enum VersionedSchedule { + PWasm, +} /// Definition of the cost schedule and other parameterisations for the EVM. pub struct Schedule { @@ -121,6 +128,10 @@ pub struct Schedule { pub eip1283: bool, /// VM execution does not increase null signed address nonce if this field is true. pub keep_unsigned_nonce: bool, + /// Latest VM version for contract creation transaction. + pub latest_version: U256, + /// All supported non-legacy VM versions. + pub versions: HashMap, /// Wasm extra schedule settings, if wasm activated pub wasm: Option, } @@ -254,6 +265,8 @@ impl Schedule { kill_dust: CleanDustMode::Off, eip1283: false, keep_unsigned_nonce: false, + latest_version: U256::zero(), + versions: HashMap::new(), wasm: None, } } @@ -328,6 +341,8 @@ impl Schedule { kill_dust: CleanDustMode::Off, eip1283: false, keep_unsigned_nonce: false, + latest_version: U256::zero(), + versions: HashMap::new(), wasm: None, } } diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index 49af84259..367be2533 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -144,6 +144,7 @@ impl Ext for FakeExt { gas: &U256, value: &U256, code: &[u8], + _parent_version: &U256, address: CreateContractAddress, _trap: bool, ) -> ::std::result::Result { diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 1e6129ba1..e9fd9063b 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -131,6 +131,7 @@ impl WasmInterpreter { sender: self.params.sender, origin: self.params.origin, code_address: self.params.code_address, + code_version: self.params.code_version, value: self.params.value.value(), }, ); diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 164bde99e..c45a83f73 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -25,6 +25,7 @@ pub struct RuntimeContext { pub sender: Address, pub origin: Address, pub code_address: Address, + pub code_version: U256, pub value: U256, } @@ -529,7 +530,7 @@ impl<'a> Runtime<'a> { * U256::from(self.ext.schedule().wasm().opcodes_mul) / U256::from(self.ext.schedule().wasm().opcodes_div); - match self.ext.create(&gas_left, &endowment, &code, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") { + match self.ext.create(&gas_left, &endowment, &code, &self.context.code_version, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") { vm::ContractCreateResult::Created(address, gas_left) => { self.memory.set(result_ptr, address.as_bytes())?; self.gas_counter = self.gas_limit - diff --git a/json/src/blockchain/account.rs b/json/src/blockchain/account.rs index aa6f6f8bf..535992ae0 100644 --- a/json/src/blockchain/account.rs +++ b/json/src/blockchain/account.rs @@ -31,6 +31,9 @@ pub struct Account { pub nonce: Uint, /// Storage. pub storage: BTreeMap, + /// Version. + #[serde(default)] + pub version: Uint, } #[cfg(test)] diff --git a/json/src/spec/account.rs b/json/src/spec/account.rs index 3d18ecd6c..7e849a6bc 100644 --- a/json/src/spec/account.rs +++ b/json/src/spec/account.rs @@ -33,6 +33,8 @@ pub struct Account { pub nonce: Option, /// Code. pub code: Option, + /// Version. + pub version: Option, /// Storage. pub storage: Option>, /// Constructor. diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index 765384a6b..dc3e3fec1 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -118,8 +118,10 @@ pub struct Params { pub transaction_permission_contract: Option
, /// Block at which the transaction permission contract should start being used. pub transaction_permission_contract_transition: Option, - /// Wasm activation block height, if not activated from start + /// Wasm activation block height, if not activated from start. pub wasm_activation_transition: Option, + /// Define a separate wasm version instead of using the prefix. + pub wasm_version: Option, /// KIP4 activiation block height. pub kip4_transition: Option, /// KIP6 activiation block height. diff --git a/json/src/vm/transaction.rs b/json/src/vm/transaction.rs index 4a9374531..10ca308df 100644 --- a/json/src/vm/transaction.rs +++ b/json/src/vm/transaction.rs @@ -40,6 +40,9 @@ pub struct Transaction { pub origin: Address, /// Sent value. pub value: Uint, + /// Contract code version. + #[serde(default)] + pub code_version: Uint, } #[cfg(test)]