From e6a918fed9ae2ebe61c4ecb89e631dadb0434377 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Thu, 18 Jan 2024 13:50:54 +0100 Subject: [PATCH] Submit proof as Groth16Proof struct --- contracts/Groth16.sol | 18 ++++++++++++++++++ contracts/Marketplace.sol | 3 ++- contracts/Proofs.sol | 24 +++++++++++------------- test/Proofs.test.js | 4 ++-- test/examples.js | 6 +++++- test/proof.js | 31 +++++++++++++++---------------- 6 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 contracts/Groth16.sol diff --git a/contracts/Groth16.sol b/contracts/Groth16.sol new file mode 100644 index 0000000..c790e48 --- /dev/null +++ b/contracts/Groth16.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.8; + +struct G1Point { + uint x; + uint y; +} + +struct G2Point { + uint[2] x; + uint[2] y; +} + +struct Groth16Proof { + G1Point a; + G2Point b; + G1Point c; +} diff --git a/contracts/Marketplace.sol b/contracts/Marketplace.sol index 54ecc08..2fd431d 100644 --- a/contracts/Marketplace.sol +++ b/contracts/Marketplace.sol @@ -9,6 +9,7 @@ import "./Requests.sol"; import "./Proofs.sol"; import "./StateRetrieval.sol"; import "./Verifier.sol"; +import "./Groth16.sol"; contract Marketplace is Proofs, StateRetrieval { using EnumerableSet for EnumerableSet.Bytes32Set; @@ -100,7 +101,7 @@ contract Marketplace is Proofs, StateRetrieval { function fillSlot( RequestId requestId, uint256 slotIndex, - uint256[8] calldata proof + Groth16Proof calldata proof ) public requestIsKnown(requestId) { Request storage request = _requests[requestId]; require(slotIndex < request.ask.slots, "Invalid slot"); diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index 3c466bd..4c3b34f 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -5,6 +5,7 @@ import "./Configuration.sol"; import "./Requests.sol"; import "./Periods.sol"; import "./Verifier.sol"; +import "./Groth16.sol"; abstract contract Proofs is Periods { ProofConfig private _config; @@ -108,19 +109,8 @@ abstract contract Proofs is Periods { return isRequired && pointer < _config.downtime; } - function submitProof(SlotId id, uint256[8] calldata proof) public { + function submitProof(SlotId id, Groth16Proof calldata proof) public { require(!_received[id][_blockPeriod()], "Proof already submitted"); - uint256[2] memory a; - uint256[2][2] memory b; - uint256[2] memory c; - a[0] = proof[0]; - a[1] = proof[1]; - b[0][0] = proof[2]; - b[0][1] = proof[3]; - b[1][0] = proof[4]; - b[1][1] = proof[5]; - c[0] = proof[6]; - c[1] = proof[7]; // TODO: The `pubSignals` should be constructed from information that we already know: // - external entropy (for example some fresh ethereum block header) - this gives us the unbiased randomness we use to sample which cells to prove @@ -133,7 +123,15 @@ abstract contract Proofs is Periods { ] = 16074246370508166450132968585287196391860062495017081813239200574579640171677; pubSignals[2] = 3; - require(_verifier.verifyProof(a, b, c, pubSignals), "Invalid proof"); + require( + _verifier.verifyProof( + [proof.a.x, proof.a.y], + [proof.b.x, proof.b.y], + [proof.c.x, proof.c.y], + pubSignals + ), + "Invalid proof" + ); _received[id][_blockPeriod()] = true; emit ProofSubmitted(id); } diff --git a/test/Proofs.test.js b/test/Proofs.test.js index 9c718b5..c67d85d 100644 --- a/test/Proofs.test.js +++ b/test/Proofs.test.js @@ -14,6 +14,7 @@ const { periodic } = require("./time") const { loadProof } = require("./proof") const { SlotState } = require("./requests") const binomialTest = require("@stdlib/stats-binomial-test") +const { exampleProof } = require("./examples") describe("Proofs", function () { const slotId = hexlify(randomBytes(32)) @@ -204,8 +205,7 @@ describe("Proofs", function () { }) it("fails proof submission when proof is incorrect", async function () { - let invalid = new Uint8Array(proof) - invalid[42] = 42 + let invalid = exampleProof() await expect(proofs.submitProof(slotId, invalid)).to.be.reverted }) diff --git a/test/examples.js b/test/examples.js index b3e4a02..e12e81b 100644 --- a/test/examples.js +++ b/test/examples.js @@ -39,6 +39,10 @@ const exampleRequest = async () => { } } -const exampleProof = () => ([1, 2, 3, 4, 5, 6, 7, 8]) +const exampleProof = () => ({ + a: { x: 1, y: 2 }, + b: { x: [3, 4], y: [5, 6]}, + c: { x: 7, y: 8 } +}) module.exports = { exampleConfiguration, exampleRequest, exampleProof } diff --git a/test/proof.js b/test/proof.js index 7b4fd59..73b9672 100644 --- a/test/proof.js +++ b/test/proof.js @@ -5,30 +5,29 @@ const { BigNumber } = ethers const BASE_PATH = __dirname + "/../verifier/networks" const PROOF_FILE_NAME = "example-proof/proof.json" -function G1ToUInts(point) { - return [ - point[0], - point[1] - ] +function G1ToStruct(point) { + return { + x: point[0], + y: point[1] + } } -function G2ToUInts(point) { - return [ - point[0][1], - point[0][0], - point[1][1], - point[1][0] - ] +function G2ToStruct(point) { + return { + x: [ point[0][1], point[0][0] ], + y: [ point[1][1], point[1][0] ] + } } function loadProof(name) { const proof = JSON.parse( fs.readFileSync(`${BASE_PATH}/${name}/${PROOF_FILE_NAME}`) ) - return [] - .concat(G1ToUInts(proof['pi_a'])) - .concat(G2ToUInts(proof['pi_b'])) - .concat(G1ToUInts(proof['pi_c'])) + return { + a: G1ToStruct(proof['pi_a']), + b: G2ToStruct(proof['pi_b']), + c: G1ToStruct(proof['pi_c']) + } } module.exports = { loadProof }