diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index a96409e..dab15f6 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -10,9 +10,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: aiken-lang/setup-aiken@v1 + - uses: aiken-lang/setup-aiken@v1.0.3 with: - version: v1.1.12 + version: v1.1.19 - run: aiken fmt --check working-directory: zkp - run: aiken check -D diff --git a/zkp/aiken.toml b/zkp/aiken.toml index 3e1ac32..f1ce231 100644 --- a/zkp/aiken.toml +++ b/zkp/aiken.toml @@ -1,6 +1,6 @@ name = "adao/zkp" version = "0.0.0" -compiler = "v1.1.12" +compiler = "v1.1.19" plutus = "v3" license = "Apache-2.0" description = "Aiken contracts for project 'adao/zkp'" diff --git a/zkp/lib/groth/groth.ak b/zkp/lib/groth/groth.ak index 92936dc..157674b 100644 --- a/zkp/lib/groth/groth.ak +++ b/zkp/lib/groth/groth.ak @@ -1,209 +1,217 @@ -use aiken/builtin +use aiken/builtin.{ + bls12_381_final_verify, bls12_381_g1_add, bls12_381_g1_scalar_mul, + bls12_381_g1_uncompress, bls12_381_g2_uncompress, bls12_381_miller_loop, + bls12_381_mul_miller_loop_result, +} use aiken/collection/list -use aiken/crypto/bls12_381/g1 -use aiken/crypto/bls12_381/g2 -use aiken/crypto/bls12_381/scalar -use common/common - -// Groth16 specific types -pub type GrothVerificationKey { - n_public: Int, - vk_alpha_g1: common.G1Point, - vk_beta_g2: common.G2Point, - vk_gamma_g2: common.G2Point, - vk_delta_g2: common.G2Point, - vk_ic: List, + +pub type CompressedProof { + a: ByteArray, + b: ByteArray, + c: ByteArray, } -pub type GrothProof { - pi_a: common.G1Point, - pi_b: common.G2Point, - pi_c: common.G1Point, +pub type CompressedVK { + alpha: ByteArray, + beta: ByteArray, + gamma: ByteArray, + delta: ByteArray, + vkIC: List, } -pub type GrothError { - InvalidProofFormat - InvalidVerificationKey - PairingCheckFailed - InvalidPublicInput +pub type Proof { + a: G1Element, + b: G2Element, + c: G1Element, } -pub type G1PointOrGrothError { - PGP(common.G1Point) - PGG(GrothError) +pub type VerifierKey { + alpha: G1Element, + beta: G2Element, + gamma: G2Element, + delta: G2Element, + vkIC: List, } -pub type BoolOrGrothError { - BGB(Bool) - BGG(GrothError) +pub fn uncompress_proof(c: CompressedProof) -> Proof { + Proof { + a: bls12_381_g1_uncompress(c.a), + b: bls12_381_g2_uncompress(c.b), + c: bls12_381_g1_uncompress(c.c), + } } -// Main verification function -pub fn verify( - vk: GrothVerificationKey, - proof: GrothProof, - public_inputs: List, -) -> BoolOrGrothError { - // Verify sizes match - expect - when verify_sizes(vk, public_inputs) is { - Some(err) -> { - trace BGG(err) - fail - } - None -> True - } - // Verify proof elements are valid - expect - when verify_proof_elements(proof) is { - Some(err) -> { - trace BGG(err) - fail - } - None -> True - } - // Prepare inputs (negate first element) - let prepared_inputs = prepare_inputs(public_inputs) - // Compute the linear combination of inputs with IC - when compute_linear_combination(vk.vk_ic, prepared_inputs) is { - PGG(err) -> BGG(err) - PGP(vk_x) -> { - // Perform pairing checks - let e_ab = pairing(proof.pi_a, proof.pi_b) - let e_alpha_beta = pairing(vk.vk_alpha_g1, vk.vk_beta_g2) - let e_vk_gamma = pairing(vk_x, vk.vk_gamma_g2) - let e_c_delta = pairing(proof.pi_c, vk.vk_delta_g2) - - // Final verification - let mlr1 = - builtin.bls12_381_mul_miller_loop_result(e_alpha_beta, e_vk_gamma) - let mlr2 = builtin.bls12_381_mul_miller_loop_result(mlr1, e_c_delta) - if builtin.bls12_381_final_verify(e_ab, mlr2) { - BGB(True) - } else { - BGG(PairingCheckFailed) - } - } +pub fn uncompress_vk(c: CompressedVK) -> VerifierKey { + VerifierKey { + alpha: bls12_381_g1_uncompress(c.alpha), + beta: bls12_381_g2_uncompress(c.beta), + gamma: bls12_381_g2_uncompress(c.gamma), + delta: bls12_381_g2_uncompress(c.delta), + vkIC: list.map(c.vkIC, fn(c_ic) { bls12_381_g1_uncompress(c_ic) }), } } -// Helper function for pairing computation -// Optimized to use the standard library's BLS12-381 implementation more efficiently -fn pairing(g1: common.G1Point, g2: common.G2Point) { - // Convert G1Point to ByteArray - let g1_bytes = common.serialize_g1_point(g1) - // Convert G2Point to ByteArray - let g2_bytes = common.serialize_g2_point(g2) - // Use the standard library's decompress functions directly - let g1_element = g1.decompress(g1_bytes) - let g2_element = g2.decompress(g2_bytes) - // Perform the pairing - builtin.bls12_381_miller_loop(g1_element, g2_element) +pub fn verify_compressed( + vk: CompressedVK, + proof: CompressedProof, + inputs: List, +) { + let vk = uncompress_vk(vk) + let proof = uncompress_proof(proof) + verify(vk, proof, inputs) } -// Helper function to compute linear combination -fn compute_linear_combination( - ic: List, - inputs: List, -) -> G1PointOrGrothError { - when ic is { - [] -> PGG(InvalidVerificationKey) - [vk_0, ..rest] -> { - // Convert the first IC element to a G1 element - let vk_0_bytes = common.serialize_g1_point(vk_0) - let init_element = g1.decompress(vk_0_bytes) - // Combine the rest of the inputs - let result = combine_inputs(rest, inputs, init_element) - // Convert back to our G1Point representation - let result_bytes = g1.compress(result) - when common.g1_decompress(result_bytes) is { - Some(point) -> PGP(point) - None -> PGG(InvalidVerificationKey) - } - } - } +pub fn pairing_check( + a: G1Element, + b: G2Element, + c: G1Element, + delta: G2Element, + alpha: G1Element, + beta: G2Element, + k: G1Element, + gamma: G2Element, +) -> Bool { + // e(A,B) = e(α,β) ∙ e(C,δ) ∙ e(K,γ) + // e: miller loop + // ∙: mul miller loop + // =: final verify + + // Pair Components w/ Miller Loop + let ab = bls12_381_miller_loop(a, b) + let alphabeta = bls12_381_miller_loop(alpha, beta) + + let kgamma = bls12_381_miller_loop(k, gamma) + let cdelta = bls12_381_miller_loop(c, delta) + + // Combine Miller Loop Results + let mlr1 = bls12_381_mul_miller_loop_result(alphabeta, kgamma) + let mlr2 = bls12_381_mul_miller_loop_result(mlr1, cdelta) + + // Final Equality Check + bls12_381_final_verify(ab, mlr2) } -// Optimized function to combine inputs using the standard library's BLS12-381 implementation -fn combine_inputs(ic: List, inputs: List, acc) { - when (ic, inputs) is { - ([], _) | (_, []) -> acc - ([vk_i, ..rest_ic], [input, ..rest_inputs]) -> { - // Convert the IC element to a G1 element - let vk_i_bytes = common.serialize_g1_point(vk_i) - let vk_i_element = g1.decompress(vk_i_bytes) - // Convert Field to Scalar for g1.scale - let scalar_input = - when scalar.new(input) is { - Some(s) -> s - None -> scalar.zero - } - // Multiply by the input and add to accumulator - let term = g1.scale(vk_i_element, scalar_input) - let new_acc = g1.add(acc, term) - // Continue with the rest of the inputs - combine_inputs(rest_ic, rest_inputs, new_acc) +// Main verification function +pub fn verify(vk: VerifierKey, proof: Proof, inputs: List) -> Bool { + let k = + when vk.vkIC is { + [] -> fail @"empty vkIC?" + [head, ..tail] -> derive(tail, inputs, head) } - } -} -// Helper function to negate a field element -fn negate_field(x: common.Field) -> common.Field { - if x == 0 { - x - } else { - common.bls12_381_prime - x - } + // Pairing check + pairing_check( + proof.a, + proof.b, + proof.c, + vk.delta, + vk.alpha, + vk.beta, + k, + vk.gamma, + ) } -// Helper function to verify the proof size matches expected public input size -fn verify_sizes( - vk: GrothVerificationKey, - public_inputs: List, -) -> Option { - if list.length(public_inputs) != vk.n_public { - Some(InvalidPublicInput) - } else { - None +pub fn derive( + vk_ic: List, + public: List, + result: G1Element, +) -> G1Element { + when vk_ic is { + [] -> result + [i, ..vk_ic] -> + when public is { + [] -> fail + [scalar, ..public] -> + derive( + vk_ic, + public, + bls12_381_g1_add(result, bls12_381_g1_scalar_mul(scalar, i)), + ) + } } } -// Helper function to prepare public inputs for verification -fn prepare_inputs(inputs: List) -> List { - // The first element should be negated as per the protocol - when inputs is { - [] -> [] - [first, ..rest] -> [negate_field(first), ..rest] - } +test groth_verify_pass_1() { + // Template of VK + let vk: CompressedVK = + CompressedVK { + alpha: #"8e3d9e248feda194cb6fa0a3b64fd2a380cb5e94836bf8148bf97ebcbb5819d9a78f63102f0293c104bcbb2f810d8eb4", + beta: #"8cd68a7186a908212680a0234d8210c20328f8fb3ce1d69c9aec9330a5802d6cfaf6d7cf3176133221c19188590cb4141874ea7bbfcb9872931e115d882c46b90c3dcbcee10062d1c9b9b0a691d7bec7d2735f06495c7f71dea210e55b2782df", + gamma: #"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + delta: #"938231fcec443fbdeb1079ff126b8f69bd8579ffe82d39923214d4345395beee60200288fa20c97ae50f3212131b6f8802af2f9f515c65af6a9a6c294c738590104376a0af44731d6699db6a286608774243f7d1dddc4605eb340e65e15060a5", + vkIC: [ + #"b5813c90d3455acb8608fdf66e8601f24ef048f3fdf9384862d77c72cb5cddfde2b307976b1b0319c42ba985f94be60a", + #"a41a0e6370c054be0f9acce08e97f6d5702d9daa9e9a934e8a377f553593baa1c58062adc73d63558653422d54d6f50c", + #"8e02a87c519c3145f984d25fdf769f74fbc36626c385d4554fb4bc1d7a12cbf669d40f257023b8b3c9a31e631aa8f981", + ], + } + + // Template of Proof + let pk: CompressedProof = + CompressedProof { + a: #"8b84d092731c653b1accdda79c51e3f5d289bed7311189d927deadef0470e437e6d1d400634726512a79a015867424e3", + b: #"92cb1c125816e4b522c7f430a5d74a61116b6189de7b2341f040194c02f10d9ef0cf081f4029444a65ea74e69d98b1cf08d3864087d5d2dee2ed6ab102f9b78e65d341f0824341a9fc25d0ea9dacccc5d355b4eddb0057949370a19c47135b0e", + c: #"a4ef633c858a3ff194db50eacdf715f7296fb3d1202c54b543284e9656b69aa90f33ac0e2572d3ab847b88268dcd1f7e", + } + // Template of public values + let public_values: List = [561, 3] + + verify_compressed(vk, pk, public_values) } -// Helper function to verify proof elements are valid curve points -fn verify_proof_elements(proof: GrothProof) -> Option { - // Convert G1 points to ByteArray - let pi_a_bytes = common.serialize_g1_point(proof.pi_a) - let pi_c_bytes = common.serialize_g1_point(proof.pi_c) - // Convert G2 point to ByteArray - let pi_b_bytes = common.serialize_g2_point(proof.pi_b) - // Validate the points - let valid_a = - when common.g1_decompress(pi_a_bytes) is { - Some(_) -> True - None -> False +test groth_verify_fail_1() fail { + // Template of VK + let vk: CompressedVK = + CompressedVK { + alpha: #"8e3d9e248feda194cb6fa0a3b64fd2a380cb5e94836bf8148bf97ebcbb5819d9a78f63102f0293c104bcbb2f810d8eb4", + beta: #"8cd68a7186a908212680a0234d8210c20328f8fb3ce1d69c9aec9330a5802d6cfaf6d7cf3176133221c19188590cb4141874ea7bbfcb9872931e115d882c46b90c3dcbcee10062d1c9b9b0a691d7bec7d2735f06495c7f71dea210e55b2782df", + gamma: #"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + delta: #"938231fcec443fbdeb1079ff126b8f69bd8579ffe82d39923214d4345395beee60200288fa20c97ae50f3212131b6f8802af2f9f515c65af6a9a6c294c738590104376a0af44731d6699db6a286608774243f7d1dddc4605eb340e65e15060a5", + vkIC: [ + #"b5813c90d3455acb8608fdf66e8601f24ef048f3fdf9384862d77c72cb5cddfde2b307976b1b0319c42ba985f94be60a", + #"a41a0e6370c054be0f9acce08e97f6d5702d9daa9e9a934e8a377f553593baa1c58062adc73d63558653422d54d6f50c", + #"8e02a87c519c3145f984d25fdf769f74fbc36626c385d4554fb4bc1d7a12cbf669d40f257023b8b3c9a31e631aa8f981", + ], + } + + // Template of Proof + let pk: CompressedProof = + CompressedProof { + a: #"a4ef633c858a3ff194db50eacdf715f7296fb3d1202c54b543284e9656b69aa90f33ac0e2572d3ab847b88268dcd1f7e", + b: #"92cb1c125816e4b522c7f430a5d74a61116b6189de7b2341f040194c02f10d9ef0cf081f4029444a65ea74e69d98b1cf08d3864087d5d2dee2ed6ab102f9b78e65d341f0824341a9fc25d0ea9dacccc5d355b4eddb0057949370a19c47135b0e", + c: #"8b84d092731c653b1accdda79c51e3f5d289bed7311189d927deadef0470e437e6d1d400634726512a79a015867424e3", } - let valid_b = - when common.g2_decompress(pi_b_bytes) is { - Some(_) -> True - None -> False + // Template of public values + let public_values: List = [561, 3] + + verify_compressed(vk, pk, public_values) +} + +test groth_verify_pass_2() { + // Template of VK + let vk: CompressedVK = + CompressedVK { + alpha: #"8e3d9e248feda194cb6fa0a3b64fd2a380cb5e94836bf8148bf97ebcbb5819d9a78f63102f0293c104bcbb2f810d8eb4", + beta: #"8cd68a7186a908212680a0234d8210c20328f8fb3ce1d69c9aec9330a5802d6cfaf6d7cf3176133221c19188590cb4141874ea7bbfcb9872931e115d882c46b90c3dcbcee10062d1c9b9b0a691d7bec7d2735f06495c7f71dea210e55b2782df", + gamma: #"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", + delta: #"938231fcec443fbdeb1079ff126b8f69bd8579ffe82d39923214d4345395beee60200288fa20c97ae50f3212131b6f8802af2f9f515c65af6a9a6c294c738590104376a0af44731d6699db6a286608774243f7d1dddc4605eb340e65e15060a5", + vkIC: [ + #"b5813c90d3455acb8608fdf66e8601f24ef048f3fdf9384862d77c72cb5cddfde2b307976b1b0319c42ba985f94be60a", + #"a41a0e6370c054be0f9acce08e97f6d5702d9daa9e9a934e8a377f553593baa1c58062adc73d63558653422d54d6f50c", + #"8e02a87c519c3145f984d25fdf769f74fbc36626c385d4554fb4bc1d7a12cbf669d40f257023b8b3c9a31e631aa8f981", + ], } - let valid_c = - when common.g1_decompress(pi_c_bytes) is { - Some(_) -> True - None -> False + + // Template of Proof + let pk: CompressedProof = + CompressedProof { + a: #"98ca847cc04a6f67ac85a628521450323d7aa5335d4c2c48e9780b659cf7ea8ece2d0b305c9ff9dcfb3e548d61bbaebe", + b: #"a8e6ba4dbce6aa84de8ca1cd39d42353fcac89fe8cb800e728ada3ca4ae3b07baa68f76b9e4fa73eebf78cc609fa85d6166a3b69cd08cc59f2ff36e52dfdf231540a4212fdd4a142504c76066bddea342dd0183b2b11ed62cfc1497189a4db52", + c: #"8bc8cc3f11483138cc55d5f0389e67231f9e8465e5cb4a5a668e6e298d5c4febb2a18e86881c84dd03c5d33db65af272", } + // Template of public values + let public_values: List = [8827, 7] - if valid_a && valid_b && valid_c { - None - } else { - Some(InvalidProofFormat) - } + verify_compressed(vk, pk, public_values) } diff --git a/zkp/plutus.json b/zkp/plutus.json new file mode 100644 index 0000000..852267f --- /dev/null +++ b/zkp/plutus.json @@ -0,0 +1,14 @@ +{ + "preamble": { + "title": "adao/zkp", + "description": "Aiken contracts for project 'adao/zkp'", + "version": "0.0.0", + "plutusVersion": "v3", + "compiler": { + "name": "Aiken", + "version": "v1.1.15+f03633e" + }, + "license": "Apache-2.0" + }, + "validators": [] +} \ No newline at end of file