diff --git a/common/src/contract/cryptography/mod.rs b/common/src/contract/cryptography/mod.rs index 5806b8c5..b02bfeb0 100644 --- a/common/src/contract/cryptography/mod.rs +++ b/common/src/contract/cryptography/mod.rs @@ -3,11 +3,9 @@ pub mod types; mod pairings_bn254; mod polynomial_eval; mod transcript; -mod turbo_plonk; pub const fn cryptography_libraries() -> &'static str { concat!( - crate::TURBOPLONK_LIBRARY!(), crate::TYPES_LIBRARY!(), crate::PAIRINGSBN254_LIBRARY!(), crate::POLYNOMIALEVAL_LIBRARY!(), diff --git a/common/src/contract/cryptography/pairings_bn254.rs b/common/src/contract/cryptography/pairings_bn254.rs index 2ddaeba4..0d51252d 100644 --- a/common/src/contract/cryptography/pairings_bn254.rs +++ b/common/src/contract/cryptography/pairings_bn254.rs @@ -8,8 +8,10 @@ macro_rules! PAIRINGSBN254_LIBRARY { * @dev Provides some basic methods to compute bilinear pairings, construct group elements and misc numerical methods */ library Bn254Crypto { - uint256 constant p_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant p_mod = + 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 constant r_mod = + 21888242871839275222246405745257275088548364400416034343698204186575808495617; // Perform a modular exponentiation. This method is ideal for small exponents (~64 bits or less), as // it is cheaper than using the pow precompile @@ -24,8 +26,11 @@ library Bn254Crypto { assembly { let endpoint := add(exponent, 0x01) - for {} lt(count, endpoint) { count := add(count, count) } - { + for { + + } lt(count, endpoint) { + count := add(count, count) + } { if and(exponent, count) { result := mulmod(result, input, modulus) } @@ -36,8 +41,7 @@ library Bn254Crypto { return result; } - function invert(uint256 fr) internal view returns (uint256) - { + function invert(uint256 fr) internal view returns (uint256) { uint256 output; bool success; uint256 p = r_mod; @@ -70,11 +74,12 @@ library Bn254Crypto { return Types.G1Point(xValue, yValue); } - function new_g2(uint256 x0, uint256 x1, uint256 y0, uint256 y1) - internal - pure - returns (Types.G2Point memory) - { + function new_g2( + uint256 x0, + uint256 x1, + uint256 y0, + uint256 y1 + ) internal pure returns (Types.G2Point memory) { return Types.G2Point(x0, x1, y0, y1); } @@ -83,15 +88,15 @@ library Bn254Crypto { } function P2() internal pure returns (Types.G2Point memory) { - return Types.G2Point({ - x0: 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, - x1: 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed, - y0: 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, - y1: 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - }); + return + Types.G2Point({ + x0: 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, + x1: 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed, + y0: 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, + y1: 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa + }); } - /// Evaluate the following pairing product: /// e(a1, a2).e(-b1, b2) == 1 function pairingProd2( @@ -119,14 +124,7 @@ library Bn254Crypto { mstore(add(mPtr, 0x120), mload(add(b2, 0x20))) mstore(add(mPtr, 0x140), mload(add(b2, 0x40))) mstore(add(mPtr, 0x160), mload(add(b2, 0x60))) - success := staticcall( - gas(), - 8, - mPtr, - 0x180, - 0x00, - 0x20 - ) + success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) out := mload(0x00) } require(success, "Pairing check failed!"); @@ -134,13 +132,13 @@ library Bn254Crypto { } /** - * validate the following: - * x != 0 - * y != 0 - * x < p - * y < p - * y^2 = x^3 + 3 mod p - */ + * validate the following: + * x != 0 + * y != 0 + * x < p + * y < p + * y^2 = x^3 + 3 mod p + */ function validateG1Point(Types.G1Point memory point) internal pure { bool is_well_formed; uint256 p = p_mod; @@ -149,15 +147,16 @@ library Bn254Crypto { let y := mload(add(point, 0x20)) is_well_formed := and( - and( - and(lt(x, p), lt(y, p)), - not(or(iszero(x), iszero(y))) - ), + and(and(lt(x, p), lt(y, p)), not(or(iszero(x), iszero(y)))), eq(mulmod(y, y, p), addmod(mulmod(x, mulmod(x, x, p), p), 3, p)) ) } - require(is_well_formed, "Bn254: G1 point not on curve, or is malformed"); + require( + is_well_formed, + "Bn254: G1 point not on curve, or is malformed" + ); } } + "# }; } diff --git a/common/src/contract/cryptography/polynomial_eval.rs b/common/src/contract/cryptography/polynomial_eval.rs index e42b44e6..0215182d 100644 --- a/common/src/contract/cryptography/polynomial_eval.rs +++ b/common/src/contract/cryptography/polynomial_eval.rs @@ -57,11 +57,11 @@ library PolynomialEval { mPtr := mload(0x40) mstore(0x40, add(mPtr, 0x200)) } - + // store denominators in mPtr -> mPtr + 0x80 assembly { mstore(mPtr, public_input_delta_denominator) // store denominator - mstore(add(mPtr, 0x20), vanishing_numerator) // store numerator, because we want the inverse of the zero poly + mstore(add(mPtr, 0x20), vanishing_denominator) // store denominator mstore(add(mPtr, 0x40), l_start_denominator) // store denominator mstore(add(mPtr, 0x60), l_end_denominator) // store denominator @@ -94,10 +94,18 @@ library PolynomialEval { accumulator := mulmod(accumulator, mload(mPtr), p) mstore(mPtr, intermediate) - public_input_delta := mulmod(public_input_delta_numerator, mload(mPtr), p) + public_input_delta := mulmod( + public_input_delta_numerator, + mload(mPtr), + p + ) + + zero_polynomial_eval := mulmod( + vanishing_numerator, + mload(add(mPtr, 0x20)), + p + ) - zero_polynomial_eval := mulmod(vanishing_denominator, mload(add(mPtr, 0x20)), p) - l_start := mulmod(lagrange_numerator, mload(add(mPtr, 0x40)), p) l_end := mulmod(lagrange_numerator, mload(add(mPtr, 0x60)), p) @@ -111,69 +119,81 @@ library PolynomialEval { uint256 gamma = challenges.gamma; uint256 work_root = vk.work_root; - uint256 endpoint = (vk.num_inputs * 0x20) - 0x60; + uint256 endpoint = (vk.num_inputs * 0x20) - 0x20; uint256 public_inputs; - uint256 accumulating_root = challenges.beta; + uint256 root_1 = challenges.beta; + uint256 root_2 = challenges.beta; uint256 numerator_value = 1; uint256 denominator_value = 1; // we multiply length by 0x20 because our loop step size is 0x20 not 0x01 - // we subtract 0x60 because our loop is unrolled 4 times an we don't want to overshoot + // we subtract 0x20 because our loop is unrolled 2 times an we don't want to overshoot // perform this computation in assembly to improve efficiency. We are sensitive to the cost of this loop as // it scales with the number of public inputs uint256 p = Bn254Crypto.r_mod; + bool valid = true; assembly { - public_inputs := add(calldataload(0x24), 0x24) + root_1 := mulmod(root_1, 0x05, p) + root_2 := mulmod(root_2, 0x07, p) + public_inputs := add(calldataload(0x04), 0x24) // get public inputs from calldata. N.B. If Contract ABI Changes this code will need to be updated! endpoint := add(endpoint, public_inputs) // Do some loop unrolling to reduce number of conditional jump operations - for {} lt(public_inputs, endpoint) {} - { - let N0 := add(mulmod(accumulating_root, 0x05, p), addmod(calldataload(public_inputs), gamma, p)) - let D0 := add(mulmod(accumulating_root, 0x07, p), N0) - - accumulating_root := mulmod(accumulating_root, work_root, p) + for { - let N1 := add(mulmod(accumulating_root, 0x05, p), addmod(calldataload(add(public_inputs, 0x20)), gamma, p)) - let D1 := add(mulmod(accumulating_root, 0x07, p), N1) + } lt(public_inputs, endpoint) { - accumulating_root := mulmod(accumulating_root, work_root, p) + } { + let input0 := calldataload(public_inputs) + let N0 := add(root_1, add(input0, gamma)) + let D0 := add(root_2, N0) // 4x overloaded - let N2 := add(mulmod(accumulating_root, 0x05, p), addmod(calldataload(add(public_inputs, 0x40)), gamma, p)) - let D2 := add(mulmod(accumulating_root, 0x07, p), N2) + root_1 := mulmod(root_1, work_root, p) + root_2 := mulmod(root_2, work_root, p) - accumulating_root := mulmod(accumulating_root, work_root, p) + let input1 := calldataload(add(public_inputs, 0x20)) + let N1 := add(root_1, add(input1, gamma)) - let N3 := add(mulmod(accumulating_root, 0x05, p), addmod(calldataload(add(public_inputs, 0x60)), gamma, p)) - - denominator_value := mulmod(mulmod(mulmod(mulmod(D2, D1, p), D0, p), denominator_value, p), add(N3, mulmod(accumulating_root, 0x07, p)), p) - numerator_value := mulmod(mulmod(mulmod(mulmod(N3, N2, p), N1, p), N0, p), numerator_value, p) + denominator_value := mulmod( + mulmod(D0, denominator_value, p), + add(N1, root_2), + p + ) + numerator_value := mulmod(mulmod(N1, N0, p), numerator_value, p) - accumulating_root := mulmod(accumulating_root, work_root, p) + root_1 := mulmod(root_1, work_root, p) + root_2 := mulmod(root_2, work_root, p) - public_inputs := add(public_inputs, 0x80) + valid := and(valid, and(lt(input0, p), lt(input1, p))) + public_inputs := add(public_inputs, 0x40) } - endpoint := add(endpoint, 0x60) - for {} lt(public_inputs, endpoint) { public_inputs := add(public_inputs, 0x20) } - { - let T0 := addmod(calldataload(public_inputs), gamma, p) + endpoint := add(endpoint, 0x20) + for { + + } lt(public_inputs, endpoint) { + public_inputs := add(public_inputs, 0x20) + } { + let input0 := calldataload(public_inputs) + valid := and(valid, lt(input0, p)) + let T0 := addmod(input0, gamma, p) numerator_value := mulmod( numerator_value, - add(mulmod(accumulating_root, 0x05, p), T0), // 0x05 = coset_generator0 + add(root_1, T0), // 0x05 = coset_generator0 p ) denominator_value := mulmod( denominator_value, - add(mulmod(accumulating_root, 0x0c, p), T0), // 0x0c = coset_generator7 + add(add(root_1, root_2), T0), // 0x0c = coset_generator7 p ) - accumulating_root := mulmod(accumulating_root, work_root, p) + root_1 := mulmod(root_1, work_root, p) + root_2 := mulmod(root_2, work_root, p) } } - + require(valid, "public inputs are greater than circuit modulus"); return (numerator_value, denominator_value); } @@ -181,11 +201,26 @@ library PolynomialEval { * @dev Computes the vanishing polynoimal and lagrange evaluations L1 and Ln. * @return Returns fractions as numerators and denominators. We combine with the public input fraction and compute inverses as a batch */ - function compute_lagrange_and_vanishing_fractions(Types.VerificationKey memory vk, uint256 zeta - ) internal pure returns (uint256, uint256, uint256, uint256, uint256) { - + function compute_lagrange_and_vanishing_fractions( + Types.VerificationKey memory vk, + uint256 zeta + ) + internal + pure + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { uint256 p = Bn254Crypto.r_mod; - uint256 vanishing_numerator = Bn254Crypto.pow_small(zeta, vk.circuit_size, p); + uint256 vanishing_numerator = Bn254Crypto.pow_small( + zeta, + vk.circuit_size, + p + ); vk.zeta_pow_n = vanishing_numerator; assembly { vanishing_numerator := addmod(vanishing_numerator, sub(p, 1), p) @@ -199,19 +234,30 @@ library PolynomialEval { uint256 l_end_denominator; uint256 z = zeta; // copy input var to prevent stack depth errors assembly { - // vanishing_denominator = (z - w^{n-1})(z - w^{n-2})(z - w^{n-3})(z - w^{n-4}) // we need to cut 4 roots of unity out of the vanishing poly, the last 4 constraints are not satisfied due to randomness // added to ensure the proving system is zero-knowledge vanishing_denominator := addmod(z, sub(p, work_root), p) work_root := mulmod(work_root, accumulating_root, p) - vanishing_denominator := mulmod(vanishing_denominator, addmod(z, sub(p, work_root), p), p) + vanishing_denominator := mulmod( + vanishing_denominator, + addmod(z, sub(p, work_root), p), + p + ) work_root := mulmod(work_root, accumulating_root, p) - vanishing_denominator := mulmod(vanishing_denominator, addmod(z, sub(p, work_root), p), p) + vanishing_denominator := mulmod( + vanishing_denominator, + addmod(z, sub(p, work_root), p), + p + ) work_root := mulmod(work_root, accumulating_root, p) - vanishing_denominator := mulmod(vanishing_denominator, addmod(z, sub(p, work_root), p), p) + vanishing_denominator := mulmod( + vanishing_denominator, + addmod(z, sub(p, work_root), p), + p + ) } - + work_root = vk.work_root; uint256 lagrange_numerator; assembly { @@ -224,17 +270,26 @@ library PolynomialEval { accumulating_root := mulmod(accumulating_root, accumulating_root, p) accumulating_root := mulmod(accumulating_root, work_root, p) - l_end_denominator := addmod(mulmod(accumulating_root, z, p), sub(p, 1), p) + l_end_denominator := addmod( + mulmod(accumulating_root, z, p), + sub(p, 1), + p + ) } - return (vanishing_numerator, vanishing_denominator, lagrange_numerator, l_start_denominator, l_end_denominator); + return ( + vanishing_numerator, + vanishing_denominator, + lagrange_numerator, + l_start_denominator, + l_end_denominator + ); } function compute_arithmetic_gate_quotient_contribution( Types.ChallengeTranscript memory challenges, Types.Proof memory proof - ) internal pure returns (uint256) { - + ) internal view returns (uint256) { uint256 q_arith = proof.q_arith; uint256 wire3 = proof.w3; uint256 wire4 = proof.w4; @@ -256,7 +311,6 @@ library PolynomialEval { t1 := mulmod(mulmod(t1, t2, p), alpha_base, p) - alpha_base := mulmod(alpha_base, alpha, p) alpha_base := mulmod(alpha_base, alpha, p) } @@ -269,9 +323,7 @@ library PolynomialEval { function compute_pedersen_gate_quotient_contribution( Types.ChallengeTranscript memory challenges, Types.Proof memory proof - ) internal pure returns (uint256) { - - + ) internal view returns (uint256) { uint256 alpha = challenges.alpha; uint256 gate_id = 0; uint256 alpha_base = challenges.alpha_base; @@ -287,15 +339,10 @@ library PolynomialEval { let wire4_neg := sub(p, wire_t0) delta := addmod(wire_t1, mulmod(wire4_neg, 0x04, p), p) - gate_id := - mulmod( + gate_id := mulmod( mulmod( mulmod( - mulmod( - add(delta, 0x01), - add(delta, 0x03), - p - ), + mulmod(add(delta, 0x01), add(delta, 0x03), p), add(delta, sub(p, 0x01)), p ), @@ -306,8 +353,12 @@ library PolynomialEval { p ) alpha_base := mulmod(alpha_base, alpha, p) - - gate_id := addmod(gate_id, sub(p, mulmod(wire_t2, alpha_base, p)), p) + + gate_id := addmod( + gate_id, + sub(p, mulmod(wire_t2, alpha_base, p)), + p + ) alpha_base := mulmod(alpha_base, alpha, p) } @@ -338,12 +389,7 @@ library PolynomialEval { t2 := mulmod(mulmod(delta, wire_t2, p), selector_value, p) t2 := addmod(t2, t2, p) - t0 := - mulmod( - addmod(t0, addmod(t1, t2, p), p), - alpha_base, - p - ) + t0 := mulmod(addmod(t0, addmod(t1, t2, p), p), alpha_base, p) gate_id := addmod(gate_id, t0, p) alpha_base := mulmod(alpha_base, alpha, p) @@ -364,18 +410,22 @@ library PolynomialEval { t1 := addmod(wire_t0, sub(p, wire_t4), p) t2 := addmod( - sub(p, mulmod(selector_value, delta, p)), - wire_t2, - p + sub(p, mulmod(selector_value, delta, p)), + wire_t2, + p ) - gate_id := addmod(gate_id, mulmod(add(t0, mulmod(t1, t2, p)), alpha_base, p), p) + gate_id := addmod( + gate_id, + mulmod(add(t0, mulmod(t1, t2, p)), alpha_base, p), + p + ) alpha_base := mulmod(alpha_base, alpha, p) } selector_value = proof.q_c; - + wire_t1 = proof.w4; // w4 wire_t2 = proof.w3; // w3 assembly { @@ -390,9 +440,16 @@ library PolynomialEval { alpha_base := mulmod(alpha_base, alpha, p) } - + assembly { - let x_init_id := sub(p, mulmod(mulmod(wire_t0, selector_value, p), mulmod(wire_t2, alpha_base, p), p)) + let x_init_id := sub( + p, + mulmod( + mulmod(wire_t0, selector_value, p), + mulmod(wire_t2, alpha_base, p), + p + ) + ) gate_id := addmod(gate_id, x_init_id, p) @@ -403,16 +460,23 @@ library PolynomialEval { wire_t1 = proof.w3; // w3 wire_t2 = proof.w4; // w4 assembly { - let y_init_id := mulmod(add(0x01, sub(p, wire_t2)), selector_value, p) + let y_init_id := mulmod( + add(0x01, sub(p, wire_t2)), + selector_value, + p + ) t1 := sub(p, mulmod(wire_t0, wire_t1, p)) - y_init_id := mulmod(add(y_init_id, t1), mulmod(alpha_base, selector_value, p), p) + y_init_id := mulmod( + add(y_init_id, t1), + mulmod(alpha_base, selector_value, p), + p + ) gate_id := addmod(gate_id, y_init_id, p) alpha_base := mulmod(alpha_base, alpha, p) - } selector_value = proof.q_ecc; assembly { @@ -429,8 +493,7 @@ library PolynomialEval { uint256 lagrange_start, uint256 lagrange_end, Types.Proof memory proof - ) internal pure returns (uint256) { - + ) internal view returns (uint256) { uint256 numerator_collector; uint256 alpha = challenges.alpha; uint256 beta = challenges.beta; @@ -446,53 +509,29 @@ library PolynomialEval { uint256 sigma2 = proof.sigma2; uint256 sigma3 = proof.sigma3; assembly { + let t0 := add(add(wire1, gamma), mulmod(beta, sigma1, p)) - let t0 := add( - add(wire1, gamma), - mulmod(beta, sigma1, p) - ) + let t1 := add(add(wire2, gamma), mulmod(beta, sigma2, p)) - let t1 := add( - add(wire2, gamma), - mulmod(beta, sigma2, p) - ) - - let t2 := add( - add(wire3, gamma), - mulmod(beta, sigma3, p) - ) + let t2 := add(add(wire3, gamma), mulmod(beta, sigma3, p)) t0 := mulmod(t0, mulmod(t1, t2, p), p) - t0 := mulmod( - t0, - add(wire4, gamma), - p - ) + t0 := mulmod(t0, add(wire4, gamma), p) - t0 := mulmod( - t0, - grand_product, - p - ) + t0 := mulmod(t0, grand_product, p) - t0 := mulmod( - t0, - alpha, - p - ) + t0 := mulmod(t0, alpha, p) numerator_collector := sub(p, t0) } } - uint256 alpha_base = challenges.alpha_base; { uint256 lstart = lagrange_start; uint256 lend = lagrange_end; uint256 public_delta = public_input_delta; - uint256 linearization_poly = proof.linearization_polynomial; assembly { let alpha_squared := mulmod(alpha, alpha, p) let alpha_cubed := mulmod(alpha, alpha_squared, p) @@ -502,9 +541,12 @@ library PolynomialEval { let t2 := addmod(grand_product, sub(p, public_delta), p) t1 := mulmod(t1, t2, p) - numerator_collector := addmod(numerator_collector, sub(p, t0), p) + numerator_collector := addmod( + numerator_collector, + sub(p, t0), + p + ) numerator_collector := addmod(numerator_collector, t1, p) - numerator_collector := addmod(numerator_collector, linearization_poly, p) alpha_base := mulmod(alpha_base, alpha_cubed, p) } } @@ -514,14 +556,15 @@ library PolynomialEval { return numerator_collector; } - function compute_quotient_polynomial( + // compute_r_0 + function compute_linear_polynomial_constant( uint256 zero_poly_inverse, uint256 public_input_delta, Types.ChallengeTranscript memory challenges, uint256 lagrange_start, uint256 lagrange_end, Types.Proof memory proof - ) internal pure returns (uint256) { + ) internal view returns (uint256) { uint256 t0 = compute_permutation_quotient_contribution( public_input_delta, challenges, @@ -530,17 +573,23 @@ library PolynomialEval { proof ); - uint256 t1 = compute_arithmetic_gate_quotient_contribution(challenges, proof); + uint256 t1 = compute_arithmetic_gate_quotient_contribution( + challenges, + proof + ); - uint256 t2 = compute_pedersen_gate_quotient_contribution(challenges, proof); + uint256 t2 = compute_pedersen_gate_quotient_contribution( + challenges, + proof + ); - uint256 quotient_eval; + uint256 r_0; uint256 p = Bn254Crypto.r_mod; assembly { - quotient_eval := addmod(t0, addmod(t1, t2, p), p) - quotient_eval := mulmod(quotient_eval, zero_poly_inverse, p) + r_0 := addmod(t0, addmod(t1, t2, p), p) + // r_0 := mulmod(r_0, zero_poly_inverse, p) // not necessary for the simplified Plonk } - return quotient_eval; + return r_0; } function compute_linearised_opening_terms( @@ -549,10 +598,27 @@ library PolynomialEval { Types.VerificationKey memory vk, Types.Proof memory proof ) internal view returns (Types.G1Point memory) { - Types.G1Point memory accumulator = compute_grand_product_opening_group_element(proof, vk, challenges, L1_fr); - Types.G1Point memory arithmetic_term = compute_arithmetic_selector_opening_group_element(proof, vk, challenges); - uint256 range_multiplier = compute_range_gate_opening_scalar(proof, challenges); - uint256 logic_multiplier = compute_logic_gate_opening_scalar(proof, challenges); + Types.G1Point + memory accumulator = compute_grand_product_opening_group_element( + proof, + vk, + challenges, + L1_fr + ); + Types.G1Point + memory arithmetic_term = compute_arithmetic_selector_opening_group_element( + proof, + vk, + challenges + ); + uint256 range_multiplier = compute_range_gate_opening_scalar( + proof, + challenges + ); + uint256 logic_multiplier = compute_logic_gate_opening_scalar( + proof, + challenges + ); Types.G1Point memory QRANGE = vk.QRANGE; Types.G1Point memory QLOGIC = vk.QLOGIC; @@ -574,24 +640,46 @@ library PolynomialEval { // we use mPtr to store accumulated point mstore(add(mPtr, 0x40), mload(accumulator)) mstore(add(mPtr, 0x60), mload(add(accumulator, 0x20))) - success := and(success, staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40)) + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40) + ) // logic_multiplier.[QLOGIC] mstore(add(mPtr, 0x40), mload(QLOGIC)) mstore(add(mPtr, 0x60), mload(add(QLOGIC, 0x20))) mstore(add(mPtr, 0x80), logic_multiplier) - success := and(success, staticcall(gas(), 7, add(mPtr, 0x40), 0x60, add(mPtr, 0x40), 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(mPtr, 0x40), + 0x60, + add(mPtr, 0x40), + 0x40 + ) + ) // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40)) + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40) + ) // add arithmetic into accumulator mstore(add(mPtr, 0x40), mload(arithmetic_term)) mstore(add(mPtr, 0x60), mload(add(arithmetic_term, 0x20))) - success := and(success, staticcall(gas(), 6, mPtr, 0x80, accumulator, 0x40)) + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, accumulator, 0x40) + ) } - require(success, "compute_linearised_opening_terms group operations fail"); - + require( + success, + "compute_linearised_opening_terms group operations fail" + ); + return accumulator; } @@ -610,13 +698,26 @@ library PolynomialEval { accumulator_ptr := mload(0x40) mstore(0x40, add(accumulator_ptr, 0xa0)) } - + // For the simplified plonk, we need to multiply -Z_H(z) with [T1], + // proof.zero_poly_eval = Z_H(z) + uint256 zero_poly_eval_neg = p - vk.zero_polynomial_eval; + // [T2], [T3], [T4] // first term Types.G1Point memory work_point = proof.T1; work_point.validateG1Point(); assembly { mstore(accumulator_ptr, mload(work_point)) mstore(add(accumulator_ptr, 0x20), mload(add(work_point, 0x20))) + mstore(add(accumulator_ptr, 0x40), zero_poly_eval_neg) + // computing zero_poly_eval_neg * [T1] + success := staticcall( + gas(), + 7, + accumulator_ptr, + 0x60, + accumulator_ptr, + 0x40 + ) } // second term @@ -627,13 +728,33 @@ library PolynomialEval { assembly { mstore(add(accumulator_ptr, 0x40), mload(work_point)) mstore(add(accumulator_ptr, 0x60), mload(add(work_point, 0x20))) - mstore(add(accumulator_ptr, 0x80), scalar_multiplier) + mstore( + add(accumulator_ptr, 0x80), + mulmod(scalar_multiplier, zero_poly_eval_neg, p) + ) + + // compute zero_poly_eval_neg * zeta_n * [T2] + success := staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) - // compute zeta_n.[T2] - success := staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40) - // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // third term @@ -644,14 +765,36 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x40), mload(work_point)) mstore(add(accumulator_ptr, 0x60), mload(add(work_point, 0x20))) - mstore(add(accumulator_ptr, 0x80), scalar_multiplier) + mstore( + add(accumulator_ptr, 0x80), + mulmod(scalar_multiplier, zero_poly_eval_neg, p) + ) + + // compute zero_poly_eval_neg * zeta_n^2 * [T3] + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) - // compute zeta_n^2.[T3] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) - + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // fourth term @@ -662,23 +805,62 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x40), mload(work_point)) mstore(add(accumulator_ptr, 0x60), mload(add(work_point, 0x20))) - mstore(add(accumulator_ptr, 0x80), scalar_multiplier) + mstore( + add(accumulator_ptr, 0x80), + mulmod(scalar_multiplier, zero_poly_eval_neg, p) + ) + + // compute zero_poly_eval_neg * zeta_n^3 * [T4] + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) - // compute zeta_n^3.[T4] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // fifth term work_point = partial_opening_commitment; work_point.validateG1Point(); - assembly { + assembly { // add partial opening commitment into accumulator - mstore(add(accumulator_ptr, 0x40), mload(partial_opening_commitment)) - mstore(add(accumulator_ptr, 0x60), mload(add(partial_opening_commitment, 0x20))) - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + mstore( + add(accumulator_ptr, 0x40), + mload(partial_opening_commitment) + ) + mstore( + add(accumulator_ptr, 0x60), + mload(add(partial_opening_commitment, 0x20)) + ) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } uint256 u_plus_one = challenges.u; @@ -697,10 +879,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v0(u + 1).[W1] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // W2 @@ -715,10 +917,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v1(u + 1).[W2] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // W3 @@ -733,13 +955,32 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v2(u + 1).[W3] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } - // W4 v_challenge = challenges.v3; work_point = proof.W4; @@ -752,10 +993,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v3(u + 1).[W4] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // SIGMA1 @@ -768,10 +1029,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v4.[SIGMA1] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // SIGMA2 @@ -784,10 +1065,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v5.[SIGMA2] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // SIGMA3 @@ -800,10 +1101,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v6.[SIGMA3] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } // QARITH @@ -816,10 +1137,30 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v7.[QARITH] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } Types.G1Point memory output; @@ -833,26 +1174,41 @@ library PolynomialEval { mstore(add(accumulator_ptr, 0x80), scalar_multiplier) // compute v8.[QECC] - success := and(success, staticcall(gas(), 7, add(accumulator_ptr, 0x40), 0x60, add(accumulator_ptr, 0x40), 0x40)) - + success := and( + success, + staticcall( + gas(), + 7, + add(accumulator_ptr, 0x40), + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) + ) + // add scalar mul output into output point - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, output, 0x40)) + success := and( + success, + staticcall(gas(), 6, accumulator_ptr, 0x80, output, 0x40) + ) } - - require(success, "compute_batch_opening_commitment group operations error"); + + require( + success, + "compute_batch_opening_commitment group operations error" + ); return output; } - function compute_batch_evaluation_scalar_multiplier(Types.Proof memory proof, Types.ChallengeTranscript memory challenges) - internal - pure - returns (uint256) - { + function compute_batch_evaluation_scalar_multiplier( + Types.Proof memory proof, + Types.ChallengeTranscript memory challenges + ) internal view returns (uint256) { uint256 p = Bn254Crypto.r_mod; uint256 opening_scalar; - uint256 lhs; - uint256 rhs; + uint256 lhs; // stores nu challenges + uint256 rhs; // stores evaluations of polynomials lhs = challenges.v0; rhs = proof.w1; @@ -913,17 +1269,17 @@ library PolynomialEval { assembly { opening_scalar := addmod(opening_scalar, mulmod(lhs, rhs, p), p) } - - lhs = challenges.v10; - rhs = proof.linearization_polynomial; - assembly { - opening_scalar := addmod(opening_scalar, mulmod(lhs, rhs, p), p) - } - - lhs = proof.quotient_polynomial_eval; + + // lhs = 1; //challenges.v10; (should be -1 for simplified Plonk) + rhs = proof.r_0; // linearization_polynomial should be r_0 for simplified Plonk assembly { - opening_scalar := addmod(opening_scalar, lhs, p) + opening_scalar := addmod(opening_scalar, sub(p, rhs), p) } + // should be removed for simplified Plonk + // lhs = proof.quotient_polynomial_eval; + // assembly { + // opening_scalar := addmod(opening_scalar, lhs, p) + // } lhs = challenges.v0; rhs = proof.w1_omega; @@ -931,23 +1287,35 @@ library PolynomialEval { assembly { shifted_opening_scalar := mulmod(lhs, rhs, p) } - + lhs = challenges.v1; rhs = proof.w2_omega; assembly { - shifted_opening_scalar := addmod(shifted_opening_scalar, mulmod(lhs, rhs, p), p) + shifted_opening_scalar := addmod( + shifted_opening_scalar, + mulmod(lhs, rhs, p), + p + ) } lhs = challenges.v2; rhs = proof.w3_omega; assembly { - shifted_opening_scalar := addmod(shifted_opening_scalar, mulmod(lhs, rhs, p), p) + shifted_opening_scalar := addmod( + shifted_opening_scalar, + mulmod(lhs, rhs, p), + p + ) } lhs = challenges.v3; rhs = proof.w4_omega; assembly { - shifted_opening_scalar := addmod(shifted_opening_scalar, mulmod(lhs, rhs, p), p) + shifted_opening_scalar := addmod( + shifted_opening_scalar, + mulmod(lhs, rhs, p), + p + ) } lhs = proof.grand_product_at_z_omega; @@ -972,10 +1340,8 @@ library PolynomialEval { Types.VerificationKey memory vk, Types.ChallengeTranscript memory challenges ) internal view returns (Types.G1Point memory) { - uint256 q_arith = proof.q_arith; uint256 q_ecc = proof.q_ecc; - uint256 linear_challenge = challenges.v10; uint256 alpha_base = challenges.alpha_base; uint256 scaling_alpha = challenges.alpha_base; uint256 alpha = challenges.alpha; @@ -994,14 +1360,22 @@ library PolynomialEval { uint256 w4 = proof.w4; uint256 w4_omega = proof.w4_omega; assembly { - delta := addmod(w4_omega, sub(p, mulmod(w4, 0x04, p)), p) + delta := addmod( + w4_omega, + sub(p, mulmod(w4, 0x04, p)), + p + ) } } uint256 w1 = proof.w1; assembly { - scalar_multiplier := mulmod(w1, linear_challenge, p) - scalar_multiplier := mulmod(scalar_multiplier, alpha_base, p) + scalar_multiplier := w1 + scalar_multiplier := mulmod( + scalar_multiplier, + alpha_base, + p + ) scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) scaling_alpha := mulmod(scaling_alpha, alpha, p) @@ -1011,7 +1385,7 @@ library PolynomialEval { t0 := mulmod(t0, q_ecc, p) t0 := mulmod(t0, scaling_alpha, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t0, linear_challenge, p), p) + scalar_multiplier := addmod(scalar_multiplier, t0, p) } Types.G1Point memory Q1 = vk.Q1; Q1.validateG1Point(); @@ -1021,7 +1395,14 @@ library PolynomialEval { mstore(mPtr, mload(Q1)) mstore(add(mPtr, 0x20), mload(add(Q1, 0x20))) mstore(add(mPtr, 0x40), scalar_multiplier) - success := staticcall(gas(), 7, mPtr, 0x60, accumulator_ptr, 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + accumulator_ptr, + 0x40 + ) } require(success, "G1 point multiplication failed!"); } @@ -1030,12 +1411,16 @@ library PolynomialEval { { uint256 w2 = proof.w2; assembly { - scalar_multiplier := mulmod(w2, linear_challenge, p) - scalar_multiplier := mulmod(scalar_multiplier, alpha_base, p) + scalar_multiplier := w2 + scalar_multiplier := mulmod( + scalar_multiplier, + alpha_base, + p + ) scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) let t0 := mulmod(scaling_alpha, q_ecc, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t0, linear_challenge, p), p) + scalar_multiplier := addmod(scalar_multiplier, t0, p) } Types.G1Point memory Q2 = vk.Q2; @@ -1048,10 +1433,27 @@ library PolynomialEval { mstore(add(mPtr, 0x40), scalar_multiplier) // write scalar mul output 0x40 bytes ahead of accumulator - success := staticcall(gas(), 7, mPtr, 0x60, add(accumulator_ptr, 0x40), 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } require(success, "G1 point multiplication failed!"); } @@ -1061,9 +1463,17 @@ library PolynomialEval { { uint256 w3 = proof.w3; assembly { - scalar_multiplier := mulmod(w3, linear_challenge, p) - scalar_multiplier := mulmod(scalar_multiplier, alpha_base, p) - scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) + scalar_multiplier := w3 + scalar_multiplier := mulmod( + scalar_multiplier, + alpha_base, + p + ) + scalar_multiplier := mulmod( + scalar_multiplier, + q_arith, + p + ) } } { @@ -1084,7 +1494,11 @@ library PolynomialEval { t1 := addmod(t1, t1, p) t1 := mulmod(t1, q_ecc, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t1, linear_challenge, p), p) + scalar_multiplier := addmod( + scalar_multiplier, + t1, + p + ) } } } @@ -1099,13 +1513,12 @@ library PolynomialEval { } uint256 w3_omega = proof.w3_omega; assembly { - t0 := mulmod(t0, w3_omega, p) t0 := mulmod(t0, scaling_alpha, p) t0 := mulmod(t0, q_ecc, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t0, linear_challenge, p), p) + scalar_multiplier := addmod(scalar_multiplier, t0, p) } } @@ -1119,10 +1532,27 @@ library PolynomialEval { mstore(add(mPtr, 0x40), scalar_multiplier) // write scalar mul output 0x40 bytes ahead of accumulator - success := staticcall(gas(), 7, mPtr, 0x60, add(accumulator_ptr, 0x40), 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } require(success, "G1 point multiplication failed!"); } @@ -1133,16 +1563,20 @@ library PolynomialEval { uint256 w4 = proof.w4; uint256 q_c = proof.q_c; assembly { - scalar_multiplier := mulmod(w4, linear_challenge, p) + scalar_multiplier := w4 scalar_multiplier := mulmod(scalar_multiplier, alpha_base, p) scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) - scaling_alpha := mulmod(scaling_alpha, mulmod(alpha, alpha, p), p) + scaling_alpha := mulmod( + scaling_alpha, + mulmod(alpha, alpha, p), + p + ) let t0 := mulmod(w3, q_ecc, p) t0 := mulmod(t0, q_c, p) t0 := mulmod(t0, scaling_alpha, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t0, linear_challenge, p), p) + scalar_multiplier := addmod(scalar_multiplier, t0, p) } Types.G1Point memory Q4 = vk.Q4; @@ -1155,10 +1589,27 @@ library PolynomialEval { mstore(add(mPtr, 0x40), scalar_multiplier) // write scalar mul output 0x40 bytes ahead of accumulator - success := staticcall(gas(), 7, mPtr, 0x60, add(accumulator_ptr, 0x40), 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } require(success, "G1 point multiplication failed!"); } @@ -1171,18 +1622,21 @@ library PolynomialEval { let neg_w4 := sub(p, w4) scalar_multiplier := mulmod(w4, w4, p) scalar_multiplier := addmod(scalar_multiplier, neg_w4, p) - scalar_multiplier := mulmod(scalar_multiplier, addmod(w4, sub(p, 2), p), p) + scalar_multiplier := mulmod( + scalar_multiplier, + addmod(w4, sub(p, 2), p), + p + ) scalar_multiplier := mulmod(scalar_multiplier, alpha_base, p) scalar_multiplier := mulmod(scalar_multiplier, alpha, p) scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) - scalar_multiplier := mulmod(scalar_multiplier, linear_challenge, p) let t0 := addmod(0x01, neg_w4, p) t0 := mulmod(t0, q_ecc, p) t0 := mulmod(t0, q_c, p) t0 := mulmod(t0, scaling_alpha, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t0, linear_challenge, p), p) + scalar_multiplier := addmod(scalar_multiplier, t0, p) } Types.G1Point memory Q5 = vk.Q5; @@ -1195,14 +1649,31 @@ library PolynomialEval { mstore(add(mPtr, 0x40), scalar_multiplier) // write scalar mul output 0x40 bytes ahead of accumulator - success := staticcall(gas(), 7, mPtr, 0x60, add(accumulator_ptr, 0x40), 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } require(success, "G1 point multiplication failed!"); } - + // QM Selector { { @@ -1211,21 +1682,23 @@ library PolynomialEval { assembly { scalar_multiplier := mulmod(w1, w2, p) - scalar_multiplier := mulmod(scalar_multiplier, linear_challenge, p) - scalar_multiplier := mulmod(scalar_multiplier, alpha_base, p) + scalar_multiplier := mulmod( + scalar_multiplier, + alpha_base, + p + ) scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) } } uint256 w3 = proof.w3; uint256 q_c = proof.q_c; assembly { - scaling_alpha := mulmod(scaling_alpha, alpha, p) let t0 := mulmod(w3, q_ecc, p) t0 := mulmod(t0, q_c, p) t0 := mulmod(t0, scaling_alpha, p) - scalar_multiplier := addmod(scalar_multiplier, mulmod(t0, linear_challenge, p), p) + scalar_multiplier := addmod(scalar_multiplier, t0, p) } Types.G1Point memory QM = vk.QM; @@ -1238,10 +1711,27 @@ library PolynomialEval { mstore(add(mPtr, 0x40), scalar_multiplier) // write scalar mul output 0x40 bytes ahead of accumulator - success := staticcall(gas(), 7, mPtr, 0x60, add(accumulator_ptr, 0x40), 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) // add scalar mul output into accumulator - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, accumulator_ptr, 0x40)) + success := and( + success, + staticcall( + gas(), + 6, + accumulator_ptr, + 0x80, + accumulator_ptr, + 0x40 + ) + ) } require(success, "G1 point multiplication failed!"); } @@ -1251,7 +1741,7 @@ library PolynomialEval { { uint256 q_c_challenge = challenges.v9; assembly { - scalar_multiplier := mulmod(linear_challenge, alpha_base, p) + scalar_multiplier := alpha_base scalar_multiplier := mulmod(scalar_multiplier, q_arith, p) // TurboPlonk requires an explicit evaluation of q_c @@ -1270,20 +1760,28 @@ library PolynomialEval { mstore(add(mPtr, 0x40), scalar_multiplier) // write scalar mul output 0x40 bytes ahead of accumulator - success := staticcall(gas(), 7, mPtr, 0x60, add(accumulator_ptr, 0x40), 0x40) + success := staticcall( + gas(), + 7, + mPtr, + 0x60, + add(accumulator_ptr, 0x40), + 0x40 + ) // add scalar mul output into output point - success := and(success, staticcall(gas(), 6, accumulator_ptr, 0x80, output, 0x40)) + success := and( + success, + staticcall(gas(), 6, accumulator_ptr, 0x80, output, 0x40) + ) } require(success, "G1 point multiplication failed!"); - } challenges.alpha_base = alpha_base; return output; } - // Compute kate opening scalar for logic gate opening scalars // This method evalautes the polynomial identity used to evaluate either // a 2-bit AND or XOR operation in a single constraint @@ -1328,7 +1826,7 @@ library PolynomialEval { { uint256 wire3 = proof.w3; - assembly{ + assembly { t4 := mulmod(wire3, 0x02, p) identity := addmod(identity, sub(p, t4), p) identity := mulmod(identity, alpha, p) @@ -1417,12 +1915,10 @@ library PolynomialEval { identity := addmod(identity, t2, p) } } - uint256 linear_nu = challenges.v10; uint256 alpha_base = challenges.alpha_base; assembly { identity := mulmod(identity, alpha_base, p) - identity := mulmod(identity, linear_nu, p) } } // update alpha @@ -1431,7 +1927,7 @@ library PolynomialEval { assembly { alpha := mulmod(alpha, alpha, p) alpha := mulmod(alpha, alpha, p) - alpha_base := mulmod(alpha_base, alpha, p) + alpha_base := mulmod(alpha_base, alpha, p) } challenges.alpha_base = alpha_base; @@ -1452,13 +1948,15 @@ library PolynomialEval { uint256 alpha_base = challenges.alpha_base; uint256 range_acc; uint256 p = Bn254Crypto.r_mod; - uint256 linear_challenge = challenges.v10; assembly { let delta_1 := addmod(wire3, sub(p, mulmod(wire4, 0x04, p)), p) let delta_2 := addmod(wire2, sub(p, mulmod(wire3, 0x04, p)), p) let delta_3 := addmod(wire1, sub(p, mulmod(wire2, 0x04, p)), p) - let delta_4 := addmod(wire4_omega, sub(p, mulmod(wire1, 0x04, p)), p) - + let delta_4 := addmod( + wire4_omega, + sub(p, mulmod(wire1, 0x04, p)), + p + ) let t0 := mulmod(delta_1, delta_1, p) t0 := addmod(t0, sub(p, delta_1), p) @@ -1500,8 +1998,6 @@ library PolynomialEval { t0 := mulmod(t0, alpha_base, p) range_acc := addmod(range_acc, t0, p) alpha_base := mulmod(alpha_base, alpha, p) - - range_acc := mulmod(range_acc, linear_challenge, p) } challenges.alpha_base = alpha_base; @@ -1519,7 +2015,7 @@ library PolynomialEval { uint256 zeta = challenges.zeta; uint256 gamma = challenges.gamma; uint256 p = Bn254Crypto.r_mod; - + uint256 partial_grand_product; uint256 sigma_multiplier; @@ -1528,8 +2024,16 @@ library PolynomialEval { uint256 sigma1 = proof.sigma1; assembly { let witness_term := addmod(w1, gamma, p) - partial_grand_product := addmod(mulmod(beta, zeta, p), witness_term, p) - sigma_multiplier := addmod(mulmod(sigma1, beta, p), witness_term, p) + partial_grand_product := addmod( + mulmod(beta, zeta, p), + witness_term, + p + ) + sigma_multiplier := addmod( + mulmod(sigma1, beta, p), + witness_term, + p + ) } } { @@ -1537,8 +2041,20 @@ library PolynomialEval { uint256 sigma2 = proof.sigma2; assembly { let witness_term := addmod(w2, gamma, p) - partial_grand_product := mulmod(partial_grand_product, addmod(mulmod(mulmod(zeta, 0x05, p), beta, p), witness_term, p), p) - sigma_multiplier := mulmod(sigma_multiplier, addmod(mulmod(sigma2, beta, p), witness_term, p), p) + partial_grand_product := mulmod( + partial_grand_product, + addmod( + mulmod(mulmod(zeta, 0x05, p), beta, p), + witness_term, + p + ), + p + ) + sigma_multiplier := mulmod( + sigma_multiplier, + addmod(mulmod(sigma2, beta, p), witness_term, p), + p + ) } } { @@ -1546,38 +2062,88 @@ library PolynomialEval { uint256 sigma3 = proof.sigma3; assembly { let witness_term := addmod(w3, gamma, p) - partial_grand_product := mulmod(partial_grand_product, addmod(mulmod(mulmod(zeta, 0x06, p), beta, p), witness_term, p), p) + partial_grand_product := mulmod( + partial_grand_product, + addmod( + mulmod(mulmod(zeta, 0x06, p), beta, p), + witness_term, + p + ), + p + ) - sigma_multiplier := mulmod(sigma_multiplier, addmod(mulmod(sigma3, beta, p), witness_term, p), p) + sigma_multiplier := mulmod( + sigma_multiplier, + addmod(mulmod(sigma3, beta, p), witness_term, p), + p + ) } } { uint256 w4 = proof.w4; assembly { - partial_grand_product := mulmod(partial_grand_product, addmod(addmod(mulmod(mulmod(zeta, 0x07, p), beta, p), gamma, p), w4, p), p) + partial_grand_product := mulmod( + partial_grand_product, + addmod( + addmod( + mulmod(mulmod(zeta, 0x07, p), beta, p), + gamma, + p + ), + w4, + p + ), + p + ) } } { - uint256 linear_challenge = challenges.v10; uint256 alpha_base = challenges.alpha_base; uint256 alpha = challenges.alpha; uint256 separator_challenge = challenges.u; uint256 grand_product_at_z_omega = proof.grand_product_at_z_omega; uint256 l_start = L1_fr; assembly { - partial_grand_product := mulmod(partial_grand_product, alpha_base, p) + partial_grand_product := mulmod( + partial_grand_product, + alpha_base, + p + ) - sigma_multiplier := mulmod(mulmod(sub(p, mulmod(mulmod(sigma_multiplier, grand_product_at_z_omega, p), alpha_base, p)), beta, p), linear_challenge, p) + sigma_multiplier := mulmod( + sub( + p, + mulmod( + mulmod( + sigma_multiplier, + grand_product_at_z_omega, + p + ), + alpha_base, + p + ) + ), + beta, + p + ) alpha_base := mulmod(mulmod(alpha_base, alpha, p), alpha, p) - partial_grand_product := addmod(mulmod(addmod(partial_grand_product, mulmod(l_start, alpha_base, p), p), linear_challenge, p), separator_challenge, p) + partial_grand_product := addmod( + addmod( + partial_grand_product, + mulmod(l_start, alpha_base, p), + p + ), + separator_challenge, + p + ) alpha_base := mulmod(alpha_base, alpha, p) } challenges.alpha_base = alpha_base; } - + //Need to understand the below code: Types.G1Point memory Z = proof.Z; Types.G1Point memory SIGMA4 = vk.SIGMA4; Types.G1Point memory accumulator; @@ -1594,17 +2160,36 @@ library PolynomialEval { mstore(add(mPtr, 0x40), mload(SIGMA4)) mstore(add(mPtr, 0x60), mload(add(SIGMA4, 0x20))) mstore(add(mPtr, 0x80), sigma_multiplier) - success := and(success, staticcall(gas(), 7, add(mPtr, 0x40), 0x60, add(mPtr, 0x40), 0x40)) - - success := and(success, staticcall(gas(), 6, mPtr, 0x80, accumulator, 0x40)) + success := and( + success, + staticcall( + gas(), + 7, + add(mPtr, 0x40), + 0x60, + add(mPtr, 0x40), + 0x40 + ) + ) + + // mload(mPtr) : (partial_grand_product * [Z]).x + // mload(mPtr + 32) : (partial_grand_product * [Z]).y + // mload(mPtr + 64) : (sigma_multiplier * [SIGMA_4]).x + // mload(mPtr + 96) : (sigma_multiplier * [SIGMA_4]).y + + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, accumulator, 0x40) + ) } - require(success, "compute_grand_product_opening_scalar group operations failure"); + require( + success, + "compute_grand_product_opening_scalar group operations failure" + ); return accumulator; } } - - "#}; } diff --git a/common/src/contract/cryptography/transcript.rs b/common/src/contract/cryptography/transcript.rs index dd16e52a..e4e72bae 100644 --- a/common/src/contract/cryptography/transcript.rs +++ b/common/src/contract/cryptography/transcript.rs @@ -3,181 +3,36 @@ macro_rules! TRANSCRIPT_LIBRARY { () => { r#" /** - * @title Challenge transcript library - * @dev Used to collect the data necessary to calculate the various challenges: beta, gamma, alpha, zeta, nu[7], u + * @title Transcript library + * @dev Generates Plonk random challenges */ -library TranscriptLibrary { - // When creating `transcript.data` we pre-allocate all the memory required to store the entire transcript, minus public inputs - uint256 constant NUM_TRANSCRIPT_BYTES = 1248; - - struct Transcript { - bytes data; +library Transcript { + struct TranscriptData { bytes32 current_challenge; - uint32 challenge_counter; - uint256 num_public_inputs; - } - - /** - * Instantiate a transcript and calculate the initial challenge, from which other challenges are derived. - * - * Resembles the preamble round in the Plonk prover - */ - function new_transcript(uint256 circuit_size, uint256 num_public_inputs) - internal - pure - returns (Transcript memory transcript) - { - transcript.current_challenge = compute_initial_challenge(circuit_size, num_public_inputs); - transcript.challenge_counter = 0; - - // manually format the transcript.data bytes array - // This is because we want to reserve memory that is greatly in excess of the array's initial size - bytes memory transcript_data_pointer; - bytes32 transcript_data = transcript.current_challenge; - uint256 total_transcript_bytes = NUM_TRANSCRIPT_BYTES; - assembly { - transcript_data_pointer := mload(0x40) - mstore(0x40, add(transcript_data_pointer, total_transcript_bytes)) - // update length of transcript.data - mstore(transcript_data_pointer, 0x20) - // insert current challenge - mstore(add(transcript_data_pointer, 0x20), transcript_data) - } - transcript.data = transcript_data_pointer; - transcript.num_public_inputs = num_public_inputs; } - /** * Compute keccak256 hash of 2 4-byte variables (circuit_size, num_public_inputs) */ - function compute_initial_challenge(uint256 circuit_size, uint256 num_public_inputs) internal pure returns (bytes32 challenge) { + function generate_initial_challenge( + TranscriptData memory self, + uint256 circuit_size, + uint256 num_public_inputs + ) internal pure { + bytes32 challenge; assembly { let mPtr := mload(0x40) mstore8(add(mPtr, 0x20), shr(24, circuit_size)) mstore8(add(mPtr, 0x21), shr(16, circuit_size)) mstore8(add(mPtr, 0x22), shr(8, circuit_size)) - mstore8(add(mPtr, 0x23), circuit_size) + mstore8(add(mPtr, 0x23), circuit_size) mstore8(add(mPtr, 0x24), shr(24, num_public_inputs)) mstore8(add(mPtr, 0x25), shr(16, num_public_inputs)) mstore8(add(mPtr, 0x26), shr(8, num_public_inputs)) mstore8(add(mPtr, 0x27), num_public_inputs) challenge := keccak256(add(mPtr, 0x20), 0x08) } - } - - /** - * Add a uint256 into the transcript - */ - function update_with_u256(Transcript memory self, uint256 value) internal pure { - bytes memory data_ptr = self.data; - assembly { - // update length of transcript data - let array_length := mload(data_ptr) - mstore(data_ptr, add(0x20, array_length)) - // insert new 32-byte value at the end of the array - mstore(add(data_ptr, add(array_length, 0x20)), value) - } - } - - /** - * Add a g1 point into the transcript - */ - function update_with_g1(Transcript memory self, Types.G1Point memory p) internal pure { - // in the C++ prover, the y coord is appended first before the x - bytes memory data_ptr = self.data; - assembly { - // update length of transcript data - let array_length := mload(data_ptr) - mstore(data_ptr, add(0x40, array_length)) - // insert new 64-byte value at the end of the array - mstore(add(data_ptr, add(array_length, 0x20)), mload(add(p, 0x20))) - mstore(add(data_ptr, add(array_length, 0x40)), mload(p)) - } - } - - - /** - * Add a g1 point into the transcript - */ - function update_with_four_g1_elements( - Transcript memory self, - Types.G1Point memory p1, - Types.G1Point memory p2, - Types.G1Point memory p3, - Types.G1Point memory p4 - ) internal pure { - // in the C++ prover, the y coord is appended first before the x - bytes memory data_ptr = self.data; - assembly { - // update length of transcript data - let array_length := mload(data_ptr) - mstore(data_ptr, add(0x100, array_length)) - data_ptr := add(data_ptr, array_length) - // insert new 64-byte value at the end of the array - mstore(add(data_ptr, 0x20), mload(add(p1, 0x20))) - mstore(add(data_ptr, 0x40), mload(p1)) - mstore(add(data_ptr, 0x60), mload(add(p2, 0x20))) - mstore(add(data_ptr, 0x80), mload(p2)) - mstore(add(data_ptr, 0xa0), mload(add(p3, 0x20))) - mstore(add(data_ptr, 0xc0), mload(p3)) - mstore(add(data_ptr, 0xe0), mload(add(p4, 0x20))) - mstore(add(data_ptr, 0x100), mload(p4)) - } - } - - /** - * Append byte - */ - function append_byte(Transcript memory self, uint8 value) internal pure { - bytes memory data_ptr = self.data; - uint256 array_length = 0; - assembly { - // update length of transcript data - array_length := mload(data_ptr) - mstore(data_ptr, add(0x01, array_length)) - // insert new 1-byte value at the end of the array - mstore8(add(data_ptr, add(array_length, 0x20)), value) - } - } - - /** - * Reset challenge array to equal a single bytes32 value - */ - function reset_to_bytes32(Transcript memory self, bytes32 value) internal pure { - bytes memory data_ptr = self.data; - { - assembly { - mstore(data_ptr, 0x20) - mstore(add(data_ptr, 0x20), value) - } - } - } - - /** - * Draw a challenge - */ - function get_challenge(Transcript memory self) internal pure returns (uint256) { - bytes32 challenge; - bytes memory data_ptr = self.data; - assembly { - let length := mload(data_ptr) - challenge := keccak256(add(data_ptr, 0x20), length) - } self.current_challenge = challenge; - - // reset self.data by setting length to 0x20 and update first element - { - assembly { - mstore(data_ptr, 0x20) - mstore(add(data_ptr, 0x20), challenge) - } - } - uint256 p = Bn254Crypto.r_mod; - assembly { - challenge := mod(challenge, p) - } - return (uint256)(challenge); } /** @@ -185,32 +40,29 @@ library TranscriptLibrary { * The number of public inputs can be extremely large for rollups and we want to minimize mem consumption. * => we directly allocate memory to hash the public inputs, in order to prevent the global memory pointer from increasing */ - function get_beta_gamma_challenges( - Transcript memory self, + function generate_beta_gamma_challenges( + TranscriptData memory self, Types.ChallengeTranscript memory challenges, uint256 num_public_inputs - ) internal pure { + ) internal pure { bytes32 challenge; bytes32 old_challenge = self.current_challenge; uint256 p = Bn254Crypto.r_mod; uint256 reduced_challenge; assembly { let m_ptr := mload(0x40) - // N.B. If the calldata ABI changes this code will need to change! // We can copy all of the public inputs, followed by the wire commitments, into memory // using calldatacopy mstore(m_ptr, old_challenge) m_ptr := add(m_ptr, 0x20) - let inputs_start := add(calldataload(0x24), 0x24) // start of call data - let num_calldata_bytes := mul(num_public_inputs, 0x20) + let inputs_start := add(calldataload(0x04), 0x24) + // num_calldata_bytes = public input size + 256 bytes for the 4 wire commitments + let num_calldata_bytes := add(0x100, mul(num_public_inputs, 0x20)) calldatacopy(m_ptr, inputs_start, num_calldata_bytes) - inputs_start := add(calldataload(0x04), 0x24) // start of proof data - calldatacopy(add(m_ptr, num_calldata_bytes), inputs_start, 0x100) - let start := mload(0x40) - let length := add(num_calldata_bytes, 0x120) + let length := add(num_calldata_bytes, 0x20) challenge := keccak256(start, length) reduced_challenge := mod(challenge, p) @@ -225,19 +77,60 @@ library TranscriptLibrary { reduced_challenge := mod(challenge, p) } challenges.gamma = reduced_challenge; - - bytes memory data_ptr = self.data; self.current_challenge = challenge; + } - // reset self.data by setting length to 0x20 and update first element - { - assembly { - mstore(data_ptr, 0x20) - mstore(add(data_ptr, 0x20), challenge) - } + function generate_alpha_challenge( + TranscriptData memory self, + Types.ChallengeTranscript memory challenges, + Types.G1Point memory Z + ) internal pure { + bytes32 challenge; + bytes32 old_challenge = self.current_challenge; + uint256 p = Bn254Crypto.r_mod; + uint256 reduced_challenge; + assembly { + let m_ptr := mload(0x40) + mstore(m_ptr, old_challenge) + mstore(add(m_ptr, 0x20), mload(add(Z, 0x20))) + mstore(add(m_ptr, 0x40), mload(Z)) + challenge := keccak256(m_ptr, 0x60) + reduced_challenge := mod(challenge, p) } + challenges.alpha = reduced_challenge; + challenges.alpha_base = reduced_challenge; + self.current_challenge = challenge; } + function generate_zeta_challenge( + TranscriptData memory self, + Types.ChallengeTranscript memory challenges, + Types.G1Point memory T1, + Types.G1Point memory T2, + Types.G1Point memory T3, + Types.G1Point memory T4 + ) internal pure { + bytes32 challenge; + bytes32 old_challenge = self.current_challenge; + uint256 p = Bn254Crypto.r_mod; + uint256 reduced_challenge; + assembly { + let m_ptr := mload(0x40) + mstore(m_ptr, old_challenge) + mstore(add(m_ptr, 0x20), mload(add(T1, 0x20))) + mstore(add(m_ptr, 0x40), mload(T1)) + mstore(add(m_ptr, 0x60), mload(add(T2, 0x20))) + mstore(add(m_ptr, 0x80), mload(T2)) + mstore(add(m_ptr, 0xa0), mload(add(T3, 0x20))) + mstore(add(m_ptr, 0xc0), mload(T3)) + mstore(add(m_ptr, 0xe0), mload(add(T4, 0x20))) + mstore(add(m_ptr, 0x100), mload(T4)) + challenge := keccak256(m_ptr, 0x120) + reduced_challenge := mod(challenge, p) + } + challenges.zeta = reduced_challenge; + self.current_challenge = challenge; + } /** * We compute our initial nu challenge by hashing the following proof elements (with the current challenge): @@ -248,29 +141,32 @@ library TranscriptLibrary { * These values are placed linearly in the proofData, we can extract them with a calldatacopy call * */ - function get_nu_challenges(Transcript memory self, uint256 quotient_poly_eval, Types.ChallengeTranscript memory challenges) internal pure - { - // get a calldata pointer that points to the start of the data we want to copy - uint256 calldata_ptr; - assembly { - calldata_ptr := add(calldataload(0x04), 0x24) - } - // There are NINE G1 group elements added into the transcript in the `beta` round, that we need to skip over - assembly { - calldata_ptr := add(calldata_ptr, 0x240) // 9 * 0x40 = 0x240 - } - + function generate_nu_challenges( + TranscriptData memory self, + Types.ChallengeTranscript memory challenges, + // uint256 quotient_poly_eval, + uint256 num_public_inputs + ) internal pure { uint256 p = Bn254Crypto.r_mod; - bytes memory data_ptr = self.data; + bytes32 current_challenge = self.current_challenge; uint256 base_v_challenge; uint256 updated_v; // We want to copy SIXTEEN field elements from calldata into memory to hash // But we start by adding the quotient poly evaluation to the hash transcript assembly { - mstore(add(data_ptr, 0x40), quotient_poly_eval) - calldatacopy(add(data_ptr, 0x60), calldata_ptr, 0x200) // 16 * 0x20 = 0x200 - base_v_challenge := keccak256(add(data_ptr, 0x20), 0x240) // hash length = 0x240, we include the previous challenge in the hash + // get a calldata pointer that points to the start of the data we want to copy + let calldata_ptr := add(calldataload(0x04), 0x24) + // skip over the public inputs + calldata_ptr := add(calldata_ptr, mul(num_public_inputs, 0x20)) + // There are NINE G1 group elements added into the transcript in the `beta` round, that we need to skip over + calldata_ptr := add(calldata_ptr, 0x240) // 9 * 0x40 = 0x240 + + let m_ptr := mload(0x40) + mstore(m_ptr, current_challenge) + // mstore(add(m_ptr, 0x20), quotient_poly_eval) + calldatacopy(add(m_ptr, 0x20), calldata_ptr, 0x1e0) // 15 * 0x20 = 0x1e0 + base_v_challenge := keccak256(m_ptr, 0x200) // hash length = 0x200, we include the previous challenge in the hash updated_v := mod(base_v_challenge, p) } @@ -331,18 +227,37 @@ library TranscriptLibrary { assembly { mstore8(0x20, 0x0a) challenge := keccak256(0x00, 0x21) - - mstore(data_ptr, 0x20) - mstore(add(data_ptr, 0x20), challenge) updated_v := mod(challenge, p) } challenges.v10 = updated_v; self.current_challenge = challenge; } -} - - + function generate_separator_challenge( + TranscriptData memory self, + Types.ChallengeTranscript memory challenges, + Types.G1Point memory PI_Z, + Types.G1Point memory PI_Z_OMEGA + ) internal pure { + bytes32 challenge; + bytes32 old_challenge = self.current_challenge; + uint256 p = Bn254Crypto.r_mod; + uint256 reduced_challenge; + assembly { + let m_ptr := mload(0x40) + mstore(m_ptr, old_challenge) + mstore(add(m_ptr, 0x20), mload(add(PI_Z, 0x20))) + mstore(add(m_ptr, 0x40), mload(PI_Z)) + mstore(add(m_ptr, 0x60), mload(add(PI_Z_OMEGA, 0x20))) + mstore(add(m_ptr, 0x80), mload(PI_Z_OMEGA)) + challenge := keccak256(m_ptr, 0xa0) + reduced_challenge := mod(challenge, p) + } + challenges.u = reduced_challenge; + self.current_challenge = challenge; + } +} + "# }; } diff --git a/common/src/contract/cryptography/turbo_plonk.rs b/common/src/contract/cryptography/turbo_plonk.rs deleted file mode 100644 index 23ecc555..00000000 --- a/common/src/contract/cryptography/turbo_plonk.rs +++ /dev/null @@ -1,345 +0,0 @@ -#[macro_export] -macro_rules! TURBOPLONK_LIBRARY { - () => { r#" - - -/** - * @title TurboPlonk verification algo implementation - * @dev Implements the Turbo Plonk verification algorithm, through the use of five functions that - * calculate challenges, setup initial state, evaluate the necessary polynomials and executes the pairing - * check. - * - * Expected to be inherited by `Verifier.sol` - * - * Copyright 2020 Spilsbury Holdings Ltd - * - * Licensed under the GNU General Public License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -library TurboPlonk { - using Bn254Crypto for Types.G1Point; - using Bn254Crypto for Types.G2Point; - using TranscriptLibrary for TranscriptLibrary.Transcript; - - /** - * @dev Evaluate the various remaining polynomials: partial_opening, batch_opening, batch_evaluation - * @param decoded_proof - deserialised proof - * @param vk - verification key - * @param challenges - all challenges (alpha, beta, gamma, zeta, nu[NUM_NU_CHALLENGES], u) stored in - * ChallengeTranscript struct form - * @param L1 - lagrange 1 evaluation - * @return batch_opening commitment and batch_evaluation commitment, both represented as G1 Points - */ - function evaluate_polynomials( - Types.Proof memory decoded_proof, - Types.VerificationKey memory vk, - Types.ChallengeTranscript memory challenges, - uint256 L1 - ) internal view returns (Types.G1Point memory, uint256) { - Types.G1Point memory partial_opening_commitment = PolynomialEval.compute_linearised_opening_terms( - challenges, - L1, - vk, - decoded_proof - ); - - Types.G1Point memory batch_opening_commitment = PolynomialEval.compute_batch_opening_commitment( - challenges, - vk, - partial_opening_commitment, - decoded_proof - ); - - - uint256 batch_evaluation_g1_scalar = PolynomialEval.compute_batch_evaluation_scalar_multiplier( - decoded_proof, - challenges - ); - - return (batch_opening_commitment, batch_evaluation_g1_scalar); - } - - /** - * @dev Compute partial state of the verifier, specifically: public input delta evaluation, zero polynomial - * evaluation, the lagrange evaluations and the quotient polynomial evaluations - * - * Note: This uses the batch inversion Montgomery trick to reduce the number of - * inversions, and therefore the number of calls to the bn128 modular exponentiation - * precompile. - * - * Specifically, each function call: compute_public_input_delta() etc. at some point needs to invert a - * value to calculate a denominator in a fraction. Instead of performing this inversion as it is needed, we - * instead 'save up' the denominator calculations. The inputs to this are returned from the various functions - * and then we perform all necessary inversions in one go at the end of `evalaute_field_operations()`. This - * gives us the various variables that need to be returned. - * - * @param decoded_proof - deserialised proof - * @param vk - verification key - * @param challenges - all challenges (alpha, beta, gamma, zeta, nu[NUM_NU_CHALLENGES], u) stored in - * ChallengeTranscript struct form - * @return quotient polynomial evaluation (field element) and lagrange 1 evaluation (field element) - */ - function evalaute_field_operations( - Types.Proof memory decoded_proof, - Types.VerificationKey memory vk, - Types.ChallengeTranscript memory challenges - ) internal view returns (uint256, uint256) { - uint256 public_input_delta; - uint256 zero_polynomial_eval; - uint256 l_start; - uint256 l_end; - { - (uint256 public_input_numerator, uint256 public_input_denominator) = PolynomialEval.compute_public_input_delta( - challenges, - vk - ); - - ( - uint256 vanishing_numerator, - uint256 vanishing_denominator, - uint256 lagrange_numerator, - uint256 l_start_denominator, - uint256 l_end_denominator - ) = PolynomialEval.compute_lagrange_and_vanishing_fractions(vk, challenges.zeta); - - - (zero_polynomial_eval, public_input_delta, l_start, l_end) = PolynomialEval.compute_batch_inversions( - public_input_numerator, - public_input_denominator, - vanishing_numerator, - vanishing_denominator, - lagrange_numerator, - l_start_denominator, - l_end_denominator - ); - } - - uint256 quotient_eval = PolynomialEval.compute_quotient_polynomial( - zero_polynomial_eval, - public_input_delta, - challenges, - l_start, - l_end, - decoded_proof - ); - - return (quotient_eval, l_start); - } - - /** - * @dev Perform the pairing check - * @param batch_opening_commitment - G1 point representing the calculated batch opening commitment - * @param batch_evaluation_g1_scalar - uint256 representing the batch evaluation scalar multiplier to be applied to the G1 generator point - * @param challenges - all challenges (alpha, beta, gamma, zeta, nu[NUM_NU_CHALLENGES], u) stored in - * ChallengeTranscript struct form - * @param vk - verification key - * @param decoded_proof - deserialised proof - * @return bool specifying whether the pairing check was successful - */ - function perform_pairing( - Types.G1Point memory batch_opening_commitment, - uint256 batch_evaluation_g1_scalar, - Types.ChallengeTranscript memory challenges, - Types.Proof memory decoded_proof, - Types.VerificationKey memory vk - ) internal view returns (bool) { - - uint256 u = challenges.u; - bool success; - uint256 p = Bn254Crypto.r_mod; - Types.G1Point memory rhs; - Types.G1Point memory PI_Z_OMEGA = decoded_proof.PI_Z_OMEGA; - Types.G1Point memory PI_Z = decoded_proof.PI_Z; - PI_Z.validateG1Point(); - PI_Z_OMEGA.validateG1Point(); - - // rhs = zeta.[PI_Z] + u.zeta.omega.[PI_Z_OMEGA] + [batch_opening_commitment] - batch_evaluation_g1_scalar.[1] - // scope this block to prevent stack depth errors - { - uint256 zeta = challenges.zeta; - uint256 pi_z_omega_scalar = vk.work_root; - assembly { - pi_z_omega_scalar := mulmod(pi_z_omega_scalar, zeta, p) - pi_z_omega_scalar := mulmod(pi_z_omega_scalar, u, p) - batch_evaluation_g1_scalar := sub(p, batch_evaluation_g1_scalar) - - // store accumulator point at mptr - let mPtr := mload(0x40) - - // set accumulator = batch_opening_commitment - mstore(mPtr, mload(batch_opening_commitment)) - mstore(add(mPtr, 0x20), mload(add(batch_opening_commitment, 0x20))) - - // compute zeta.[PI_Z] and add into accumulator - mstore(add(mPtr, 0x40), mload(PI_Z)) - mstore(add(mPtr, 0x60), mload(add(PI_Z, 0x20))) - mstore(add(mPtr, 0x80), zeta) - success := staticcall(gas(), 7, add(mPtr, 0x40), 0x60, add(mPtr, 0x40), 0x40) - success := and(success, staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40)) - - // compute u.zeta.omega.[PI_Z_OMEGA] and add into accumulator - mstore(add(mPtr, 0x40), mload(PI_Z_OMEGA)) - mstore(add(mPtr, 0x60), mload(add(PI_Z_OMEGA, 0x20))) - mstore(add(mPtr, 0x80), pi_z_omega_scalar) - success := and(success, staticcall(gas(), 7, add(mPtr, 0x40), 0x60, add(mPtr, 0x40), 0x40)) - success := and(success, staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40)) - - // compute -batch_evaluation_g1_scalar.[1] - mstore(add(mPtr, 0x40), 0x01) // hardcoded generator point (1, 2) - mstore(add(mPtr, 0x60), 0x02) - mstore(add(mPtr, 0x80), batch_evaluation_g1_scalar) - success := and(success, staticcall(gas(), 7, add(mPtr, 0x40), 0x60, add(mPtr, 0x40), 0x40)) - - // add -batch_evaluation_g1_scalar.[1] and the accumulator point, write result into rhs - success := and(success, staticcall(gas(), 6, mPtr, 0x80, rhs, 0x40)) - } - } - - Types.G1Point memory lhs; - assembly { - // store accumulator point at mptr - let mPtr := mload(0x40) - - // copy [PI_Z] into mPtr - mstore(mPtr, mload(PI_Z)) - mstore(add(mPtr, 0x20), mload(add(PI_Z, 0x20))) - - // compute u.[PI_Z_OMEGA] and write to (mPtr + 0x40) - mstore(add(mPtr, 0x40), mload(PI_Z_OMEGA)) - mstore(add(mPtr, 0x60), mload(add(PI_Z_OMEGA, 0x20))) - mstore(add(mPtr, 0x80), u) - success := and(success, staticcall(gas(), 7, add(mPtr, 0x40), 0x60, add(mPtr, 0x40), 0x40)) - - // add [PI_Z] + u.[PI_Z_OMEGA] and write result into lhs - success := and(success, staticcall(gas(), 6, mPtr, 0x80, lhs, 0x40)) - } - - // negate lhs y-coordinate - uint256 q = Bn254Crypto.p_mod; - assembly { - mstore(add(lhs, 0x20), sub(q, mload(add(lhs, 0x20)))) - } - - if (vk.contains_recursive_proof) - { - // If the proof itself contains an accumulated proof, - // we will have extracted two G1 elements `recursive_P1`, `recursive_p2` from the public inputs - - // We need to evaluate that e(recursive_P1, [x]_2) == e(recursive_P2, [1]_2) to finish verifying the inner proof - // We do this by creating a random linear combination between (lhs, recursive_P1) and (rhs, recursivee_P2) - // That way we still only need to evaluate one pairing product - - // We use `challenge.u * challenge.u` as the randomness to create a linear combination - // challenge.u is produced by hashing the entire transcript, which contains the public inputs (and by extension the recursive proof) - - // i.e. [lhs] = [lhs] + u.u.[recursive_P1] - // [rhs] = [rhs] + u.u.[recursive_P2] - Types.G1Point memory recursive_P1 = decoded_proof.recursive_P1; - Types.G1Point memory recursive_P2 = decoded_proof.recursive_P2; - recursive_P1.validateG1Point(); - recursive_P2.validateG1Point(); - assembly { - let mPtr := mload(0x40) - - // compute u.u.[recursive_P1] - mstore(mPtr, mload(recursive_P1)) - mstore(add(mPtr, 0x20), mload(add(recursive_P1, 0x20))) - mstore(add(mPtr, 0x40), mulmod(u, u, p)) // separator_challenge = u * u - success := and(success, staticcall(gas(), 7, mPtr, 0x60, add(mPtr, 0x60), 0x40)) - - // compute u.u.[recursive_P2] (u*u is still in memory at (mPtr + 0x40), no need to re-write it) - mstore(mPtr, mload(recursive_P2)) - mstore(add(mPtr, 0x20), mload(add(recursive_P2, 0x20))) - success := and(success, staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40)) - - // compute u.u.[recursiveP2] + rhs and write into rhs - mstore(add(mPtr, 0xa0), mload(rhs)) - mstore(add(mPtr, 0xc0), mload(add(rhs, 0x20))) - success := and(success, staticcall(gas(), 6, add(mPtr, 0x60), 0x80, rhs, 0x40)) - - // compute u.u.[recursiveP1] + lhs and write into lhs - mstore(add(mPtr, 0x40), mload(lhs)) - mstore(add(mPtr, 0x60), mload(add(lhs, 0x20))) - success := and(success, staticcall(gas(), 6, mPtr, 0x80, lhs, 0x40)) - } - } - - require(success, "perform_pairing G1 operations preamble fail"); - - return Bn254Crypto.pairingProd2(rhs, Bn254Crypto.P2(), lhs, vk.g2_x); - } - - /** - * @dev Calculate the alpha, beta, gamma and zeta challenges - * Makes use of the Transcript library - * @param decoded_proof - deserialised proof - * @param vk - verification key - * @return challenge transcript containing alpha, beta, gamma, zeta and seperately the - * general helper transcript containing the data necessary future challenges - */ - function construct_alpha_beta_gamma_zeta_challenges( - Types.Proof memory decoded_proof, - Types.VerificationKey memory vk - ) internal pure returns (Types.ChallengeTranscript memory, TranscriptLibrary.Transcript memory) { - TranscriptLibrary.Transcript memory transcript = TranscriptLibrary.new_transcript( - vk.circuit_size, - vk.num_inputs - ); - - Types.ChallengeTranscript memory challenges; - - transcript.get_beta_gamma_challenges(challenges, vk.num_inputs); - transcript.update_with_g1(decoded_proof.Z); - challenges.alpha = transcript.get_challenge(); - challenges.alpha_base = challenges.alpha; - - transcript.update_with_four_g1_elements( - decoded_proof.T1, - decoded_proof.T2, - decoded_proof.T3, - decoded_proof.T4 - ); - challenges.zeta = transcript.get_challenge(); - - return (challenges, transcript); - } - - /** - * @dev Calculate the remaining challenges: nu[] and u. - * For Turbo PLONK, 11 challenges are calculated. Makes use of the Transcript library - * - * @param decoded_proof - deserialised proof - * @param transcript - general transcript containing the data necessary to calculate subsequent - * challenges - * @param challenges - challenge transcript containing alpha, beta, gamma, zeta - * @return challenge transcript containing the original challenges, together with the calculated - * nu's and u challenges - */ - function construct_nu_u_challenges( - Types.Proof memory decoded_proof, - TranscriptLibrary.Transcript memory transcript, - Types.ChallengeTranscript memory challenges - ) internal pure returns (Types.ChallengeTranscript memory) { - - transcript.get_nu_challenges(decoded_proof.quotient_polynomial_eval, challenges); - - - transcript.update_with_g1(decoded_proof.PI_Z); - transcript.update_with_g1(decoded_proof.PI_Z_OMEGA); - challenges.u = transcript.get_challenge(); - - return challenges; - } -} - - - "# }; -} diff --git a/common/src/contract/cryptography/types.rs b/common/src/contract/cryptography/types.rs index 7ea7281f..9778c9ba 100644 --- a/common/src/contract/cryptography/types.rs +++ b/common/src/contract/cryptography/types.rs @@ -2,7 +2,6 @@ macro_rules! TYPES_LIBRARY { () => { r#" - /** * @title Bn254Crypto library used for the fr, g1 and g2 point types * @dev Used to manipulate fr, g1, g2 types, perform modular arithmetic on them and call @@ -17,12 +16,16 @@ library Types { uint256 constant PROGRAM_WIDTH = 4; uint256 constant NUM_NU_CHALLENGES = 11; - uint256 constant coset_generator0 = 0x0000000000000000000000000000000000000000000000000000000000000005; - uint256 constant coset_generator1 = 0x0000000000000000000000000000000000000000000000000000000000000006; - uint256 constant coset_generator2 = 0x0000000000000000000000000000000000000000000000000000000000000007; + uint256 constant coset_generator0 = + 0x0000000000000000000000000000000000000000000000000000000000000005; + uint256 constant coset_generator1 = + 0x0000000000000000000000000000000000000000000000000000000000000006; + uint256 constant coset_generator2 = + 0x0000000000000000000000000000000000000000000000000000000000000007; // TODO: add external_coset_generator() method to compute this - uint256 constant coset_generator7 = 0x000000000000000000000000000000000000000000000000000000000000000c; + uint256 constant coset_generator7 = + 0x000000000000000000000000000000000000000000000000000000000000000c; struct G1Point { uint256 x; @@ -59,7 +62,7 @@ library Types { uint256 q_arith; uint256 q_ecc; uint256 q_c; - uint256 linearization_polynomial; + // uint256 linearization_polynomial; uint256 grand_product_at_z_omega; uint256 w1_omega; uint256 w2_omega; @@ -69,7 +72,8 @@ library Types { G1Point PI_Z_OMEGA; G1Point recursive_P1; G1Point recursive_P2; - uint256 quotient_polynomial_eval; + // uint256 quotient_polynomial_eval; + uint256 r_0; } struct ChallengeTranscript { @@ -116,13 +120,13 @@ library Types { bool contains_recursive_proof; uint256 recursive_proof_indices; G2Point g2_x; - // zeta challenge raised to the power of the circuit size. // Not actually part of the verification key, but we put it here to prevent stack depth errors uint256 zeta_pow_n; + // necessary fot the simplified plonk + uint256 zero_polynomial_eval; } } - "# }; } diff --git a/common/src/contract/turbo_verifier.rs b/common/src/contract/turbo_verifier.rs index 7fa3cc96..93f87266 100644 --- a/common/src/contract/turbo_verifier.rs +++ b/common/src/contract/turbo_verifier.rs @@ -30,6 +30,659 @@ const TURBOVERIFIER: &str = r#" // SPDX-License-Identifier: GPL-2.0-only // Copyright 2020 Spilsbury Holdings Ltd +pragma solidity >=0.6.0; +pragma experimental ABIEncoderV2; + +/** + * @title Turbo Plonk proof verification contract + * @dev Top level Plonk proof verification contract, which allows Plonk proof to be verified + * + * Copyright 2020 Spilsbury Holdings Ltd + * + * Licensed under the GNU General Public License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +contract TurboVerifier { + using Bn254Crypto for Types.G1Point; + using Bn254Crypto for Types.G2Point; + using Transcript for Transcript.TranscriptData; + + /** + Calldata formatting: + + 0x00 - 0x04 : function signature + 0x04 - 0x24 : proof_data pointer (location in calldata that contains the proof_data array) + 0x44 - 0x64 : length of `proof_data` array + 0x64 - ???? : array containing our zk proof data + **/ + /** + * @dev Verify a Plonk proof + * @param - array of serialized proof data + */ + function verify(bytes calldata) + external + view + returns (bool result) + { + + Types.VerificationKey memory vk = get_verification_key(); + uint256 num_public_inputs = vk.num_inputs; + + // parse the input calldata and construct a Proof object + Types.Proof memory decoded_proof = deserialize_proof( + num_public_inputs, + vk + ); + + Transcript.TranscriptData memory transcript; + transcript.generate_initial_challenge(vk.circuit_size, vk.num_inputs); + + // reconstruct the beta, gamma, alpha and zeta challenges + Types.ChallengeTranscript memory challenges; + transcript.generate_beta_gamma_challenges(challenges, vk.num_inputs); + transcript.generate_alpha_challenge(challenges, decoded_proof.Z); + transcript.generate_zeta_challenge( + challenges, + decoded_proof.T1, + decoded_proof.T2, + decoded_proof.T3, + decoded_proof.T4 + ); + + /** + * Compute all inverses that will be needed throughout the program here. + * + * This is an efficiency improvement - it allows us to make use of the batch inversion Montgomery trick, + * which allows all inversions to be replaced with one inversion operation, at the expense of a few + * additional multiplications + **/ + (uint256 r_0, uint256 L1) = evalaute_field_operations( + decoded_proof, + vk, + challenges + ); + decoded_proof.r_0 = r_0; + + // reconstruct the nu and u challenges + // Need to change nu and u according to the simplified Plonk + transcript.generate_nu_challenges(challenges, vk.num_inputs); + + transcript.generate_separator_challenge( + challenges, + decoded_proof.PI_Z, + decoded_proof.PI_Z_OMEGA + ); + + //reset 'alpha base' + challenges.alpha_base = challenges.alpha; + // Computes step 9 -> [D]_1 + Types.G1Point memory linearised_contribution = PolynomialEval + .compute_linearised_opening_terms( + challenges, + L1, + vk, + decoded_proof + ); + // Computes step 10 -> [F]_1 + Types.G1Point memory batch_opening_commitment = PolynomialEval + .compute_batch_opening_commitment( + challenges, + vk, + linearised_contribution, + decoded_proof + ); + + uint256 batch_evaluation_g1_scalar = PolynomialEval + .compute_batch_evaluation_scalar_multiplier( + decoded_proof, + challenges + ); + + result = perform_pairing( + batch_opening_commitment, + batch_evaluation_g1_scalar, + challenges, + decoded_proof, + vk + ); + require(result, "Proof failed"); + } + + $0 + + /** + * @dev Compute partial state of the verifier, specifically: public input delta evaluation, zero polynomial + * evaluation, the lagrange evaluations and the quotient polynomial evaluations + * + * Note: This uses the batch inversion Montgomery trick to reduce the number of + * inversions, and therefore the number of calls to the bn128 modular exponentiation + * precompile. + * + * Specifically, each function call: compute_public_input_delta() etc. at some point needs to invert a + * value to calculate a denominator in a fraction. Instead of performing this inversion as it is needed, we + * instead 'save up' the denominator calculations. The inputs to this are returned from the various functions + * and then we perform all necessary inversions in one go at the end of `evalaute_field_operations()`. This + * gives us the various variables that need to be returned. + * + * @param decoded_proof - deserialised proof + * @param vk - verification key + * @param challenges - all challenges (alpha, beta, gamma, zeta, nu[NUM_NU_CHALLENGES], u) stored in + * ChallengeTranscript struct form + * @return quotient polynomial evaluation (field element) and lagrange 1 evaluation (field element) + */ + function evalaute_field_operations( + Types.Proof memory decoded_proof, + Types.VerificationKey memory vk, + Types.ChallengeTranscript memory challenges + ) internal view returns (uint256, uint256) { + uint256 public_input_delta; + uint256 zero_polynomial_eval; + uint256 l_start; + uint256 l_end; + { + ( + uint256 public_input_numerator, + uint256 public_input_denominator + ) = PolynomialEval.compute_public_input_delta(challenges, vk); + + ( + uint256 vanishing_numerator, + uint256 vanishing_denominator, + uint256 lagrange_numerator, + uint256 l_start_denominator, + uint256 l_end_denominator + ) = PolynomialEval.compute_lagrange_and_vanishing_fractions( + vk, + challenges.zeta + ); + + ( + zero_polynomial_eval, + public_input_delta, + l_start, + l_end + ) = PolynomialEval.compute_batch_inversions( + public_input_numerator, + public_input_denominator, + vanishing_numerator, + vanishing_denominator, + lagrange_numerator, + l_start_denominator, + l_end_denominator + ); + vk.zero_polynomial_eval = zero_polynomial_eval; + } + + uint256 r_0 = PolynomialEval.compute_linear_polynomial_constant( + zero_polynomial_eval, + public_input_delta, + challenges, + l_start, + l_end, + decoded_proof + ); + + return (r_0, l_start); + } + + /** + * @dev Perform the pairing check + * @param batch_opening_commitment - G1 point representing the calculated batch opening commitment + * @param batch_evaluation_g1_scalar - uint256 representing the batch evaluation scalar multiplier to be applied to the G1 generator point + * @param challenges - all challenges (alpha, beta, gamma, zeta, nu[NUM_NU_CHALLENGES], u) stored in + * ChallengeTranscript struct form + * @param vk - verification key + * @param decoded_proof - deserialised proof + * @return bool specifying whether the pairing check was successful + */ + function perform_pairing( + Types.G1Point memory batch_opening_commitment, + uint256 batch_evaluation_g1_scalar, + Types.ChallengeTranscript memory challenges, + Types.Proof memory decoded_proof, + Types.VerificationKey memory vk + ) internal view returns (bool) { + uint256 u = challenges.u; + bool success; + uint256 p = Bn254Crypto.r_mod; + Types.G1Point memory rhs; + Types.G1Point memory PI_Z_OMEGA = decoded_proof.PI_Z_OMEGA; + Types.G1Point memory PI_Z = decoded_proof.PI_Z; + PI_Z.validateG1Point(); + PI_Z_OMEGA.validateG1Point(); + + // rhs = zeta.[PI_Z] + u.zeta.omega.[PI_Z_OMEGA] + [batch_opening_commitment] - batch_evaluation_g1_scalar.[1] + // scope this block to prevent stack depth errors + { + uint256 zeta = challenges.zeta; + uint256 pi_z_omega_scalar = vk.work_root; + assembly { + pi_z_omega_scalar := mulmod(pi_z_omega_scalar, zeta, p) + pi_z_omega_scalar := mulmod(pi_z_omega_scalar, u, p) + batch_evaluation_g1_scalar := sub(p, batch_evaluation_g1_scalar) + + // store accumulator point at mptr + let mPtr := mload(0x40) + + // set accumulator = batch_opening_commitment + mstore(mPtr, mload(batch_opening_commitment)) + mstore( + add(mPtr, 0x20), + mload(add(batch_opening_commitment, 0x20)) + ) + + // compute zeta.[PI_Z] and add into accumulator + mstore(add(mPtr, 0x40), mload(PI_Z)) + mstore(add(mPtr, 0x60), mload(add(PI_Z, 0x20))) + mstore(add(mPtr, 0x80), zeta) + success := staticcall( + gas(), + 7, + add(mPtr, 0x40), + 0x60, + add(mPtr, 0x40), + 0x40 + ) + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40) + ) + + // compute u.zeta.omega.[PI_Z_OMEGA] and add into accumulator + mstore(add(mPtr, 0x40), mload(PI_Z_OMEGA)) + mstore(add(mPtr, 0x60), mload(add(PI_Z_OMEGA, 0x20))) + mstore(add(mPtr, 0x80), pi_z_omega_scalar) + success := and( + success, + staticcall( + gas(), + 7, + add(mPtr, 0x40), + 0x60, + add(mPtr, 0x40), + 0x40 + ) + ) + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, mPtr, 0x40) + ) + + // compute -batch_evaluation_g1_scalar.[1] + mstore(add(mPtr, 0x40), 0x01) // hardcoded generator point (1, 2) + mstore(add(mPtr, 0x60), 0x02) + mstore(add(mPtr, 0x80), batch_evaluation_g1_scalar) + success := and( + success, + staticcall( + gas(), + 7, + add(mPtr, 0x40), + 0x60, + add(mPtr, 0x40), + 0x40 + ) + ) + + // add -batch_evaluation_g1_scalar.[1] and the accumulator point, write result into rhs + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, rhs, 0x40) + ) + } + } + + Types.G1Point memory lhs; + assembly { + // store accumulator point at mptr + let mPtr := mload(0x40) + + // copy [PI_Z] into mPtr + mstore(mPtr, mload(PI_Z)) + mstore(add(mPtr, 0x20), mload(add(PI_Z, 0x20))) + + // compute u.[PI_Z_OMEGA] and write to (mPtr + 0x40) + mstore(add(mPtr, 0x40), mload(PI_Z_OMEGA)) + mstore(add(mPtr, 0x60), mload(add(PI_Z_OMEGA, 0x20))) + mstore(add(mPtr, 0x80), u) + success := and( + success, + staticcall( + gas(), + 7, + add(mPtr, 0x40), + 0x60, + add(mPtr, 0x40), + 0x40 + ) + ) + + // add [PI_Z] + u.[PI_Z_OMEGA] and write result into lhs + success := and(success, staticcall(gas(), 6, mPtr, 0x80, lhs, 0x40)) + } + + // negate lhs y-coordinate + uint256 q = Bn254Crypto.p_mod; + assembly { + mstore(add(lhs, 0x20), sub(q, mload(add(lhs, 0x20)))) + } + + if (vk.contains_recursive_proof) { + // If the proof itself contains an accumulated proof, + // we will have extracted two G1 elements `recursive_P1`, `recursive_p2` from the public inputs + + // We need to evaluate that e(recursive_P1, [x]_2) == e(recursive_P2, [1]_2) to finish verifying the inner proof + // We do this by creating a random linear combination between (lhs, recursive_P1) and (rhs, recursivee_P2) + // That way we still only need to evaluate one pairing product + + // We use `challenge.u * challenge.u` as the randomness to create a linear combination + // challenge.u is produced by hashing the entire transcript, which contains the public inputs (and by extension the recursive proof) + + // i.e. [lhs] = [lhs] + u.u.[recursive_P1] + // [rhs] = [rhs] + u.u.[recursive_P2] + Types.G1Point memory recursive_P1 = decoded_proof.recursive_P1; + Types.G1Point memory recursive_P2 = decoded_proof.recursive_P2; + recursive_P1.validateG1Point(); + recursive_P2.validateG1Point(); + assembly { + let mPtr := mload(0x40) + + // compute u.u.[recursive_P1] + mstore(mPtr, mload(recursive_P1)) + mstore(add(mPtr, 0x20), mload(add(recursive_P1, 0x20))) + mstore(add(mPtr, 0x40), mulmod(u, u, p)) // separator_challenge = u * u + success := and( + success, + staticcall(gas(), 7, mPtr, 0x60, add(mPtr, 0x60), 0x40) + ) + + // compute u.u.[recursive_P2] (u*u is still in memory at (mPtr + 0x40), no need to re-write it) + mstore(mPtr, mload(recursive_P2)) + mstore(add(mPtr, 0x20), mload(add(recursive_P2, 0x20))) + success := and( + success, + staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40) + ) + + // compute u.u.[recursiveP2] + rhs and write into rhs + mstore(add(mPtr, 0xa0), mload(rhs)) + mstore(add(mPtr, 0xc0), mload(add(rhs, 0x20))) + success := and( + success, + staticcall(gas(), 6, add(mPtr, 0x60), 0x80, rhs, 0x40) + ) + + // compute u.u.[recursiveP1] + lhs and write into lhs + mstore(add(mPtr, 0x40), mload(lhs)) + mstore(add(mPtr, 0x60), mload(add(lhs, 0x20))) + success := and( + success, + staticcall(gas(), 6, mPtr, 0x80, lhs, 0x40) + ) + } + } + + require(success, "perform_pairing G1 operations preamble fail"); + + return Bn254Crypto.pairingProd2(rhs, Bn254Crypto.P2(), lhs, vk.g2_x); + } + + /** + * @dev Deserialize a proof into a Proof struct + * @param num_public_inputs - number of public inputs in the proof. Taken from verification key + * @return proof - proof deserialized into the proof struct + */ + function deserialize_proof( + uint256 num_public_inputs, + Types.VerificationKey memory vk + ) internal pure returns (Types.Proof memory proof) { + uint256 p = Bn254Crypto.r_mod; + uint256 q = Bn254Crypto.p_mod; + uint256 data_ptr; + uint256 proof_ptr; + // first 32 bytes of bytes array contains length, skip it + assembly { + data_ptr := add(calldataload(0x04), 0x24) + proof_ptr := proof + } + + if (vk.contains_recursive_proof) { + uint256 index_counter = vk.recursive_proof_indices * 32; + uint256 x0 = 0; + uint256 y0 = 0; + uint256 x1 = 0; + uint256 y1 = 0; + assembly { + index_counter := add(index_counter, data_ptr) + x0 := calldataload(index_counter) + x0 := add(x0, shl(68, calldataload(add(index_counter, 0x20)))) + x0 := add(x0, shl(136, calldataload(add(index_counter, 0x40)))) + x0 := add(x0, shl(204, calldataload(add(index_counter, 0x60)))) + y0 := calldataload(add(index_counter, 0x80)) + y0 := add(y0, shl(68, calldataload(add(index_counter, 0xa0)))) + y0 := add(y0, shl(136, calldataload(add(index_counter, 0xc0)))) + y0 := add(y0, shl(204, calldataload(add(index_counter, 0xe0)))) + x1 := calldataload(add(index_counter, 0x100)) + x1 := add(x1, shl(68, calldataload(add(index_counter, 0x120)))) + x1 := add(x1, shl(136, calldataload(add(index_counter, 0x140)))) + x1 := add(x1, shl(204, calldataload(add(index_counter, 0x160)))) + y1 := calldataload(add(index_counter, 0x180)) + y1 := add(y1, shl(68, calldataload(add(index_counter, 0x1a0)))) + y1 := add(y1, shl(136, calldataload(add(index_counter, 0x1c0)))) + y1 := add(y1, shl(204, calldataload(add(index_counter, 0x1e0)))) + } + + proof.recursive_P1 = Bn254Crypto.new_g1(x0, y0); + proof.recursive_P2 = Bn254Crypto.new_g1(x1, y1); + } + + assembly { + let public_input_byte_length := mul(num_public_inputs, 0x20) + data_ptr := add(data_ptr, public_input_byte_length) + + // proof.W1 + mstore(mload(proof_ptr), mod(calldataload(add(data_ptr, 0x20)), q)) + mstore(add(mload(proof_ptr), 0x20), mod(calldataload(data_ptr), q)) + + // proof.W2 + mstore( + mload(add(proof_ptr, 0x20)), + mod(calldataload(add(data_ptr, 0x60)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x20)), 0x20), + mod(calldataload(add(data_ptr, 0x40)), q) + ) + + // proof.W3 + mstore( + mload(add(proof_ptr, 0x40)), + mod(calldataload(add(data_ptr, 0xa0)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x40)), 0x20), + mod(calldataload(add(data_ptr, 0x80)), q) + ) + + // proof.W4 + mstore( + mload(add(proof_ptr, 0x60)), + mod(calldataload(add(data_ptr, 0xe0)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x60)), 0x20), + mod(calldataload(add(data_ptr, 0xc0)), q) + ) + + // proof.Z + mstore( + mload(add(proof_ptr, 0x80)), + mod(calldataload(add(data_ptr, 0x120)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x80)), 0x20), + mod(calldataload(add(data_ptr, 0x100)), q) + ) + + // proof.T1 + mstore( + mload(add(proof_ptr, 0xa0)), + mod(calldataload(add(data_ptr, 0x160)), q) + ) + mstore( + add(mload(add(proof_ptr, 0xa0)), 0x20), + mod(calldataload(add(data_ptr, 0x140)), q) + ) + + // proof.T2 + mstore( + mload(add(proof_ptr, 0xc0)), + mod(calldataload(add(data_ptr, 0x1a0)), q) + ) + mstore( + add(mload(add(proof_ptr, 0xc0)), 0x20), + mod(calldataload(add(data_ptr, 0x180)), q) + ) + + // proof.T3 + mstore( + mload(add(proof_ptr, 0xe0)), + mod(calldataload(add(data_ptr, 0x1e0)), q) + ) + mstore( + add(mload(add(proof_ptr, 0xe0)), 0x20), + mod(calldataload(add(data_ptr, 0x1c0)), q) + ) + + // proof.T4 + mstore( + mload(add(proof_ptr, 0x100)), + mod(calldataload(add(data_ptr, 0x220)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x100)), 0x20), + mod(calldataload(add(data_ptr, 0x200)), q) + ) + + // proof.w1 to proof.w4 + mstore( + add(proof_ptr, 0x120), + mod(calldataload(add(data_ptr, 0x240)), p) + ) + mstore( + add(proof_ptr, 0x140), + mod(calldataload(add(data_ptr, 0x260)), p) + ) + mstore( + add(proof_ptr, 0x160), + mod(calldataload(add(data_ptr, 0x280)), p) + ) + mstore( + add(proof_ptr, 0x180), + mod(calldataload(add(data_ptr, 0x2a0)), p) + ) + + // proof.sigma1 + mstore( + add(proof_ptr, 0x1a0), + mod(calldataload(add(data_ptr, 0x2c0)), p) + ) + + // proof.sigma2 + mstore( + add(proof_ptr, 0x1c0), + mod(calldataload(add(data_ptr, 0x2e0)), p) + ) + + // proof.sigma3 + mstore( + add(proof_ptr, 0x1e0), + mod(calldataload(add(data_ptr, 0x300)), p) + ) + + // proof.q_arith + mstore( + add(proof_ptr, 0x200), + mod(calldataload(add(data_ptr, 0x320)), p) + ) + + // proof.q_ecc + mstore( + add(proof_ptr, 0x220), + mod(calldataload(add(data_ptr, 0x340)), p) + ) + + // proof.q_c + mstore( + add(proof_ptr, 0x240), + mod(calldataload(add(data_ptr, 0x360)), p) + ) + + // proof.linearization_polynomial + // mstore(add(proof_ptr, 0x260), mod(calldataload(add(data_ptr, 0x380)), p)) + + // proof.grand_product_at_z_omega + mstore( + add(proof_ptr, 0x260), + mod(calldataload(add(data_ptr, 0x380)), p) + ) + + // proof.w1_omega to proof.w4_omega + mstore( + add(proof_ptr, 0x280), + mod(calldataload(add(data_ptr, 0x3a0)), p) + ) + mstore( + add(proof_ptr, 0x2a0), + mod(calldataload(add(data_ptr, 0x3c0)), p) + ) + mstore( + add(proof_ptr, 0x2c0), + mod(calldataload(add(data_ptr, 0x3e0)), p) + ) + mstore( + add(proof_ptr, 0x2e0), + mod(calldataload(add(data_ptr, 0x400)), p) + ) + + // proof.PI_Z + //Order of x and y coordinate are reverse in case of serialization + mstore( + mload(add(proof_ptr, 0x300)), + mod(calldataload(add(data_ptr, 0x440)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x300)), 0x20), + mod(calldataload(add(data_ptr, 0x420)), q) + ) + + // proof.PI_Z_OMEGA + mstore( + mload(add(proof_ptr, 0x320)), + mod(calldataload(add(data_ptr, 0x480)), q) + ) + mstore( + add(mload(add(proof_ptr, 0x320)), 0x20), + mod(calldataload(add(data_ptr, 0x460)), q) + ) + } + } +} + +"#; + +const TURBOVERIFIER_OLD: &str = r#" +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2020 Spilsbury Holdings Ltd + pragma solidity >=0.6.0 <0.8.0; pragma experimental ABIEncoderV2;