arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/solidity-verifiers/templates/nova_cyclefold_decider.askama.sol
solidity-verifiers/templates/nova_cyclefold_decider.askama.solRaw
  1/*
  2    Sonobe's Nova + CycleFold decider verifier.
  3    Joint effort by 0xPARC & PSE.
  4
  5    More details at https://github.com/privacy-scaling-explorations/sonobe
  6    Usage and design documentation at https://privacy-scaling-explorations.github.io/sonobe-docs/
  7
  8    Uses the https://github.com/iden3/snarkjs/blob/master/templates/verifier_groth16.sol.ejs
  9    Groth16 verifier implementation and a KZG10 Solidity template adapted from
 10    https://github.com/weijiekoh/libkzg.
 11    Additionally we implement the NovaDecider contract, which combines the
 12    Groth16 and KZG10 verifiers to verify the zkSNARK proofs coming from
 13    Nova+CycleFold folding.
 14*/
 15
 16
 17/* =============================== */
 18/* KZG10 verifier methods */
 19{{ kzg10_verifier }}
 20
 21/* =============================== */
 22/* Groth16 verifier methods */
 23{{ groth16_verifier }}
 24
 25
 26/* =============================== */
 27/* Nova+CycleFold Decider verifier */
 28/**
 29 * @notice  Computes the decomposition of a `uint256` into num_limbs limbs of bits_per_limb bits each.
 30 * @dev     Compatible with sonobe::folding-schemes::folding::circuits::nonnative::nonnative_field_to_field_elements.
 31 */
 32library LimbsDecomposition {
 33    function decompose(uint256 x) internal pure returns (uint256[{{num_limbs}}] memory) {
 34        uint256[{{num_limbs}}] memory limbs;
 35        for (uint8 i = 0; i < {{num_limbs}}; i++) {
 36            limbs[i] = (x >> ({{bits_per_limb}} * i)) & ((1 << {{bits_per_limb}}) - 1);
 37        }
 38        return limbs;
 39    }
 40}
 41
 42/**
 43 * @author  PSE & 0xPARC
 44 * @title   NovaDecider contract, for verifying Nova IVC SNARK proofs.
 45 * @dev     This is an askama template which, when templated, features a Groth16 and KZG10 verifiers from which this contract inherits.
 46 */
 47contract NovaDecider is Groth16Verifier, KZG10Verifier {
 48    /**
 49     * @notice  Computes the linear combination of a and b with r as the coefficient.
 50     * @dev     All ops are done mod the BN254 scalar field prime
 51     */
 52    function rlc(uint256 a, uint256 r, uint256 b) internal pure returns (uint256 result) {
 53        assembly {
 54            result := addmod(a, mulmod(r, b, BN254_SCALAR_FIELD), BN254_SCALAR_FIELD)
 55        }
 56    }
 57
 58    /**
 59     * @notice  Verifies a nova cyclefold proof consisting of two KZG proofs and of a groth16 proof.
 60     * @dev     The selector of this function is "dynamic", since it depends on `z_len`.
 61     */
 62    function verifyNovaProof(
 63        // inputs are grouped to prevent errors due stack too deep
 64        uint256[{{ 1 + z_len * 2 }}] calldata i_z0_zi, // [i, z0, zi] where |z0| == |zi|
 65        uint256[4] calldata U_i_cmW_U_i_cmE, // [U_i_cmW[2], U_i_cmE[2]]
 66        uint256[2] calldata u_i_cmW, // [u_i_cmW[2]]
 67        uint256[3] calldata cmT_r, // [cmT[2], r]
 68        uint256[2] calldata pA, // groth16 
 69        uint256[2][2] calldata pB, // groth16
 70        uint256[2] calldata pC, // groth16
 71        uint256[4] calldata challenge_W_challenge_E_kzg_evals, // [challenge_W, challenge_E, eval_W, eval_E]
 72        uint256[2][2] calldata kzg_proof // [proof_W, proof_E]
 73    ) public view returns (bool) {
 74
 75        require(i_z0_zi[0] >= 2, "Folding: the number of folded steps should be at least 2");
 76
 77        // from gamma_abc_len, we subtract 1. 
 78        uint256[{{ public_inputs_len - 1 }}] memory public_inputs; 
 79
 80        public_inputs[0] = {{pp_hash}};
 81        public_inputs[1] = i_z0_zi[0];
 82
 83        for (uint i = 0; i < {{ z_len * 2 }}; i++) {
 84            public_inputs[2 + i] = i_z0_zi[1 + i];
 85        }
 86
 87        {
 88            // U_i.cmW + r * u_i.cmW
 89            uint256[2] memory mulScalarPoint = super.mulScalar([u_i_cmW[0], u_i_cmW[1]], cmT_r[2]);
 90            uint256[2] memory cmW = super.add([U_i_cmW_U_i_cmE[0], U_i_cmW_U_i_cmE[1]], mulScalarPoint);
 91
 92            {
 93                uint256[{{num_limbs}}] memory cmW_x_limbs = LimbsDecomposition.decompose(cmW[0]);
 94                uint256[{{num_limbs}}] memory cmW_y_limbs = LimbsDecomposition.decompose(cmW[1]);
 95        
 96                for (uint8 k = 0; k < {{num_limbs}}; k++) {
 97                    public_inputs[{{ z_len * 2 + 2 }} + k] = cmW_x_limbs[k];
 98                    public_inputs[{{ z_len * 2 + 2 + num_limbs }} + k] = cmW_y_limbs[k];
 99                }
100            }
101        
102            require(this.check(cmW, kzg_proof[0], challenge_W_challenge_E_kzg_evals[0], challenge_W_challenge_E_kzg_evals[2]), "KZG: verifying proof for challenge W failed");
103        }
104
105        {
106            // U_i.cmE + r * cmT
107            uint256[2] memory mulScalarPoint = super.mulScalar([cmT_r[0], cmT_r[1]], cmT_r[2]);
108            uint256[2] memory cmE = super.add([U_i_cmW_U_i_cmE[2], U_i_cmW_U_i_cmE[3]], mulScalarPoint);
109
110            {
111                uint256[{{num_limbs}}] memory cmE_x_limbs = LimbsDecomposition.decompose(cmE[0]);
112                uint256[{{num_limbs}}] memory cmE_y_limbs = LimbsDecomposition.decompose(cmE[1]);
113            
114                for (uint8 k = 0; k < {{num_limbs}}; k++) {
115                    public_inputs[{{ z_len * 2 + 2 + num_limbs * 2 }} + k] = cmE_x_limbs[k];
116                    public_inputs[{{ z_len * 2 + 2 + num_limbs * 3 }} + k] = cmE_y_limbs[k];
117                }
118            }
119
120            require(this.check(cmE, kzg_proof[1], challenge_W_challenge_E_kzg_evals[1], challenge_W_challenge_E_kzg_evals[3]), "KZG: verifying proof for challenge E failed");
121        }
122
123        {
124            // add challenges
125            public_inputs[{{ z_len * 2 + 2 + num_limbs * 4 }}] = challenge_W_challenge_E_kzg_evals[0];
126            public_inputs[{{ z_len * 2 + 2 + num_limbs * 4 + 1 }}] = challenge_W_challenge_E_kzg_evals[1];
127            public_inputs[{{ z_len * 2 + 2 + num_limbs * 4 + 2 }}] = challenge_W_challenge_E_kzg_evals[2];
128            public_inputs[{{ z_len * 2 + 2 + num_limbs * 4 + 3 }}] = challenge_W_challenge_E_kzg_evals[3];
129
130            uint256[{{num_limbs}}] memory cmT_x_limbs;
131            uint256[{{num_limbs}}] memory cmT_y_limbs;
132        
133            cmT_x_limbs = LimbsDecomposition.decompose(cmT_r[0]);
134            cmT_y_limbs = LimbsDecomposition.decompose(cmT_r[1]);
135        
136            for (uint8 k = 0; k < {{num_limbs}}; k++) {
137                public_inputs[{{ z_len * 2 + 2 + num_limbs * 4 }} + 4 + k] = cmT_x_limbs[k]; 
138                public_inputs[{{ z_len * 2 + 2 + num_limbs * 5 }} + 4 + k] = cmT_y_limbs[k];
139            }
140
141            bool success_g16 = this.verifyProof(pA, pB, pC, public_inputs);
142            require(success_g16 == true, "Groth16: verifying proof failed");
143        }
144
145        return(true);
146    }
147}