Skip to content

Commit

Permalink
feat: support transient storage opcodes (EIP-1153)
Browse files Browse the repository at this point in the history
  • Loading branch information
RomarQ committed May 1, 2024
1 parent f7a23df commit ee15955
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 28 deletions.
2 changes: 2 additions & 0 deletions interpreter/src/etable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ where
table.0[Opcode::SLOAD.as_usize()] = eval_sload as _;
table.0[Opcode::SSTORE.as_usize()] = eval_sstore as _;
table.0[Opcode::GAS.as_usize()] = eval_gas as _;
table.0[Opcode::TLOAD.as_usize()] = eval_tload as _;
table.0[Opcode::TSTORE.as_usize()] = eval_tstore as _;
table.0[Opcode::LOG0.as_usize()] = eval_log0 as _;
table.0[Opcode::LOG1.as_usize()] = eval_log1 as _;
table.0[Opcode::LOG2.as_usize()] = eval_log2 as _;
Expand Down
18 changes: 18 additions & 0 deletions interpreter/src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,24 @@ pub fn eval_gas<S: GasState, H: RuntimeEnvironment + RuntimeBackend, Tr>(
self::system::gas(machine, handle)
}

pub fn eval_tload<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>(
machine: &mut Machine<S>,
handle: &mut H,
_opcode: Opcode,
_position: usize,
) -> Control<Tr> {
self::system::tload(machine, handle)
}

pub fn eval_tstore<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>(
machine: &mut Machine<S>,
handle: &mut H,
_opcode: Opcode,
_position: usize,
) -> Control<Tr> {
self::system::tstore(machine, handle)
}

pub fn eval_log0<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>(
machine: &mut Machine<S>,
handle: &mut H,
Expand Down
29 changes: 22 additions & 7 deletions interpreter/src/eval/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ pub fn balance<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, T
handler: &mut H,
) -> Control<Tr> {
pop!(machine, address);
handler.mark_hot(address.into(), None);
push_u256!(machine, handler.balance(address.into()));

Control::Continue
Expand Down Expand Up @@ -127,7 +126,6 @@ pub fn extcodesize<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBacken
handler: &mut H,
) -> Control<Tr> {
pop!(machine, address);
handler.mark_hot(address.into(), None);
let code_size = handler.code_size(address.into());
push_u256!(machine, code_size);

Expand All @@ -139,7 +137,6 @@ pub fn extcodehash<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBacken
handler: &mut H,
) -> Control<Tr> {
pop!(machine, address);
handler.mark_hot(address.into(), None);
let code_hash = handler.code_hash(address.into());
push!(machine, code_hash);

Expand All @@ -152,8 +149,6 @@ pub fn extcodecopy<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBacken
) -> Control<Tr> {
pop!(machine, address);
pop_u256!(machine, memory_offset, code_offset, len);

handler.mark_hot(address.into(), None);
try_or_fail!(machine.memory.resize_offset(memory_offset, len));

let code = handler.code(address.into());
Expand Down Expand Up @@ -263,7 +258,6 @@ pub fn sload<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>
handler: &mut H,
) -> Control<Tr> {
pop!(machine, index);
handler.mark_hot(machine.state.as_ref().context.address, Some(index));
let value = handler.storage(machine.state.as_ref().context.address, index);
push!(machine, value);

Expand All @@ -275,7 +269,6 @@ pub fn sstore<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr
handler: &mut H,
) -> Control<Tr> {
pop!(machine, index, value);
handler.mark_hot(machine.state.as_ref().context.address, Some(index));

match handler.set_storage(machine.state.as_ref().context.address, index, value) {
Ok(()) => Control::Continue,
Expand All @@ -292,6 +285,28 @@ pub fn gas<S: GasState, H: RuntimeEnvironment + RuntimeBackend, Tr>(
Control::Continue
}

pub fn tload<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>(
machine: &mut Machine<S>,
handler: &mut H,
) -> Control<Tr> {
pop!(machine, index);
let value = handler.transient_storage(machine.state.as_ref().context.address, index);
push!(machine, value);

Control::Continue
}

pub fn tstore<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>(
machine: &mut Machine<S>,
handler: &mut H,
) -> Control<Tr> {
pop!(machine, index, value);
match handler.set_transient_storage(machine.state.as_ref().context.address, index, value) {
Ok(()) => Control::Continue,
Err(e) => Control::Exit(e.into()),
}
}

pub fn log<S: AsRef<RuntimeState>, H: RuntimeEnvironment + RuntimeBackend, Tr>(
machine: &mut Machine<S>,
n: u8,
Expand Down
4 changes: 4 additions & 0 deletions interpreter/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ impl Opcode {
pub const SSTORE: Opcode = Opcode(0x55);
/// `GAS`
pub const GAS: Opcode = Opcode(0x5a);
/// `TLOAD`
pub const TLOAD: Opcode = Opcode(0x5c);
/// `TSTORE`
pub const TSTORE: Opcode = Opcode(0x5d);
/// `LOGn`
pub const LOG0: Opcode = Opcode(0xa0);
pub const LOG1: Opcode = Opcode(0xa1);
Expand Down
9 changes: 9 additions & 0 deletions interpreter/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ pub trait RuntimeBaseBackend {
fn code(&self, address: H160) -> Vec<u8>;
/// Get storage value of address at index.
fn storage(&self, address: H160, index: H256) -> H256;
/// Get transient storage value of address at index.
fn transient_storage(&self, address: H160, index: H256) -> H256;

/// Check whether an address exists.
fn exists(&self, address: H160) -> bool;
Expand All @@ -138,6 +140,13 @@ pub trait RuntimeBackend: RuntimeBaseBackend {
fn mark_hot(&mut self, address: H160, index: Option<H256>);
/// Set storage value of address at index.
fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError>;
/// Set transient storage value of address at index, transient storage gets discarded after every transaction. (see EIP-1153)
fn set_transient_storage(
&mut self,
address: H160,
index: H256,
value: H256,
) -> Result<(), ExitError>;
/// Create a log owned by address with given topics and data.
fn log(&mut self, log: Log) -> Result<(), ExitError>;
/// Mark an address to be deleted.
Expand Down
8 changes: 8 additions & 0 deletions interpreter/tests/usability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ impl RuntimeBackend for UnimplementedHandler {
fn set_storage(&mut self, _address: H160, _index: H256, _value: H256) -> Result<(), ExitError> {
unimplemented!()
}
fn set_transient_storage(
&mut self,
_address: H160,
_index: H256,
_value: H256,
) -> Result<(), ExitError> {
unimplemented!()
}
fn log(&mut self, _log: Log) -> Result<(), ExitError> {
unimplemented!()
}
Expand Down
22 changes: 22 additions & 0 deletions jsontests/src/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct InMemoryAccount {
pub code: Vec<u8>,
pub nonce: U256,
pub storage: BTreeMap<H256, H256>,
pub transient_storage: BTreeMap<H256, H256>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -62,6 +63,16 @@ impl InMemoryBackend {
}
}

for ((address, key), value) in changeset.transient_storage.clone() {
let account = self.state.entry(address).or_default();

if value == H256::default() {
account.transient_storage.remove(&key);
} else {
account.transient_storage.insert(key, value);
}
}

for address in changeset.deletes.clone() {
self.state.remove(&address);
}
Expand Down Expand Up @@ -142,6 +153,17 @@ impl RuntimeBaseBackend for InMemoryBackend {
.unwrap_or(H256::default())
}

fn transient_storage(&self, address: H160, index: H256) -> H256 {
self.state
.get(&address)
.cloned()
.unwrap_or(Default::default())
.transient_storage
.get(&index)
.cloned()
.unwrap_or(H256::default())
}

fn nonce(&self, address: H160) -> U256 {
self.state
.get(&address)
Expand Down
1 change: 1 addition & 0 deletions jsontests/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub fn run_test(
code: account.code.0,
nonce: account.nonce,
storage,
transient_storage: Default::default(),
},
)
})
Expand Down
39 changes: 39 additions & 0 deletions src/backend/overlayed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct OverlayedChangeSet {
pub nonces: BTreeMap<H160, U256>,
pub storage_resets: BTreeSet<H160>,
pub storages: BTreeMap<(H160, H256), H256>,
pub transient_storage: BTreeMap<(H160, H256), H256>,
pub deletes: BTreeSet<H160>,
}

Expand Down Expand Up @@ -46,6 +47,7 @@ impl<B> OverlayedBackend<B> {
nonces: self.substate.nonces,
storage_resets: self.substate.storage_resets,
storages: self.substate.storages,
transient_storage: self.substate.transient_storage,
deletes: self.substate.deletes,
},
)
Expand Down Expand Up @@ -115,6 +117,14 @@ impl<B: RuntimeBaseBackend> RuntimeBaseBackend for OverlayedBackend<B> {
}
}

fn transient_storage(&self, address: H160, index: H256) -> H256 {
if let Some(value) = self.substate.known_transient_storage(address, index) {
value
} else {
self.backend.transient_storage(address, index)
}
}

fn exists(&self, address: H160) -> bool {
if let Some(exists) = self.substate.known_exists(address) {
exists
Expand Down Expand Up @@ -154,6 +164,18 @@ impl<B: RuntimeBaseBackend> RuntimeBackend for OverlayedBackend<B> {
Ok(())
}

fn set_transient_storage(
&mut self,
address: H160,
index: H256,
value: H256,
) -> Result<(), ExitError> {
self.substate
.transient_storage
.insert((address, index), value);
Ok(())
}

fn log(&mut self, log: Log) -> Result<(), ExitError> {
self.substate.logs.push(log);
Ok(())
Expand Down Expand Up @@ -240,6 +262,11 @@ impl<B: RuntimeBaseBackend> TransactionalBackend for OverlayedBackend<B> {
for ((address, key), value) in child.storages {
self.substate.storages.insert((address, key), value);
}
for ((address, key), value) in child.transient_storage {
self.substate
.transient_storage
.insert((address, key), value);
}
for address in child.deletes {
self.substate.deletes.insert(address);
}
Expand All @@ -257,6 +284,7 @@ struct Substate {
nonces: BTreeMap<H160, U256>,
storage_resets: BTreeSet<H160>,
storages: BTreeMap<(H160, H256), H256>,
transient_storage: BTreeMap<(H160, H256), H256>,
deletes: BTreeSet<H160>,
}

Expand All @@ -270,6 +298,7 @@ impl Substate {
nonces: Default::default(),
storage_resets: Default::default(),
storages: Default::default(),
transient_storage: Default::default(),
deletes: Default::default(),
}
}
Expand Down Expand Up @@ -316,6 +345,16 @@ impl Substate {
}
}

pub fn known_transient_storage(&self, address: H160, key: H256) -> Option<H256> {
if let Some(value) = self.transient_storage.get(&(address, key)) {
Some(*value)
} else if let Some(parent) = self.parent.as_ref() {
parent.known_transient_storage(address, key)
} else {
None
}
}

pub fn known_exists(&self, address: H160) -> Option<bool> {
if self.balances.contains_key(&address)
|| self.nonces.contains_key(&address)
Expand Down
7 changes: 7 additions & 0 deletions src/standard/gasometer/costs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ pub fn sstore_cost(
},
)
}
pub fn tload_cost(config: &Config) -> Result<u64, ExitException> {
Ok(config.gas_sload)
}

pub fn tstore_cost(config: &Config) -> Result<u64, ExitException> {
Ok(config.gas_sload)
}

pub fn suicide_cost(value: U256, is_cold: bool, target_exists: bool, config: &Config) -> u64 {
let eip161 = !config.empty_considered_exists;
Expand Down
Loading

0 comments on commit ee15955

Please sign in to comment.