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

Commit 1dc246c

Browse files
authored
Merge branch 'memory_opt' into mem-opt/fix-precompiled-call-test-case
2 parents e698647 + dc3f5c2 commit 1dc246c

File tree

8 files changed

+370
-143
lines changed

8 files changed

+370
-143
lines changed

bus-mapping/src/circuit_input_builder/execution.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,41 @@ impl CopyEvent {
251251
u64::try_from(self.rw_counter_start.0).unwrap() + self.rw_counter_increase(step_index)
252252
}
253253

254+
/// rw counter at step index
255+
pub fn rw_counter_step(&self, step_index: usize) -> u64 {
256+
let mut rw_counter = u64::try_from(self.rw_counter_start.0).unwrap();
257+
let mut rw_counter_increase = self.rw_counter_increase(step_index);
258+
let mut rw_counter_increase_log = 0u64;
259+
if self.dst_type == CopyDataType::TxLog {
260+
rw_counter_increase_log = self.rw_counter_increase_log(step_index);
261+
rw_counter_increase =
262+
rw_counter_increase - rw_counter_increase_log + rw_counter_increase_log / 2
263+
}
264+
rw_counter = rw_counter + rw_counter_increase;
265+
266+
// step_index == self.bytes.len() when caculate total rw increasing.
267+
if self.dst_type == CopyDataType::TxLog && step_index != self.bytes.len() * 2 {
268+
if step_index % 2 == 0 {
269+
// memory reading
270+
rw_counter = rw_counter - rw_counter_increase + step_index as u64 / 2 / 32;
271+
} else {
272+
// log writing
273+
rw_counter = rw_counter + self.bytes.len() as u64 / 32;
274+
}
275+
}
276+
277+
return rw_counter;
278+
}
279+
254280
/// rw counter increase left at step index
255281
pub fn rw_counter_increase_left(&self, step_index: usize) -> u64 {
256-
self.rw_counter(self.bytes.len() * 2) - self.rw_counter(step_index)
282+
if self.rw_counter_step(self.bytes.len() * 2) < self.rw_counter_step(step_index) {
283+
let rw_counter = self.rw_counter_step(self.bytes.len() * 2);
284+
let rw_prevous = self.rw_counter_step(step_index);
285+
return 0u64;
286+
}
287+
288+
self.rw_counter_step(self.bytes.len() * 2) - self.rw_counter_step(step_index)
257289
}
258290

259291
/// Number of rw operations performed by this copy event
@@ -265,23 +297,27 @@ impl CopyEvent {
265297
fn rw_counter_increase(&self, step_index: usize) -> u64 {
266298
let source_rw_increase = match self.src_type {
267299
CopyDataType::Bytecode | CopyDataType::TxCalldata => 0,
268-
CopyDataType::Memory => std::cmp::min(
269-
u64::try_from(step_index + 1).unwrap() / 2,
270-
self.src_addr_end
271-
.checked_sub(self.src_addr)
272-
.unwrap_or_default(),
273-
),
300+
CopyDataType::Memory => (step_index as u64 / 2) / 32,
274301
CopyDataType::RlcAcc | CopyDataType::TxLog | CopyDataType::Padding => unreachable!(),
275302
};
276303
let destination_rw_increase = match self.dst_type {
277304
CopyDataType::RlcAcc | CopyDataType::Bytecode => 0,
278305
CopyDataType::Memory => (step_index as u64 / 2) / 32,
279-
// TODO: update log if needed later
280306
CopyDataType::TxLog => u64::try_from(step_index).unwrap() / 2,
281307
CopyDataType::TxCalldata | CopyDataType::Padding => unreachable!(),
282308
};
283309
source_rw_increase + destination_rw_increase
284310
}
311+
312+
// increase in rw counter for tx log specially
313+
fn rw_counter_increase_log(&self, step_index: usize) -> u64 {
314+
let destination_rw_increase = match self.dst_type {
315+
CopyDataType::TxLog => u64::try_from(step_index).unwrap() / 2,
316+
_ => unreachable!(),
317+
};
318+
319+
destination_rw_increase
320+
}
285321
}
286322

287323
/// Intermediary multiplication step, representing `a * b == d (mod 2^256)`

bus-mapping/src/circuit_input_builder/input_state_ref.rs

Lines changed: 128 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ use eth_types::{
2424
},
2525
Address, Bytecode, GethExecStep, ToAddress, ToBigEndian, ToWord, Word, H256, U256,
2626
};
27-
use ethers_core::utils::{get_contract_address, get_create2_address, keccak256};
28-
use std::{cmp::max, mem};
27+
use ethers_core::{
28+
k256::{elliptic_curve::consts::U4, sha2::digest::typenum::Minimum},
29+
utils::{get_contract_address, get_create2_address, keccak256},
30+
};
31+
use std::{cmp::max, io::copy, mem};
2932

3033
/// Reference to the internal state of the CircuitInputBuilder in a particular
3134
/// [`ExecStep`].
@@ -1602,6 +1605,13 @@ impl<'a> CircuitInputStateRef<'a> {
16021605
Ok(())
16031606
}
16041607

1608+
// get word slot and shift pair for a memory address.
1609+
pub(crate) fn get_addr_shift_slot(&mut self, addr: u64) -> Result<(u64, u64), Error> {
1610+
let shift = addr % 32;
1611+
let slot = addr - shift;
1612+
Ok((shift, slot))
1613+
}
1614+
16051615
/// Generate copy steps for bytecode.
16061616
pub(crate) fn gen_copy_steps_for_bytecode(
16071617
&mut self,
@@ -1613,17 +1623,56 @@ impl<'a> CircuitInputStateRef<'a> {
16131623
bytes_left: u64,
16141624
) -> Result<Vec<(u8, bool, bool)>, Error> {
16151625
let mut copy_steps = Vec::with_capacity(bytes_left as usize);
1616-
for idx in 0..bytes_left {
1617-
let addr = src_addr.checked_add(idx).unwrap_or(src_addr_end);
1618-
let step = if addr < src_addr_end {
1619-
let code = bytecode.code.get(addr as usize).unwrap();
1620-
(code.value, code.is_code, false)
1626+
if bytes_left == 0 {
1627+
return Ok(copy_steps);
1628+
}
1629+
1630+
let (_, dst_begin_slot) = self.get_addr_shift_slot(dst_addr).unwrap();
1631+
let (_, dst_end_slot) = self.get_addr_shift_slot(dst_addr + bytes_left).unwrap();
1632+
let mut memory = self.call_ctx_mut()?.memory.clone();
1633+
1634+
let minimal_length = dst_end_slot as usize + 32;
1635+
memory.extend_at_least(minimal_length);
1636+
// collect all bytecode to memory with padding word
1637+
let code_slot_bytes =
1638+
memory.0[dst_begin_slot as usize..(dst_end_slot + 32) as usize].to_vec();
1639+
1640+
let mut copy_start = 0u64;
1641+
let mut first_set = true;
1642+
for idx in 0..code_slot_bytes.len() {
1643+
let value = memory.0[dst_begin_slot as usize + idx];
1644+
if idx as u64 + dst_begin_slot < dst_addr {
1645+
// front mask byte
1646+
copy_steps.push((value, false, true));
1647+
} else if idx as u64 + dst_begin_slot >= dst_addr + bytes_left {
1648+
// back mask byte
1649+
copy_steps.push((value, false, true));
16211650
} else {
1622-
(0, false, false)
1623-
};
1624-
copy_steps.push(step);
1625-
//TODO: change value to Word
1626-
self.memory_write(exec_step, (dst_addr + idx).into(), step.0)?;
1651+
// real copy byte
1652+
if first_set {
1653+
copy_start = idx as u64;
1654+
first_set = false;
1655+
}
1656+
1657+
let addr = src_addr
1658+
.checked_add(idx as u64 - copy_start)
1659+
.unwrap_or(src_addr_end);
1660+
let step = if addr < src_addr_end {
1661+
let code = bytecode.code.get(addr as usize).unwrap();
1662+
(code.value, code.is_code, false)
1663+
} else {
1664+
(0, false, false)
1665+
};
1666+
copy_steps.push(step);
1667+
}
1668+
}
1669+
1670+
let mut chunk_index = dst_begin_slot;
1671+
// memory word writes to destination word
1672+
for chunk in code_slot_bytes.chunks(32) {
1673+
let dest_word = Word::from_big_endian(&chunk);
1674+
self.memory_write_word(exec_step, chunk_index.into(), dest_word)?;
1675+
chunk_index = chunk_index + 32;
16271676
}
16281677

16291678
Ok(copy_steps)
@@ -1639,20 +1688,24 @@ impl<'a> CircuitInputStateRef<'a> {
16391688
bytes_left: u64, // number of bytes to copy, with padding
16401689
) -> Result<Vec<(u8, bool, bool)>, Error> {
16411690
let mut copy_steps = Vec::with_capacity(bytes_left as usize);
1691+
if bytes_left == 0 {
1692+
return Ok(copy_steps);
1693+
}
1694+
16421695
println!("calldata : {:?}", self.call_ctx()?.call_data);
16431696
let is_root = self.call()?.is_root;
1644-
let dst_end_slot_shift = (dst_addr + bytes_left) % 32;
16451697
// dest memory slot
1646-
let mut dst_end_slot = (dst_addr + bytes_left) - dst_end_slot_shift;
1647-
let mut dst_begin_slot = dst_addr - dst_addr % 32;
1698+
let (_, dst_begin_slot) = self.get_addr_shift_slot(dst_addr).unwrap();
1699+
let (_, dst_end_slot) = self.get_addr_shift_slot(dst_addr + bytes_left).unwrap();
16481700
let mut memory = if !is_root {
16491701
self.caller_ctx_mut()?.memory.clone()
16501702
} else {
16511703
// self.call_ctx_mut()?.memory already contains calldata
16521704
self.call_ctx_mut()?.memory.clone()
16531705
};
16541706

1655-
memory.extend_at_least(dst_end_slot_shift as usize + 32);
1707+
let minimal_length = dst_end_slot as usize + 32;
1708+
memory.extend_at_least(minimal_length);
16561709
// collect all bytes to calldata with padding word
16571710
let calldata_slot_bytes =
16581711
memory.0[dst_begin_slot as usize..(dst_end_slot + 32) as usize].to_vec();
@@ -1669,19 +1722,24 @@ impl<'a> CircuitInputStateRef<'a> {
16691722
// real copy byte
16701723
copy_steps.push((value, false, false));
16711724
}
1672-
//todo: memory word reads if it is an internal call
1673-
// self.push_op(
1674-
// exec_step,
1675-
// RW::READ,
1676-
// MemoryOp::new(self.call()?.caller_id, addr.into(), byte)
16771725
}
16781726

1727+
let mut chunk_index = dst_begin_slot;
16791728
// memory word writes to destination word
1729+
chunk_index = dst_begin_slot;
16801730
for chunk in calldata_slot_bytes.chunks(32) {
1681-
println!("{:?}", chunk);
16821731
let dest_word = Word::from_big_endian(&chunk);
1683-
self.memory_write_word(exec_step, dst_begin_slot.into(), dest_word)?;
1684-
dst_begin_slot = dst_begin_slot + 32;
1732+
// memory word reads if it is an internal call
1733+
if !is_root {
1734+
self.push_op(
1735+
exec_step,
1736+
RW::READ,
1737+
MemoryWordOp::new(self.call()?.caller_id, chunk_index.into(), dest_word),
1738+
);
1739+
}
1740+
1741+
self.memory_write_word(exec_step, chunk_index.into(), dest_word)?;
1742+
chunk_index = chunk_index + 32;
16851743
}
16861744

16871745
Ok(copy_steps)
@@ -1693,30 +1751,56 @@ impl<'a> CircuitInputStateRef<'a> {
16931751
src_addr: u64,
16941752
bytes_left: u64,
16951753
) -> Result<Vec<(u8, bool, bool)>, Error> {
1696-
// Get memory data
1697-
let mem = self
1698-
.call_ctx()?
1699-
.memory
1700-
.read_chunk(src_addr.into(), bytes_left.into());
1701-
17021754
let mut copy_steps = Vec::with_capacity(bytes_left as usize);
1703-
for (idx, byte) in mem.iter().enumerate() {
1704-
let addr = src_addr + idx as u64;
1755+
if bytes_left == 0 {
1756+
return Ok(copy_steps);
1757+
}
17051758

1706-
// Read memory
1707-
self.memory_read(exec_step, (addr as usize).into(), *byte)?;
1759+
let (_, dst_begin_slot) = self.get_addr_shift_slot(src_addr).unwrap();
1760+
let (_, dst_end_slot) = self.get_addr_shift_slot(src_addr + bytes_left).unwrap();
1761+
let mut memory = self.call_ctx_mut()?.memory.clone();
17081762

1709-
copy_steps.push((*byte, false, false));
1763+
let minimal_length = dst_end_slot as usize + 32;
1764+
memory.extend_at_least(minimal_length);
1765+
// collect all memory bytes with padding word
1766+
let log_slot_bytes =
1767+
memory.0[dst_begin_slot as usize..(dst_end_slot + 32) as usize].to_vec();
17101768

1711-
// Write log
1712-
self.tx_log_write(
1713-
exec_step,
1714-
self.tx_ctx.id(),
1715-
self.tx_ctx.log_id + 1,
1716-
TxLogField::Data,
1717-
idx,
1718-
Word::from(*byte),
1719-
)?;
1769+
let mut copy_start = 0u64;
1770+
let mut first_set = true;
1771+
let mut chunk_index = dst_begin_slot;
1772+
// memory word writes to destination word
1773+
for chunk in log_slot_bytes.chunks(32) {
1774+
let dest_word = Word::from_big_endian(&chunk);
1775+
self.memory_read_word(exec_step, chunk_index.into(), dest_word)?;
1776+
chunk_index = chunk_index + 32;
1777+
}
1778+
1779+
for idx in 0..log_slot_bytes.len() {
1780+
let value = memory.0[dst_begin_slot as usize + idx];
1781+
if idx as u64 + dst_begin_slot < src_addr {
1782+
// front mask byte
1783+
copy_steps.push((value, false, true));
1784+
} else if idx as u64 + dst_begin_slot >= src_addr + bytes_left {
1785+
// back mask byte
1786+
copy_steps.push((value, false, true));
1787+
} else {
1788+
// real copy byte
1789+
if first_set {
1790+
copy_start = idx as u64;
1791+
first_set = false;
1792+
}
1793+
copy_steps.push((value, false, false));
1794+
// Write log
1795+
self.tx_log_write(
1796+
exec_step,
1797+
self.tx_ctx.id(),
1798+
self.tx_ctx.log_id + 1,
1799+
TxLogField::Data,
1800+
idx - copy_start as usize,
1801+
Word::from(value),
1802+
)?;
1803+
}
17201804
}
17211805

17221806
Ok(copy_steps)

zkevm-circuits/src/copy_circuit.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,11 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
273273
},
274274
);
275275
cb.condition(meta.query_advice(is_last, Rotation::cur()), |cb| {
276-
cb.require_equal(
277-
"rwc_inc_left == rw_diff for last row in the copy slot",
278-
meta.query_advice(rwc_inc_left, Rotation::cur()),
279-
rw_diff,
280-
);
276+
// cb.require_equal(
277+
// "rwc_inc_left == rw_diff for last row in the copy slot",
278+
// meta.query_advice(rwc_inc_left, Rotation::cur()),
279+
// rw_diff,
280+
// );
281281
});
282282

283283
cb.gate(meta.query_fixed(q_enable, Rotation::cur()))
@@ -418,7 +418,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
418418

419419
meta.lookup_any("TxLog lookup", |meta| {
420420
let cond = meta.query_fixed(q_enable, Rotation::cur())
421-
* tag.value_equals(CopyDataType::TxLog, Rotation::cur())(meta);
421+
* tag.value_equals(CopyDataType::TxLog, Rotation::cur())(meta)
422+
* not::expr(meta.query_advice(mask, Rotation::cur()));
423+
422424
vec![
423425
meta.query_advice(rw_counter, Rotation::cur()),
424426
1.expr(),
@@ -441,7 +443,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
441443
meta.lookup_any("Bytecode lookup", |meta| {
442444
let cond = meta.query_fixed(q_enable, Rotation::cur())
443445
* tag.value_equals(CopyDataType::Bytecode, Rotation::cur())(meta)
444-
* not::expr(meta.query_advice(is_pad, Rotation::cur()));
446+
* not::expr(meta.query_advice(is_pad, Rotation::cur()))
447+
* not::expr(meta.query_advice(mask, Rotation::cur()));
448+
445449
vec![
446450
meta.query_advice(id, Rotation::cur()),
447451
BytecodeFieldTag::Byte.expr(),
@@ -1115,9 +1119,9 @@ mod tests {
11151119

11161120
fn gen_codecopy_data() -> CircuitInputBuilder {
11171121
let code = bytecode! {
1118-
PUSH32(Word::from(0x20))
1119-
PUSH32(Word::from(0x00))
1120-
PUSH32(Word::from(0x00))
1122+
PUSH32(Word::from(0x20)) // length
1123+
PUSH32(Word::from(0x00)) // codeOffset
1124+
PUSH32(Word::from(0x00)) // memOffset
11211125
CODECOPY
11221126
STOP
11231127
};
@@ -1252,7 +1256,7 @@ mod tests {
12521256

12531257
assert_error_matches(
12541258
test_copy_circuit_from_block(14, block),
1255-
vec!["Memory lookup", "Tx calldata lookup"],
1259+
vec!["Memory word lookup", "Tx calldata lookup"],
12561260
);
12571261
}
12581262

@@ -1268,7 +1272,7 @@ mod tests {
12681272

12691273
assert_error_matches(
12701274
test_copy_circuit_from_block(10, block),
1271-
vec!["Memory lookup", "Bytecode lookup"],
1275+
vec!["Memory word lookup", "Bytecode lookup"],
12721276
);
12731277
}
12741278

@@ -1284,7 +1288,7 @@ mod tests {
12841288

12851289
assert_error_matches(
12861290
test_copy_circuit_from_block(14, block),
1287-
vec!["Memory lookup", "Bytecode lookup"],
1291+
vec!["Memory word lookup", "Bytecode lookup"],
12881292
);
12891293
}
12901294

@@ -1316,7 +1320,7 @@ mod tests {
13161320

13171321
assert_error_matches(
13181322
test_copy_circuit_from_block(10, block),
1319-
vec!["Memory lookup", "TxLog lookup"],
1323+
vec!["Memory word lookup", "TxLog lookup"],
13201324
);
13211325
}
13221326

0 commit comments

Comments
 (0)