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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user