Skip to content

Commit

Permalink
feat: support transient storage opcodes (EIP-1153) (#278)
Browse files Browse the repository at this point in the history
* feat: support transient storage opcodes (EIP-1153)

* test: fix UnimplementedHandler

* move eip2929 fix to a different PR

* revert mutability in argument

* adds flag for enabling eip-1153

* use gas_storage_read_warm for tload and tstore
  • Loading branch information
RomarQ committed Jun 4, 2024
1 parent 699fd9a commit 63b19f7
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 2 deletions.
3 changes: 3 additions & 0 deletions interpreter/src/etable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@ where

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 @@ -674,6 +674,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)
}

macro_rules! eval_log {
($($num:expr),*) => {
$(paste::paste! {
Expand Down
22 changes: 22 additions & 0 deletions interpreter/src/eval/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,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
5 changes: 5 additions & 0 deletions interpreter/src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ impl Opcode {
/// `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 @@ -115,6 +115,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 @@ -140,6 +142,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
11 changes: 11 additions & 0 deletions interpreter/tests/usability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ impl RuntimeBaseBackend for UnimplementedHandler {
fn storage(&self, _address: H160, _index: H256) -> H256 {
unimplemented!()
}
fn transient_storage(&self, _address: H160, _index: H256) -> H256 {
unimplemented!()
}

fn exists(&self, _address: H160) -> bool {
unimplemented!()
Expand Down Expand Up @@ -143,6 +146,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 @@ -25,6 +25,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 @@ -66,6 +67,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 @@ -146,6 +157,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 @@ -144,6 +144,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 @@ -21,6 +21,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 @@ -49,6 +50,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 @@ -118,6 +120,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 @@ -157,6 +167,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 @@ -243,6 +265,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 @@ -260,6 +287,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 @@ -273,6 +301,7 @@ impl Substate {
nonces: Default::default(),
storage_resets: Default::default(),
storages: Default::default(),
transient_storage: Default::default(),
deletes: Default::default(),
}
}
Expand Down Expand Up @@ -319,6 +348,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
11 changes: 11 additions & 0 deletions src/standard/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ pub struct Config {
pub has_base_fee: bool,
/// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md)
pub has_push0: bool,
/// Enables transient storage. See [EIP-1153](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1153.md)
pub eip_1153_enabled: bool,
}

impl Config {
Expand Down Expand Up @@ -150,6 +152,7 @@ impl Config {
has_ext_code_hash: false,
has_base_fee: false,
has_push0: false,
eip_1153_enabled: false,
}
}

Expand Down Expand Up @@ -203,6 +206,7 @@ impl Config {
has_ext_code_hash: true,
has_base_fee: false,
has_push0: false,
eip_1153_enabled: false,
}
}

Expand Down Expand Up @@ -237,6 +241,7 @@ impl Config {
disallow_executable_format,
warm_coinbase_address,
max_initcode_size,
eip_1153_enabled,
} = inputs;

// See https://eips.ethereum.org/EIPS/eip-2929
Expand Down Expand Up @@ -299,6 +304,7 @@ impl Config {
has_ext_code_hash: true,
has_base_fee,
has_push0,
eip_1153_enabled,
}
}
}
Expand All @@ -315,6 +321,7 @@ struct DerivedConfigInputs {
disallow_executable_format: bool,
warm_coinbase_address: bool,
max_initcode_size: Option<usize>,
eip_1153_enabled: bool,
}

impl DerivedConfigInputs {
Expand All @@ -329,6 +336,7 @@ impl DerivedConfigInputs {
disallow_executable_format: false,
warm_coinbase_address: false,
max_initcode_size: None,
eip_1153_enabled: false,
}
}

Expand All @@ -343,6 +351,7 @@ impl DerivedConfigInputs {
disallow_executable_format: true,
warm_coinbase_address: false,
max_initcode_size: None,
eip_1153_enabled: false,
}
}

Expand All @@ -357,6 +366,7 @@ impl DerivedConfigInputs {
disallow_executable_format: true,
warm_coinbase_address: false,
max_initcode_size: None,
eip_1153_enabled: false,
}
}

Expand All @@ -372,6 +382,7 @@ impl DerivedConfigInputs {
warm_coinbase_address: true,
// 2 * 24576 as per EIP-3860
max_initcode_size: Some(0xC000),
eip_1153_enabled: false,
}
}
}
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_storage_read_warm)
}

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

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 63b19f7

Please sign in to comment.