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

Commit d28a696

Browse files
committed
tstore in busmapping with test case
1 parent 1c1cd7e commit d28a696

File tree

5 files changed

+244
-6
lines changed

5 files changed

+244
-6
lines changed

bus-mapping/src/circuit_input_builder/input_state_ref.rs

+4
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,10 @@ impl<'a> CircuitInputStateRef<'a> {
977977
OpEnum::Storage(op) => {
978978
self.sdb.set_storage(&op.address, &op.key, &op.value);
979979
}
980+
OpEnum::TransientStorage(op) => {
981+
self.sdb
982+
.set_transient_storage(&op.address, &op.key, &op.value)
983+
}
980984
OpEnum::TxAccessListAccount(op) => {
981985
if !op.is_warm_prev && op.is_warm {
982986
self.sdb.add_account_to_access_list(op.address);

bus-mapping/src/evm/opcodes.rs

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod stackonlyop;
4545
mod stop;
4646
mod swap;
4747
mod tload;
48+
mod tstore;
4849

4950
mod error_code_store;
5051
mod error_invalid_creation_code;
@@ -112,6 +113,7 @@ use stackonlyop::StackOnlyOpcode;
112113
use stop::Stop;
113114
use swap::Swap;
114115
use tload::Tload;
116+
use tstore::Tstore;
115117

116118
#[cfg(any(feature = "test", test))]
117119
pub use crate::precompile::PrecompileCallArgs;
@@ -228,6 +230,7 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
228230
OpcodeId::GAS => StackOnlyOpcode::<0, 1>::gen_associated_ops,
229231
OpcodeId::JUMPDEST => Dummy::gen_associated_ops,
230232
OpcodeId::TLOAD => Tload::gen_associated_ops,
233+
OpcodeId::TSTORE => Tstore::gen_associated_ops,
231234
OpcodeId::DUP1 => Dup::<1>::gen_associated_ops,
232235
OpcodeId::DUP2 => Dup::<2>::gen_associated_ops,
233236
OpcodeId::DUP3 => Dup::<3>::gen_associated_ops,

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

+3-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ impl Opcode for Tload {
1818
geth_steps: &[GethExecStep],
1919
) -> Result<Vec<ExecStep>, Error> {
2020
let geth_step = &geth_steps[0];
21-
let next_geth_step = &geth_steps[1];
2221
let mut exec_step = state.new_step(geth_step)?;
2322

2423
let call_id = state.call()?.call_id;
@@ -59,11 +58,9 @@ impl Opcode for Tload {
5958
// Manage first stack read at latest stack position
6059
state.stack_read(&mut exec_step, stack_position, key)?;
6160

62-
// Storage read
63-
let value = next_geth_step
64-
.stack
65-
.last()
66-
.expect("No value in stack in TLOAD next step");
61+
// Transient Storage read
62+
let (_, value) = state.sdb.get_transient_storage(&contract_addr, &key);
63+
let value = *value;
6764

6865
state.push_op(
6966
&mut exec_step,

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

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
use super::Opcode;
2+
use crate::{
3+
circuit_input_builder::{CircuitInputStateRef, ExecStep},
4+
operation::{CallContextField, TransientStorageOp},
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::TSTORE`](crate::evm::OpcodeId::TSTORE)
11+
/// `OpcodeId`.
12+
#[derive(Debug, Copy, Clone)]
13+
pub(crate) struct Tstore;
14+
15+
impl Opcode for Tstore {
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 contract_addr = state.call()?.address;
24+
25+
state.call_context_read(
26+
&mut exec_step,
27+
state.call()?.call_id,
28+
CallContextField::TxId,
29+
Word::from(state.tx_ctx.id()),
30+
)?;
31+
state.call_context_read(
32+
&mut exec_step,
33+
state.call()?.call_id,
34+
CallContextField::IsStatic,
35+
Word::from(state.call()?.is_static as u8),
36+
)?;
37+
38+
state.call_context_read(
39+
&mut exec_step,
40+
state.call()?.call_id,
41+
CallContextField::RwCounterEndOfReversion,
42+
Word::from(state.call()?.rw_counter_end_of_reversion),
43+
)?;
44+
45+
state.call_context_read(
46+
&mut exec_step,
47+
state.call()?.call_id,
48+
CallContextField::IsPersistent,
49+
Word::from(state.call()?.is_persistent as u8),
50+
)?;
51+
52+
state.call_context_read(
53+
&mut exec_step,
54+
state.call()?.call_id,
55+
CallContextField::CalleeAddress,
56+
state.call()?.address.to_word(),
57+
)?;
58+
59+
let key = geth_step.stack.nth_last(0)?;
60+
let key_stack_position = geth_step.stack.nth_last_filled(0);
61+
let value = geth_step.stack.nth_last(1)?;
62+
let value_stack_position = geth_step.stack.nth_last_filled(1);
63+
64+
state.stack_read(&mut exec_step, key_stack_position, key)?;
65+
state.stack_read(&mut exec_step, value_stack_position, value)?;
66+
67+
let (_, value_prev) = state.sdb.get_transient_storage(&contract_addr, &key);
68+
let value_prev = *value_prev;
69+
70+
state.push_op_reversible(
71+
&mut exec_step,
72+
TransientStorageOp::new(
73+
state.call()?.address,
74+
key,
75+
value,
76+
value_prev,
77+
state.tx_ctx.id(),
78+
),
79+
)?;
80+
81+
Ok(vec![exec_step])
82+
}
83+
}
84+
85+
#[cfg(test)]
86+
mod tstore_tests {
87+
use super::*;
88+
use crate::{
89+
circuit_input_builder::ExecState,
90+
mock::BlockData,
91+
operation::{CallContextOp, StackOp, RW},
92+
};
93+
use eth_types::{
94+
bytecode,
95+
evm_types::{OpcodeId, StackAddress},
96+
geth_types::GethData,
97+
Word,
98+
};
99+
use mock::{test_ctx::helpers::tx_from_1_to_0, TestContext, MOCK_ACCOUNTS};
100+
use pretty_assertions::assert_eq;
101+
102+
#[test]
103+
fn tstore_opcode() {
104+
let code = bytecode! {
105+
// Write 0x6f to storage slot 0
106+
PUSH1(0x6fu64)
107+
PUSH1(0x00u64)
108+
TSTORE
109+
PUSH1(0x00u64)
110+
TLOAD
111+
STOP
112+
};
113+
let expected_prev_value = 0x00u64;
114+
115+
// Get the execution steps from the external tracer
116+
let block: GethData = TestContext::<2, 1>::new(
117+
None,
118+
|accs| {
119+
accs[0]
120+
.address(MOCK_ACCOUNTS[0])
121+
.balance(Word::from(10u64.pow(19)))
122+
.code(code)
123+
.storage(vec![(0x00u64.into(), 0x6fu64.into())].into_iter());
124+
accs[1]
125+
.address(MOCK_ACCOUNTS[1])
126+
.balance(Word::from(10u64.pow(19)));
127+
},
128+
tx_from_1_to_0,
129+
|block, _tx| block.number(0xcafeu64),
130+
)
131+
.unwrap()
132+
.into();
133+
134+
let builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder();
135+
let builder = builder
136+
.handle_block(&block.eth_block, &block.geth_traces)
137+
.unwrap();
138+
139+
let step = builder.block.txs()[0]
140+
.steps()
141+
.iter()
142+
.rev() // find last tstore
143+
.find(|step| step.exec_state == ExecState::Op(OpcodeId::TSTORE))
144+
.unwrap();
145+
146+
assert_eq!(
147+
[0, 1, 2, 3, 4]
148+
.map(|idx| &builder.block.container.call_context
149+
[step.bus_mapping_instance[idx].as_usize()])
150+
.map(|operation| (operation.rw(), operation.op())),
151+
[
152+
(
153+
RW::READ,
154+
&CallContextOp::new(1, CallContextField::TxId, Word::from(0x01)),
155+
),
156+
(
157+
RW::READ,
158+
&CallContextOp::new(1, CallContextField::IsStatic, Word::from(0x00)),
159+
),
160+
(
161+
RW::READ,
162+
&CallContextOp::new(
163+
1,
164+
CallContextField::RwCounterEndOfReversion,
165+
Word::from(0x00)
166+
),
167+
),
168+
(
169+
RW::READ,
170+
&CallContextOp::new(1, CallContextField::IsPersistent, Word::from(0x01)),
171+
),
172+
(
173+
RW::READ,
174+
&CallContextOp::new(
175+
1,
176+
CallContextField::CalleeAddress,
177+
MOCK_ACCOUNTS[0].to_word(),
178+
),
179+
),
180+
]
181+
);
182+
183+
assert_eq!(
184+
[5, 6]
185+
.map(|idx| &builder.block.container.stack[step.bus_mapping_instance[idx].as_usize()])
186+
.map(|operation| (operation.rw(), operation.op())),
187+
[
188+
(
189+
RW::READ,
190+
&StackOp::new(1, StackAddress::from(1022), Word::from(0x0u32))
191+
),
192+
(
193+
RW::READ,
194+
&StackOp::new(1, StackAddress::from(1023), Word::from(0x6fu32))
195+
),
196+
]
197+
);
198+
199+
let storage_op =
200+
&builder.block.container.transient_storage[step.bus_mapping_instance[7].as_usize()];
201+
assert_eq!(
202+
(storage_op.rw(), storage_op.op()),
203+
(
204+
RW::WRITE,
205+
&TransientStorageOp::new(
206+
MOCK_ACCOUNTS[0],
207+
Word::from(0x0u32),
208+
Word::from(0x6fu32),
209+
Word::from(expected_prev_value),
210+
1,
211+
)
212+
)
213+
);
214+
}
215+
}

bus-mapping/src/state_db.rs

+19
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ pub struct StateDB {
140140
// state before current transaction, to calculate gas cost for some opcodes like sstore.
141141
// So both dirty storage and committed storage are needed.
142142
dirty_storage: HashMap<(Address, Word), Word>,
143+
// Transient storage, which is cleared after the transaction.
144+
transient_storage: HashMap<(Address, Word), Word>,
143145
// Accounts that have been through `SELFDESTRUCT` under the situation that `is_persistent` is
144146
// `true`. These accounts will be reset once `commit_tx` is called.
145147
destructed_account: HashSet<Address>,
@@ -190,6 +192,17 @@ impl StateDB {
190192
}
191193
}
192194

195+
/// Get a reference to the transient storage value from [`Account`] at `addr`, at
196+
/// `key`. Returns false and a zero [`Word`] when the [`Account`] or `key`
197+
/// wasn't found in the state.
198+
/// Returns transient storage value, which is cleared after current tx
199+
pub fn get_transient_storage(&self, addr: &Address, key: &Word) -> (bool, &Word) {
200+
match self.transient_storage.get(&(*addr, *key)) {
201+
Some(v) => (true, v),
202+
None => (false, &VALUE_ZERO),
203+
}
204+
}
205+
193206
/// Get a reference to the storage value from [`Account`] at `addr`, at
194207
/// `key`. Returns false and a zero [`Word`] when the [`Account`] or `key`
195208
/// wasn't found in the state.
@@ -227,6 +240,12 @@ impl StateDB {
227240
self.dirty_storage.insert((*addr, *key), *value);
228241
}
229242

243+
/// Set transient storage value at `addr` and `key`.
244+
/// Transient storage is cleared after transaction execution.
245+
pub fn set_transient_storage(&mut self, addr: &Address, key: &Word, value: &Word) {
246+
self.transient_storage.insert((*addr, *key), *value);
247+
}
248+
230249
/// Get nonce of account with `addr`.
231250
pub fn get_nonce(&self, addr: &Address) -> u64 {
232251
let (_, account) = self.get_account(addr);

0 commit comments

Comments
 (0)