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

Commit bc7ae85

Browse files
committed
draft transient support in bus_mappings
1 parent 5542cb6 commit bc7ae85

File tree

6 files changed

+329
-5
lines changed

6 files changed

+329
-5
lines changed

bus-mapping/src/evm/opcodes.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod sstore;
4444
mod stackonlyop;
4545
mod stop;
4646
mod swap;
47+
mod tload;
4748

4849
mod error_code_store;
4950
mod error_invalid_creation_code;
@@ -110,7 +111,7 @@ use stackonlyop::StackOnlyOpcode;
110111
use stop::Stop;
111112
use swap::Swap;
112113

113-
#[cfg(feature = "test")]
114+
#[cfg(any(feature = "test", test))]
114115
pub use crate::precompile::PrecompileCallArgs;
115116

116117
/// Generic opcode trait which defines the logic of the

bus-mapping/src/evm/opcodes/tload.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
use super::Opcode;
2+
use crate::{
3+
circuit_input_builder::{CircuitInputStateRef, ExecStep},
4+
operation::{CallContextField, StorageOp, TxAccessListAccountStorageOp, RW},
5+
Error,
6+
};
7+
use eth_types::{GethExecStep, ToWord, Word};
8+
9+
/// Placeholder structure used to implement [`Opcode`] trait over it
10+
/// corresponding to the [`OpcodeId::TLOAD`](crate::evm::OpcodeId::TLOAD)
11+
/// `OpcodeId`.
12+
#[derive(Debug, Copy, Clone)]
13+
pub(crate) struct Tload;
14+
15+
impl Opcode for Tload {
16+
fn gen_associated_ops(
17+
state: &mut CircuitInputStateRef,
18+
geth_steps: &[GethExecStep],
19+
) -> Result<Vec<ExecStep>, Error> {
20+
let geth_step = &geth_steps[0];
21+
let mut exec_step = state.new_step(geth_step)?;
22+
23+
let call_id = state.call()?.call_id;
24+
let contract_addr = state.call()?.address;
25+
26+
state.call_context_read(
27+
&mut exec_step,
28+
call_id,
29+
CallContextField::TxId,
30+
Word::from(state.tx_ctx.id()),
31+
)?;
32+
33+
state.call_context_read(
34+
&mut exec_step,
35+
call_id,
36+
CallContextField::RwCounterEndOfReversion,
37+
Word::from(state.call()?.rw_counter_end_of_reversion),
38+
)?;
39+
40+
state.call_context_read(
41+
&mut exec_step,
42+
call_id,
43+
CallContextField::IsPersistent,
44+
Word::from(state.call()?.is_persistent as u8),
45+
)?;
46+
47+
state.call_context_read(
48+
&mut exec_step,
49+
call_id,
50+
CallContextField::CalleeAddress,
51+
contract_addr.to_word(),
52+
)?;
53+
54+
// First stack read
55+
let key = geth_step.stack.last()?;
56+
let stack_position = geth_step.stack.last_filled();
57+
58+
// Manage first stack read at latest stack position
59+
state.stack_read(&mut exec_step, stack_position, key)?;
60+
61+
// Storage read
62+
let value = geth_step.storage.get_or_err(&key)?;
63+
64+
let is_warm = state
65+
.sdb
66+
.check_account_storage_in_access_list(&(contract_addr, key));
67+
68+
let (_, committed_value) = state.sdb.get_committed_storage(&contract_addr, &key);
69+
let committed_value = *committed_value;
70+
state.push_op(
71+
&mut exec_step,
72+
RW::READ,
73+
StorageOp::new(
74+
contract_addr,
75+
key,
76+
value,
77+
value,
78+
state.tx_ctx.id(),
79+
committed_value,
80+
),
81+
)?;
82+
83+
// First stack write
84+
state.stack_write(&mut exec_step, stack_position, value)?;
85+
state.push_op(
86+
&mut exec_step,
87+
RW::READ,
88+
TxAccessListAccountStorageOp {
89+
tx_id: state.tx_ctx.id(),
90+
address: contract_addr,
91+
key,
92+
is_warm,
93+
is_warm_prev: is_warm,
94+
},
95+
)?;
96+
state.push_op_reversible(
97+
&mut exec_step,
98+
TxAccessListAccountStorageOp {
99+
tx_id: state.tx_ctx.id(),
100+
address: contract_addr,
101+
key,
102+
is_warm: true,
103+
is_warm_prev: is_warm,
104+
},
105+
)?;
106+
107+
Ok(vec![exec_step])
108+
}
109+
}
110+
111+
#[cfg(test)]
112+
mod tload_tests {
113+
use super::*;
114+
use crate::{
115+
circuit_input_builder::ExecState,
116+
mock::BlockData,
117+
operation::{StackOp, TransientStorageOp},
118+
};
119+
use eth_types::{
120+
bytecode,
121+
evm_types::{OpcodeId, StackAddress},
122+
geth_types::GethData,
123+
Word,
124+
};
125+
use mock::{
126+
test_ctx::{helpers::*, TestContext},
127+
MOCK_ACCOUNTS,
128+
};
129+
use pretty_assertions::assert_eq;
130+
131+
fn test_ok() {
132+
let code = bytecode! {
133+
// Load transient storage slot 0
134+
PUSH1(0x00u64)
135+
TLOAD
136+
STOP
137+
};
138+
let expected_loaded_value = 0;
139+
140+
// Get the execution steps from the external tracer
141+
let block: GethData = TestContext::<2, 1>::new(
142+
None,
143+
account_0_code_account_1_no_code(code),
144+
tx_from_1_to_0,
145+
|block, _tx| block.number(0xcafeu64),
146+
)
147+
.unwrap()
148+
.into();
149+
150+
let builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder();
151+
let builder = builder
152+
.handle_block(&block.eth_block, &block.geth_traces)
153+
.unwrap();
154+
155+
println!("{:#?}", builder.block.txs()[0].steps());
156+
let step = builder.block.txs()[0]
157+
.steps()
158+
.iter()
159+
.find(|step| step.exec_state == ExecState::Op(OpcodeId::TLOAD))
160+
.unwrap();
161+
162+
assert_eq!(
163+
[4, 6]
164+
.map(|idx| &builder.block.container.stack[step.bus_mapping_instance[idx].as_usize()])
165+
.map(|operation| (operation.rw(), operation.op())),
166+
[
167+
(
168+
RW::READ,
169+
&StackOp::new(1, StackAddress::from(1023), Word::from(0x0u32))
170+
),
171+
(
172+
RW::WRITE,
173+
&StackOp::new(1, StackAddress::from(1023), Word::from(expected_loaded_value))
174+
)
175+
]
176+
);
177+
178+
let transient_storage_op =
179+
&builder.block.container.transient_storage[step.bus_mapping_instance[5].as_usize()];
180+
assert_eq!(
181+
(transient_storage_op.rw(), transient_storage_op.op()),
182+
(
183+
RW::READ,
184+
&TransientStorageOp::new(
185+
MOCK_ACCOUNTS[0],
186+
Word::from(0x0u32),
187+
Word::from(expected_loaded_value),
188+
Word::from(expected_loaded_value),
189+
1,
190+
)
191+
)
192+
);
193+
}
194+
195+
#[test]
196+
fn tload_opcode_impl_warm() {
197+
test_ok()
198+
}
199+
}

bus-mapping/src/exec_trace.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ impl fmt::Debug for OperationRef {
1818
Target::Memory => "Memory",
1919
Target::Stack => "Stack",
2020
Target::Storage => "Storage",
21+
Target::TransientStorage => "TransientStorage",
2122
Target::TxAccessListAccount => "TxAccessListAccount",
2223
Target::TxAccessListAccountStorage => "TxAccessListAccountStorage",
2324
Target::TxRefund => "TxRefund",

bus-mapping/src/operation.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ pub enum Target {
102102
Stack,
103103
/// Means the target of the operation is the Storage.
104104
Storage,
105+
/// Means the target of the operation is the TransientStorage.
106+
TransientStorage,
105107
/// Means the target of the operation is the TxAccessListAccount.
106108
TxAccessListAccount,
107109
/// Means the target of the operation is the TxAccessListAccountStorage.
@@ -410,6 +412,103 @@ impl Ord for StorageOp {
410412
}
411413
}
412414

415+
/// Represents a [`READ`](RW::READ)/[`WRITE`](RW::WRITE) into the transient storage
416+
/// implied by an specific
417+
/// [`OpcodeId`](eth_types::evm_types::opcode_ids::OpcodeId) of
418+
/// the [`ExecStep`](crate::circuit_input_builder::ExecStep).
419+
#[derive(Clone, PartialEq, Eq)]
420+
pub struct TransientStorageOp {
421+
/// Account Address
422+
pub address: Address,
423+
/// Transient Storage Key
424+
pub key: Word,
425+
/// Transient Storage Value after the operation
426+
pub value: Word,
427+
/// Transient Storage Value before the operation
428+
pub value_prev: Word,
429+
/// Transaction ID: Transaction index in the block starting at 1.
430+
pub tx_id: usize,
431+
}
432+
433+
impl fmt::Debug for TransientStorageOp {
434+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435+
f.write_str("TransientStorageOp { ")?;
436+
f.write_fmt(format_args!(
437+
"tx_id: {:?}, addr: {:?}, key: {:?}, val_prev: 0x{:x}, val: 0x{:x}",
438+
self.tx_id, self.address, self.key, self.value_prev, self.value
439+
))?;
440+
f.write_str(" }")
441+
}
442+
}
443+
444+
impl TransientStorageOp {
445+
/// Create a new instance of a `TransientStorageOp` from it's components.
446+
pub const fn new(
447+
address: Address,
448+
key: Word,
449+
value: Word,
450+
value_prev: Word,
451+
tx_id: usize,
452+
) -> TransientStorageOp {
453+
TransientStorageOp {
454+
address,
455+
key,
456+
value,
457+
value_prev,
458+
tx_id,
459+
}
460+
}
461+
462+
/// Returns the [`Target`] (operation type) of this operation.
463+
pub const fn target(&self) -> Target {
464+
Target::TransientStorage
465+
}
466+
467+
/// Returns the [`Address`] corresponding to this transient storage operation.
468+
pub const fn address(&self) -> &Address {
469+
&self.address
470+
}
471+
472+
/// Returns the [`Word`] used as key for this operation.
473+
pub const fn key(&self) -> &Word {
474+
&self.key
475+
}
476+
477+
/// Returns the [`Word`] read or written by this operation.
478+
pub const fn value(&self) -> &Word {
479+
&self.value
480+
}
481+
482+
/// Returns the [`Word`] at key found previous to this operation.
483+
pub const fn value_prev(&self) -> &Word {
484+
&self.value_prev
485+
}
486+
}
487+
488+
impl Op for TransientStorageOp {
489+
fn into_enum(self) -> OpEnum {
490+
OpEnum::TransientStorage(self)
491+
}
492+
493+
fn reverse(&self) -> Self {
494+
let mut rev = self.clone();
495+
swap(&mut rev.value, &mut rev.value_prev);
496+
rev
497+
}
498+
}
499+
500+
impl PartialOrd for TransientStorageOp {
501+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
502+
Some(self.cmp(other))
503+
}
504+
}
505+
506+
impl Ord for TransientStorageOp {
507+
fn cmp(&self, other: &Self) -> Ordering {
508+
(&self.address, &self.key).cmp(&(&other.address, &other.key))
509+
}
510+
}
511+
413512
/// Represents a change in the Account AccessList implied by a `BeginTx`,
414513
/// `EXTCODECOPY`, `EXTCODESIZE`, `EXTCODEHASH` `BALANCE`, `SELFDESTRUCT`,
415514
/// `*CALL`* or `CREATE*` step.
@@ -1055,6 +1154,8 @@ pub enum OpEnum {
10551154
Memory(MemoryOp),
10561155
/// Storage
10571156
Storage(StorageOp),
1157+
/// TransientStorage
1158+
TransientStorage(TransientStorageOp),
10581159
/// TxAccessListAccount
10591160
TxAccessListAccount(TxAccessListAccountOp),
10601161
/// TxAccessListAccountStorage

bus-mapping/src/operation/container.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{
22
AccountOp, CallContextOp, MemoryOp, Op, OpEnum, Operation, PaddingOp, RWCounter, StackOp,
3-
StartOp, StepStateOp, StorageOp, Target, TxAccessListAccountOp, TxAccessListAccountStorageOp,
4-
TxLogOp, TxReceiptOp, TxRefundOp, RW,
3+
StartOp, StepStateOp, StorageOp, Target, TransientStorageOp, TxAccessListAccountOp,
4+
TxAccessListAccountStorageOp, TxLogOp, TxReceiptOp, TxRefundOp, RW,
55
};
66
use crate::exec_trace::OperationRef;
77
use itertools::Itertools;
@@ -28,6 +28,8 @@ pub struct OperationContainer {
2828
pub stack: Vec<Operation<StackOp>>,
2929
/// Operations of StorageOp
3030
pub storage: Vec<Operation<StorageOp>>,
31+
/// Operations of TransientStorageOp
32+
pub transient_storage: Vec<Operation<TransientStorageOp>>, // TODO change
3133
/// Operations of TxAccessListAccountOp
3234
pub tx_access_list_account: Vec<Operation<TxAccessListAccountOp>>,
3335
/// Operations of TxAccessListAccountStorageOp
@@ -64,6 +66,7 @@ impl OperationContainer {
6466
memory: Vec::new(),
6567
stack: Vec::new(),
6668
storage: Vec::new(),
69+
transient_storage: Vec::new(),
6770
tx_access_list_account: Vec::new(),
6871
tx_access_list_account_storage: Vec::new(),
6972
tx_refund: Vec::new(),
@@ -120,6 +123,11 @@ impl OperationContainer {
120123
});
121124
OperationRef::from((Target::Storage, self.storage.len() - 1))
122125
}
126+
OpEnum::TransientStorage(op) => {
127+
self.transient_storage
128+
.push(Operation::new(rwc, rwc_inner_chunk, rw, op));
129+
OperationRef::from((Target::TransientStorage, self.transient_storage.len() - 1))
130+
}
123131
OpEnum::TxAccessListAccount(op) => {
124132
self.tx_access_list_account.push(if reversible {
125133
Operation::new_reversible(rwc, rwc_inner_chunk, rw, op)

0 commit comments

Comments
 (0)