1
1
// ---------------------------------------------------------------------------//
2
2
// Copyright (c) 2024 Elena Tatuzova <[email protected] >
3
+ // Copyright (c) 2025 Antoine Cyr <[email protected] >
3
4
//
4
5
// MIT License
5
6
//
23
24
// ---------------------------------------------------------------------------//
24
25
#pragma once
25
26
26
- #include < nil/blueprint/zkevm_bbf/types/hashed_buffers.hpp>
27
27
#include < nil/blueprint/zkevm_bbf/subcomponents/bytecode_table.hpp>
28
28
#include < nil/blueprint/zkevm_bbf/subcomponents/keccak_table.hpp>
29
+ #include < nil/blueprint/zkevm_bbf/types/hashed_buffers.hpp>
29
30
30
31
namespace nil {
31
32
namespace blueprint {
32
33
namespace bbf {
34
+ // Input: zkevm_keccak_buffers, rlc_challenge
35
+ // Integrates bytecode_table and keccak_table with additional constraints and
36
+ // lookups to verify zkEVM bytecode properties.
33
37
template <typename FieldType, GenerationStage stage>
34
38
class bytecode : public generic_component <FieldType, stage> {
35
39
using typename generic_component<FieldType, stage>::context_type;
@@ -39,74 +43,81 @@ namespace nil {
39
43
using generic_component<FieldType, stage>::lookup;
40
44
using generic_component<FieldType, stage>::lookup_table;
41
45
42
- using BytecodeTable = bytecode_table<FieldType,stage>;
43
- using KeccakTable = keccak_table<FieldType,stage>;
46
+ using BytecodeTable = bytecode_table<FieldType, stage>;
47
+ using KeccakTable = keccak_table<FieldType, stage>;
44
48
45
- public:
49
+ public:
46
50
using typename generic_component<FieldType, stage>::table_params;
47
- using typename generic_component<FieldType,stage>::TYPE;
51
+ using typename generic_component<FieldType, stage>::TYPE;
48
52
49
53
struct input_type {
50
- TYPE rlc_challenge;
51
-
52
- BytecodeTable::input_type bytecodes;
53
- KeccakTable::private_input_type keccak_buffers;
54
+ TYPE rlc_challenge; // Random linear combination challenge
55
+ BytecodeTable::input_type bytecodes; // Bytecode buffers with hashes
56
+ KeccakTable::private_input_type
57
+ keccak_buffers; // Keccak-specific input
54
58
};
55
59
56
60
std::size_t max_bytecode_size;
57
61
std::size_t max_keccak_blocks;
58
62
59
- static table_params get_minimal_requirements (std::size_t max_bytecode_size,
60
- std::size_t max_keccak_blocks) {
61
- return {
62
- .witnesses = 15 ,
63
- .public_inputs = 1 ,
64
- .constants = 10 ,
65
- .rows = max_bytecode_size + max_keccak_blocks
66
- };
63
+ static table_params get_minimal_requirements (
64
+ std::size_t max_bytecode_size, std::size_t max_keccak_blocks) {
65
+ return {.witnesses = 15 ,
66
+ .public_inputs = 1 ,
67
+ .constants = 10 ,
68
+ .rows = max_bytecode_size + max_keccak_blocks};
67
69
}
68
70
69
- static void allocate_public_inputs (
70
- context_type &context, input_type &input,
71
- std::size_t max_bytecode_size, std::size_t max_keccak_blocks) {
72
- context.allocate (input.rlc_challenge , 0 , 0 , column_type::public_input);
71
+ static void allocate_public_inputs (context_type &context,
72
+ input_type &input,
73
+ std::size_t max_bytecode_size,
74
+ std::size_t max_keccak_blocks) {
75
+ context.allocate (input.rlc_challenge , 0 , 0 ,
76
+ column_type::public_input);
73
77
}
74
78
75
- bytecode (context_type &context_object,
76
- input_type input,
77
- std:: size_t max_bytecode_size_,
78
- std:: size_t max_keccak_blocks_
79
- ) : max_bytecode_size(max_bytecode_size_),
80
- max_keccak_blocks (max_keccak_blocks_),
81
- generic_component<FieldType,stage>(context_object)
82
- {
83
- std::vector<std:: size_t > bytecode_lookup_area = { 0 , 1 , 2 , 3 , 4 , 5 } ;
84
- std::vector<std:: size_t > keccak_lookup_area = { 0 , 1 , 2 , 3 };
85
- context_type bytecode_ct = context_object.subcontext (bytecode_lookup_area, 0 , max_bytecode_size);
86
- context_type keccak_ct = context_object. subcontext ( keccak_lookup_area, max_bytecode_size, max_bytecode_size + max_keccak_blocks);
79
+ bytecode (context_type &context_object, input_type input,
80
+ std:: size_t max_bytecode_size_, std:: size_t max_keccak_blocks_)
81
+ : max_bytecode_size( max_bytecode_size_) ,
82
+ max_keccak_blocks ( max_keccak_blocks_),
83
+ generic_component<FieldType, stage>(context_object) {
84
+ std::vector<std:: size_t > bytecode_lookup_area = { 0 , 1 , 2 , 3 , 4 , 5 };
85
+ std::vector<std:: size_t > keccak_lookup_area = { 0 , 1 , 2 , 3 };
86
+ context_type bytecode_ct = context_object. subcontext (
87
+ bytecode_lookup_area, 0 , max_bytecode_size) ;
88
+ context_type keccak_ct =
89
+ context_object.subcontext (keccak_lookup_area, max_bytecode_size,
90
+ max_bytecode_size + max_keccak_blocks);
87
91
88
92
BytecodeTable bc_t (bytecode_ct, input.bytecodes , max_bytecode_size);
89
- KeccakTable (keccak_ct, {input.rlc_challenge , input.keccak_buffers }, max_keccak_blocks);
93
+ KeccakTable (keccak_ct, {input.rlc_challenge , input.keccak_buffers },
94
+ max_keccak_blocks);
90
95
91
96
const std::vector<TYPE> &tag = bc_t .tag ;
92
97
const std::vector<TYPE> &index = bc_t .index ;
93
98
const std::vector<TYPE> &value = bc_t .value ;
94
99
const std::vector<TYPE> &is_opcode = bc_t .is_opcode ;
95
100
const std::vector<TYPE> &hash_hi = bc_t .hash_hi ;
96
101
const std::vector<TYPE> &hash_lo = bc_t .hash_lo ;
97
- std::vector<TYPE> rlc_challenge = std::vector<TYPE>(max_bytecode_size);
98
- std::vector<TYPE> push_size = std::vector<TYPE>(max_bytecode_size);
99
- std::vector<TYPE> length_left = std::vector<TYPE>(max_bytecode_size);
100
- std::vector<TYPE> value_rlc = std::vector<TYPE>(max_bytecode_size);
101
- std::vector<TYPE> is_byte = std::vector<TYPE>(max_bytecode_size);
102
+ std::vector<TYPE> rlc_challenge (
103
+ max_bytecode_size); // RLC challenge per row
104
+ std::vector<TYPE> push_size (
105
+ max_bytecode_size); // Tracks PUSH operation size
106
+ std::vector<TYPE> length_left (
107
+ max_bytecode_size); // Remaining length of bytecode
108
+ std::vector<TYPE> value_rlc (
109
+ max_bytecode_size); // RLC of bytecode values
110
+ std::vector<TYPE> is_byte (
111
+ max_bytecode_size); // Flags if row is a byte
102
112
103
113
if constexpr (stage == GenerationStage::ASSIGNMENT) {
104
114
std::size_t cur = 0 ;
105
115
const auto &bytecodes = input.bytecodes .get_data ();
106
- for (std::size_t i = 0 ; i < bytecodes.size (); i++){
116
+ for (std::size_t i = 0 ; i < bytecodes.size (); i++) {
107
117
TYPE push_size_value = 0 ;
108
118
auto buffer = bytecodes[i].first ;
109
119
TYPE length_left_value = buffer.size ();
120
+
110
121
// HEADER
111
122
rlc_challenge[cur] = input.rlc_challenge ;
112
123
push_size[cur] = 0 ;
@@ -116,75 +127,115 @@ namespace nil {
116
127
push_size_value = 0 ;
117
128
length_left_value--;
118
129
cur++;
130
+
119
131
// BYTES
120
- for (std::size_t j = 0 ; j < bytecodes[i].first .size (); j++, cur++){
132
+ for (std::size_t j = 0 ; j < bytecodes[i].first .size ();
133
+ j++, cur++) {
121
134
auto byte = buffer[j];
122
- if (push_size_value == 0 ){
123
- if (byte > 0x5f && byte < 0x80 ) push_size_value = byte - 0x5f ;
135
+ if (push_size_value == 0 ) {
136
+ if (byte > 0x5f && byte < 0x80 )
137
+ push_size_value = byte - 0x5f ; // Set PUSH size
124
138
} else {
125
139
push_size_value--;
126
140
}
127
- is_byte[cur] = tag[cur] * (3 - tag[cur]) * TYPE (2 ).inversed ();
141
+ is_byte[cur] =
142
+ tag[cur] * (3 - tag[cur]) * TYPE (2 ).inversed ();
128
143
push_size[cur] = push_size_value;
129
144
rlc_challenge[cur] = input.rlc_challenge ;
130
145
length_left[cur] = length_left_value;
131
- value_rlc[cur] = value_rlc[cur - 1 ] * input.rlc_challenge + byte;
146
+ value_rlc[cur] =
147
+ value_rlc[cur - 1 ] * input.rlc_challenge + byte;
132
148
length_left_value--;
133
149
}
134
150
}
135
151
}
136
- // allocate things that are not part of bytecode_table
137
- for (std::size_t i = 0 ; i < max_bytecode_size; i++) {
138
- allocate (push_size[i],6 ,i);
139
- allocate (value_rlc[i],7 ,i);
140
- allocate (length_left[i],8 ,i);
141
- allocate (rlc_challenge[i],9 ,i);
142
- allocate (is_byte[i],10 ,i);
152
+
153
+ // Allocate columns that are not part of the bytecode_table
154
+ for (std::size_t i = 0 ; i < max_bytecode_size; i++) {
155
+ allocate (push_size[i], 6 , i);
156
+ allocate (value_rlc[i], 7 , i);
157
+ allocate (length_left[i], 8 , i);
158
+ allocate (rlc_challenge[i], 9 , i);
159
+ allocate (is_byte[i], 10 , i);
143
160
}
144
- // constrain all bytecode values
145
- // if (make_links) {
146
- // copy_constrain(input.rlc_challenge, rlc_challenge[0]);
147
- // }
161
+
148
162
static const auto zerohash = zkevm_keccak_hash ({});
149
- for (std::size_t i = 0 ; i < max_bytecode_size; i++) {
150
- constrain (tag[i] * (tag[i] - 1 ) * (tag[i] - 2 )); // 0. TAG is zeroes, one or two
151
- constrain ((tag[i] - 2 ) * (tag[i] - 1 ) * index[i]); // 1. INDEX for HEADER and unused bytes is zero
152
- constrain ((tag[i] - 2 ) * (tag[i] - 1 ) * (length_left[i] - value[i])); // 4. In contract header length_left == contract length
153
- constrain (is_opcode[i] * (is_opcode[i] - 1 )); // 7. is_opcode is zeroes or ones
154
- constrain ((tag[i] - 2 ) * (tag[i] - 1 ) * is_opcode[i]); // 8. is_opcode on HEADER are zeroes
155
- constrain ((tag[i] - 2 ) * (tag[i] - 1 ) * (value_rlc[i] - length_left[i])); // 14. value_rlc for HEADERS == 0;
163
+ for (std::size_t i = 0 ; i < max_bytecode_size; i++) {
164
+ // 0. TAG is zeroes, one or two
165
+ constrain (tag[i] * (tag[i] - 1 ) * (tag[i] - 2 ));
166
+ // 1. INDEX for HEADER and unused bytes is zero
167
+ constrain ((tag[i] - 2 ) * (tag[i] - 1 ) * index[i]);
168
+ // 4. In contract header length_left == contract length
169
+ constrain ((tag[i] - 2 ) * (tag[i] - 1 ) *
170
+ (length_left[i] - value[i]));
171
+ // 7. is_opcode is zeroes or ones
172
+ constrain (is_opcode[i] * (is_opcode[i] - 1 ));
173
+ // 8. is_opcode on HEADER are zeroes
174
+ constrain ((tag[i] - 2 ) * (tag[i] - 1 ) * is_opcode[i]);
175
+ // 14. value_rlc for HEADERS == length_left
176
+ constrain ((tag[i] - 2 ) * (tag[i] - 1 ) *
177
+ (value_rlc[i] - length_left[i]));
156
178
157
179
if (i > 0 ) {
158
- constrain ((tag[i-1 ] - 2 ) * (tag[i-1 ] - 1 ) * index[i]); // 2. INDEX for first contract byte is zero
159
- constrain (tag[i-1 ] * tag[i] * (index[i] - index[i-1 ] - 1 )); // 3. INDEX is incremented for all bytes
160
- constrain (tag[i] * (length_left[i-1 ] - length_left[i] - 1 )); // 5. In contract bytes each row decrement length_left
161
- constrain (tag[i-1 ] * (tag[i] - 2 ) * (tag[i] - 1 ) * length_left[i-1 ]); // 6. Length_left is zero for last byte in the contract
162
- constrain ((tag[i] - 2 ) * (tag[i-1 ] - 1 ) * tag[i] * (is_opcode[i] - 1 )); // 9. Fist is_opcode on BYTE after HEADER is 1
163
- constrain ((tag[i] - 2 ) * tag[i] * (is_opcode[i] - 1 ) * (push_size[i-1 ] - push_size[i] - 1 )); // 10. PUSH_SIZE decreases for non-opcodes except metadata
164
- constrain (is_opcode[i] * push_size[i-1 ]); // 11. before opcode push_size is always zero
165
- constrain (tag[i] * (hash_hi[i-1 ] - hash_hi[i])); // 12. for all bytes hash is similar to previous
166
- constrain (tag[i] * (hash_lo[i-1 ] - hash_lo[i])); // 13. for all bytes hash is similar to previous
167
- constrain (tag[i] * (value_rlc[i] - value_rlc[i-1 ] * rlc_challenge[i] - value[i])); // 15. for all bytes RLC is correct
168
- constrain (tag[i] * (rlc_challenge[i] - rlc_challenge[i-1 ])); // 16. for each BYTEs rlc_challenge are similar
180
+ // 2. INDEX for first contract byte is zero
181
+ constrain ((tag[i - 1 ] - 2 ) * (tag[i - 1 ] - 1 ) * index[i]);
182
+ // 3. INDEX is incremented for all bytes
183
+ constrain (tag[i - 1 ] * tag[i] *
184
+ (index[i] - index[i - 1 ] - 1 ));
185
+ // 5. In contract bytes each row decrement length_left
186
+ constrain (tag[i] * (length_left[i - 1 ] - length_left[i] - 1 ));
187
+ // 6. Length_left is zero for last byte in the contract
188
+ constrain (tag[i - 1 ] * (tag[i] - 2 ) * (tag[i] - 1 ) *
189
+ length_left[i - 1 ]);
190
+ // 9. First is_opcode on BYTE after HEADER is 1
191
+ constrain ((tag[i] - 2 ) * (tag[i - 1 ] - 1 ) * tag[i] *
192
+ (is_opcode[i] - 1 ));
193
+ // 10. PUSH_SIZE decreases for non-opcodes except metadata
194
+ constrain ((tag[i] - 2 ) * tag[i] * (is_opcode[i] - 1 ) *
195
+ (push_size[i - 1 ] - push_size[i] - 1 ));
196
+ // 11. before opcode push_size is always zero
197
+ constrain (is_opcode[i] * push_size[i - 1 ]);
198
+ // 12. for all bytes hash is similar to previous
199
+ constrain (tag[i] * (hash_hi[i - 1 ] - hash_hi[i]));
200
+ // 13. for all bytes hash is similar to previous
201
+ constrain (tag[i] * (hash_lo[i - 1 ] - hash_lo[i]));
202
+ // 15. for all bytes RLC is correct
203
+ constrain (tag[i] *
204
+ (value_rlc[i] -
205
+ value_rlc[i - 1 ] * rlc_challenge[i] - value[i]));
206
+ // 16. for each BYTEs rlc_challenge are similar
207
+ constrain (tag[i] * (rlc_challenge[i] - rlc_challenge[i - 1 ]));
169
208
}
170
- if (i> 0 && i < max_bytecode_size-1 ) {
171
- constrain (tag[i+1 ] * (rlc_challenge[i] - rlc_challenge[i-1 ])); // 17. rlc_challenge is similar for different contracts
209
+ if (i > 0 && i < max_bytecode_size - 1 ) {
210
+ // 17. rlc_challenge is similar for different contracts
211
+ constrain (tag[i + 1 ] *
212
+ (rlc_challenge[i] - rlc_challenge[i - 1 ]));
172
213
}
173
- lookup (tag[i]*value[i]*(2 -tag[i])," byte_range_table/full" );
174
- lookup (std::vector<TYPE>({value[i]*is_opcode[i], push_size[i]*is_opcode[i], is_opcode[i]})," zkevm_opcodes/full" );
175
-
176
- if ( i > 0 ){
177
- // is last
178
- lookup (std::vector<TYPE>({
179
- tag[i] + 1 - tag[i], // TODO: update math::expression constructor with constant parameter
180
- is_byte[i-1 ] * (1 - is_byte[i]) * value_rlc[i-1 ],
181
- is_byte[i-1 ] * (1 - is_byte[i]) * hash_hi[i-1 ] + (1 - is_byte[i-1 ] * (1 - is_byte[i])) * w_hi<FieldType>(zerohash),
182
- is_byte[i-1 ] * (1 - is_byte[i]) * hash_lo[i-1 ] + (1 - is_byte[i-1 ] * (1 - is_byte[i])) * w_lo<FieldType>(zerohash)
183
- }), " keccak_table" );
214
+ lookup (tag[i] * value[i] * (2 - tag[i]), " byte_range_table/full" );
215
+ lookup (std::vector<TYPE>({value[i] * is_opcode[i],
216
+ push_size[i] * is_opcode[i],
217
+ is_opcode[i]}),
218
+ " zkevm_opcodes/full" );
219
+
220
+ if (i > 0 ) {
221
+ // Lookup for last byte
222
+ lookup (
223
+ std::vector<TYPE>(
224
+ {// TODO: update math::expression constructor with
225
+ // constant parameter
226
+ tag[i] + 1 - tag[i],
227
+ is_byte[i - 1 ] * (1 - is_byte[i]) * value_rlc[i - 1 ],
228
+ is_byte[i - 1 ] * (1 - is_byte[i]) * hash_hi[i - 1 ] +
229
+ (1 - is_byte[i - 1 ] * (1 - is_byte[i])) *
230
+ w_hi<FieldType>(zerohash),
231
+ is_byte[i - 1 ] * (1 - is_byte[i]) * hash_lo[i - 1 ] +
232
+ (1 - is_byte[i - 1 ] * (1 - is_byte[i])) *
233
+ w_lo<FieldType>(zerohash)}),
234
+ " keccak_table" );
184
235
}
185
236
}
186
237
};
187
238
};
188
- }
189
- }
190
- }
239
+ } // namespace bbf
240
+ } // namespace blueprint
241
+ } // namespace nil
0 commit comments