1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use super::{Opcode, OpcodeId};
use crate::{
    circuit_input_builder::{CircuitInputStateRef, ExecStep},
    error::{ExecError, OogError},
    operation::{CallContextField, StorageOp, TxAccessListAccountStorageOp, RW},
    Error,
};
use eth_types::{GethExecStep, ToWord};

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

impl Opcode for OOGSloadSstore {
    fn gen_associated_ops(
        state: &mut CircuitInputStateRef,
        geth_steps: &[GethExecStep],
    ) -> Result<Vec<ExecStep>, Error> {
        let geth_step = &geth_steps[0];
        debug_assert!([OpcodeId::SLOAD, OpcodeId::SSTORE].contains(&geth_step.op));

        let mut exec_step = state.new_step(geth_step)?;
        exec_step.error = Some(ExecError::OutOfGas(OogError::SloadSstore));

        let call_id = state.call()?.call_id;
        let callee_address = state.call()?.address;
        let tx_id = state.tx_ctx.id();

        state.call_context_read(
            &mut exec_step,
            call_id,
            CallContextField::TxId,
            tx_id.into(),
        )?;

        state.call_context_read(
            &mut exec_step,
            call_id,
            CallContextField::IsStatic,
            (state.call()?.is_static as u8).into(),
        )?;

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

        let key = geth_step.stack.last()?;
        state.stack_read(&mut exec_step, geth_step.stack.last_filled(), key)?;

        let is_warm = state
            .sdb
            .check_account_storage_in_access_list(&(callee_address, key));
        state.push_op(
            &mut exec_step,
            RW::READ,
            TxAccessListAccountStorageOp {
                tx_id,
                address: callee_address,
                key,
                is_warm,
                is_warm_prev: is_warm,
            },
        )?;

        // Special operations are only used for SSTORE.
        if geth_step.op == OpcodeId::SSTORE {
            let value = geth_step.stack.nth_last(1)?;
            state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(1), value)?;

            let (_, value_prev) = state.sdb.get_storage(&callee_address, &key);
            let (_, original_value) = state.sdb.get_committed_storage(&callee_address, &key);

            state.push_op(
                &mut exec_step,
                RW::READ,
                StorageOp::new(
                    callee_address,
                    key,
                    *value_prev,
                    *value_prev,
                    tx_id,
                    *original_value,
                ),
            )?;
        }

        state.handle_return(&mut [&mut exec_step], geth_steps, true)?;
        Ok(vec![exec_step])
    }
}