|
| 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 | +} |
0 commit comments