Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

EIP-1702: Generalized Account Versioning Scheme #10771

Merged
merged 11 commits into from
Jul 8, 2019
5 changes: 4 additions & 1 deletion ethcore/evm/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ struct InterpreterParams {
pub code_address: Address,
/// Hash of currently executed code.
pub code_hash: Option<H256>,
/// Code version.
pub code_version: U256,
/// Receive address. Usually equal to code_address,
/// except when called using CALLCODE.
pub address: Address,
Expand All @@ -144,6 +146,7 @@ impl From<ActionParams> 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,
Expand Down Expand Up @@ -531,7 +534,7 @@ impl<Cost: CostType> Interpreter<Cost> {

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));
Expand Down
10 changes: 2 additions & 8 deletions ethcore/light/src/on_demand/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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::<BasicAccount>(&val)?))
},
None => {
trace!(target: "on_demand", "Account {:?} not found", self.address);
Expand Down
29 changes: 23 additions & 6 deletions ethcore/pod-account/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ pub struct PodAccount {
pub code: Option<Bytes>,
/// The storage of the account.
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>
Expand Down Expand Up @@ -93,6 +96,7 @@ impl From<ethjson::blockchain::Account> for PodAccount {
let value: U256 = value.into();
(BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value))
}).collect(),
version: a.version.into(),
}
}
}
Expand All @@ -108,6 +112,7 @@ impl From<ethjson::spec::Account> 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),
}
}
}
Expand Down Expand Up @@ -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()),
Expand All @@ -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()),
Expand All @@ -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()),
Expand All @@ -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(),
Expand All @@ -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,
Expand Down
46 changes: 27 additions & 19 deletions ethcore/src/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,13 @@ impl<'a> CallCreateExecutive<'a> {
}

fn transfer_exec_balance_and_init_contract<B: 'a + StateBackend>(params: &ActionParams, schedule: &Schedule, state: &mut State<B>, substate: &mut Substate) -> vm::Result<()> {
let nonce_offset = if schedule.no_empty {1} else {0}.into();
let nonce_offset = if schedule.no_empty { 1 } else { 0 }.into();
let prev_bal = state.balance(&params.address)?;
if let ActionValue::Transfer(val) = params.value {
state.sub_balance(&params.sender, &val, &mut substate.to_cleanup_mode(&schedule))?;
state.new_contract(&params.address, val.saturating_add(prev_bal), nonce_offset)?;
state.new_contract(&params.address, val.saturating_add(prev_bal), nonce_offset, params.code_version)?;
} else {
state.new_contract(&params.address, prev_bal, nonce_offset)?;
state.new_contract(&params.address, prev_bal, nonce_offset, params.code_version)?;
}

Ok(())
Expand Down Expand Up @@ -451,12 +451,15 @@ impl<'a> CallCreateExecutive<'a> {
let origin_info = OriginInfo::from(&params);
let exec = self.factory.create(params, self.schedule, self.depth);

let out = {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
let out = match exec {
Some(exec) => {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
},
None => Ok(Err(vm::Error::OutOfGas)),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the correct error, is it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the correct error -- None means the validation phrase fails. In that case, according to the spec we should return out of gas.

};

let res = match out {
Expand Down Expand Up @@ -499,12 +502,15 @@ impl<'a> CallCreateExecutive<'a> {
let origin_info = OriginInfo::from(&params);
let exec = self.factory.create(params, self.schedule, self.depth);

let out = {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
let out = match exec {
Some(exec) => {
let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer);
match exec.exec(&mut ext) {
Ok(val) => Ok(val.finalize(ext)),
Err(err) => Err(err),
}
},
None => Ok(Err(vm::Error::OutOfGas)),
};

let res = match out {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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();
Expand Down
22 changes: 14 additions & 8 deletions ethcore/src/externalities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -214,6 +216,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
gas: &U256,
value: &U256,
code: &[u8],
parent_version: &U256,
address_scheme: CreateContractAddress,
trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
Expand All @@ -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,
Expand Down Expand Up @@ -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),
};

Expand All @@ -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,
Expand Down Expand Up @@ -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."),
}
Expand All @@ -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."),
}
Expand Down
22 changes: 17 additions & 5 deletions ethcore/src/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -31,11 +32,22 @@ pub struct VmFactory {
}

impl VmFactory {
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<dyn Exec> {
if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
Box::new(WasmInterpreter::new(params))
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Option<Box<dyn Exec>> {
if params.code_version.is_zero() {
Some(if schedule.wasm.is_some() && schedule.versions.is_empty() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
Box::new(WasmInterpreter::new(params))
} else {
self.evm.create(params, schedule, depth)
})
} else {
self.evm.create(params, schedule, depth)
let version_config = schedule.versions.get(&params.code_version);

match version_config {
Some(VersionedSchedule::PWasm) => {
Some(Box::new(WasmInterpreter::new(params)))
},
None => None,
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion ethcore/src/json_tests/executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
gas: &U256,
value: &U256,
code: &[u8],
_code_version: &U256,
address: CreateContractAddress,
_trap: bool
) -> Result<ContractCreateResult, vm::TrapKind> {
Expand Down Expand Up @@ -298,7 +299,7 @@ fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8]
&mut tracer,
&mut vm_tracer,
));
let mut evm = vm_factory.create(params, &schedule, 0);
let mut evm = vm_factory.create(params, &schedule, 0).expect("Current tests are all of version 0; factory always return Some; qed");
let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed");
// a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone();
Expand Down
1 change: 1 addition & 0 deletions ethcore/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading