Skip to content

Commit b4eaa64

Browse files
committed
feat: add GroupByExec in sol
1 parent 26189a7 commit b4eaa64

File tree

7 files changed

+680
-3
lines changed

7 files changed

+680
-3
lines changed

crates/proof-of-sql/src/sql/evm_proof_plan/plans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,10 @@ impl EVMSliceExec {
306306
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
307307
pub(crate) struct EVMGroupByExec {
308308
table_number: usize,
309+
where_clause: EVMDynProofExpr,
309310
group_by_exprs: Vec<usize>,
310311
sum_expr: Vec<EVMDynProofExpr>,
311312
count_alias_name: String,
312-
where_clause: EVMDynProofExpr,
313313
}
314314

315315
impl EVMGroupByExec {

solidity/src/base/Constants.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ uint32 constant CAST_EXPR_VARIANT = 9;
104104

105105
/// @dev Filter variant constant for proof plans
106106
uint32 constant FILTER_EXEC_VARIANT = 0;
107+
/// @dev Group By variant constant for proof plans
108+
uint32 constant GROUP_BY_EXEC_VARIANT = 1;
107109

108110
/// @dev Boolean variant constant for column types
109111
uint32 constant DATA_TYPE_BOOLEAN_VARIANT = 0;
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
// This is licensed under the Cryptographic Open Software License 1.0
3+
pragma solidity ^0.8.28;
4+
5+
import "../base/Constants.sol";
6+
import "../base/Errors.sol";
7+
import {VerificationBuilder} from "../builder/VerificationBuilder.pre.sol";
8+
9+
/// @title GroupByExec
10+
/// @dev Library for handling group by execution plans
11+
library GroupByExec {
12+
/// @notice Evaluates a group by execution plan
13+
/// @custom:as-yul-wrapper
14+
/// #### Wrapped Yul Function
15+
/// ##### Signature
16+
/// ```yul
17+
/// group_by_exec_evaluate(plan_ptr, builder_ptr) -> plan_ptr_out, evaluations_ptr
18+
/// ```
19+
/// ##### Parameters
20+
/// * `plan_ptr` - calldata pointer to the group by execution plan
21+
/// * `builder_ptr` - memory pointer to the verification builder
22+
/// ##### Return Values
23+
/// * `plan_ptr_out` - pointer to the remaining plan after consuming the group by execution plan
24+
/// * `evaluations_ptr` - pointer to the evaluations
25+
/// @notice Evaluates expressions for group by operation and verifies the aggregation constraints
26+
/// @notice ##### Constraints
27+
/// * Inputs: \\(G_1,\ldots,G_\ell=\texttt{g_in}\\), \\(A_1,\ldots,A_m=\texttt{sum_in}\\),
28+
/// and \\(S=\texttt{sel_in}\\) with input characteristic \\(\chi_{[0,n)}=\texttt{input_chi_eval}\\).
29+
/// * Outputs: \\(G_1',\ldots,G_\ell'=\texttt{g_out}\\), \\(A_1',\ldots,A_m'=\texttt{sum_out}\\),
30+
/// and \\(C=\texttt{count_out}\\) with output characteristic \\(\chi_{[0,m')}=\texttt{output_chi_eval}\\).
31+
/// * Challenges: \\(\alpha=\texttt{alpha}\\), \\(\beta=\texttt{beta}\\)
32+
/// * Helpers:
33+
/// \\(g_{in,fold} \=\texttt{g_in_fold} :\equiv \alpha \sum_{j=1}^{\ell} G_j \beta^{\ell-j}\\)
34+
/// \\(g_{out,fold} \=\texttt{g_out_fold} :\equiv \alpha \sum_{j=1}^{\ell} G_j' \beta^{\ell-j}\\)
35+
/// \\(sum_{in,fold} \=\texttt{sum_in_fold} :\equiv \chi_{[0,n)} + \beta \sum_{j=1}^{m} A_j \beta^{m-j}\\)
36+
/// \\(sum_{out,fold} \=\texttt{sum_out_fold} :\equiv C + \beta \sum_{j=1}^{m} A_j' \beta^{m-j}\\)
37+
/// \\(G^\star = \texttt{g_in_star}\\) and \\(G^{\star'} = \texttt{g_out_star}\\)
38+
/// * Constraints:
39+
/// \\[\begin{aligned}
40+
/// G^\star \cdot S \cdot sum_{in,fold} - G^{\star'} \cdot sum_{out,fold} &\overset{\sum}{=} 0\\\\
41+
/// G^\star + G^\star \cdot g_{in,fold} - \chi_{[0,n)} &\equiv 0\\\\
42+
/// G^{\star'} + G^{\star'} \cdot g_{out,fold} - \chi_{[0,m')} &\equiv 0\\\\
43+
/// \end{aligned}\\]
44+
/// Note: the notation \\(A\overset{\sum}{=}B\\) is used to indicate the zero-sum constraint.
45+
/// @dev Evaluates a group by execution plan
46+
/// @param __plan The group by execution plan data
47+
/// @param __builder The verification builder
48+
/// @return __planOut The remaining plan after processing
49+
/// @return __builderOut The verification builder result
50+
/// @return __evaluationsPtr The evaluations pointer
51+
function __groupByExecEvaluate( // solhint-disable-line gas-calldata-parameters
52+
bytes calldata __plan, VerificationBuilder.Builder memory __builder)
53+
external
54+
pure
55+
returns (
56+
bytes calldata __planOut,
57+
VerificationBuilder.Builder memory __builderOut,
58+
uint256[] memory __evaluationsPtr
59+
)
60+
{
61+
uint256[] memory __evaluations;
62+
assembly {
63+
// IMPORT-YUL ../base/Errors.sol
64+
function err(code) {
65+
revert(0, 0)
66+
}
67+
// IMPORT-YUL ../base/Queue.pre.sol
68+
function dequeue(queue_ptr) -> value {
69+
revert(0, 0)
70+
}
71+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
72+
function builder_consume_challenge(builder_ptr) -> value {
73+
revert(0, 0)
74+
}
75+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
76+
function builder_consume_final_round_mle(builder_ptr) -> value {
77+
revert(0, 0)
78+
}
79+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
80+
function builder_consume_chi_evaluation(builder_ptr) -> value {
81+
revert(0, 0)
82+
}
83+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
84+
function builder_produce_zerosum_constraint(builder_ptr, evaluation, degree) {
85+
revert(0, 0)
86+
}
87+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
88+
function builder_produce_identity_constraint(builder_ptr, evaluation, degree) {
89+
revert(0, 0)
90+
}
91+
// IMPORT-YUL ../base/SwitchUtil.pre.sol
92+
function case_const(lhs, rhs) {
93+
revert(0, 0)
94+
}
95+
// IMPORT-YUL ../base/Array.pre.sol
96+
function get_array_element(arr_ptr, index) -> value {
97+
revert(0, 0)
98+
}
99+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
100+
function builder_get_column_evaluation(builder_ptr, column_num) -> value {
101+
revert(0, 0)
102+
}
103+
// IMPORT-YUL ../proof_exprs/ColumnExpr.pre.sol
104+
function column_expr_evaluate(expr_ptr, builder_ptr) -> expr_ptr_out, eval {
105+
revert(0, 0)
106+
}
107+
// IMPORT-YUL ../proof_exprs/LiteralExpr.pre.sol
108+
function literal_expr_evaluate(expr_ptr, chi_eval) -> expr_ptr_out, eval {
109+
revert(0, 0)
110+
}
111+
// IMPORT-YUL ../proof_exprs/EqualsExpr.pre.sol
112+
function equals_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, result_eval {
113+
revert(0, 0)
114+
}
115+
// IMPORT-YUL ../proof_exprs/AddExpr.pre.sol
116+
function add_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
117+
revert(0, 0)
118+
}
119+
// IMPORT-YUL ../proof_exprs/SubtractExpr.pre.sol
120+
function subtract_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
121+
revert(0, 0)
122+
}
123+
// IMPORT-YUL ../proof_exprs/MultiplyExpr.pre.sol
124+
function multiply_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
125+
revert(0, 0)
126+
}
127+
// IMPORT-YUL ../proof_exprs/AndExpr.pre.sol
128+
function and_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
129+
revert(0, 0)
130+
}
131+
// IMPORT-YUL ../proof_exprs/OrExpr.pre.sol
132+
function or_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
133+
revert(0, 0)
134+
}
135+
// IMPORT-YUL ../proof_exprs/NotExpr.pre.sol
136+
function not_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
137+
revert(0, 0)
138+
}
139+
// IMPORT-YUL ../proof_exprs/ProofExpr.pre.sol
140+
function proof_expr_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
141+
revert(0, 0)
142+
}
143+
// IMPORT-YUL ../base/DataType.pre.sol
144+
function read_entry(result_ptr, data_type_variant) -> result_ptr_out, entry {
145+
revert(0, 0)
146+
}
147+
// IMPORT-YUL ../base/DataType.pre.sol
148+
function read_data_type(ptr) -> ptr_out, data_type {
149+
revert(0, 0)
150+
}
151+
// IMPORT-YUL FilterExec.pre.sol
152+
function compute_folds(plan_ptr, builder_ptr, input_chi_eval) ->
153+
plan_ptr_out,
154+
c_fold,
155+
d_fold,
156+
evaluations_ptr
157+
{
158+
revert(0, 0)
159+
}
160+
// IMPORT-YUL FilterExec.pre.sol
161+
function filter_exec_evaluate(expr_ptr, builder_ptr, chi_eval) -> expr_ptr_out, eval {
162+
revert(0, 0)
163+
}
164+
// IMPORT-YUL ../builder/VerificationBuilder.pre.sol
165+
function builder_get_table_chi_evaluation(builder_ptr, table_num) -> value {
166+
revert(0, 0)
167+
}
168+
169+
function compute_groupby_folds(plan_ptr, builder_ptr, input_chi_eval) ->
170+
plan_ptr_out,
171+
g_in_fold,
172+
g_out_fold,
173+
sum_in_fold,
174+
sum_out_fold,
175+
evaluations_ptr
176+
{
177+
let beta := builder_consume_challenge(builder_ptr)
178+
179+
// Process group by columns
180+
let group_by_column_count := shr(UINT64_PADDING_BITS, calldataload(plan_ptr))
181+
plan_ptr := add(plan_ptr, UINT64_SIZE)
182+
183+
evaluations_ptr := mload(FREE_PTR)
184+
mstore(evaluations_ptr, group_by_column_count)
185+
evaluations_ptr := add(evaluations_ptr, WORD_SIZE)
186+
187+
g_in_fold := 0
188+
for { let i := group_by_column_count } i { i := sub(i, 1) } {
189+
let g_in
190+
plan_ptr, g_in := column_expr_evaluate(plan_ptr, builder_ptr)
191+
g_in_fold := addmod(mulmod(g_in_fold, beta, MODULUS), g_in, MODULUS)
192+
}
193+
194+
g_out_fold := 0
195+
for { let i := group_by_column_count } i { i := sub(i, 1) } {
196+
let g_out := builder_consume_final_round_mle(builder_ptr)
197+
g_out_fold := addmod(mulmod(g_out_fold, beta, MODULUS), g_out, MODULUS)
198+
199+
mstore(evaluations_ptr, g_out)
200+
evaluations_ptr := add(evaluations_ptr, WORD_SIZE)
201+
}
202+
203+
// Process sum columns
204+
let sum_count := shr(UINT64_PADDING_BITS, calldataload(plan_ptr))
205+
plan_ptr := add(plan_ptr, UINT64_SIZE)
206+
207+
evaluations_ptr := mload(FREE_PTR)
208+
mstore(FREE_PTR, add(evaluations_ptr, mul(add(sum_count, 1), WORD_SIZE)))
209+
mstore(evaluations_ptr, sum_count)
210+
evaluations_ptr := add(evaluations_ptr, WORD_SIZE)
211+
212+
sum_in_fold := 0
213+
for { let i := sum_count } i { i := sub(i, 1) } {
214+
let sum_in
215+
plan_ptr, sum_in := proof_expr_evaluate(plan_ptr, builder_ptr, input_chi_eval)
216+
sum_in_fold := addmod(mulmod(sum_in_fold, beta, MODULUS), sum_in, MODULUS)
217+
}
218+
219+
sum_out_fold := 0
220+
for { let i := sum_count } i { i := sub(i, 1) } {
221+
let sum_out := builder_consume_final_round_mle(builder_ptr)
222+
sum_out_fold := addmod(mulmod(sum_out_fold, beta, MODULUS), sum_out, MODULUS)
223+
224+
mstore(evaluations_ptr, sum_out)
225+
evaluations_ptr := add(evaluations_ptr, WORD_SIZE)
226+
}
227+
228+
evaluations_ptr := mload(FREE_PTR)
229+
mstore(FREE_PTR, add(evaluations_ptr, add(WORD_SIZE, mul(sum_count, WORD_SIZE))))
230+
plan_ptr_out := plan_ptr
231+
}
232+
233+
function group_by_exec_evaluate(plan_ptr, builder_ptr) -> plan_ptr_out, evaluations_ptr {
234+
let alpha := builder_consume_challenge(builder_ptr)
235+
236+
// Table input
237+
let input_chi_eval :=
238+
builder_get_table_chi_evaluation(builder_ptr, shr(UINT64_PADDING_BITS, calldataload(plan_ptr)))
239+
plan_ptr := add(plan_ptr, UINT64_SIZE)
240+
241+
// Where clause evaluation
242+
let selection_eval
243+
plan_ptr, selection_eval := proof_expr_evaluate(plan_ptr, builder_ptr, input_chi_eval)
244+
245+
// Compute the folds
246+
plan_ptr, g_in_fold, g_out_fold, sum_in_fold, sum_out_fold, evaluations_ptr
247+
= compute_groupby_folds(plan_ptr, builder_ptr, input_chi_eval)
248+
// Skip the count alias (we don't need to parse it for verification)
249+
let count_alias
250+
plan_ptr, count_alias := read_binary(plan_ptr)
251+
252+
// Consume count column evaluation
253+
let count_out_eval := builder_consume_final_round_mle(builder_ptr)
254+
255+
// Get chi evaluation
256+
let output_chi_eval := builder_consume_chi_evaluation(builder_ptr)
257+
258+
// Verify the group by operation
259+
// Get the g_in_star and g_out_star evaluations
260+
let g_in_star_eval := builder_consume_final_round_mle(builder_ptr)
261+
let g_out_star_eval := builder_consume_final_round_mle(builder_ptr)
262+
263+
// First constraint: sum g_in_star * sel_in * sum_in_fold - g_out_star * sum_out_fold = 0
264+
builder_produce_zerosum_constraint(
265+
builder_ptr,
266+
addmod(
267+
mulmod(mulmod(g_in_star_eval, selection_eval, MODULUS), sum_in_fold, MODULUS),
268+
mulmod(mulmod(g_out_star_eval, sum_out_fold, MODULUS), MODULUS_MINUS_ONE, MODULUS),
269+
MODULUS
270+
),
271+
3
272+
)
273+
274+
// Second constraint: g_in_star + g_in_star * g_in_fold - input_chi_eval = 0
275+
builder_produce_identity_constraint(
276+
builder_ptr,
277+
addmod(
278+
addmod(g_in_star_eval, mulmod(g_in_star_eval, g_in_fold, MODULUS), MODULUS),
279+
mulmod(input_chi_eval, MODULUS_MINUS_ONE, MODULUS),
280+
MODULUS
281+
),
282+
2
283+
)
284+
285+
// Third constraint: g_out_star + g_out_star * g_out_fold - output_chi_eval = 0
286+
builder_produce_identity_constraint(
287+
builder_ptr,
288+
addmod(
289+
addmod(g_out_star_eval, mulmod(g_out_star_eval, g_out_fold, MODULUS), MODULUS),
290+
mulmod(output_chi_eval, MODULUS_MINUS_ONE, MODULUS),
291+
MODULUS
292+
),
293+
2
294+
)
295+
296+
// Prepare result evaluations (group_by + sum + count columns)
297+
let evaluations_length := add(add(group_by_count, sum_count), 1)
298+
evaluations_ptr := mload(FREE_PTR)
299+
mstore(evaluations_ptr, evaluations_length)
300+
301+
// Copy group by column evaluations
302+
for { let i := 0 } lt(i, group_by_count) { i := add(i, 1) } {
303+
mstore(
304+
add(add(evaluations_ptr, WORD_SIZE), mul(i, WORD_SIZE)),
305+
mload(add(add(g_out_evals_ptr, WORD_SIZE), mul(i, WORD_SIZE)))
306+
)
307+
}
308+
309+
// Copy sum column evaluations
310+
for { let i := 0 } lt(i, sum_count) { i := add(i, 1) } {
311+
mstore(
312+
add(add(evaluations_ptr, WORD_SIZE), mul(add(i, group_by_count), WORD_SIZE)),
313+
mload(add(add(sum_out_evals_ptr, WORD_SIZE), mul(i, WORD_SIZE)))
314+
)
315+
}
316+
317+
// Add count column evaluation
318+
mstore(
319+
add(add(evaluations_ptr, WORD_SIZE), mul(add(group_by_count, sum_count), WORD_SIZE)), count_out_eval
320+
)
321+
322+
// Update free memory pointer
323+
mstore(FREE_PTR, add(evaluations_ptr, mul(add(evaluations_length, 1), WORD_SIZE)))
324+
325+
plan_ptr_out := plan_ptr
326+
}
327+
328+
let __planOutOffset
329+
__planOutOffset, __evaluations := group_by_exec_evaluate(__plan.offset, __builder)
330+
__planOut.offset := __planOutOffset
331+
// slither-disable-next-line write-after-write
332+
__planOut.length := sub(__plan.length, sub(__planOutOffset, __plan.offset))
333+
}
334+
__evaluationsPtr = __evaluations;
335+
__builderOut = __builder;
336+
}
337+
}

solidity/src/proof_plans/ProofPlan.pre.sol

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {VerificationBuilder} from "../builder/VerificationBuilder.pre.sol";
1010
/// @dev Library for handling proof plans
1111
library ProofPlan {
1212
enum PlanVariant {
13-
Filter
13+
Filter,
14+
GroupBy
1415
}
1516

1617
/// @notice Evaluates a proof plan
@@ -156,6 +157,14 @@ library ProofPlan {
156157
function read_data_type(ptr) -> ptr_out, data_type {
157158
revert(0, 0)
158159
}
160+
// IMPORT-YUL GroupByExec.pre.sol
161+
function fold_vals(beta, vals_ptr, vals_length) -> result {
162+
revert(0, 0)
163+
}
164+
// IMPORT-YUL GroupByExec.pre.sol
165+
function group_by_exec_evaluate(plan_ptr, builder_ptr) -> plan_ptr_out, evaluations_ptr {
166+
revert(0, 0)
167+
}
159168

160169
function proof_plan_evaluate(plan_ptr, builder_ptr) -> plan_ptr_out, evaluations_ptr {
161170
let proof_plan_variant := shr(UINT32_PADDING_BITS, calldataload(plan_ptr))
@@ -166,6 +175,10 @@ library ProofPlan {
166175
case_const(0, FILTER_EXEC_VARIANT)
167176
plan_ptr_out, evaluations_ptr := filter_exec_evaluate(plan_ptr, builder_ptr)
168177
}
178+
case 1 {
179+
case_const(1, GROUP_BY_EXEC_VARIANT)
180+
plan_ptr_out, evaluations_ptr := group_by_exec_evaluate(plan_ptr, builder_ptr)
181+
}
169182
default { err(ERR_UNSUPPORTED_PROOF_PLAN_VARIANT) }
170183
}
171184

0 commit comments

Comments
 (0)