Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
draft transient support in bus_mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
zemse committed Apr 2, 2024
1 parent 5542cb6 commit bc7ae85
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 5 deletions.
3 changes: 2 additions & 1 deletion bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod sstore;
mod stackonlyop;
mod stop;
mod swap;
mod tload;

mod error_code_store;
mod error_invalid_creation_code;
Expand Down Expand Up @@ -110,7 +111,7 @@ use stackonlyop::StackOnlyOpcode;
use stop::Stop;
use swap::Swap;

#[cfg(feature = "test")]
#[cfg(any(feature = "test", test))]
pub use crate::precompile::PrecompileCallArgs;

/// Generic opcode trait which defines the logic of the
Expand Down
199 changes: 199 additions & 0 deletions bus-mapping/src/evm/opcodes/tload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use super::Opcode;
use crate::{
circuit_input_builder::{CircuitInputStateRef, ExecStep},
operation::{CallContextField, StorageOp, TxAccessListAccountStorageOp, RW},
Error,
};
use eth_types::{GethExecStep, ToWord, Word};

/// Placeholder structure used to implement [`Opcode`] trait over it
/// corresponding to the [`OpcodeId::TLOAD`](crate::evm::OpcodeId::TLOAD)
/// `OpcodeId`.
#[derive(Debug, Copy, Clone)]
pub(crate) struct Tload;

impl Opcode for Tload {
fn gen_associated_ops(
state: &mut CircuitInputStateRef,
geth_steps: &[GethExecStep],
) -> Result<Vec<ExecStep>, Error> {
let geth_step = &geth_steps[0];
let mut exec_step = state.new_step(geth_step)?;

let call_id = state.call()?.call_id;
let contract_addr = state.call()?.address;

state.call_context_read(
&mut exec_step,
call_id,
CallContextField::TxId,
Word::from(state.tx_ctx.id()),
)?;

state.call_context_read(
&mut exec_step,
call_id,
CallContextField::RwCounterEndOfReversion,
Word::from(state.call()?.rw_counter_end_of_reversion),
)?;

state.call_context_read(
&mut exec_step,
call_id,
CallContextField::IsPersistent,
Word::from(state.call()?.is_persistent as u8),
)?;

state.call_context_read(
&mut exec_step,
call_id,
CallContextField::CalleeAddress,
contract_addr.to_word(),
)?;

// First stack read
let key = geth_step.stack.last()?;
let stack_position = geth_step.stack.last_filled();

// Manage first stack read at latest stack position
state.stack_read(&mut exec_step, stack_position, key)?;

// Storage read
let value = geth_step.storage.get_or_err(&key)?;

let is_warm = state
.sdb
.check_account_storage_in_access_list(&(contract_addr, key));

let (_, committed_value) = state.sdb.get_committed_storage(&contract_addr, &key);
let committed_value = *committed_value;
state.push_op(
&mut exec_step,
RW::READ,
StorageOp::new(
contract_addr,
key,
value,
value,
state.tx_ctx.id(),
committed_value,
),
)?;

// First stack write
state.stack_write(&mut exec_step, stack_position, value)?;
state.push_op(
&mut exec_step,
RW::READ,
TxAccessListAccountStorageOp {
tx_id: state.tx_ctx.id(),
address: contract_addr,
key,
is_warm,
is_warm_prev: is_warm,
},
)?;
state.push_op_reversible(
&mut exec_step,
TxAccessListAccountStorageOp {
tx_id: state.tx_ctx.id(),
address: contract_addr,
key,
is_warm: true,
is_warm_prev: is_warm,
},
)?;

Ok(vec![exec_step])
}
}

#[cfg(test)]
mod tload_tests {
use super::*;
use crate::{
circuit_input_builder::ExecState,
mock::BlockData,
operation::{StackOp, TransientStorageOp},
};
use eth_types::{
bytecode,
evm_types::{OpcodeId, StackAddress},
geth_types::GethData,
Word,
};
use mock::{
test_ctx::{helpers::*, TestContext},
MOCK_ACCOUNTS,
};
use pretty_assertions::assert_eq;

fn test_ok() {
let code = bytecode! {
// Load transient storage slot 0
PUSH1(0x00u64)
TLOAD
STOP
};
let expected_loaded_value = 0;

// Get the execution steps from the external tracer
let block: GethData = TestContext::<2, 1>::new(
None,
account_0_code_account_1_no_code(code),
tx_from_1_to_0,
|block, _tx| block.number(0xcafeu64),
)
.unwrap()
.into();

let builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder();
let builder = builder
.handle_block(&block.eth_block, &block.geth_traces)
.unwrap();

println!("{:#?}", builder.block.txs()[0].steps());
let step = builder.block.txs()[0]
.steps()
.iter()
.find(|step| step.exec_state == ExecState::Op(OpcodeId::TLOAD))
.unwrap();

assert_eq!(
[4, 6]
.map(|idx| &builder.block.container.stack[step.bus_mapping_instance[idx].as_usize()])
.map(|operation| (operation.rw(), operation.op())),
[
(
RW::READ,
&StackOp::new(1, StackAddress::from(1023), Word::from(0x0u32))
),
(
RW::WRITE,
&StackOp::new(1, StackAddress::from(1023), Word::from(expected_loaded_value))
)
]
);

let transient_storage_op =
&builder.block.container.transient_storage[step.bus_mapping_instance[5].as_usize()];
assert_eq!(
(transient_storage_op.rw(), transient_storage_op.op()),
(
RW::READ,
&TransientStorageOp::new(
MOCK_ACCOUNTS[0],
Word::from(0x0u32),
Word::from(expected_loaded_value),
Word::from(expected_loaded_value),
1,
)
)
);
}

#[test]
fn tload_opcode_impl_warm() {
test_ok()
}
}
1 change: 1 addition & 0 deletions bus-mapping/src/exec_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ impl fmt::Debug for OperationRef {
Target::Memory => "Memory",
Target::Stack => "Stack",
Target::Storage => "Storage",
Target::TransientStorage => "TransientStorage",
Target::TxAccessListAccount => "TxAccessListAccount",
Target::TxAccessListAccountStorage => "TxAccessListAccountStorage",
Target::TxRefund => "TxRefund",
Expand Down
101 changes: 101 additions & 0 deletions bus-mapping/src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ pub enum Target {
Stack,
/// Means the target of the operation is the Storage.
Storage,
/// Means the target of the operation is the TransientStorage.
TransientStorage,
/// Means the target of the operation is the TxAccessListAccount.
TxAccessListAccount,
/// Means the target of the operation is the TxAccessListAccountStorage.
Expand Down Expand Up @@ -410,6 +412,103 @@ impl Ord for StorageOp {
}
}

/// Represents a [`READ`](RW::READ)/[`WRITE`](RW::WRITE) into the transient storage
/// implied by an specific
/// [`OpcodeId`](eth_types::evm_types::opcode_ids::OpcodeId) of
/// the [`ExecStep`](crate::circuit_input_builder::ExecStep).
#[derive(Clone, PartialEq, Eq)]
pub struct TransientStorageOp {
/// Account Address
pub address: Address,
/// Transient Storage Key
pub key: Word,
/// Transient Storage Value after the operation
pub value: Word,
/// Transient Storage Value before the operation
pub value_prev: Word,
/// Transaction ID: Transaction index in the block starting at 1.
pub tx_id: usize,
}

impl fmt::Debug for TransientStorageOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("TransientStorageOp { ")?;
f.write_fmt(format_args!(
"tx_id: {:?}, addr: {:?}, key: {:?}, val_prev: 0x{:x}, val: 0x{:x}",
self.tx_id, self.address, self.key, self.value_prev, self.value
))?;
f.write_str(" }")
}
}

impl TransientStorageOp {
/// Create a new instance of a `TransientStorageOp` from it's components.
pub const fn new(
address: Address,
key: Word,
value: Word,
value_prev: Word,
tx_id: usize,
) -> TransientStorageOp {
TransientStorageOp {
address,
key,
value,
value_prev,
tx_id,
}
}

/// Returns the [`Target`] (operation type) of this operation.
pub const fn target(&self) -> Target {
Target::TransientStorage
}

/// Returns the [`Address`] corresponding to this transient storage operation.
pub const fn address(&self) -> &Address {
&self.address
}

/// Returns the [`Word`] used as key for this operation.
pub const fn key(&self) -> &Word {
&self.key
}

/// Returns the [`Word`] read or written by this operation.
pub const fn value(&self) -> &Word {
&self.value
}

/// Returns the [`Word`] at key found previous to this operation.
pub const fn value_prev(&self) -> &Word {
&self.value_prev
}
}

impl Op for TransientStorageOp {
fn into_enum(self) -> OpEnum {
OpEnum::TransientStorage(self)
}

fn reverse(&self) -> Self {
let mut rev = self.clone();
swap(&mut rev.value, &mut rev.value_prev);
rev
}
}

impl PartialOrd for TransientStorageOp {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for TransientStorageOp {
fn cmp(&self, other: &Self) -> Ordering {
(&self.address, &self.key).cmp(&(&other.address, &other.key))
}
}

/// Represents a change in the Account AccessList implied by a `BeginTx`,
/// `EXTCODECOPY`, `EXTCODESIZE`, `EXTCODEHASH` `BALANCE`, `SELFDESTRUCT`,
/// `*CALL`* or `CREATE*` step.
Expand Down Expand Up @@ -1055,6 +1154,8 @@ pub enum OpEnum {
Memory(MemoryOp),
/// Storage
Storage(StorageOp),
/// TransientStorage
TransientStorage(TransientStorageOp),
/// TxAccessListAccount
TxAccessListAccount(TxAccessListAccountOp),
/// TxAccessListAccountStorage
Expand Down
12 changes: 10 additions & 2 deletions bus-mapping/src/operation/container.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{
AccountOp, CallContextOp, MemoryOp, Op, OpEnum, Operation, PaddingOp, RWCounter, StackOp,
StartOp, StepStateOp, StorageOp, Target, TxAccessListAccountOp, TxAccessListAccountStorageOp,
TxLogOp, TxReceiptOp, TxRefundOp, RW,
StartOp, StepStateOp, StorageOp, Target, TransientStorageOp, TxAccessListAccountOp,
TxAccessListAccountStorageOp, TxLogOp, TxReceiptOp, TxRefundOp, RW,
};
use crate::exec_trace::OperationRef;
use itertools::Itertools;
Expand All @@ -28,6 +28,8 @@ pub struct OperationContainer {
pub stack: Vec<Operation<StackOp>>,
/// Operations of StorageOp
pub storage: Vec<Operation<StorageOp>>,
/// Operations of TransientStorageOp
pub transient_storage: Vec<Operation<TransientStorageOp>>, // TODO change
/// Operations of TxAccessListAccountOp
pub tx_access_list_account: Vec<Operation<TxAccessListAccountOp>>,
/// Operations of TxAccessListAccountStorageOp
Expand Down Expand Up @@ -64,6 +66,7 @@ impl OperationContainer {
memory: Vec::new(),
stack: Vec::new(),
storage: Vec::new(),
transient_storage: Vec::new(),
tx_access_list_account: Vec::new(),
tx_access_list_account_storage: Vec::new(),
tx_refund: Vec::new(),
Expand Down Expand Up @@ -120,6 +123,11 @@ impl OperationContainer {
});
OperationRef::from((Target::Storage, self.storage.len() - 1))
}
OpEnum::TransientStorage(op) => {
self.transient_storage
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
OperationRef::from((Target::TransientStorage, self.transient_storage.len() - 1))
}
OpEnum::TxAccessListAccount(op) => {
self.tx_access_list_account.push(if reversible {
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)
Expand Down
Loading

0 comments on commit bc7ae85

Please sign in to comment.