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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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