use super::Opcode;
use crate::{
circuit_input_builder::{CircuitInputStateRef, ExecStep},
Error,
};
use eth_types::{evm_types::MemoryAddress, GethExecStep, ToBigEndian};
#[derive(Debug, Copy, Clone)]
pub(crate) struct Mload;
impl Opcode for Mload {
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 stack_value_read = geth_step.stack.last()?;
let stack_position = geth_step.stack.last_filled();
state.stack_read(&mut exec_step, stack_position, stack_value_read)?;
let mut mem_read_addr: MemoryAddress = stack_value_read.try_into()?;
let mem_read_value = geth_steps[1].stack.last()?;
state.stack_write(&mut exec_step, stack_position, mem_read_value)?;
for _ in mem_read_value.to_be_bytes() {
state.memory_read(&mut exec_step, mem_read_addr)?;
mem_read_addr += MemoryAddress::from(1);
}
let offset = geth_step.stack.nth_last(0)?;
let offset_addr: MemoryAddress = offset.try_into()?;
let minimal_length = offset_addr.0 + 32;
state.call_ctx_mut()?.memory.extend_at_least(minimal_length);
Ok(vec![exec_step])
}
}
#[cfg(test)]
mod mload_tests {
use super::*;
use crate::{
circuit_input_builder::ExecState,
mock::BlockData,
operation::{MemoryOp, StackOp, RW},
};
use eth_types::{
bytecode,
evm_types::{OpcodeId, StackAddress},
geth_types::GethData,
Word,
};
use itertools::Itertools;
use mock::test_ctx::{helpers::*, TestContext};
use pretty_assertions::assert_eq;
#[test]
fn mload_opcode_impl() {
let code = bytecode! {
.setup_state()
PUSH1(0x40u64)
MLOAD
STOP
};
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();
let step = builder.block.txs()[0]
.steps()
.iter()
.find(|step| step.exec_state == ExecState::Op(OpcodeId::MLOAD))
.unwrap();
assert_eq!(
[0, 1]
.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(0x40))
),
(
RW::WRITE,
&StackOp::new(1, StackAddress::from(1023), Word::from(0x80))
)
]
);
assert_eq!(
(2..34)
.map(|idx| &builder.block.container.memory
[step.bus_mapping_instance[idx].as_usize()])
.map(|operation| (operation.rw(), operation.op().clone()))
.collect_vec(),
Word::from(0x80)
.to_be_bytes()
.into_iter()
.enumerate()
.map(|(idx, byte)| (RW::READ, MemoryOp::new(1, MemoryAddress(idx + 0x40), byte)))
.collect_vec()
)
}
}