Skip to content

Commit 8610ac3

Browse files
committed
Memory constraints
1 parent af6d732 commit 8610ac3

File tree

12 files changed

+1585
-47
lines changed

12 files changed

+1585
-47
lines changed

barretenberg/cpp/pil/vm2/memory.pil

Lines changed: 139 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,59 @@
1+
include "constants_gen.pil";
2+
include "precomputed.pil";
13
include "range_check.pil";
24

35
namespace memory;
46

5-
pol commit sel;
6-
pol commit clk;
7-
pol commit address;
7+
// Link to design document: https://docs.google.com/document/d/1NM5zU39NG5xJReOjSObwWllbpqTCO4flr9v8kdRDK3E
8+
9+
// Main memory values directly derived from the memory events.
810
pol commit value;
911
pol commit tag;
10-
pol commit rw;
11-
pol commit space_id;
12+
pol commit space_id; // Memory space identifier (16-bit).
13+
pol commit address; // Memory address (32-bit).
14+
pol commit clk; // Clock cycle. (32-bit) Row index of corresponding opcode in the execution trace.
15+
pol commit rw; // Memory operation type: 0 for read, 1 for write.
16+
17+
// Boolean selectors.
18+
pol commit sel; // Main selector to toggle an active row (used by any interaction lookups.).
19+
pol commit last_access; // Last memory access for a given global address
20+
pol commit sel_rng_chk; // Every active row except the last one
21+
pol commit sel_tag_is_ff; // Toggles if tag == FF
22+
pol commit sel_rng_write; // Toggles if rw == 1 and tag != FF (Activate range check for write values.)
23+
24+
// Derived values.
25+
pol commit global_addr; // Global unique address derived from space_id and address.
26+
pol commit timestamp; // Timestamp derived from clk and rw.
27+
pol commit diff; // Difference between timestamp or global_addr values of consecutive rows to prove that memory is correctly sorted.
28+
pol commit limb[3]; // 16-bit decomposition limbs for diff.
29+
30+
// Others
31+
pol commit max_bits; // Number of bits corresponding to the tag. (Retrieved from precomputed subtrace.)
32+
33+
// Trace entries ordering.
34+
// The memory entries are sorted in ascending order by space_id, address, clk, then rw.
35+
// Equivalently, they are sorted by global_addr and then timestamp.
36+
// last_access == 1 iff the last memory access for a given global address.
37+
38+
// Trace shape
39+
40+
// +-----+-----------+-------+-----+-----+---------------+------------+------------+--------------+-------------+
41+
// | sel | space_id | address | clk | rw | global_addr | timestamp | last_access| diff | sel_rng_chk |
42+
// +-----+-----------+-------+-----+-----+---------------+------------+------------+--------------+-------------+
43+
// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 11 | 0 |
44+
// | 1 | 1 | 27 | 5 | 1 | 2^32 + 27 | 5 * 2 + 1 | 1 | 1 | 1 |
45+
// | 1 | 1 | 28 | 12 | 1 | 2^32 + 28 | 12 * 2 + 1 | 1 | 2 * 2^32 + 3 | 1 |
46+
// | 1 | 3 | 31 | 7 | 0 | 3 * 2^32 + 31 | 7 * 2 + 0 | 0 | 3 | 1 |
47+
// | 1 | 3 | 31 | 8 | 1 | 3 * 2^32 + 31 | 8 * 2 + 1 | 1 | 2^32 + 15 | 1 |
48+
// | 1 | 4 | 46 | 5 | 0 | 4 * 2^32 + 46 | 5 * 2 + 0 | 0 | 0 | 1 |
49+
// | 1 | 4 | 46 | 5 | 0 | 4 * 2^32 + 46 | 5 * 2 + 0 | 0 | 1 | 1 |
50+
// | 1 | 4 | 46 | 5 | 1 | 4 * 2^32 + 46 | 5 * 2 + 1 | 0 | 1 | 1 |
51+
// | 1 | 4 | 46 | 6 | 1 | 4 * 2^32 + 46 | 6 * 2 + 1 | 1 | 43 | 1 |
52+
// | 1 | 4 | 89 | 2 | 1 | 4 * 2^32 + 89 | 2 * 2 + 1 | 1 |-4 * 2^32 - 89| 0 |
53+
// +-----+-----------+-------+-----+-------------+----------------------+----------+--------------+-------------+
54+
55+
#[skippable_if]
56+
sel + precomputed.first_row = 0;
1257

1358
// Permutation selectors (addressing.pil).
1459
pol commit sel_addressing_base;
@@ -53,11 +98,94 @@ sel = // Addressing.
5398
+ sel_sha256_op[4] + sel_sha256_op[5] + sel_sha256_op[6] + sel_sha256_op[7];
5499
*/
55100

56-
#[skippable_if]
57-
sel = 0;
101+
// Other boolean constraints.
102+
sel * (1 - sel) = 0; // Ensure mutual exclusivity of the permutation selectors.
103+
last_access * (1 - last_access) = 0;
104+
rw * (1 - rw) = 0; // TODO: should already be constrained by each source of interaction lookups.
105+
sel_tag_is_ff * (1 - sel_tag_is_ff) = 0;
106+
107+
// Trace must be contiguous.
108+
// Except on first row: sel == 0 ==> sel' == 0
109+
// As a consequence, the trace is empty or it starts just after the first row and
110+
// is contiguous.
111+
#[MEM_CONTIGUOUS]
112+
(1 - precomputed.first_row) * (1 - sel) * sel' = 0;
113+
114+
// Boolean toggles at all active rows except the last one.
115+
// It is boolean by definition.
116+
#[SEL_RNG_CHK]
117+
sel_rng_chk = sel * sel';
118+
119+
// Derived values.
120+
121+
#[GLOBAL_ADDR]
122+
global_addr = space_id * 2**32 + address;
123+
124+
#[TIMESTAMP]
125+
timestamp = 2 * clk + rw;
126+
127+
// last_access derivation
128+
// last_access == 0 iff global_addr' == global_addr
129+
pol GLOB_ADDR_DIFF = global_addr' - global_addr;
130+
pol commit glob_addr_diff_inv; // Helper column for non zero equality check
131+
#[LAST_ACCESS]
132+
sel_rng_chk * (GLOB_ADDR_DIFF * ((1 - last_access) * (1 - glob_addr_diff_inv) + glob_addr_diff_inv) - last_access) = 0;
133+
134+
// diff derivation
135+
// We alternate between two different diffs:
136+
// - last_access == 1: diff = global_addr' - global_addr
137+
// - last_access == 0: diff = timestamp' - timestamp - rw' * rw
138+
#[DIFF]
139+
diff = sel_rng_chk * (last_access * GLOB_ADDR_DIFF + (1 - last_access) * (timestamp' - timestamp - rw' * rw));
140+
141+
// Decompose diff into 16-bit limbs.
142+
#[DIFF_DECOMP]
143+
diff = limb[0] + limb[1] * 2**16 + limb[2] * 2**32;
144+
145+
// Range check limbs
146+
147+
#[RANGE_CHECK_LIMB_0]
148+
sel_rng_chk { limb[0] } in precomputed.sel_range_16 { precomputed.clk };
149+
#[RANGE_CHECK_LIMB_1]
150+
sel_rng_chk { limb[1] } in precomputed.sel_range_16 { precomputed.clk };
151+
#[RANGE_CHECK_LIMB_2]
152+
sel_rng_chk { limb[2] } in precomputed.sel_range_16 { precomputed.clk };
153+
154+
// Memory Initialization
155+
#[MEMORY_INIT_VALUE]
156+
(last_access + precomputed.first_row) * (1 - rw') * value' = 0;
157+
#[MEMORY_INIT_TAG]
158+
(last_access + precomputed.first_row) * (1 - rw') * (tag' - constants.MEM_TAG_FF) = 0;
159+
160+
// Read-Write Consistency
161+
// Note that last_access == 0 on the first row with our trace generation. If the first
162+
// memory entry is a READ, then the following constraints will be satisifed because
163+
// value == 0 and tag == MEM_TAG_FF == 0 will be set on the first row which corresponds
164+
// to an initial read.
165+
#[READ_WRITE_CONSISTENCY_VALUE]
166+
(1 - last_access) * (1 - rw') * (value' - value) = 0;
167+
#[READ_WRITE_CONSISTENCY_TAG]
168+
(1 - last_access) * (1 - rw') * (tag' - tag) = 0;
169+
170+
171+
// We prove that sel_tag_is_ff == 1 <==> tag == MEM_TAG_FF
172+
pol TAG_FF_DIFF = tag - constants.MEM_TAG_FF;
173+
pol commit tag_ff_diff_inv;
174+
#[TAG_IS_FF]
175+
sel * (TAG_FF_DIFF * (sel_tag_is_ff * (1 - tag_ff_diff_inv) + tag_ff_diff_inv) + sel_tag_is_ff - 1) = 0;
176+
177+
// Boolean by definition.
178+
#[SEL_RNG_WRITE]
179+
sel_rng_write = rw * (1 - sel_tag_is_ff);
58180

59-
sel * (sel - 1) = 0;
60-
rw * (1 - rw) = 0;
181+
// Retrieve the number of bits for the range check
182+
#[TAG_MAX_BITS]
183+
sel_rng_write { tag, max_bits }
184+
in
185+
precomputed.sel_tag_parameters { precomputed.clk, precomputed.tag_max_bits };
61186

62-
// TODO: consider tag-value consistency checking.
63-
// TODO: consider address range checking.
187+
// Range check for the tagged value validation
188+
#[RANGE_CHECK_WRITE_TAGGED_VALUE]
189+
sel_rng_write {value, max_bits}
190+
in
191+
range_check.sel { range_check.value, range_check.rng_chk_bits };

barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ template <typename Flavor> class SumcheckProverRound {
267267
// Construct extended univariates containers; one per thread
268268
ExtendedEdges extended_edges;
269269
for (size_t chunk_idx = 0; chunk_idx < num_of_chunks; chunk_idx++) {
270-
size_t start = chunk_idx * chunk_size + thread_idx * chunk_thread_portion_size;
271-
size_t end = chunk_idx * chunk_size + (thread_idx + 1) * chunk_thread_portion_size;
270+
size_t start = (chunk_idx * chunk_size) + (thread_idx * chunk_thread_portion_size);
271+
size_t end = (chunk_idx * chunk_size) + ((thread_idx + 1) * chunk_thread_portion_size);
272272
for (size_t edge_idx = start; edge_idx < end; edge_idx += 2) {
273273
extend_edges(extended_edges, polynomials, edge_idx);
274274
// Compute the \f$ \ell \f$-th edge's univariate contribution,

barretenberg/cpp/src/barretenberg/vm2/constraining/check_circuit.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ void run_check_circuit(AvmFlavor::ProverPolynomials& polys, size_t num_rows, boo
5858
}
5959
}
6060
}
61-
// Do final check for the linearly dependent part of the relation.
61+
// Do final check (accumulation over all rows) for all the subrelations and in
62+
// particular the linearly dependent ones. (Linearly independent sub-relations are
63+
// checked to be zero as each row by the above loop so their accumulation is zero.)
6264
for (size_t j = 0; j < result.size(); ++j) {
6365
if (!result[j].is_zero()) {
6466
throw std::runtime_error(format(

0 commit comments

Comments
 (0)