|
| 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 SignExpr |
| 10 | +/// @dev Library for handling the sign of an evaluation |
| 11 | +library SignExpr { |
| 12 | + /// @notice Evaluates a sign expression by finding the sign of an expression evaluation |
| 13 | + /// @custom:as-yul-wrapper |
| 14 | + /// #### Wrapped Yul Function |
| 15 | + /// ##### Signature |
| 16 | + /// ```yul |
| 17 | + /// sign_expr_evaluate(expr_eval, builder_ptr, chi_eval) -> eval |
| 18 | + /// ``` |
| 19 | + /// ##### Parameters |
| 20 | + /// * `expr_eval` - the expression evaluation |
| 21 | + /// * `builder_ptr` - memory pointer to the verification builder |
| 22 | + /// * `chi_eval` - the chi value for evaluation |
| 23 | + /// ##### Return Values |
| 24 | + /// * `eval` - the evaluation result from the builder's final round MLE (or bit distribution) |
| 25 | + /// @notice Evaluates the sign of an expression |
| 26 | + /// ##### Proof Plan Encoding |
| 27 | + /// The sign expression is encoded as follows: |
| 28 | + /// The expression |
| 29 | + /// @param __exprEval The expression evaluation |
| 30 | + /// @param __builder The verification builder |
| 31 | + /// @param __chiEval The chi value for evaluation |
| 32 | + /// @return __builderOut The verification builder result |
| 33 | + /// @return __eval The evaluated result |
| 34 | + function __signExprEvaluate( // solhint-disable-line gas-calldata-parameters |
| 35 | + uint256 __exprEval, VerificationBuilder.Builder memory __builder, uint256 __chiEval) |
| 36 | + internal |
| 37 | + pure |
| 38 | + returns (VerificationBuilder.Builder memory __builderOut, uint256 __eval) |
| 39 | + { |
| 40 | + assembly { |
| 41 | + // IMPORT-YUL ../base/Errors.sol |
| 42 | + function err(code) { |
| 43 | + revert(0, 0) |
| 44 | + } |
| 45 | + // IMPORT-YUL ../base/Queue.pre.sol |
| 46 | + function dequeue(queue_ptr) -> value { |
| 47 | + revert(0, 0) |
| 48 | + } |
| 49 | + // IMPORT-YUL ../base/Queue.pre.sol |
| 50 | + function dequeue_uint512(queue_ptr) -> value { |
| 51 | + revert(0, 0) |
| 52 | + } |
| 53 | + // IMPORT-YUL ../base/MathUtil.sol |
| 54 | + function addmod_bn254(lhs, rhs) -> sum { |
| 55 | + revert(0, 0) |
| 56 | + } |
| 57 | + // IMPORT-YUL ../base/MathUtil.sol |
| 58 | + function submod_bn254(lhs, rhs) -> difference { |
| 59 | + revert(0, 0) |
| 60 | + } |
| 61 | + // IMPORT-YUL ../base/MathUtil.sol |
| 62 | + function mulmod_bn254(lhs, rhs) -> product { |
| 63 | + revert(0, 0) |
| 64 | + } |
| 65 | + // IMPORT-YUL ../base/SwitchUtil.pre.sol |
| 66 | + function case_const(lhs, rhs) { |
| 67 | + revert(0, 0) |
| 68 | + } |
| 69 | + // IMPORT-YUL ../builder/VerificationBuilder.pre.sol |
| 70 | + function builder_consume_final_round_mle(builder_ptr) -> value { |
| 71 | + revert(0, 0) |
| 72 | + } |
| 73 | + // IMPORT-YUL ../base/Array.pre.sol |
| 74 | + function get_array_element(arr_ptr, index) -> value { |
| 75 | + revert(0, 0) |
| 76 | + } |
| 77 | + // IMPORT-YUL ../builder/VerificationBuilder.pre.sol |
| 78 | + function builder_consume_bit_distribution(builder_ptr) -> vary_mask, leading_bit_mask { |
| 79 | + revert(0, 0) |
| 80 | + } |
| 81 | + // IMPORT-YUL ../builder/VerificationBuilder.pre.sol |
| 82 | + function builder_produce_identity_constraint(builder_ptr, evaluation, degree) { |
| 83 | + revert(0, 0) |
| 84 | + } |
| 85 | + // IMPORT-YUL ../base/DataType.pre.sol |
| 86 | + function read_entry(result_ptr, data_type_variant) -> result_ptr_out, entry { |
| 87 | + revert(0, 0) |
| 88 | + } |
| 89 | + // IMPORT-YUL ../base/DataType.pre.sol |
| 90 | + function read_binary(result_ptr) -> result_ptr_out, entry { |
| 91 | + revert(0, 0) |
| 92 | + } |
| 93 | + // IMPORT-YUL ../base/DataType.pre.sol |
| 94 | + function read_data_type(ptr) -> ptr_out, data_type { |
| 95 | + revert(0, 0) |
| 96 | + } |
| 97 | + |
| 98 | + function sign_expr_evaluate(expr_eval, builder_ptr, chi_eval) -> result_eval { |
| 99 | + let vary_mask |
| 100 | + let leading_bit_mask |
| 101 | + vary_mask, leading_bit_mask := builder_consume_bit_distribution(builder_ptr) |
| 102 | + |
| 103 | + // Other than the lead bit, no bit should vary past some max bit position, depending on the field |
| 104 | + if and(vary_mask, MODULUS_INVALID_VARY_MASK) { err(ERR_INVALID_VARYING_BITS) } |
| 105 | + |
| 106 | + // The lead bit of the leading_bit_mask dictates the sign, if it's constant sign. |
| 107 | + // So this will be the value if sign is constant. Otherwise, it will be overwritten |
| 108 | + let sign_eval := mulmod_bn254(shr(255, leading_bit_mask), chi_eval) |
| 109 | + |
| 110 | + // For future computations, leading_bit_mask should have a 1 in the lead bit |
| 111 | + leading_bit_mask := or(leading_bit_mask, shl(255, 1)) |
| 112 | + |
| 113 | + // leading_bit_inverse_mask identifies columns that match the inverse of the lead bit column |
| 114 | + // So !vary_mask ^ leading_bit_mask, with a lead bit of zero. |
| 115 | + let leading_bit_inverse_mask := shr(1, shl(1, xor(not(vary_mask), leading_bit_mask))) |
| 116 | + |
| 117 | + // sum_eval should ultimately add up to the original column of data |
| 118 | + // It will effectively be a recomposition of the bit decomposition |
| 119 | + let sum_eval := 0 |
| 120 | + |
| 121 | + for { let i := 0 } vary_mask { |
| 122 | + i := add(i, 1) |
| 123 | + vary_mask := shr(1, vary_mask) |
| 124 | + } { |
| 125 | + if and(vary_mask, 1) { |
| 126 | + // For any varying bits... |
| 127 | + let bit_eval := builder_consume_final_round_mle(builder_ptr) |
| 128 | + |
| 129 | + // Verify that every eval is a bit |
| 130 | + // bit_eval - bit_eval * bit_eval = 0 |
| 131 | + builder_produce_identity_constraint( |
| 132 | + builder_ptr, submod_bn254(bit_eval, mulmod_bn254(bit_eval, bit_eval)), 2 |
| 133 | + ) |
| 134 | + |
| 135 | + switch i |
| 136 | + // If the lead bit varies, that we get the sign from the mles. |
| 137 | + case 255 { sign_eval := bit_eval } |
| 138 | + // For varying non lead bits, |
| 139 | + // we add bit_eval * 2ⁱ to the sum in order to recompose the original value of the column |
| 140 | + default { sum_eval := addmod_bn254(sum_eval, mulmod_bn254(bit_eval, shl(i, 1))) } |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + result_eval := submod_bn254(chi_eval, sign_eval) |
| 145 | + |
| 146 | + // For constant and lead bits... |
| 147 | + // sum += sign_eval * leading_bit_mask + (sign_eval - chi_eval) * leading_bit_inverse_mask - chi_eval * (1 << 255) |
| 148 | + sum_eval := |
| 149 | + submod_bn254( |
| 150 | + addmod_bn254( |
| 151 | + addmod_bn254(sum_eval, mulmod_bn254(sign_eval, leading_bit_mask)), |
| 152 | + mulmod_bn254(result_eval, leading_bit_inverse_mask) |
| 153 | + ), |
| 154 | + mulmod_bn254(chi_eval, shl(255, 1)) |
| 155 | + ) |
| 156 | + |
| 157 | + // Verify the bit recomposition matches the original column evaluation |
| 158 | + if sub(sum_eval, expr_eval) { err(ERR_BIT_DECOMPOSITION_INVALID) } |
| 159 | + } |
| 160 | + |
| 161 | + __eval := sign_expr_evaluate(__exprEval, __builder, __chiEval) |
| 162 | + } |
| 163 | + __builderOut = __builder; |
| 164 | + } |
| 165 | +} |
0 commit comments