Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Fix/callop memory #555

Merged
merged 10 commits into from
Jun 26, 2023
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

186 changes: 186 additions & 0 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1658,6 +1658,192 @@ impl<'a> CircuitInputStateRef<'a> {
Ok(copy_steps)
}

pub(crate) fn gen_copy_steps_for_precompile_calldata(
&mut self,
exec_step: &mut ExecStep,
call_id: usize,
src_addr: u64,
copy_length: u64,
caller_memory: &Vec<u8>,
) -> Result<Vec<(u8, bool, bool)>, Error> {
let mut copy_steps = Vec::with_capacity(copy_length as usize);
if copy_length == 0 {
return Ok(copy_steps);
}

let (_, src_begin_slot) = self.get_addr_shift_slot(src_addr).unwrap();
let (_, src_end_slot) = self.get_addr_shift_slot(src_addr + copy_length).unwrap();
let slot_count = src_end_slot - src_begin_slot;

let mut caller_memory = caller_memory.clone();

let minimal_length = src_end_slot as usize + 32;
if caller_memory.len() < minimal_length {
caller_memory.resize(minimal_length, 0);
}
let calldata_slot_bytes =
caller_memory[src_begin_slot as usize..(src_end_slot + 32) as usize].to_vec();

Self::gen_memory_copy_steps(
&mut copy_steps,
&caller_memory,
slot_count as usize + 32,
src_addr as usize,
src_begin_slot as usize,
copy_length as usize,
);

let mut chunk_index = src_begin_slot;
for chunk in calldata_slot_bytes.chunks(32) {
self.push_op(
exec_step,
RW::READ,
MemoryWordOp::new(
call_id,
chunk_index.into(),
Word::from_big_endian(&chunk)
),
);
chunk_index += 32;
}

Ok(copy_steps)
}

pub(crate) fn gen_copy_steps_for_precompile_callee_memory(
&mut self,
exec_step: &mut ExecStep,
call_id: usize,
result: &Vec<u8>,
) -> Result<Vec<(u8, bool, bool)>, Error> {
let mut copy_steps = Vec::with_capacity(result.len() as usize);
if result.len() == 0 {
return Ok(copy_steps);
}

let (_, end_slot) = self.get_addr_shift_slot(result.len() as u64)?;
let minimal_length = end_slot as usize + 32;
let mut memory = result.clone();
if memory.len() < minimal_length {
memory.resize(minimal_length, 0);
}

Self::gen_memory_copy_steps(
&mut copy_steps,
&memory,
end_slot as usize + 32,
0,
0,
result.len() as usize,
);

let mut chunk_index = 0;
for chunk in memory.chunks(32) {
self.push_op(
exec_step,
RW::WRITE,
MemoryWordOp::new(
call_id,
chunk_index.into(),
Word::from_big_endian(&chunk)
)
);
chunk_index += 32;
}

Ok(copy_steps)
}

// TODO: this is kind like returndatacopy, we should reuse it.
pub(crate) fn gen_copy_steps_for_precompile_returndata(
&mut self,
exec_step: &mut ExecStep,
call_id: usize,
caller_id: usize,
dst_addr: u64,
copy_length: usize,
result: &Vec<u8>,
) -> Result<(Vec<(u8, bool, bool)>, Vec<(u8, bool, bool)>), Error> {
let mut read_steps = Vec::with_capacity(copy_length as usize);
let mut write_steps = Vec::with_capacity(copy_length as usize);
if copy_length == 0 {
return Ok((read_steps, write_steps));
}
let src_begin_slot = 0;
let (_, src_end_slot) = self.get_addr_shift_slot(copy_length as u64).unwrap();
assert!(copy_length <= result.len());
let (_, dst_begin_slot) = self.get_addr_shift_slot(dst_addr).unwrap();
let (_, dst_end_slot) = self.get_addr_shift_slot(dst_addr + copy_length as u64).unwrap();

let slot_count = max(src_end_slot - src_begin_slot, dst_end_slot - dst_begin_slot) as usize;
let src_end_slot = src_begin_slot as usize + slot_count;
let dst_end_slot = dst_begin_slot as usize + slot_count;

let mut src_memory = result.clone();
if src_memory.len() < src_end_slot as usize + 32 {
src_memory.resize(src_end_slot as usize + 32, 0);
}

let mut dst_memory = self.caller_ctx()?.memory.clone();
let minimal_length = dst_end_slot as usize + 32;
dst_memory.extend_at_least(minimal_length);

let read_slot_bytes =
src_memory[src_begin_slot as usize..(src_end_slot + 32) as usize].to_vec();
let write_slot_bytes =
dst_memory.0[dst_begin_slot as usize..(dst_end_slot + 32) as usize].to_vec();

Self::gen_memory_copy_steps(
&mut read_steps,
&src_memory,
slot_count + 32,
0,
src_begin_slot as usize,
copy_length as usize,
);

Self::gen_memory_copy_steps(
&mut write_steps,
&dst_memory.0,
slot_count + 32,
dst_addr as usize,
dst_begin_slot as usize,
copy_length as usize,
);


let mut src_chunk_index = src_begin_slot;
let mut dst_chunk_index = dst_begin_slot;

for (read_chunk, write_chunk) in read_slot_bytes.chunks(32).zip(write_slot_bytes.chunks(32))
{

self.push_op(
exec_step,
RW::READ,
MemoryWordOp::new(
call_id,
src_chunk_index.into(),
Word::from_big_endian(&read_chunk)
)
);
src_chunk_index += 32;

self.push_op(
exec_step,
RW::WRITE,
MemoryWordOp::new(
caller_id,
dst_chunk_index.into(),
Word::from_big_endian(&write_chunk)
),
);
dst_chunk_index += 32;
}

Ok((read_steps, write_steps))
}

/// Generate copy steps for call data.
pub(crate) fn gen_copy_steps_for_call_data_root(
&mut self,
Expand Down
80 changes: 28 additions & 52 deletions bus-mapping/src/evm/opcodes/callop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
},
evm::opcodes::precompiles::gen_associated_ops as precompile_associated_ops,
operation::{
AccountField, CallContextField, MemoryOp, MemoryWordOp, TxAccessListAccountOp, RW,
AccountField, CallContextField, MemoryWordOp, TxAccessListAccountOp, RW,
},
precompile::{execute_precompiled, is_precompiled, PrecompileCalls},
state_db::CodeDB,
Expand Down Expand Up @@ -353,25 +353,16 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
// &result, ret_offset, length, true)?; insert a copy event (input)
// for this step
let rw_counter_start = state.block_ctx.rwc;

if call.call_data_length > 0 {
let bytes: Vec<(u8, bool, bool)> = caller_memory
.iter()
.skip(call.call_data_offset as usize)
.take(call.call_data_length as usize)
.map(|b| (*b, false, false))
.collect();
for (i, &(byte, _is_code, _mask)) in bytes.iter().enumerate() {
// push caller memory read
state.push_op(
&mut exec_step,
RW::READ,
MemoryOp::new(
call.caller_id,
(call.call_data_offset + i as u64).into(),
byte,
),
);
}
let copy_steps = state.gen_copy_steps_for_precompile_calldata(
&mut exec_step,
call.caller_id,
call.call_data_offset,
call.call_data_length,
&caller_memory,
)?;

state.push_copy(
&mut exec_step,
CopyEvent {
Expand All @@ -384,7 +375,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
dst_addr: 0,
log_id: None,
rw_counter_start,
bytes,
bytes: copy_steps,
aux_bytes: None,
},
);
Expand All @@ -393,17 +384,11 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
// write the result in the callee's memory.
let rw_counter_start = state.block_ctx.rwc;
if call.is_success() && call.call_data_length > 0 && !result.is_empty() {
//todo: update this bytes
let bytes: Vec<(u8, bool, bool)> =
result.iter().map(|b| (*b, false, false)).collect();
for (i, &(byte, _is_code, _mask)) in bytes.iter().enumerate() {
// push callee memory write
state.push_op(
&mut exec_step,
RW::WRITE,
MemoryOp::new(call.call_id, i.into(), byte),
);
}
let copy_steps = state.gen_copy_steps_for_precompile_callee_memory(
&mut exec_step,
call.call_id,
&result,
)?;
state.push_copy(
&mut exec_step,
CopyEvent {
Expand All @@ -416,7 +401,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
dst_addr: 0,
log_id: None,
rw_counter_start,
bytes,
bytes: copy_steps,
aux_bytes: None,
},
);
Expand All @@ -425,37 +410,28 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
// insert another copy event (output) for this step.
let rw_counter_start = state.block_ctx.rwc;
if call.is_success() && call.call_data_length > 0 && length > 0 {
let bytes: Vec<(u8, bool, bool)> = result
.iter()
.take(length)
.map(|b| (*b, false, false))
.collect();
for (i, &(byte, _is_code, _mask)) in bytes.iter().enumerate() {
// push caller memory write
state.push_op(
&mut exec_step,
RW::WRITE,
MemoryOp::new(
call.caller_id,
(call.return_data_offset + i as u64).into(),
byte,
),
);
}
let (read_steps, write_steps) = state.gen_copy_steps_for_precompile_returndata(
&mut exec_step,
call.call_id,
call.caller_id,
call.return_data_offset,
length,
&result,
)?;
state.push_copy(
&mut exec_step,
CopyEvent {
src_id: NumberOrHash::Number(call.call_id),
src_type: CopyDataType::Precompile(precompile_call),
src_type: CopyDataType::Memory,
src_addr: 0,
src_addr_end: length as u64,
dst_id: NumberOrHash::Number(call.caller_id),
dst_type: CopyDataType::Memory,
dst_addr: call.return_data_offset,
log_id: None,
rw_counter_start,
bytes,
aux_bytes: None,
bytes: read_steps,
aux_bytes: Some(write_steps),
},
);
}
Expand Down
1 change: 1 addition & 0 deletions zkevm-circuits/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pretty_assertions = "1.0.0"
cli-table = "0.4"
serde = { version = "1.0.130", features = ["derive"] }
serde_json = "1.0.78"
paste = "1.0"

[features]
default = ["test", "test-circuits", "enable-sign-verify"]
Expand Down
Loading