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:
parent
fe7bc545bf
commit
141f6a047e
@ -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,
|
||||
@ -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,
|
||||
@ -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));
|
||||
|
@ -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::<BasicAccount>(&val)?))
|
||||
},
|
||||
None => {
|
||||
trace!(target: "on_demand", "Account {:?} not found", self.address);
|
||||
|
@ -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>
|
||||
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
@ -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(¶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();
|
||||
|
@ -160,10 +160,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
if self.env_info.number + 256 >= self.machine.params().eip210_transition {
|
||||
let blockhash_contract_address = self.machine.params().eip210_contract_address;
|
||||
let code_res = self.state.code(&blockhash_contract_address)
|
||||
.and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash)));
|
||||
.and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash)))
|
||||
.and_then(|(code, hash)| self.state.code_version(&blockhash_contract_address).map(|version| (code, hash, version)));
|
||||
|
||||
let (code, code_hash) = match code_res {
|
||||
Ok((code, hash)) => (code, hash),
|
||||
let (code, code_hash, code_version) = match code_res {
|
||||
Ok((code, hash, version)) => (code, hash, version),
|
||||
Err(_) => return H256::zero(),
|
||||
};
|
||||
let data: H256 = BigEndianHash::from_uint(number);
|
||||
@ -178,6 +179,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
gas_price: 0.into(),
|
||||
code: code,
|
||||
code_hash: code_hash,
|
||||
code_version: code_version,
|
||||
data: Some(data.as_bytes().to_vec()),
|
||||
call_type: CallType::Call,
|
||||
params_type: vm::ParamsType::Separate,
|
||||
@ -214,6 +216,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
gas: &U256,
|
||||
value: &U256,
|
||||
code: &[u8],
|
||||
parent_version: &U256,
|
||||
address_scheme: CreateContractAddress,
|
||||
trap: bool,
|
||||
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
|
||||
@ -237,6 +240,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
value: ActionValue::Transfer(*value),
|
||||
code: Some(Arc::new(code.to_vec())),
|
||||
code_hash: code_hash,
|
||||
code_version: *parent_version,
|
||||
data: None,
|
||||
call_type: CallType::None,
|
||||
params_type: vm::ParamsType::Embedded,
|
||||
@ -275,10 +279,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
trace!(target: "externalities", "call");
|
||||
|
||||
let code_res = self.state.code(code_address)
|
||||
.and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash)));
|
||||
.and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash)))
|
||||
.and_then(|(code, hash)| self.state.code_version(code_address).map(|version| (code, hash, version)));
|
||||
|
||||
let (code, code_hash) = match code_res {
|
||||
Ok((code, hash)) => (code, hash),
|
||||
let (code, code_hash, code_version) = match code_res {
|
||||
Ok((code, hash, version)) => (code, hash, version),
|
||||
Err(_) => return Ok(MessageCallResult::Failed),
|
||||
};
|
||||
|
||||
@ -292,6 +297,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
|
||||
gas_price: self.origin_info.gas_price,
|
||||
code: code,
|
||||
code_hash: code_hash,
|
||||
code_version: code_version,
|
||||
data: Some(data.to_vec()),
|
||||
call_type: call_type,
|
||||
params_type: vm::ParamsType::Separate,
|
||||
@ -611,7 +617,7 @@ mod tests {
|
||||
|
||||
let address = {
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
|
||||
match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce, false) {
|
||||
match ext.create(&U256::max_value(), &U256::zero(), &[], &U256::zero(), CreateContractAddress::FromSenderAndNonce, false) {
|
||||
Ok(ContractCreateResult::Created(address, _)) => address,
|
||||
_ => panic!("Test create failed; expected Created, got Failed/Reverted."),
|
||||
}
|
||||
@ -633,7 +639,7 @@ mod tests {
|
||||
let address = {
|
||||
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
|
||||
|
||||
match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::zero()), false) {
|
||||
match ext.create(&U256::max_value(), &U256::zero(), &[], &U256::zero(), CreateContractAddress::FromSenderSaltAndCodeHash(H256::zero()), false) {
|
||||
Ok(ContractCreateResult::Created(address, _)) => address,
|
||||
_ => panic!("Test create failed; expected Created, got Failed/Reverted."),
|
||||
}
|
||||
|
@ -17,8 +17,9 @@
|
||||
use trie::TrieFactory;
|
||||
use ethtrie::RlpCodec;
|
||||
use account_db::Factory as AccountFactory;
|
||||
use ethereum_types::U256;
|
||||
use evm::{Factory as EvmFactory, VMType};
|
||||
use vm::{Exec, ActionParams, Schedule};
|
||||
use vm::{Exec, ActionParams, VersionedSchedule, Schedule};
|
||||
use wasm::WasmInterpreter;
|
||||
use keccak_hasher::KeccakHasher;
|
||||
|
||||
@ -31,11 +32,22 @@ pub struct VmFactory {
|
||||
}
|
||||
|
||||
impl VmFactory {
|
||||
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<dyn Exec> {
|
||||
if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
|
||||
Box::new(WasmInterpreter::new(params))
|
||||
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Option<Box<dyn Exec>> {
|
||||
if params.code_version.is_zero() {
|
||||
Some(if schedule.wasm.is_some() && schedule.versions.is_empty() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
|
||||
Box::new(WasmInterpreter::new(params))
|
||||
} else {
|
||||
self.evm.create(params, schedule, depth)
|
||||
})
|
||||
} else {
|
||||
self.evm.create(params, schedule, depth)
|
||||
let version_config = schedule.versions.get(¶ms.code_version);
|
||||
|
||||
match version_config {
|
||||
Some(VersionedSchedule::PWasm) => {
|
||||
Some(Box::new(WasmInterpreter::new(params)))
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
|
||||
gas: &U256,
|
||||
value: &U256,
|
||||
code: &[u8],
|
||||
_code_version: &U256,
|
||||
address: CreateContractAddress,
|
||||
_trap: bool
|
||||
) -> Result<ContractCreateResult, vm::TrapKind> {
|
||||
@ -298,7 +299,7 @@ fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8]
|
||||
&mut tracer,
|
||||
&mut vm_tracer,
|
||||
));
|
||||
let mut evm = vm_factory.create(params, &schedule, 0);
|
||||
let mut evm = vm_factory.create(params, &schedule, 0).expect("Current tests are all of version 0; factory always return Some; qed");
|
||||
let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed");
|
||||
// a return in finalize will not alter callcreates
|
||||
let callcreates = ex.callcreates.clone();
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -35,6 +35,7 @@ const ACC_EMPTY: BasicAccount = BasicAccount {
|
||||
balance: U256([0, 0, 0, 0]),
|
||||
storage_root: KECCAK_NULL_RLP,
|
||||
code_hash: KECCAK_EMPTY,
|
||||
code_version: U256([0, 0, 0, 0]),
|
||||
};
|
||||
|
||||
// whether an encoded account has code and how it is referred to.
|
||||
@ -84,10 +85,14 @@ pub fn to_fat_rlps(
|
||||
let mut leftover: Option<Vec<u8>> = None;
|
||||
loop {
|
||||
account_stream.append(account_hash);
|
||||
account_stream.begin_list(5);
|
||||
let use_short_version = acc.code_version.is_zero();
|
||||
match use_short_version {
|
||||
true => { account_stream.begin_list(5); },
|
||||
false => { account_stream.begin_list(6); },
|
||||
}
|
||||
|
||||
account_stream.append(&acc.nonce)
|
||||
.append(&acc.balance);
|
||||
.append(&acc.balance);
|
||||
|
||||
// [has_code, code_hash].
|
||||
if acc.code_hash == KECCAK_EMPTY {
|
||||
@ -107,6 +112,10 @@ pub fn to_fat_rlps(
|
||||
}
|
||||
}
|
||||
|
||||
if !use_short_version {
|
||||
account_stream.append(&acc.code_version);
|
||||
}
|
||||
|
||||
account_stream.begin_unbounded_list();
|
||||
if account_stream.len() > target_chunk_size {
|
||||
// account does not fit, push an empty record to mark a new chunk
|
||||
@ -170,6 +179,12 @@ pub fn from_fat_rlp(
|
||||
return Ok((ACC_EMPTY, None));
|
||||
}
|
||||
|
||||
let use_short_version = match rlp.item_count()? {
|
||||
5 => true,
|
||||
6 => false,
|
||||
_ => return Err(rlp::DecoderError::RlpIncorrectListLen.into()),
|
||||
};
|
||||
|
||||
let nonce = rlp.val_at(0)?;
|
||||
let balance = rlp.val_at(1)?;
|
||||
let code_state: CodeState = {
|
||||
@ -193,13 +208,19 @@ pub fn from_fat_rlp(
|
||||
}
|
||||
};
|
||||
|
||||
let code_version = if use_short_version {
|
||||
U256::zero()
|
||||
} else {
|
||||
rlp.val_at(4)?
|
||||
};
|
||||
|
||||
{
|
||||
let mut storage_trie = if storage_root.is_zero() {
|
||||
TrieDBMut::new(acct_db, &mut storage_root)
|
||||
} else {
|
||||
TrieDBMut::from_existing(acct_db, &mut storage_root)?
|
||||
};
|
||||
let pairs = rlp.at(4)?;
|
||||
let pairs = rlp.at(if use_short_version { 4 } else { 5 })?;
|
||||
for pair_rlp in pairs.iter() {
|
||||
let k: Bytes = pair_rlp.val_at(0)?;
|
||||
let v: Bytes = pair_rlp.val_at(1)?;
|
||||
@ -209,10 +230,11 @@ pub fn from_fat_rlp(
|
||||
}
|
||||
|
||||
let acc = BasicAccount {
|
||||
nonce: nonce,
|
||||
balance: balance,
|
||||
storage_root: storage_root,
|
||||
code_hash: code_hash,
|
||||
nonce,
|
||||
balance,
|
||||
storage_root,
|
||||
code_hash,
|
||||
code_version,
|
||||
};
|
||||
|
||||
Ok((acc, new_code))
|
||||
@ -246,6 +268,28 @@ mod tests {
|
||||
balance: 123456789.into(),
|
||||
storage_root: KECCAK_NULL_RLP,
|
||||
code_hash: KECCAK_EMPTY,
|
||||
code_version: 0.into(),
|
||||
};
|
||||
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp).unwrap(), account);
|
||||
let p = Progress::default();
|
||||
let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap();
|
||||
let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap();
|
||||
assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encoding_version() {
|
||||
let mut db = get_temp_state_db();
|
||||
let addr = Address::random();
|
||||
|
||||
let account = BasicAccount {
|
||||
nonce: 50.into(),
|
||||
balance: 123456789.into(),
|
||||
storage_root: KECCAK_NULL_RLP,
|
||||
code_hash: KECCAK_EMPTY,
|
||||
code_version: 1.into(),
|
||||
};
|
||||
|
||||
let thin_rlp = ::rlp::encode(&account);
|
||||
@ -270,6 +314,7 @@ mod tests {
|
||||
balance: 987654321.into(),
|
||||
storage_root: root,
|
||||
code_hash: KECCAK_EMPTY,
|
||||
code_version: 0.into(),
|
||||
}
|
||||
};
|
||||
|
||||
@ -297,6 +342,7 @@ mod tests {
|
||||
balance: 987654321.into(),
|
||||
storage_root: root,
|
||||
code_hash: KECCAK_EMPTY,
|
||||
code_version: 0.into(),
|
||||
}
|
||||
};
|
||||
|
||||
@ -337,6 +383,7 @@ mod tests {
|
||||
balance: 123456789.into(),
|
||||
storage_root: KECCAK_NULL_RLP,
|
||||
code_hash,
|
||||
code_version: 0.into(),
|
||||
};
|
||||
|
||||
let account2 = BasicAccount {
|
||||
@ -344,6 +391,7 @@ mod tests {
|
||||
balance: 98765432123456789usize.into(),
|
||||
storage_root: KECCAK_NULL_RLP,
|
||||
code_hash,
|
||||
code_version: 0.into(),
|
||||
};
|
||||
|
||||
let mut used_code = HashSet::new();
|
||||
|
@ -31,7 +31,7 @@ use rustc_hex::{FromHex, ToHex};
|
||||
use types::BlockNumber;
|
||||
use types::encoded;
|
||||
use types::header::Header;
|
||||
use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType};
|
||||
use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType, VersionedSchedule};
|
||||
|
||||
use builtin::Builtin;
|
||||
use engines::{
|
||||
@ -131,6 +131,9 @@ pub struct CommonParams {
|
||||
pub remove_dust_contracts: bool,
|
||||
/// Wasm activation blocknumber, if any disabled initially.
|
||||
pub wasm_activation_transition: BlockNumber,
|
||||
/// Wasm account version, activated after `wasm_activation_transition`. If this field is defined, do not use code
|
||||
/// prefix to determine VM to execute.
|
||||
pub wasm_version: Option<U256>,
|
||||
/// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated.
|
||||
pub kip4_transition: BlockNumber,
|
||||
/// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated.
|
||||
@ -208,6 +211,9 @@ impl CommonParams {
|
||||
wasm.have_gasleft = true;
|
||||
}
|
||||
schedule.wasm = Some(wasm);
|
||||
if let Some(version) = self.wasm_version {
|
||||
schedule.versions.insert(version, VersionedSchedule::PWasm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,6 +333,7 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
BlockNumber::max_value,
|
||||
Into::into
|
||||
),
|
||||
wasm_version: p.wasm_version.map(Into::into),
|
||||
kip4_transition: p.kip4_transition.map_or_else(
|
||||
BlockNumber::max_value,
|
||||
Into::into
|
||||
@ -665,6 +672,7 @@ impl Spec {
|
||||
let params = ActionParams {
|
||||
code_address: address.clone(),
|
||||
code_hash: Some(keccak(constructor)),
|
||||
code_version: U256::zero(),
|
||||
address: address.clone(),
|
||||
sender: from.clone(),
|
||||
origin: from.clone(),
|
||||
|
@ -497,14 +497,14 @@ impl<B: Backend> State<B> {
|
||||
|
||||
/// Create a new contract at address `contract`. If there is already an account at the address
|
||||
/// it will have its code reset, ready for `init_code()`.
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> {
|
||||
pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> {
|
||||
let original_storage_root = self.original_storage_root(contract)?;
|
||||
let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset);
|
||||
if overflow {
|
||||
return Err(Box::new(TrieError::DecoderError(H256::from(*contract),
|
||||
rlp::DecoderError::Custom("Nonce overflow".into()))));
|
||||
}
|
||||
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root))));
|
||||
self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root))));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -732,6 +732,12 @@ impl<B: Backend> State<B> {
|
||||
|a| a.as_ref().map(|a| a.code_hash()))
|
||||
}
|
||||
|
||||
/// Get an account's code version.
|
||||
pub fn code_version(&self, a: &Address) -> TrieResult<U256> {
|
||||
self.ensure_cached(a, RequireCache::None, true,
|
||||
|a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero()))
|
||||
}
|
||||
|
||||
/// Get accounts' code size.
|
||||
pub fn code_size(&self, a: &Address) -> TrieResult<Option<usize>> {
|
||||
self.ensure_cached(a, RequireCache::CodeSize, true,
|
||||
@ -790,13 +796,13 @@ impl<B: Backend> State<B> {
|
||||
/// Initialise the code of account `a` so that it is `code`.
|
||||
/// NOTE: Account should have been created with `new_contract`.
|
||||
pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
|
||||
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code);
|
||||
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.init_code(code);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the code of account `a` so that it is `code`.
|
||||
pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> {
|
||||
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code);
|
||||
self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1069,11 +1075,11 @@ impl<B: Backend> State<B> {
|
||||
};
|
||||
|
||||
// Storage must be fetched after ensure_cached to avoid borrow problem.
|
||||
(*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()))
|
||||
(*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version())
|
||||
})
|
||||
})?;
|
||||
|
||||
if let Some((balance, nonce, storage_keys, code)) = account {
|
||||
if let Some((balance, nonce, storage_keys, code, version)) = account {
|
||||
let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| {
|
||||
let mut s = s?;
|
||||
|
||||
@ -1082,7 +1088,7 @@ impl<B: Backend> State<B> {
|
||||
})?;
|
||||
|
||||
m.insert(address, PodAccount {
|
||||
balance, nonce, storage, code
|
||||
balance, nonce, storage, code, version
|
||||
});
|
||||
}
|
||||
|
||||
@ -1270,6 +1276,7 @@ impl<B: Backend> State<B> {
|
||||
nonce: self.account_start_nonce,
|
||||
code_hash: KECCAK_EMPTY,
|
||||
storage_root: KECCAK_NULL_RLP,
|
||||
code_version: 0.into(),
|
||||
});
|
||||
|
||||
Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account))
|
||||
@ -2190,7 +2197,7 @@ mod tests {
|
||||
let a = Address::zero();
|
||||
let (root, db) = {
|
||||
let mut state = get_temp_state();
|
||||
state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap();
|
||||
state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap();
|
||||
state.init_code(&a, vec![1, 2, 3]).unwrap();
|
||||
assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3])));
|
||||
state.commit().unwrap();
|
||||
@ -2426,7 +2433,7 @@ mod tests {
|
||||
state.clear();
|
||||
|
||||
let c0 = state.checkpoint();
|
||||
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
|
||||
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
|
||||
let c1 = state.checkpoint();
|
||||
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap();
|
||||
let c2 = state.checkpoint();
|
||||
@ -2486,7 +2493,7 @@ mod tests {
|
||||
|
||||
let cm1 = state.checkpoint();
|
||||
let c0 = state.checkpoint();
|
||||
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
|
||||
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
|
||||
let c1 = state.checkpoint();
|
||||
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap();
|
||||
let c2 = state.checkpoint();
|
||||
@ -2558,7 +2565,7 @@ mod tests {
|
||||
let a = Address::from_low_u64_be(1000);
|
||||
|
||||
state.checkpoint(); // c1
|
||||
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
|
||||
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
|
||||
state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap();
|
||||
state.checkpoint(); // c2
|
||||
state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap();
|
||||
@ -2585,7 +2592,7 @@ mod tests {
|
||||
state.clear();
|
||||
|
||||
state.checkpoint(); // c1
|
||||
state.new_contract(&a, U256::zero(), U256::zero()).unwrap();
|
||||
state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap();
|
||||
state.checkpoint(); // c2
|
||||
state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap();
|
||||
state.revert_to_checkpoint(); // revert to c2
|
||||
@ -2633,7 +2640,7 @@ mod tests {
|
||||
state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account
|
||||
state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account
|
||||
state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account
|
||||
state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account
|
||||
state.new_contract(&e, 100.into(), 1.into(), 0.into()).unwrap(); // create a contract account
|
||||
state.init_code(&e, vec![0x00]).unwrap();
|
||||
state.commit().unwrap();
|
||||
state.drop()
|
||||
@ -2685,7 +2692,8 @@ mod tests {
|
||||
balance: U256::from(100),
|
||||
nonce: U256::zero(),
|
||||
code: Some(Default::default()),
|
||||
storage: Default::default()
|
||||
storage: Default::default(),
|
||||
version: U256::zero(),
|
||||
}), None).as_ref());
|
||||
}
|
||||
|
||||
@ -2718,12 +2726,14 @@ mod tests {
|
||||
code: Some(Default::default()),
|
||||
storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))]
|
||||
.into_iter().collect(),
|
||||
version: U256::zero(),
|
||||
}), Some(&PodAccount {
|
||||
balance: U256::zero(),
|
||||
nonce: U256::zero(),
|
||||
code: Some(Default::default()),
|
||||
storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))]
|
||||
.into_iter().collect(),
|
||||
version: U256::zero(),
|
||||
})).as_ref());
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -71,6 +71,8 @@ pub struct Account {
|
||||
code_size: Option<usize>,
|
||||
// Code cache of the account.
|
||||
code_cache: Arc<Bytes>,
|
||||
// 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<BasicAccount> 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<BasicAccount> for Account {
|
||||
impl Account {
|
||||
#[cfg(test)]
|
||||
/// 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 {
|
||||
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());
|
||||
|
@ -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<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)?
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ pub struct ActionParams {
|
||||
pub value: ActionValue,
|
||||
/// Code being executed.
|
||||
pub code: Option<Arc<Bytes>>,
|
||||
/// Code version being executed.
|
||||
pub code_version: U256,
|
||||
/// Input data.
|
||||
pub data: Option<Bytes>,
|
||||
/// 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<ethjson::vm::Transaction> 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(),
|
||||
|
@ -97,6 +97,7 @@ pub trait Ext {
|
||||
gas: &U256,
|
||||
value: &U256,
|
||||
code: &[u8],
|
||||
parent_version: &U256,
|
||||
address: CreateContractAddress,
|
||||
trap: bool,
|
||||
) -> ::std::result::Result<ContractCreateResult, TrapKind>;
|
||||
|
@ -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};
|
||||
|
@ -15,6 +15,13 @@
|
||||
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<U256, VersionedSchedule>,
|
||||
/// Wasm extra schedule settings, if wasm activated
|
||||
pub wasm: Option<WasmCosts>,
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ impl Ext for FakeExt {
|
||||
gas: &U256,
|
||||
value: &U256,
|
||||
code: &[u8],
|
||||
_parent_version: &U256,
|
||||
address: CreateContractAddress,
|
||||
_trap: bool,
|
||||
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
|
||||
|
@ -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(),
|
||||
},
|
||||
);
|
||||
|
@ -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 -
|
||||
|
@ -31,6 +31,9 @@ pub struct Account {
|
||||
pub nonce: Uint,
|
||||
/// Storage.
|
||||
pub storage: BTreeMap<Uint, Uint>,
|
||||
/// Version.
|
||||
#[serde(default)]
|
||||
pub version: Uint,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -33,6 +33,8 @@ pub struct Account {
|
||||
pub nonce: Option<Uint>,
|
||||
/// Code.
|
||||
pub code: Option<Bytes>,
|
||||
/// Version.
|
||||
pub version: Option<Uint>,
|
||||
/// Storage.
|
||||
pub storage: Option<BTreeMap<Uint, Uint>>,
|
||||
/// Constructor.
|
||||
|
@ -118,8 +118,10 @@ pub struct Params {
|
||||
pub transaction_permission_contract: Option<Address>,
|
||||
/// Block at which the transaction permission contract should start being used.
|
||||
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>,
|
||||
/// Define a separate wasm version instead of using the prefix.
|
||||
pub wasm_version: Option<Uint>,
|
||||
/// KIP4 activiation block height.
|
||||
pub kip4_transition: Option<Uint>,
|
||||
/// KIP6 activiation block height.
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user