Skip to content

Commit

Permalink
feat: implement mcopy opcode (#1209)
Browse files Browse the repository at this point in the history
* draft buss mapping for mcopy

* rename to mcopy.rs

* add mcopy.rs

* add helper gen_copy_steps_for_memory_to_memory

* add MCOPY in opcode_ids

* handle opcode_id others as_u8, constant_gas etc.

* draft mcopy gadgets

* add gadget to execution config

* remove unused fields

* update buss mapping test

* update gadget tests

* remove unnecessary testing stuff

* update ExecutionState

* add cancun config

* update buss mapping test

* remove commented codes

* enable multi copy case

* fix buss mapping tests

* update gas cost

* remove tx id lookup

* fix memory_word_size

* update word size if no copy happens

* add copy circuit test

* remove debug log

* fmt align

* minor update

* add OOGMemoryCopy test for mcopy

* make oog memorycopy include mcopy opcode

* revert temp debug changes

* add more test data

* minor updates

* fmt adjust

* fix clippy

* add testool

* fix memory word

* increase fix table rows detected by ci

* update testool

* remove unused import

* clippy update

* fix large dest memory addr

* refactor to use memory_expansion for dest_addr

* handle dest_addr < src_addr

* update per cargo check

* intermediate clean up

* fix testool

* fix u64 overflow

* update oog memorycopy to cover src_offset overflow case

* fix clippy; fix codehash.txt

* remove debug codes

* fix clippy

* reverted commented for testing

* remove outdated comment and todo for overlap case

---------

Co-authored-by: Zhuo Zhang <mycinbrin@gmail.com>
  • Loading branch information
DreamWuGit and lispc authored May 13, 2024
1 parent 69b9a3e commit 1cb76ea
Show file tree
Hide file tree
Showing 20 changed files with 756 additions and 36 deletions.
79 changes: 78 additions & 1 deletion bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1950,6 +1950,7 @@ impl<'a> CircuitInputStateRef<'a> {
copy_length,
&bytecode.code,
&mut self.call_ctx_mut()?.memory,
false,
);

let copy_steps = CopyEventStepsBuilder::new()
Expand Down Expand Up @@ -2039,6 +2040,7 @@ impl<'a> CircuitInputStateRef<'a> {
copy_length,
result,
&mut self.caller_ctx_mut()?.memory,
false,
);

let read_slot_bytes = MemoryRef(result).read_chunk(src_range);
Expand Down Expand Up @@ -2096,6 +2098,7 @@ impl<'a> CircuitInputStateRef<'a> {
copy_length,
&call_ctx.call_data,
&mut call_ctx.memory,
false,
);

let copy_steps = CopyEventStepsBuilder::memory_range(range)
Expand Down Expand Up @@ -2144,6 +2147,7 @@ impl<'a> CircuitInputStateRef<'a> {
copy_length,
call_data,
&mut call_ctx.memory,
false,
);

let read_slot_bytes = self.caller_ctx()?.memory.read_chunk(src_range);
Expand Down Expand Up @@ -2205,6 +2209,7 @@ impl<'a> CircuitInputStateRef<'a> {
copy_length,
return_data,
&mut call_ctx.memory,
false,
);
let read_slot_bytes = self.call()?.last_callee_memory.read_chunk(src_range);

Expand Down Expand Up @@ -2247,6 +2252,71 @@ impl<'a> CircuitInputStateRef<'a> {
Ok((read_steps, write_steps, prev_bytes))
}

// generates copy steps for memory to memory case.
pub(crate) fn gen_copy_steps_for_memory_to_memory(
&mut self,
exec_step: &mut ExecStep,
src_addr: impl Into<MemoryAddress>,
dst_addr: impl Into<MemoryAddress>,
copy_length: impl Into<MemoryAddress>,
) -> Result<(CopyEventSteps, CopyEventSteps, Vec<u8>), Error> {
let copy_length = copy_length.into().0;
if copy_length == 0 {
return Ok((vec![], vec![], vec![]));
}

// current call's memory
let memory = self.call_ctx()?.memory.clone();
let call_ctx = self.call_ctx_mut()?;
let (src_range, dst_range, write_slot_bytes) = combine_copy_slot_bytes(
src_addr.into().0,
dst_addr.into().0,
copy_length,
&memory.0,
&mut call_ctx.memory,
true,
);
let read_slot_bytes = memory.read_chunk(src_range);

let read_steps = CopyEventStepsBuilder::memory_range(src_range)
.source(read_slot_bytes.as_slice())
.build();
let write_steps = CopyEventStepsBuilder::memory_range(dst_range)
.source(write_slot_bytes.as_slice())
.build();

let mut src_chunk_index = src_range.start_slot().0;
let mut dst_chunk_index = dst_range.start_slot().0;
let mut prev_bytes: Vec<u8> = vec![];
// memory word reads from source and writes to destination word
let call_id = self.call()?.call_id;
for (read_chunk, write_chunk) in read_slot_bytes.chunks(32).zip(write_slot_bytes.chunks(32))
{
self.push_op(
exec_step,
RW::READ,
MemoryOp::new(
call_id,
src_chunk_index.into(),
Word::from_big_endian(read_chunk),
),
)?;
trace!("read chunk: {call_id} {src_chunk_index} {read_chunk:?}");
src_chunk_index += 32;

self.write_chunk_for_copy_step(
exec_step,
write_chunk,
dst_chunk_index,
&mut prev_bytes,
)?;

dst_chunk_index += 32;
}

Ok((read_steps, write_steps, prev_bytes))
}

pub(crate) fn gen_copy_steps_for_log(
&mut self,
exec_step: &mut ExecStep,
Expand Down Expand Up @@ -2349,13 +2419,20 @@ fn combine_copy_slot_bytes(
copy_length: usize,
src_data: &[impl Into<u8> + Clone],
dst_memory: &mut Memory,
is_memory_copy: bool, // indicates memroy --> memory copy type.
) -> (MemoryWordRange, MemoryWordRange, Vec<u8>) {
let mut src_range = MemoryWordRange::align_range(src_addr, copy_length);
let mut dst_range = MemoryWordRange::align_range(dst_addr, copy_length);
src_range.ensure_equal_length(&mut dst_range);

// Extend call memory.
dst_memory.extend_for_range(dst_addr.into(), copy_length.into());
// if is_memory_copy=true, both dst_addr and src_addr are memory address
if is_memory_copy && dst_addr < src_addr {
dst_memory.extend_for_range(src_addr.into(), copy_length.into());
} else {
dst_memory.extend_for_range(dst_addr.into(), copy_length.into());
}

let dst_begin_slot = dst_range.start_slot().0;
let dst_end_slot = dst_range.end_slot().0;

Expand Down
1 change: 1 addition & 0 deletions bus-mapping/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ pub(crate) fn get_step_reported_error(op: &OpcodeId, error: GethExecError) -> Ex
OpcodeId::CALLDATACOPY
| OpcodeId::CODECOPY
| OpcodeId::EXTCODECOPY
| OpcodeId::MCOPY
| OpcodeId::RETURNDATACOPY => OogError::MemoryCopy,
OpcodeId::BALANCE | OpcodeId::EXTCODESIZE | OpcodeId::EXTCODEHASH => {
OogError::AccountAccess
Expand Down
4 changes: 4 additions & 0 deletions bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ mod extcodesize;
mod gasprice;
mod jumpi;
mod logs;
mod mcopy;
mod mload;
mod mstore;
mod number;
Expand Down Expand Up @@ -116,6 +117,7 @@ use extcodehash::Extcodehash;
use extcodesize::Extcodesize;
use gasprice::GasPrice;
use logs::Log;
use mcopy::MCopy;
use mload::Mload;
use mstore::Mstore;
use origin::Origin;
Expand Down Expand Up @@ -235,6 +237,7 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
OpcodeId::SELFBALANCE => Selfbalance::gen_associated_ops,
OpcodeId::BASEFEE => GetBlockHeaderField::<{ OpcodeId::BASEFEE }>::gen_associated_ops,
OpcodeId::POP => StackPopOnlyOpcode::<1>::gen_associated_ops,
OpcodeId::MCOPY => MCopy::gen_associated_ops,
OpcodeId::MLOAD => Mload::gen_associated_ops,
OpcodeId::MSTORE => Mstore::<false>::gen_associated_ops,
OpcodeId::MSTORE8 => Mstore::<true>::gen_associated_ops,
Expand Down Expand Up @@ -477,6 +480,7 @@ pub fn gen_associated_ops(
} else {
None
};

if let Some(exec_error) = state.get_step_err(geth_step, next_step).unwrap() {
log::debug!(
"geth error {:?} occurred in {:?} at pc {:?}",
Expand Down
3 changes: 2 additions & 1 deletion bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ impl Opcode for OOGMemoryCopy {
OpcodeId::CALLDATACOPY,
OpcodeId::CODECOPY,
OpcodeId::EXTCODECOPY,
OpcodeId::RETURNDATACOPY
OpcodeId::RETURNDATACOPY,
OpcodeId::MCOPY,
]
.contains(&geth_step.op));

Expand Down
Loading

0 comments on commit 1cb76ea

Please sign in to comment.