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

@ -118,6 +118,8 @@ struct InterpreterParams {
pub code_address: Address, pub code_address: Address,
/// Hash of currently executed code. /// Hash of currently executed code.
pub code_hash: Option<H256>, pub code_hash: Option<H256>,
/// Code version.
pub code_version: U256,
/// Receive address. Usually equal to code_address, /// Receive address. Usually equal to code_address,
/// except when called using CALLCODE. /// except when called using CALLCODE.
pub address: Address, pub address: Address,
@ -144,6 +146,7 @@ impl From<ActionParams> for InterpreterParams {
InterpreterParams { InterpreterParams {
code_address: params.code_address, code_address: params.code_address,
code_hash: params.code_hash, code_hash: params.code_hash,
code_version: params.code_version,
address: params.address, address: params.address,
sender: params.sender, sender: params.sender,
origin: params.origin, origin: params.origin,
@ -531,7 +534,7 @@ impl<Cost: CostType> Interpreter<Cost> {
let contract_code = self.mem.read_slice(init_off, init_size); 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 { return match create_result {
Ok(ContractCreateResult::Created(address, gas_left)) => { Ok(ContractCreateResult::Created(address, gas_left)) => {
self.stack.push(address_to_u256(address)); self.stack.push(address_to_u256(address));

View File

@ -33,7 +33,7 @@ use hash_db::HashDB;
use kvdb::DBValue; use kvdb::DBValue;
use parking_lot::Mutex; use parking_lot::Mutex;
use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field}; use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field};
use rlp::{RlpStream, Rlp}; use rlp::RlpStream;
use trie::Trie; use trie::Trie;
use vm::EnvInfo; 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()))? { match TrieDB::new(&db, &state_root).and_then(|t| t.get(keccak(&self.address).as_bytes()))? {
Some(val) => { Some(val) => {
let rlp = Rlp::new(&val); Ok(Some(rlp::decode::<BasicAccount>(&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)?,
}))
}, },
None => { None => {
trace!(target: "on_demand", "Account {:?} not found", self.address); trace!(target: "on_demand", "Account {:?} not found", self.address);

View File

@ -46,6 +46,9 @@ pub struct PodAccount {
pub code: Option<Bytes>, pub code: Option<Bytes>,
/// The storage of the account. /// The storage of the account.
pub storage: BTreeMap<H256, H256>, pub storage: BTreeMap<H256, H256>,
/// The version of the account.
#[serde(default)]
pub version: U256,
} }
fn opt_bytes_to_hex<S>(opt_bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error> fn opt_bytes_to_hex<S>(opt_bytes: &Option<Bytes>, serializer: S) -> Result<S::Ok, S::Error>
@ -93,6 +96,7 @@ impl From<ethjson::blockchain::Account> for PodAccount {
let value: U256 = value.into(); let value: U256 = value.into();
(BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value))
}).collect(), }).collect(),
version: a.version.into(),
} }
} }
} }
@ -108,6 +112,7 @@ impl From<ethjson::spec::Account> for PodAccount {
let value: U256 = value.into(); let value: U256 = value.into();
(BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value))
}).collect()), }).collect()),
version: a.version.map_or_else(U256::zero, Into::into),
} }
} }
} }
@ -165,7 +170,9 @@ mod test {
#[test] #[test]
fn existence() { 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(Some(&a), Some(&a)), None);
assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{ assert_eq!(diff_pod(None, Some(&a)), Some(AccountDiff{
balance: Diff::Born(69.into()), balance: Diff::Born(69.into()),
@ -177,8 +184,12 @@ mod test {
#[test] #[test]
fn basic() { fn basic() {
let a = PodAccount{balance: 69.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; let a = PodAccount {
let b = PodAccount{balance: 42.into(), nonce: 1.into(), code: Some(vec![]), storage: map![]}; 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 { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Changed(69.into(), 42.into()), balance: Diff::Changed(69.into(), 42.into()),
nonce: Diff::Changed(0.into(), 1.into()), nonce: Diff::Changed(0.into(), 1.into()),
@ -189,8 +200,12 @@ mod test {
#[test] #[test]
fn code() { fn code() {
let a = PodAccount{balance: 0.into(), nonce: 0.into(), code: Some(vec![]), storage: map![]}; let a = PodAccount {
let b = PodAccount{balance: 0.into(), nonce: 1.into(), code: Some(vec![0]), storage: map![]}; 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 { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same, balance: Diff::Same,
nonce: Diff::Changed(0.into(), 1.into()), 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(6) => H256::from_low_u64_be(0),
H256::from_low_u64_be(7) => H256::from_low_u64_be(0) H256::from_low_u64_be(7) => H256::from_low_u64_be(0)
], ],
version: 0.into(),
}; };
let b = PodAccount { let b = PodAccount {
balance: 0.into(), 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(7) => H256::from_low_u64_be(7),
H256::from_low_u64_be(8) => H256::from_low_u64_be(0), H256::from_low_u64_be(8) => H256::from_low_u64_be(0),
H256::from_low_u64_be(9) => H256::from_low_u64_be(9) 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 { assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff {
balance: Diff::Same, balance: Diff::Same,

View File

@ -313,9 +313,9 @@ impl<'a> CallCreateExecutive<'a> {
let prev_bal = state.balance(&params.address)?; let prev_bal = state.balance(&params.address)?;
if let ActionValue::Transfer(val) = params.value { if let ActionValue::Transfer(val) = params.value {
state.sub_balance(&params.sender, &val, &mut substate.to_cleanup_mode(&schedule))?; 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 { } else {
state.new_contract(&params.address, prev_bal, nonce_offset)?; state.new_contract(&params.address, prev_bal, nonce_offset, params.code_version)?;
} }
Ok(()) Ok(())
@ -451,12 +451,15 @@ impl<'a> CallCreateExecutive<'a> {
let origin_info = OriginInfo::from(&params); let origin_info = OriginInfo::from(&params);
let exec = self.factory.create(params, self.schedule, self.depth); let exec = self.factory.create(params, self.schedule, self.depth);
let out = { 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); 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) { match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)), Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err), Err(err) => Err(err),
} }
},
None => Ok(Err(vm::Error::OutOfGas)),
}; };
let res = match out { let res = match out {
@ -499,12 +502,15 @@ impl<'a> CallCreateExecutive<'a> {
let origin_info = OriginInfo::from(&params); let origin_info = OriginInfo::from(&params);
let exec = self.factory.create(params, self.schedule, self.depth); let exec = self.factory.create(params, self.schedule, self.depth);
let out = { 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); 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) { match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)), Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err), Err(err) => Err(err),
} }
},
None => Ok(Err(vm::Error::OutOfGas)),
}; };
let res = match out { let res = match out {
@ -874,6 +880,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: Some(Arc::new(t.data.clone())), code: Some(Arc::new(t.data.clone())),
code_version: schedule.latest_version,
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: vm::ParamsType::Embedded, params_type: vm::ParamsType::Embedded,
@ -896,6 +903,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.value),
code: self.state.code(address)?, code: self.state.code(address)?,
code_hash: self.state.code_hash(address)?, code_hash: self.state.code_hash(address)?,
code_version: self.state.code_version(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,
@ -2093,13 +2101,13 @@ mod tests {
let k = H256::zero(); let k = H256::zero();
let mut state = get_temp_state_with_factory(factory.clone()); 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.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.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.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(); state.init_code(&y2, "600060006000600061100162fffffff4".from_hex().unwrap()).unwrap();
let info = EnvInfo::default(); 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 { if self.env_info.number + 256 >= self.machine.params().eip210_transition {
let blockhash_contract_address = self.machine.params().eip210_contract_address; let blockhash_contract_address = self.machine.params().eip210_contract_address;
let code_res = self.state.code(&blockhash_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 { let (code, code_hash, code_version) = match code_res {
Ok((code, hash)) => (code, hash), Ok((code, hash, version)) => (code, hash, version),
Err(_) => return H256::zero(), Err(_) => return H256::zero(),
}; };
let data: H256 = BigEndianHash::from_uint(number); 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(), gas_price: 0.into(),
code: code, code: code,
code_hash: code_hash, code_hash: code_hash,
code_version: code_version,
data: Some(data.as_bytes().to_vec()), data: Some(data.as_bytes().to_vec()),
call_type: CallType::Call, call_type: CallType::Call,
params_type: vm::ParamsType::Separate, 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, gas: &U256,
value: &U256, value: &U256,
code: &[u8], code: &[u8],
parent_version: &U256,
address_scheme: CreateContractAddress, address_scheme: CreateContractAddress,
trap: bool, trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> { ) -> ::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), value: ActionValue::Transfer(*value),
code: Some(Arc::new(code.to_vec())), code: Some(Arc::new(code.to_vec())),
code_hash: code_hash, code_hash: code_hash,
code_version: *parent_version,
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: vm::ParamsType::Embedded, 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"); trace!(target: "externalities", "call");
let code_res = self.state.code(code_address) 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 { let (code, code_hash, code_version) = match code_res {
Ok((code, hash)) => (code, hash), Ok((code, hash, version)) => (code, hash, version),
Err(_) => return Ok(MessageCallResult::Failed), 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, gas_price: self.origin_info.gas_price,
code: code, code: code,
code_hash: code_hash, code_hash: code_hash,
code_version: code_version,
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,
@ -611,7 +617,7 @@ mod tests {
let address = { 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); 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, Ok(ContractCreateResult::Created(address, _)) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."), _ => panic!("Test create failed; expected Created, got Failed/Reverted."),
} }
@ -633,7 +639,7 @@ mod tests {
let address = { 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); 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, Ok(ContractCreateResult::Created(address, _)) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."), _ => panic!("Test create failed; expected Created, got Failed/Reverted."),
} }

View File

@ -17,8 +17,9 @@
use trie::TrieFactory; use trie::TrieFactory;
use ethtrie::RlpCodec; use ethtrie::RlpCodec;
use account_db::Factory as AccountFactory; use account_db::Factory as AccountFactory;
use ethereum_types::U256;
use evm::{Factory as EvmFactory, VMType}; use evm::{Factory as EvmFactory, VMType};
use vm::{Exec, ActionParams, Schedule}; use vm::{Exec, ActionParams, VersionedSchedule, Schedule};
use wasm::WasmInterpreter; use wasm::WasmInterpreter;
use keccak_hasher::KeccakHasher; use keccak_hasher::KeccakHasher;
@ -31,11 +32,22 @@ pub struct VmFactory {
} }
impl VmFactory { impl VmFactory {
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<dyn Exec> { pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Option<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) { 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)) Box::new(WasmInterpreter::new(params))
} else { } else {
self.evm.create(params, schedule, depth) self.evm.create(params, schedule, depth)
})
} else {
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, gas: &U256,
value: &U256, value: &U256,
code: &[u8], code: &[u8],
_code_version: &U256,
address: CreateContractAddress, address: CreateContractAddress,
_trap: bool _trap: bool
) -> Result<ContractCreateResult, vm::TrapKind> { ) -> 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 tracer,
&mut vm_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"); let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed");
// a return in finalize will not alter callcreates // a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone(); let callcreates = ex.callcreates.clone();

View File

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

View File

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

View File

@ -35,6 +35,7 @@ const ACC_EMPTY: BasicAccount = BasicAccount {
balance: U256([0, 0, 0, 0]), balance: U256([0, 0, 0, 0]),
storage_root: KECCAK_NULL_RLP, storage_root: KECCAK_NULL_RLP,
code_hash: KECCAK_EMPTY, code_hash: KECCAK_EMPTY,
code_version: U256([0, 0, 0, 0]),
}; };
// whether an encoded account has code and how it is referred to. // whether an encoded account has code and how it is referred to.
@ -84,7 +85,11 @@ pub fn to_fat_rlps(
let mut leftover: Option<Vec<u8>> = None; let mut leftover: Option<Vec<u8>> = None;
loop { loop {
account_stream.append(account_hash); 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) account_stream.append(&acc.nonce)
.append(&acc.balance); .append(&acc.balance);
@ -107,6 +112,10 @@ pub fn to_fat_rlps(
} }
} }
if !use_short_version {
account_stream.append(&acc.code_version);
}
account_stream.begin_unbounded_list(); account_stream.begin_unbounded_list();
if account_stream.len() > target_chunk_size { if account_stream.len() > target_chunk_size {
// account does not fit, push an empty record to mark a new chunk // 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)); 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 nonce = rlp.val_at(0)?;
let balance = rlp.val_at(1)?; let balance = rlp.val_at(1)?;
let code_state: CodeState = { 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() { let mut storage_trie = if storage_root.is_zero() {
TrieDBMut::new(acct_db, &mut storage_root) TrieDBMut::new(acct_db, &mut storage_root)
} else { } else {
TrieDBMut::from_existing(acct_db, &mut storage_root)? 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() { for pair_rlp in pairs.iter() {
let k: Bytes = pair_rlp.val_at(0)?; let k: Bytes = pair_rlp.val_at(0)?;
let v: Bytes = pair_rlp.val_at(1)?; let v: Bytes = pair_rlp.val_at(1)?;
@ -209,10 +230,11 @@ pub fn from_fat_rlp(
} }
let acc = BasicAccount { let acc = BasicAccount {
nonce: nonce, nonce,
balance: balance, balance,
storage_root: storage_root, storage_root,
code_hash: code_hash, code_hash,
code_version,
}; };
Ok((acc, new_code)) Ok((acc, new_code))
@ -246,6 +268,28 @@ mod tests {
balance: 123456789.into(), balance: 123456789.into(),
storage_root: KECCAK_NULL_RLP, storage_root: KECCAK_NULL_RLP,
code_hash: KECCAK_EMPTY, 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); let thin_rlp = ::rlp::encode(&account);
@ -270,6 +314,7 @@ mod tests {
balance: 987654321.into(), balance: 987654321.into(),
storage_root: root, storage_root: root,
code_hash: KECCAK_EMPTY, code_hash: KECCAK_EMPTY,
code_version: 0.into(),
} }
}; };
@ -297,6 +342,7 @@ mod tests {
balance: 987654321.into(), balance: 987654321.into(),
storage_root: root, storage_root: root,
code_hash: KECCAK_EMPTY, code_hash: KECCAK_EMPTY,
code_version: 0.into(),
} }
}; };
@ -337,6 +383,7 @@ mod tests {
balance: 123456789.into(), balance: 123456789.into(),
storage_root: KECCAK_NULL_RLP, storage_root: KECCAK_NULL_RLP,
code_hash, code_hash,
code_version: 0.into(),
}; };
let account2 = BasicAccount { let account2 = BasicAccount {
@ -344,6 +391,7 @@ mod tests {
balance: 98765432123456789usize.into(), balance: 98765432123456789usize.into(),
storage_root: KECCAK_NULL_RLP, storage_root: KECCAK_NULL_RLP,
code_hash, code_hash,
code_version: 0.into(),
}; };
let mut used_code = HashSet::new(); let mut used_code = HashSet::new();

View File

@ -31,7 +31,7 @@ use rustc_hex::{FromHex, ToHex};
use types::BlockNumber; use types::BlockNumber;
use types::encoded; use types::encoded;
use types::header::Header; use types::header::Header;
use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType}; use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType, VersionedSchedule};
use builtin::Builtin; use builtin::Builtin;
use engines::{ use engines::{
@ -131,6 +131,9 @@ pub struct CommonParams {
pub remove_dust_contracts: bool, pub remove_dust_contracts: bool,
/// Wasm activation blocknumber, if any disabled initially. /// Wasm activation blocknumber, if any disabled initially.
pub wasm_activation_transition: BlockNumber, 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. /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated.
pub kip4_transition: BlockNumber, pub kip4_transition: BlockNumber,
/// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. /// 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; wasm.have_gasleft = true;
} }
schedule.wasm = Some(wasm); 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, BlockNumber::max_value,
Into::into Into::into
), ),
wasm_version: p.wasm_version.map(Into::into),
kip4_transition: p.kip4_transition.map_or_else( kip4_transition: p.kip4_transition.map_or_else(
BlockNumber::max_value, BlockNumber::max_value,
Into::into Into::into
@ -665,6 +672,7 @@ impl Spec {
let params = ActionParams { let params = ActionParams {
code_address: address.clone(), code_address: address.clone(),
code_hash: Some(keccak(constructor)), code_hash: Some(keccak(constructor)),
code_version: U256::zero(),
address: address.clone(), address: address.clone(),
sender: from.clone(), sender: from.clone(),
origin: 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 /// 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()`. /// 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 original_storage_root = self.original_storage_root(contract)?;
let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset);
if overflow { if overflow {
return Err(Box::new(TrieError::DecoderError(H256::from(*contract), return Err(Box::new(TrieError::DecoderError(H256::from(*contract),
rlp::DecoderError::Custom("Nonce overflow".into())))); 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(()) Ok(())
} }
@ -732,6 +732,12 @@ impl<B: Backend> State<B> {
|a| a.as_ref().map(|a| a.code_hash())) |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. /// Get accounts' code size.
pub fn code_size(&self, a: &Address) -> TrieResult<Option<usize>> { pub fn code_size(&self, a: &Address) -> TrieResult<Option<usize>> {
self.ensure_cached(a, RequireCache::CodeSize, true, 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`. /// Initialise the code of account `a` so that it is `code`.
/// NOTE: Account should have been created with `new_contract`. /// NOTE: Account should have been created with `new_contract`.
pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { 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(()) Ok(())
} }
/// Reset the code of account `a` so that it is `code`. /// Reset the code of account `a` so that it is `code`.
pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { 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(()) Ok(())
} }
@ -1069,11 +1075,11 @@ impl<B: Backend> State<B> {
}; };
// Storage must be fetched after ensure_cached to avoid borrow problem. // 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 storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| {
let mut s = s?; let mut s = s?;
@ -1082,7 +1088,7 @@ impl<B: Backend> State<B> {
})?; })?;
m.insert(address, PodAccount { 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, nonce: self.account_start_nonce,
code_hash: KECCAK_EMPTY, code_hash: KECCAK_EMPTY,
storage_root: KECCAK_NULL_RLP, storage_root: KECCAK_NULL_RLP,
code_version: 0.into(),
}); });
Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account))
@ -2190,7 +2197,7 @@ mod tests {
let a = Address::zero(); let a = Address::zero();
let (root, db) = { let (root, db) = {
let mut state = get_temp_state(); 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(); state.init_code(&a, vec![1, 2, 3]).unwrap();
assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3])));
state.commit().unwrap(); state.commit().unwrap();
@ -2426,7 +2433,7 @@ mod tests {
state.clear(); state.clear();
let c0 = 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(); let c1 = state.checkpoint();
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap();
let c2 = state.checkpoint(); let c2 = state.checkpoint();
@ -2486,7 +2493,7 @@ mod tests {
let cm1 = state.checkpoint(); let cm1 = state.checkpoint();
let c0 = 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(); let c1 = state.checkpoint();
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap();
let c2 = state.checkpoint(); let c2 = state.checkpoint();
@ -2558,7 +2565,7 @@ mod tests {
let a = Address::from_low_u64_be(1000); let a = Address::from_low_u64_be(1000);
state.checkpoint(); // c1 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.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap();
state.checkpoint(); // c2 state.checkpoint(); // c2
state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap();
@ -2585,7 +2592,7 @@ mod tests {
state.clear(); state.clear();
state.checkpoint(); // c1 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.checkpoint(); // c2
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap(); state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap();
state.revert_to_checkpoint(); // revert to c2 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(&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(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account
state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust 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.init_code(&e, vec![0x00]).unwrap();
state.commit().unwrap(); state.commit().unwrap();
state.drop() state.drop()
@ -2685,7 +2692,8 @@ mod tests {
balance: U256::from(100), balance: U256::from(100),
nonce: U256::zero(), nonce: U256::zero(),
code: Some(Default::default()), code: Some(Default::default()),
storage: Default::default() storage: Default::default(),
version: U256::zero(),
}), None).as_ref()); }), None).as_ref());
} }
@ -2718,12 +2726,14 @@ mod tests {
code: Some(Default::default()), code: Some(Default::default()),
storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))] storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))]
.into_iter().collect(), .into_iter().collect(),
version: U256::zero(),
}), Some(&PodAccount { }), Some(&PodAccount {
balance: U256::zero(), balance: U256::zero(),
nonce: U256::zero(), nonce: U256::zero(),
code: Some(Default::default()), code: Some(Default::default()),
storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))] storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))]
.into_iter().collect(), .into_iter().collect(),
version: U256::zero(),
})).as_ref()); })).as_ref());
} }

View File

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

View File

@ -71,6 +71,8 @@ pub struct Account {
code_size: Option<usize>, code_size: Option<usize>,
// Code cache of the account. // Code cache of the account.
code_cache: Arc<Bytes>, code_cache: Arc<Bytes>,
// Version of the account.
code_version: U256,
// Account code new or has been modified. // Account code new or has been modified.
code_filth: Filth, code_filth: Filth,
// Cached address hash. // Cached address hash.
@ -89,6 +91,7 @@ impl From<BasicAccount> for Account {
code_hash: basic.code_hash, code_hash: basic.code_hash,
code_size: None, code_size: None,
code_cache: Arc::new(vec![]), code_cache: Arc::new(vec![]),
code_version: basic.code_version,
code_filth: Filth::Clean, code_filth: Filth::Clean,
address_hash: Cell::new(None), address_hash: Cell::new(None),
} }
@ -98,7 +101,7 @@ impl From<BasicAccount> for Account {
impl Account { impl Account {
#[cfg(test)] #[cfg(test)]
/// General constructor. /// General constructor.
pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account { pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes, version: U256) -> Account {
Account { Account {
balance: balance, balance: balance,
nonce: nonce, nonce: nonce,
@ -109,6 +112,7 @@ impl Account {
code_hash: keccak(&code), code_hash: keccak(&code),
code_size: Some(code.len()), code_size: Some(code.len()),
code_cache: Arc::new(code), code_cache: Arc::new(code),
code_version: version,
code_filth: Filth::Dirty, code_filth: Filth::Dirty,
address_hash: Cell::new(None), address_hash: Cell::new(None),
} }
@ -131,6 +135,7 @@ impl Account {
code_filth: Filth::Dirty, code_filth: Filth::Dirty,
code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), 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_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), address_hash: Cell::new(None),
} }
} }
@ -146,6 +151,7 @@ impl Account {
m m
}), }),
code: self.code().map(|x| x.to_vec()), code: self.code().map(|x| x.to_vec()),
version: self.code_version,
} }
} }
@ -161,6 +167,7 @@ impl Account {
code_hash: KECCAK_EMPTY, code_hash: KECCAK_EMPTY,
code_cache: Arc::new(vec![]), code_cache: Arc::new(vec![]),
code_size: Some(0), code_size: Some(0),
code_version: U256::zero(),
code_filth: Filth::Clean, code_filth: Filth::Clean,
address_hash: Cell::new(None), address_hash: Cell::new(None),
} }
@ -174,7 +181,7 @@ impl Account {
/// Create a new contract account. /// Create a new contract account.
/// NOTE: make sure you use `init_code` on this before `commit`ing. /// 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 { Account {
balance: balance, balance: balance,
nonce: nonce, nonce: nonce,
@ -189,6 +196,7 @@ impl Account {
code_hash: KECCAK_EMPTY, code_hash: KECCAK_EMPTY,
code_cache: Arc::new(vec![]), code_cache: Arc::new(vec![]),
code_size: None, code_size: None,
code_version: version,
code_filth: Filth::Clean, code_filth: Filth::Clean,
address_hash: Cell::new(None), address_hash: Cell::new(None),
} }
@ -319,6 +327,9 @@ impl Account {
/// return the nonce associated with this account. /// return the nonce associated with this account.
pub fn nonce(&self) -> &U256 { &self.nonce } 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. /// return the code hash associated with this account.
pub fn code_hash(&self) -> H256 { pub fn code_hash(&self) -> H256 {
self.code_hash.clone() self.code_hash.clone()
@ -529,12 +540,15 @@ impl Account {
/// Export to RLP. /// Export to RLP.
pub fn rlp(&self) -> Bytes { pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new_list(4); let basic = BasicAccount {
stream.append(&self.nonce); nonce: self.nonce,
stream.append(&self.balance); balance: self.balance,
stream.append(&self.storage_root); storage_root: self.storage_root,
stream.append(&self.code_hash); code_hash: self.code_hash,
stream.out() code_version: self.code_version,
};
rlp::encode(&basic)
} }
/// Clone basic account data /// Clone basic account data
@ -549,6 +563,7 @@ impl Account {
code_hash: self.code_hash.clone(), code_hash: self.code_hash.clone(),
code_size: self.code_size.clone(), code_size: self.code_size.clone(),
code_cache: self.code_cache.clone(), code_cache: self.code_cache.clone(),
code_version: self.code_version,
code_filth: self.code_filth, code_filth: self.code_filth,
address_hash: self.address_hash.clone(), address_hash: self.address_hash.clone(),
} }
@ -579,6 +594,7 @@ impl Account {
self.code_filth = other.code_filth; self.code_filth = other.code_filth;
self.code_cache = other.code_cache; self.code_cache = other.code_cache;
self.code_size = other.code_size; self.code_size = other.code_size;
self.code_version = other.code_version;
self.address_hash = other.address_hash; self.address_hash = other.address_hash;
if self.storage_root == other.storage_root { if self.storage_root == other.storage_root {
let mut cache = self.storage_cache.borrow_mut(); let mut cache = self.storage_cache.borrow_mut();
@ -649,7 +665,7 @@ mod tests {
let mut db = new_memory_db(); let mut db = new_memory_db();
let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero()));
let rlp = { 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.set_storage(H256::zero(), H256::from_low_u64_be(0x1234));
a.commit_storage(&Default::default(), &mut db).unwrap(); a.commit_storage(&Default::default(), &mut db).unwrap();
a.init_code(vec![]); a.init_code(vec![]);
@ -669,7 +685,7 @@ mod tests {
let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero()));
let rlp = { 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.init_code(vec![0x55, 0x44, 0xffu8]);
a.commit_code(&mut db); a.commit_code(&mut db);
a.rlp() a.rlp()
@ -684,7 +700,7 @@ mod tests {
#[test] #[test]
fn commit_storage() { 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 = new_memory_db();
let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); 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)); a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234));
@ -695,7 +711,7 @@ mod tests {
#[test] #[test]
fn commit_remove_commit_storage() { 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 = new_memory_db();
let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); 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)); a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234));
@ -709,7 +725,7 @@ mod tests {
#[test] #[test]
fn commit_code() { 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 = new_memory_db();
let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero()));
a.init_code(vec![0x55, 0x44, 0xffu8]); a.init_code(vec![0x55, 0x44, 0xffu8]);
@ -721,7 +737,7 @@ mod tests {
#[test] #[test]
fn reset_code() { 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 = new_memory_db();
let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero()));
a.init_code(vec![0x55, 0x44, 0xffu8]); a.init_code(vec![0x55, 0x44, 0xffu8]);
@ -737,7 +753,7 @@ mod tests {
#[test] #[test]
fn rlpio() { 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(); let b = Account::from_rlp(&a.rlp()).unwrap();
assert_eq!(a.balance(), b.balance()); assert_eq!(a.balance(), b.balance());
assert_eq!(a.nonce(), b.nonce()); assert_eq!(a.nonce(), b.nonce());
@ -747,7 +763,7 @@ mod tests {
#[test] #[test]
fn new_account() { 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.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
assert_eq!(*a.balance(), 69u8.into()); assert_eq!(*a.balance(), 69u8.into());
assert_eq!(*a.nonce(), 0u8.into()); assert_eq!(*a.nonce(), 0u8.into());

View File

@ -19,7 +19,7 @@
use ethereum_types::{U256, H256}; use ethereum_types::{U256, H256};
/// Basic account type. /// Basic account type.
#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct BasicAccount { pub struct BasicAccount {
/// Nonce of the account. /// Nonce of the account.
pub nonce: U256, pub nonce: U256,
@ -29,4 +29,48 @@ pub struct BasicAccount {
pub storage_root: H256, pub storage_root: H256,
/// Code hash of the account. /// Code hash of the account.
pub code_hash: H256, 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<Self, rlp::DecoderError> {
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)?
},
})
}
} }

View File

@ -84,6 +84,8 @@ pub struct ActionParams {
pub value: ActionValue, pub value: ActionValue,
/// Code being executed. /// Code being executed.
pub code: Option<Arc<Bytes>>, pub code: Option<Arc<Bytes>>,
/// Code version being executed.
pub code_version: U256,
/// Input data. /// Input data.
pub data: Option<Bytes>, pub data: Option<Bytes>,
/// Type of call /// Type of call
@ -105,6 +107,7 @@ impl Default for ActionParams {
gas_price: U256::zero(), gas_price: U256::zero(),
value: ActionValue::Transfer(U256::zero()), value: ActionValue::Transfer(U256::zero()),
code: None, code: None,
code_version: U256::zero(),
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
@ -122,6 +125,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
sender: t.sender.into(), sender: t.sender.into(),
origin: t.origin.into(), origin: t.origin.into(),
code: Some(Arc::new(t.code.into())), code: Some(Arc::new(t.code.into())),
code_version: t.code_version.into(),
data: Some(t.data.into()), data: Some(t.data.into()),
gas: t.gas.into(), gas: t.gas.into(),
gas_price: t.gas_price.into(), gas_price: t.gas_price.into(),

View File

@ -97,6 +97,7 @@ pub trait Ext {
gas: &U256, gas: &U256,
value: &U256, value: &U256,
code: &[u8], code: &[u8],
parent_version: &U256,
address: CreateContractAddress, address: CreateContractAddress,
trap: bool, trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind>; ) -> ::std::result::Result<ContractCreateResult, TrapKind>;

View File

@ -36,7 +36,7 @@ pub mod tests;
pub use action_params::{ActionParams, ActionValue, ParamsType}; pub use action_params::{ActionParams, ActionValue, ParamsType};
pub use call_type::CallType; pub use call_type::CallType;
pub use env_info::{EnvInfo, LastHashes}; 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 ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress};
pub use return_data::{ReturnData, GasLeft}; pub use return_data::{ReturnData, GasLeft};
pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError}; pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError};

View File

@ -15,6 +15,13 @@
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>. // along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
//! Cost schedule and other parameterisations for the EVM. //! 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. /// Definition of the cost schedule and other parameterisations for the EVM.
pub struct Schedule { pub struct Schedule {
@ -121,6 +128,10 @@ pub struct Schedule {
pub eip1283: bool, pub eip1283: bool,
/// VM execution does not increase null signed address nonce if this field is true. /// VM execution does not increase null signed address nonce if this field is true.
pub keep_unsigned_nonce: bool, 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<U256, VersionedSchedule>,
/// Wasm extra schedule settings, if wasm activated /// Wasm extra schedule settings, if wasm activated
pub wasm: Option<WasmCosts>, pub wasm: Option<WasmCosts>,
} }
@ -254,6 +265,8 @@ impl Schedule {
kill_dust: CleanDustMode::Off, kill_dust: CleanDustMode::Off,
eip1283: false, eip1283: false,
keep_unsigned_nonce: false, keep_unsigned_nonce: false,
latest_version: U256::zero(),
versions: HashMap::new(),
wasm: None, wasm: None,
} }
} }
@ -328,6 +341,8 @@ impl Schedule {
kill_dust: CleanDustMode::Off, kill_dust: CleanDustMode::Off,
eip1283: false, eip1283: false,
keep_unsigned_nonce: false, keep_unsigned_nonce: false,
latest_version: U256::zero(),
versions: HashMap::new(),
wasm: None, wasm: None,
} }
} }

View File

@ -144,6 +144,7 @@ impl Ext for FakeExt {
gas: &U256, gas: &U256,
value: &U256, value: &U256,
code: &[u8], code: &[u8],
_parent_version: &U256,
address: CreateContractAddress, address: CreateContractAddress,
_trap: bool, _trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> { ) -> ::std::result::Result<ContractCreateResult, TrapKind> {

View File

@ -131,6 +131,7 @@ impl WasmInterpreter {
sender: self.params.sender, sender: self.params.sender,
origin: self.params.origin, origin: self.params.origin,
code_address: self.params.code_address, code_address: self.params.code_address,
code_version: self.params.code_version,
value: self.params.value.value(), value: self.params.value.value(),
}, },
); );

View File

@ -25,6 +25,7 @@ pub struct RuntimeContext {
pub sender: Address, pub sender: Address,
pub origin: Address, pub origin: Address,
pub code_address: Address, pub code_address: Address,
pub code_version: U256,
pub value: 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_mul)
/ U256::from(self.ext.schedule().wasm().opcodes_div); / 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) => { vm::ContractCreateResult::Created(address, gas_left) => {
self.memory.set(result_ptr, address.as_bytes())?; self.memory.set(result_ptr, address.as_bytes())?;
self.gas_counter = self.gas_limit - self.gas_counter = self.gas_limit -

View File

@ -31,6 +31,9 @@ pub struct Account {
pub nonce: Uint, pub nonce: Uint,
/// Storage. /// Storage.
pub storage: BTreeMap<Uint, Uint>, pub storage: BTreeMap<Uint, Uint>,
/// Version.
#[serde(default)]
pub version: Uint,
} }
#[cfg(test)] #[cfg(test)]

View File

@ -33,6 +33,8 @@ pub struct Account {
pub nonce: Option<Uint>, pub nonce: Option<Uint>,
/// Code. /// Code.
pub code: Option<Bytes>, pub code: Option<Bytes>,
/// Version.
pub version: Option<Uint>,
/// Storage. /// Storage.
pub storage: Option<BTreeMap<Uint, Uint>>, pub storage: Option<BTreeMap<Uint, Uint>>,
/// Constructor. /// Constructor.

View File

@ -118,8 +118,10 @@ pub struct Params {
pub transaction_permission_contract: Option<Address>, pub transaction_permission_contract: Option<Address>,
/// Block at which the transaction permission contract should start being used. /// Block at which the transaction permission contract should start being used.
pub transaction_permission_contract_transition: Option<Uint>, pub transaction_permission_contract_transition: Option<Uint>,
/// Wasm activation block height, if not activated from start /// Wasm activation block height, if not activated from start.
pub wasm_activation_transition: Option<Uint>, pub wasm_activation_transition: Option<Uint>,
/// Define a separate wasm version instead of using the prefix.
pub wasm_version: Option<Uint>,
/// KIP4 activiation block height. /// KIP4 activiation block height.
pub kip4_transition: Option<Uint>, pub kip4_transition: Option<Uint>,
/// KIP6 activiation block height. /// KIP6 activiation block height.

View File

@ -40,6 +40,9 @@ pub struct Transaction {
pub origin: Address, pub origin: Address,
/// Sent value. /// Sent value.
pub value: Uint, pub value: Uint,
/// Contract code version.
#[serde(default)]
pub code_version: Uint,
} }
#[cfg(test)] #[cfg(test)]