From c0be4e9b1dae8db3d2fb78221fae84ae7f859dad Mon Sep 17 00:00:00 2001 From: Eric Mastro Date: Wed, 8 Jun 2022 15:16:08 +1000 Subject: [PATCH] WIP: reorg curves, use witnet as base Now that curve ops are transactions, need to change up tests so that we can verify the result somehow? https://docs.ethers.io/v5/api/contract/contract/#Contract--write --- contracts/Proofs.sol | 8 +- contracts/Storage.sol | 2 +- contracts/TestBn254.sol | 259 +++--- contracts/TestBn254Verifier.sol | 35 - contracts/TestProofs.sol | 2 +- contracts/ecc/Curve.sol | 16 - contracts/ecc/Types.sol | 40 + contracts/ecc/Verifier.sol | 25 - contracts/ecc/curves/Bn254.sol | 454 ++++------- .../witnet/bls-solidity/contracts/BN256G1.sol | 218 +++++ .../witnet/bls-solidity/contracts/BN256G2.sol | 764 ++++++++++++++++++ .../contracts/EllipticCurve.sol | 427 ++++++++++ contracts/ecc/verifiers/Bn254Verifier.sol | 58 -- test/Bn254.test.js | 42 +- test/Bn254Verifier.test.js | 14 +- 15 files changed, 1797 insertions(+), 567 deletions(-) delete mode 100644 contracts/TestBn254Verifier.sol delete mode 100644 contracts/ecc/Curve.sol create mode 100644 contracts/ecc/Types.sol delete mode 100644 contracts/ecc/Verifier.sol create mode 100644 contracts/ecc/vendor/witnet/bls-solidity/contracts/BN256G1.sol create mode 100644 contracts/ecc/vendor/witnet/bls-solidity/contracts/BN256G2.sol create mode 100644 contracts/ecc/vendor/witnet/elliptic-curve-solidity/contracts/EllipticCurve.sol delete mode 100644 contracts/ecc/verifiers/Bn254Verifier.sol diff --git a/contracts/Proofs.sol b/contracts/Proofs.sol index 5227f14..9d33063 100644 --- a/contracts/Proofs.sol +++ b/contracts/Proofs.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <=0.8.13; -import "./ecc/verifiers/Bn254Verifier.sol"; -import "./ecc/Verifier.sol"; +import "./ecc/curves/Bn254.sol"; +import "./ecc/Types.sol"; contract Proofs { - using Bn254Verifier for Verifier.Proof; + using Bn254 for Types.Proof; uint256 private immutable period; uint256 private immutable timeout; @@ -134,7 +134,7 @@ contract Proofs { return _isProofRequired(id, currentPeriod()); } - function _submitProof(bytes32 id, Verifier.Proof calldata proof) internal { + function _submitProof(bytes32 id, Types.Proof calldata proof) internal { require(proof._verifyProof(), "Invalid proof"); require(!received[id][currentPeriod()], "Proof already submitted"); received[id][currentPeriod()] = true; diff --git a/contracts/Storage.sol b/contracts/Storage.sol index bce0990..32d791f 100644 --- a/contracts/Storage.sol +++ b/contracts/Storage.sol @@ -86,7 +86,7 @@ contract Storage is Collateral, Marketplace, Proofs { return _getPointer(id); } - function submitProof(bytes32 contractId, Verifier.Proof calldata proof) public { + function submitProof(bytes32 contractId, Types.Proof calldata proof) public { _submitProof(contractId, proof); } diff --git a/contracts/TestBn254.sol b/contracts/TestBn254.sol index 24c9e9e..2686110 100644 --- a/contracts/TestBn254.sol +++ b/contracts/TestBn254.sol @@ -3,147 +3,150 @@ pragma solidity >=0.8.0 <=0.8.13; import "./ecc/curves/Bn254.sol"; -import "./ecc/Curve.sol"; +import "./ecc/Types.sol"; contract TestBn254 { using Bn254 for *; struct VerifyingKey { - Curve.G2Point A; - Curve.G1Point B; - Curve.G2Point C; - Curve.G2Point gamma; - Curve.G1Point gammaBeta1; - Curve.G2Point gammaBeta2; - Curve.G2Point Z; - Curve.G1Point[] IC; + Types.G2Point A; + Types.G1Point B; + Types.G2Point C; + Types.G2Point gamma; + Types.G1Point gammaBeta1; + Types.G2Point gammaBeta2; + Types.G2Point Z; + Types.G1Point[] IC; } struct Proof { - Curve.G1Point A; - Curve.G1Point A_p; - Curve.G2Point B; - Curve.G1Point B_p; - Curve.G1Point C; - Curve.G1Point C_p; - Curve.G1Point K; - Curve.G1Point H; + Types.G1Point A; + Types.G1Point A_p; + Types.G2Point B; + Types.G1Point B_p; + Types.G1Point C; + Types.G1Point C_p; + Types.G1Point K; + Types.G1Point H; } - function f() public view returns (bool) { - Curve.G1Point memory p1; - Curve.G1Point memory p2; - p1.X = 1; p1.Y = 2; - p2.X = 1; p2.Y = 2; - Curve.G1Point memory explict_sum = Bn254.g1add(p1, p2); - Curve.G1Point memory scalar_prod = Bn254.g1mul(p1, 2); - return (explict_sum.X == scalar_prod.X && - explict_sum.Y == scalar_prod.Y); + function f() public returns (bool) { + Types.G1Point memory p1; + Types.G1Point memory p2; + p1.x = 1; p1.y = 2; + p2.x = 1; p2.y = 2; + Types.G1Point memory explict_sum = Bn254._add(p1, p2); + Types.G1Point memory scalar_prod = Bn254._multiply(p1, 2); + return (explict_sum.x == scalar_prod.x && + explict_sum.y == scalar_prod.y); } - function g() public view returns (bool) { - Curve.G1Point memory x = Bn254.g1add(Bn254.P1(), Bn254.g1neg(Bn254.P1())); + function g() public returns (bool) { + Types.G1Point memory x = Bn254._add(Bn254._p1Generator(), Bn254._negate(Bn254._p1Generator())); // should be zero - return (x.X == 0 && x.Y == 0); + return (x.x == 0 && x.y == 0); } - function testMul() public view returns (bool) { - Curve.G1Point memory p; + function testMul() public returns (bool) { + Types.G1Point memory p; // @TODO The points here are reported to be not well-formed - p.X = 14125296762497065001182820090155008161146766663259912659363835465243039841726; - p.Y = 16229134936871442251132173501211935676986397196799085184804749187146857848057; - p = Bn254.g1mul(p, 13986731495506593864492662381614386532349950841221768152838255933892789078521); + p.x = 14125296762497065001182820090155008161146766663259912659363835465243039841726; + p.y = 16229134936871442251132173501211935676986397196799085184804749187146857848057; + p = Bn254._multiply(p, 13986731495506593864492662381614386532349950841221768152838255933892789078521); return - p.X == 18256332256630856740336504687838346961237861778318632856900758565550522381207 && - p.Y == 6976682127058094634733239494758371323697222088503263230319702770853579280803; + p.x == 18256332256630856740336504687838346961237861778318632856900758565550522381207 && + p.y == 6976682127058094634733239494758371323697222088503263230319702770853579280803; } - function pair() public view returns (bool) { - Curve.G2Point memory fiveTimesP2 = Curve.G2Point( - [4540444681147253467785307942530223364530218361853237193970751657229138047649, 20954117799226682825035885491234530437475518021362091509513177301640194298072], - [11631839690097995216017572651900167465857396346217730511548857041925508482915, 21508930868448350162258892668132814424284302804699005394342512102884055673846] - ); - // The prime p in the base field F_p for G1 - uint p = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - Curve.G1Point[] memory g1points = new Curve.G1Point[](2); - Curve.G2Point[] memory g2points = new Curve.G2Point[](2); - // check e(5 P1, P2)e(-P1, 5 P2) == 1 - g1points[0] = Bn254.P1().g1mul(5); - g1points[1] = Bn254.P1(); - g1points[1].Y = p - g1points[1].Y; - g2points[0] = Bn254.P2(); - g2points[1] = fiveTimesP2; - if (!Bn254.pairing(g1points, g2points)) - return false; - // check e(P1, P2)e(-P1, P2) == 0 - g1points[0] = Bn254.P1(); - g1points[1] = Bn254.P1().g1neg(); - g2points[0] = Bn254.P2(); - g2points[1] = Bn254.P2(); - if (!Bn254.pairing(g1points, g2points)) - return false; - return true; - } - function _verifyingKey() internal pure returns (VerifyingKey memory vk) { - vk.A = Curve.G2Point([0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7, 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678], [0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d, 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550]); - vk.B = Curve.G1Point(0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84); - vk.C = Curve.G2Point([0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb, 0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3], [0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713, 0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590]); - vk.gamma = Curve.G2Point([0x25f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb1, 0x22acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d], [0x065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf68, 0x06d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb]); - vk.gammaBeta1 = Curve.G1Point(0x15794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f21, 0x14db745c6780e9df549864cec19c2daf4531f6ec0c89cc1c7436cc4d8d300c6d); - vk.gammaBeta2 = Curve.G2Point([0x1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e, 0x283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39], [0x140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e, 0x0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd4]); - vk.Z = Curve.G2Point([0x217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac29, 0x0a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c], [0x26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a9855, 0x2fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d7]); - vk.IC = new Curve.G1Point[](10); - vk.IC[0] = Curve.G1Point(0x0aee46a7ea6e80a3675026dfa84019deee2a2dedb1bbe11d7fe124cb3efb4b5a, 0x044747b6e9176e13ede3a4dfd0d33ccca6321b9acd23bf3683a60adc0366ebaf); - vk.IC[1] = Curve.G1Point(0x1e39e9f0f91fa7ff8047ffd90de08785777fe61c0e3434e728fce4cf35047ddc, 0x2e0b64d75ebfa86d7f8f8e08abbe2e7ae6e0a1c0b34d028f19fa56e9450527cb); - vk.IC[2] = Curve.G1Point(0x1c36e713d4d54e3a9644dffca1fc524be4868f66572516025a61ca542539d43f, 0x042dcc4525b82dfb242b09cb21909d5c22643dcdbe98c4d082cc2877e96b24db); - vk.IC[3] = Curve.G1Point(0x17d5d09b4146424bff7e6fb01487c477bbfcd0cdbbc92d5d6457aae0b6717cc5, 0x02b5636903efbf46db9235bbe74045d21c138897fda32e079040db1a16c1a7a1); - vk.IC[4] = Curve.G1Point(0x0f103f14a584d4203c27c26155b2c955f8dfa816980b24ba824e1972d6486a5d, 0x0c4165133b9f5be17c804203af781bcf168da7386620479f9b885ecbcd27b17b); - vk.IC[5] = Curve.G1Point(0x232063b584fb76c8d07995bee3a38fa7565405f3549c6a918ddaa90ab971e7f8, 0x2ac9b135a81d96425c92d02296322ad56ffb16299633233e4880f95aafa7fda7); - vk.IC[6] = Curve.G1Point(0x09b54f111d3b2d1b2fe1ae9669b3db3d7bf93b70f00647e65c849275de6dc7fe, 0x18b2e77c63a3e400d6d1f1fbc6e1a1167bbca603d34d03edea231eb0ab7b14b4); - vk.IC[7] = Curve.G1Point(0x0c54b42137b67cc268cbb53ac62b00ecead23984092b494a88befe58445a244a, 0x18e3723d37fae9262d58b548a0575f59d9c3266db7afb4d5739555837f6b8b3e); - vk.IC[8] = Curve.G1Point(0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a, 0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e); - vk.IC[9] = Curve.G1Point(0x111e2e2a5f8828f80ddad08f9f74db56dac1cc16c1cb278036f79a84cf7a116f, 0x1d7d62e192b219b9808faa906c5ced871788f6339e8d91b83ac1343e20a16b30); - } - function _verify(uint[] memory input, Proof memory proof) internal view returns (uint) { - VerifyingKey memory vk = _verifyingKey(); - require(input.length + 1 == vk.IC.length); - // Compute the linear combination vk_x - Curve.G1Point memory vk_x = Curve.G1Point(0, 0); - for (uint i = 0; i < input.length; i++) - vk_x = Bn254.g1add(vk_x, Bn254.g1mul(vk.IC[i + 1], input[i])); - vk_x = Bn254.g1add(vk_x, vk.IC[0]); - if (!Bn254.pairingProd2(proof.A, vk.A, Bn254.g1neg(proof.A_p), Bn254.P2())) return 1; - if (!Bn254.pairingProd2(vk.B, proof.B, Bn254.g1neg(proof.B_p), Bn254.P2())) return 2; - if (!Bn254.pairingProd2(proof.C, vk.C, Bn254.g1neg(proof.C_p), Bn254.P2())) return 3; - if (!Bn254.pairingProd3( - proof.K, vk.gamma, - Bn254.g1neg(Bn254.g1add(vk_x, Bn254.g1add(proof.A, proof.C))), vk.gammaBeta2, - Bn254.g1neg(vk.gammaBeta1), proof.B - )) return 4; - if (!Bn254.pairingProd3( - Bn254.g1add(vk_x, proof.A), proof.B, - Bn254.g1neg(proof.H), vk.Z, - Bn254.g1neg(proof.C), Bn254.P2() - )) return 5; - return 0; + function verifyProof(Types.Proof memory p) public returns (bool) { + return p._verifyProof(); } + // function pair() public view returns (bool) { + // Types.G2Point memory fiveTimesP2 = Types.G2Point( + // [4540444681147253467785307942530223364530218361853237193970751657229138047649, 20954117799226682825035885491234530437475518021362091509513177301640194298072], + // [11631839690097995216017572651900167465857396346217730511548857041925508482915, 21508930868448350162258892668132814424284302804699005394342512102884055673846] + // ); + // // The prime p in the base field F_p for G1 + // uint p = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + // Types.G1Point[] memory g1points = new Types.G1Point[](2); + // Types.G2Point[] memory g2points = new Types.G2Point[](2); + // // check e(5 P1, P2)e(-P1, 5 P2) == 1 + // g1points[0] = Bn254._p1Generator().multiply(5); + // g1points[1] = Bn254._p1Generator(); + // g1points[1].y = p - g1points[1].y; + // g2points[0] = Bn254._p2Generator(); + // g2points[1] = fiveTimesP2; + // if (!Bn254.checkPairing(g1points, g2points)) + // return false; + // // check e(P1, P2)e(-P1, P2) == 0 + // g1points[0] = Bn254._p1Generator(); + // g1points[1] = Bn254._p1Generator().negate(); + // g2points[0] = Bn254._p2Generator(); + // g2points[1] = Bn254._p2Generator(); + // if (!Bn254.checkPairing(g1points, g2points)) + // return false; + // return true; + // } + // function _verifyingKey() internal pure returns (VerifyingKey memory vk) { + // vk.A = Types.G2Point([0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7, 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678], [0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d, 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550]); + // vk.B = Types.G1Point(0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84); + // vk.C = Types.G2Point([0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb, 0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3], [0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713, 0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590]); + // vk.gamma = Types.G2Point([0x25f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb1, 0x22acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d], [0x065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf68, 0x06d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb]); + // vk.gammaBeta1 = Types.G1Point(0x15794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f21, 0x14db745c6780e9df549864cec19c2daf4531f6ec0c89cc1c7436cc4d8d300c6d); + // vk.gammaBeta2 = Types.G2Point([0x1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e, 0x283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39], [0x140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e, 0x0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd4]); + // vk.Z = Types.G2Point([0x217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac29, 0x0a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c], [0x26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a9855, 0x2fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d7]); + // vk.IC = new Types.G1Point[](10); + // vk.IC[0] = Types.G1Point(0x0aee46a7ea6e80a3675026dfa84019deee2a2dedb1bbe11d7fe124cb3efb4b5a, 0x044747b6e9176e13ede3a4dfd0d33ccca6321b9acd23bf3683a60adc0366ebaf); + // vk.IC[1] = Types.G1Point(0x1e39e9f0f91fa7ff8047ffd90de08785777fe61c0e3434e728fce4cf35047ddc, 0x2e0b64d75ebfa86d7f8f8e08abbe2e7ae6e0a1c0b34d028f19fa56e9450527cb); + // vk.IC[2] = Types.G1Point(0x1c36e713d4d54e3a9644dffca1fc524be4868f66572516025a61ca542539d43f, 0x042dcc4525b82dfb242b09cb21909d5c22643dcdbe98c4d082cc2877e96b24db); + // vk.IC[3] = Types.G1Point(0x17d5d09b4146424bff7e6fb01487c477bbfcd0cdbbc92d5d6457aae0b6717cc5, 0x02b5636903efbf46db9235bbe74045d21c138897fda32e079040db1a16c1a7a1); + // vk.IC[4] = Types.G1Point(0x0f103f14a584d4203c27c26155b2c955f8dfa816980b24ba824e1972d6486a5d, 0x0c4165133b9f5be17c804203af781bcf168da7386620479f9b885ecbcd27b17b); + // vk.IC[5] = Types.G1Point(0x232063b584fb76c8d07995bee3a38fa7565405f3549c6a918ddaa90ab971e7f8, 0x2ac9b135a81d96425c92d02296322ad56ffb16299633233e4880f95aafa7fda7); + // vk.IC[6] = Types.G1Point(0x09b54f111d3b2d1b2fe1ae9669b3db3d7bf93b70f00647e65c849275de6dc7fe, 0x18b2e77c63a3e400d6d1f1fbc6e1a1167bbca603d34d03edea231eb0ab7b14b4); + // vk.IC[7] = Types.G1Point(0x0c54b42137b67cc268cbb53ac62b00ecead23984092b494a88befe58445a244a, 0x18e3723d37fae9262d58b548a0575f59d9c3266db7afb4d5739555837f6b8b3e); + // vk.IC[8] = Types.G1Point(0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a, 0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e); + // vk.IC[9] = Types.G1Point(0x111e2e2a5f8828f80ddad08f9f74db56dac1cc16c1cb278036f79a84cf7a116f, 0x1d7d62e192b219b9808faa906c5ced871788f6339e8d91b83ac1343e20a16b30); + // } + // function _verify(uint[] memory input, Proof memory proof) internal view returns (uint) { + // VerifyingKey memory vk = _verifyingKey(); + // require(input.length + 1 == vk.IC.length); + // // Compute the linear combination vk_x + // Types.G1Point memory vk_x = Types.G1Point(0, 0); + // for (uint i = 0; i < input.length; i++) + // vk_x = Bn254.add(vk_x, Bn254.multiply(vk.IC[i + 1], input[i])); + // vk_x = Bn254.add(vk_x, vk.IC[0]); + // if (!Bn254.pairingProd2(proof.A, vk.A, Bn254.negate(proof.A_p), Bn254._p2Generator())) return 1; + // if (!Bn254.pairingProd2(vk.B, proof.B, Bn254.negate(proof.B_p), Bn254._p2Generator())) return 2; + // if (!Bn254.pairingProd2(proof.C, vk.C, Bn254.negate(proof.C_p), Bn254._p2Generator())) return 3; + // if (!Bn254.pairingProd3( + // proof.K, vk.gamma, + // Bn254.negate(Bn254.add(vk_x, Bn254.add(proof.A, proof.C))), vk.gammaBeta2, + // Bn254.negate(vk.gammaBeta1), proof.B + // )) return 4; + // if (!Bn254.pairingProd3( + // Bn254.add(vk_x, proof.A), proof.B, + // Bn254.negate(proof.H), vk.Z, + // Bn254.negate(proof.C), Bn254._p2Generator() + // )) return 5; + // return 0; + // } - function verifyTx() public view returns (bool r) { - uint[] memory input = new uint[](9); - Proof memory proof; - proof.A = Curve.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497); - proof.A_p = Curve.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366); - proof.B = Curve.G2Point( - [8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055], - [15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]); - proof.B_p = Curve.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950); - proof.C = Curve.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506); - proof.C_p = Curve.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686); - proof.H = Curve.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758); - proof.K = Curve.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170); - input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521; - input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053; - input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569; - input[3] = 6627643779954497813586310325594578844876646808666478625705401786271515864467; - input[4] = 2957286918163151606545409668133310005545945782087581890025685458369200827463; - input[5] = 1384290496819542862903939282897996566903332587607290986044945365745128311081; - input[6] = 5613571677741714971687805233468747950848449704454346829971683826953541367271; - input[7] = 9643208548031422463313148630985736896287522941726746581856185889848792022807; - input[8] = 18066496933330839731877828156604; - return _verify(input, proof) == 0; - } + // function verifyTx() public view returns (bool r) { + // uint[] memory input = new uint[](9); + // Proof memory proof; + // proof.A = Types.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497); + // proof.A_p = Types.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366); + // proof.B = Types.G2Point( + // [8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055], + // [15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]); + // proof.B_p = Types.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950); + // proof.C = Types.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506); + // proof.C_p = Types.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686); + // proof.H = Types.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758); + // proof.K = Types.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170); + // input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521; + // input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053; + // input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569; + // input[3] = 6627643779954497813586310325594578844876646808666478625705401786271515864467; + // input[4] = 2957286918163151606545409668133310005545945782087581890025685458369200827463; + // input[5] = 1384290496819542862903939282897996566903332587607290986044945365745128311081; + // input[6] = 5613571677741714971687805233468747950848449704454346829971683826953541367271; + // input[7] = 9643208548031422463313148630985736896287522941726746581856185889848792022807; + // input[8] = 18066496933330839731877828156604; + // return _verify(input, proof) == 0; + // } } \ No newline at end of file diff --git a/contracts/TestBn254Verifier.sol b/contracts/TestBn254Verifier.sol deleted file mode 100644 index a81978a..0000000 --- a/contracts/TestBn254Verifier.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "./ecc/verifiers/Bn254Verifier.sol"; -import "./ecc/Verifier.sol"; - -// exposes internal functions of Proofs for testing -contract TestBn254Verifier { - using Bn254Verifier for Verifier.Proof; - function verifyProof(Verifier.Proof memory p) public view returns (bool) { - // Proof memory p; - // p.q = [ - // Curve.QElement(i, v), - // Curve.QElement(i, v), - // Curve.QElement(i, v) - // ]; - // p.mus = []; - // p.sigma = Curve.G1Point(x, y); - // p.u = [ - // Curve.G1Point(x, y), - // Curve.G1Point(x, y), - // Curve.G1Point(x, y) - // ]; - // p.publicKey = Curve.G2Point( - // [ - // 11559732032986387107991004021392285783925812861821192530917403151452391805634, - // 10857046999023057135944570762232829481370756359578518086990519993285655852781 - // ], - // [ - // 4082367875863433681332203403145435568316851327593401208105741076214120093531, - // 8495653923123431417604973247489272438418190587263600148770280649306958101930 - // ] - return p._verifyProof(); - } -} diff --git a/contracts/TestProofs.sol b/contracts/TestProofs.sol index d82638b..242a549 100644 --- a/contracts/TestProofs.sol +++ b/contracts/TestProofs.sol @@ -56,7 +56,7 @@ contract TestProofs is Proofs { return _getPointer(id); } - function submitProof(bytes32 id, Verifier.Proof calldata proof) public { + function submitProof(bytes32 id, Types.Proof calldata proof) public { _submitProof(id, proof); } diff --git a/contracts/ecc/Curve.sol b/contracts/ecc/Curve.sol deleted file mode 100644 index 5a795d7..0000000 --- a/contracts/ecc/Curve.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <=0.8.13; - -library Curve { - struct G1Point { - uint256 X; - uint256 Y; - } - - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint256[2] X; - uint256[2] Y; - } -} \ No newline at end of file diff --git a/contracts/ecc/Types.sol b/contracts/ecc/Types.sol new file mode 100644 index 0000000..32780fa --- /dev/null +++ b/contracts/ecc/Types.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.8.0 <=0.8.13; + +library Types { + struct G1PointJac { + uint256 x; + uint256 y; + uint256 z; + } + + struct G1Point { + uint256 x; + uint256 y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] x; + uint256[2] y; + } + + struct QElement { + int64 i; + uint256 v; + } + + struct Proof { + // TODO: should `q` be bounded? + QElement[] q; + uint256[10] mus; + // sigma is probably only the x coordinate + // (https://github.com/supranational/blst#serialization-format) + G1Point sigma; + // TODO: should `u` be bounded? + G1Point[] u; + bytes name; + G2Point publicKey; + } +} \ No newline at end of file diff --git a/contracts/ecc/Verifier.sol b/contracts/ecc/Verifier.sol deleted file mode 100644 index c49f17d..0000000 --- a/contracts/ecc/Verifier.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <=0.8.13; - -import "./Curve.sol"; - -library Verifier { - struct QElement { - int64 i; - uint256 v; - } - - struct Proof { - // TODO: should `q` be bounded? - QElement[] q; - uint256[10] mus; - // sigma is probably only the x coordinate - // (https://github.com/supranational/blst#serialization-format) - Curve.G1Point sigma; - // TODO: should `u` be bounded? - Curve.G1Point[] u; - bytes name; - Curve.G2Point publicKey; - } -} \ No newline at end of file diff --git a/contracts/ecc/curves/Bn254.sol b/contracts/ecc/curves/Bn254.sol index b8bf855..7031c54 100644 --- a/contracts/ecc/curves/Bn254.sol +++ b/contracts/ecc/curves/Bn254.sol @@ -2,143 +2,24 @@ // // From: https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol -pragma solidity >=0.8.0 <=0.8.13; +pragma solidity >=0.7.0 <=0.8.13; -import "../Curve.sol"; +import "../Types.sol"; +import "../vendor/witnet/elliptic-curve-solidity/contracts/EllipticCurve.sol"; +import "../vendor/witnet/bls-solidity/contracts/BN256G1.sol"; +import "../vendor/witnet/bls-solidity/contracts/BN256G2.sol"; library Bn254 { - // p = p(u) = 36u^4 + 36u^3 + 24u^2 + 6u + 1 - uint256 internal constant FIELD_ORDER = - 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; - - // Number of elements in the field (often called `q`) - // n = n(u) = 36u^4 + 36u^3 + 18u^2 + 6u + 1 - uint256 internal constant GEN_ORDER = - 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; - - uint256 internal constant CURVE_B = 3; - - // a = (p+1) / 4 - uint256 internal constant CURVE_A = - 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52; - - // struct Curve.G1Point { - // uint256 X; - // uint256 Y; - // } - - // // Encoding of field elements is: X[0] * z + X[1] - // struct Curve.G2Point { - // uint256[2] X; - // uint256[2] Y; - // } - - // (P+1) / 4 - function A() internal pure returns (uint256) { - return CURVE_A; - } - - function B() internal pure returns (uint256) { - return CURVE_B; - } - - function P() internal pure returns (uint256) { - return FIELD_ORDER; - } - - function N() internal pure returns (uint256) { - return GEN_ORDER; - } /// @return the generator of G1 - function P1() internal pure returns (Curve.G1Point memory) { - return Curve.G1Point(1, 2); - } - - function HashToPoint(uint256 s) internal view returns (Curve.G1Point memory g) { - uint256 beta = 0; - uint256 y = 0; - - // XXX: Gen Order (n) or Field Order (p) ? - uint256 x = s % GEN_ORDER; - - while (true) { - (beta, y) = FindYforX(x); - - // y^2 == beta - if (beta == mulmod(y, y, FIELD_ORDER)) { - return Curve.G1Point(x, y); - } - - x = addmod(x, 1, FIELD_ORDER); - } - } - - /** - * Given X, find Y - * - * where y = sqrt(x^3 + b) - * - * Returns: (x^3 + b), y - */ - function FindYforX(uint256 x) internal view returns (uint256, uint256) { - // beta = (x^3 + b) % p - uint256 beta = addmod( - mulmod(mulmod(x, x, FIELD_ORDER), x, FIELD_ORDER), - CURVE_B, - FIELD_ORDER - ); - - // y^2 = x^3 + b - // this acts like: y = sqrt(beta) - uint256 y = expMod(beta, CURVE_A, FIELD_ORDER); - - return (beta, y); - } - - // a - b = c; - function submod(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 a_nn; - - if (a > b) { - a_nn = a; - } else { - a_nn = a + GEN_ORDER; - } - - return addmod(a_nn - b, 0, GEN_ORDER); - } - - function expMod( - uint256 _base, - uint256 _exponent, - uint256 _modulus - ) internal view returns (uint256 retval) { - bool success; - uint256[1] memory output; - uint256[6] memory input; - input[0] = 0x20; // baseLen = new(big.Int).SetBytes(getData(input, 0, 32)) - input[1] = 0x20; // expLen = new(big.Int).SetBytes(getData(input, 32, 32)) - input[2] = 0x20; // modLen = new(big.Int).SetBytes(getData(input, 64, 32)) - input[3] = _base; - input[4] = _exponent; - input[5] = _modulus; - assembly { - success := staticcall(sub(gas(), 2000), 5, input, 0xc0, output, 0x20) - // Use "invalid" to make gas estimation work - switch success - case 0 { - invalid() - } - } - require(success); - return output[0]; + function _p1Generator() internal pure returns (Types.G1Point memory) { + return Types.G1Point(1, 2); } /// @return the generator of G2 - function P2() internal pure returns (Curve.G2Point memory) { + function _p2Generator() internal pure returns (Types.G2Point memory) { return - Curve.G2Point( + Types.G2Point( [ 11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781 @@ -150,185 +31,188 @@ library Bn254 { ); } - /// @return the negation of p, i.e. p.add(p.negate()) should be zero. - function g1neg(Curve.G1Point memory p) internal pure returns (Curve.G1Point memory) { - // The prime q in the base field F_q for G1 - uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - if (p.X == 0 && p.Y == 0) return Curve.G1Point(0, 0); - return Curve.G1Point(p.X, q - (p.Y % q)); - } - - /// @return r the sum of two points of G1 - function g1add(Curve.G1Point memory p1, Curve.G1Point memory p2) + /// @dev computes P + Q + /// @param p: G1 point p + /// @param q: G1 point q + /// @return G1 point with x and y coordinates of P+Q. + function _add(Types.G1Point memory p, Types.G1Point memory q) internal - view - returns (Curve.G1Point memory r) + returns (Types.G1Point memory) { - uint256[4] memory input; - input[0] = p1.X; - input[1] = p1.Y; - input[2] = p2.X; - input[3] = p2.Y; - bool success; - assembly { - success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) - // Use "invalid" to make gas estimation work - switch success - case 0 { - invalid() - } - } - require(success); + (uint256 x, uint256 y) = BN256G1._add([p.x, p.y, q.x, q.y]); + return Types.G1Point(x, y); } - /// @return r the product of a point on G1 and a scalar, i.e. - /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. - function g1mul(Curve.G1Point memory p, uint256 s) + /// @dev computes P*k. + /// @param p: G1 point p + /// @param k: scalar k. + /// @return G1 point with x and y coordinates of P*k. + function _multiply(Types.G1Point memory p, uint256 k) internal - view - returns (Curve.G1Point memory r) + returns (Types.G1Point memory) { - uint256[3] memory input; - input[0] = p.X; - input[1] = p.Y; - input[2] = s; - bool success; - assembly { - success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) - // Use "invalid" to make gas estimation work - switch success - case 0 { - invalid() - } - } - require(success); + (uint256 x, uint256 y) = BN256G1._multiply([p.x, p.y, k]); + return Types.G1Point(x, y); } - /// @return the result of computing the pairing check - /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 - /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should - /// return true. - function pairing(Curve.G1Point[] memory p1, Curve.G2Point[] memory p2) + + /// @dev Check whether point (x,y) is on curve BN254. + /// @param p1 G1 point + /// @return true if x,y in the curve, false else + function _isOnCurve(Types.G1Point memory p1) internal - view + pure returns (bool) { - require(p1.length == p2.length); - uint256 elements = p1.length; - uint256 inputSize = elements * 6; - uint256[] memory input = new uint256[](inputSize); - for (uint256 i = 0; i < elements; i++) { - input[i * 6 + 0] = p1[i].X; - input[i * 6 + 1] = p1[i].Y; - input[i * 6 + 2] = p2[i].X[0]; - input[i * 6 + 3] = p2[i].X[1]; - input[i * 6 + 4] = p2[i].Y[0]; - input[i * 6 + 5] = p2[i].Y[1]; - } - uint256[1] memory out; - bool success; - assembly { - success := staticcall( - sub(gas(), 2000), - 8, - add(input, 0x20), - mul(inputSize, 0x20), - out, - 0x20 - ) - // Use "invalid" to make gas estimation work - switch success - case 0 { - invalid() - } - } - require(success); - return out[0] != 0; + return BN256G1._isOnCurve([p1.x, p1.y]); + } + /** + * @notice Checks if FQ2 is on G2 + * @param p1 G2 Point + * @return True if the FQ2 is on G2 + */ + function _isOnCurve(Types.G2Point memory p1) + internal + pure + returns (bool) + { + return BN256G2._isOnCurve(p1.x[0], p1.y[0], p1.x[1], p1.y[1]); } - /// Convenience method for a pairing check for two pairs. - function pairingProd2( - Curve.G1Point memory a1, - Curve.G2Point memory a2, - Curve.G1Point memory b1, - Curve.G2Point memory b2 - ) internal view returns (bool) { - Curve.G1Point[] memory p1 = new Curve.G1Point[](2); - Curve.G2Point[] memory p2 = new Curve.G2Point[](2); - p1[0] = a1; - p1[1] = b1; - p2[0] = a2; - p2[1] = b2; - return pairing(p1, p2); + /// @dev Derives the y coordinate from a compressed-format point x [[SEC-1]](https://www.secg.org/SEC1-Ver-1.0.pdf). + /// @param prefix parity byte (0x02 even, 0x03 odd) + /// @param x coordinate x + /// @return y coordinate y + function _deriveY(uint8 prefix, uint256 x) + internal + pure + returns (uint256) + { + return BN256G1._deriveY(prefix, x); } - /// Convenience method for a pairing check for three pairs. - function pairingProd3( - Curve.G1Point memory a1, - Curve.G2Point memory a2, - Curve.G1Point memory b1, - Curve.G2Point memory b2, - Curve.G1Point memory c1, - Curve.G2Point memory c2 - ) internal view returns (bool) { - Curve.G1Point[] memory p1 = new Curve.G1Point[](3); - Curve.G2Point[] memory p2 = new Curve.G2Point[](3); - p1[0] = a1; - p1[1] = b1; - p1[2] = c1; - p2[0] = a2; - p2[1] = b2; - p2[2] = c2; - return pairing(p1, p2); + /// @dev Calculate inverse (x, -y) of point (x, y). + /// @param p1 G1 point + /// @return (x, -y) + function _negate(Types.G1Point memory p1) + internal + pure + returns (Types.G1Point memory) + { + (uint256 x, uint256 y) = EllipticCurve.ecInv(p1.x, p1.y, BN256G1.PP); + return Types.G1Point(x, y); } - /// Convenience method for a pairing check for four pairs. - function pairingProd4( - Curve.G1Point memory a1, - Curve.G2Point memory a2, - Curve.G1Point memory b1, - Curve.G2Point memory b2, - Curve.G1Point memory c1, - Curve.G2Point memory c2, - Curve.G1Point memory d1, - Curve.G2Point memory d2 - ) internal view returns (bool) { - Curve.G1Point[] memory p1 = new Curve.G1Point[](4); - Curve.G2Point[] memory p2 = new Curve.G2Point[](4); - p1[0] = a1; - p1[1] = b1; - p1[2] = c1; - p1[3] = d1; - p2[0] = a2; - p2[1] = b2; - p2[2] = c2; - p2[3] = d2; - return pairing(p1, p2); + /// @dev Substract two points (x1, y1) and (x2, y2) in affine coordinates. + /// @param p1 G1 point P1 + /// @param p2 G1 point P2 + /// @return (qx, qy) = P1-P2 in affine coordinates + function _subtract(Types.G1Point memory p1, Types.G1Point memory p2) + internal + pure + returns(Types.G1Point memory) + { + (uint256 x, uint256 y) = EllipticCurve.ecSub( + p1.x, + p1.y, + p2.x, + p2.y, + BN256G1.AA, + BN256G1.PP); + + return Types.G1Point(x, y); } - function isOnCurve(Curve.G1Point memory g1) internal pure returns (bool) { - uint256 aa = Bn254.A(); - uint256 bb = Bn254.B(); - uint256 pp = Bn254.P(); + /// @dev Function to convert a `Hash(msg|DATA)` to a point in the curve as defined in [VRF-draft-04](https://tools.ietf.org/pdf/draft-irtf-cfrg-vrf-04). + /// @param _message The message used for computing the VRF + /// @return The hash point in affine coordinates + function _hashToPoint(bytes memory _message) + internal + pure + returns (Types.G1Point memory) + { + (uint256 x, uint256 y) = BN256G1._hashToTryAndIncrement(_message); + return Types.G1Point(x, y); + } - // Implementation borrowed from the witnet/elliptic-curve-solidity lib: - // https://github.com/witnet/elliptic-curve-solidity/blob/b6886bb08333ccf6883ac42827d62c1bfdb37d44/contracts/EllipticCurve.sol#L113-L145 - if (0 == g1.X || g1.X == pp || 0 == g1.Y || g1.Y == pp) { - return false; + /// @dev Checks if e(P, Q) = e (R,S). + /// @param p: G1 point P + /// @param q: G2 point Q + /// @param r: G1 point R + /// @param s: G2 point S + /// @return true if e(P, Q) = e (R,S). + function _checkPairing( + Types.G1Point memory p, + Types.G2Point memory q, + Types.G1Point memory r, + Types.G2Point memory s + ) + internal + returns (bool) + { + return BN256G1._bn256CheckPairing( + [ + p.x, // x-coordinate of point P + p.y, // y-coordinate of point P + q.x[0], // x real coordinate of point Q + q.x[1], // x imaginary coordinate of point Q + q.y[0], // y real coordinate of point Q + q.y[1], // y imaginary coordinate of point Q + r.x, // x-coordinate of point R + r.y, // y-coordinate of point R + s.x[0], // x real coordinate of point S + s.x[1], // x imaginary coordinate of point S + s.y[0], // y real coordinate of point S + s.y[1] // y imaginary coordinate of point S + ] + ); + } + + function _verifyProof(Types.Proof memory proof) internal returns (bool) { + // var first: blst_p1 + // for qelem in q : + // var prod: blst_p1 + // prod.blst_p1_mult(hashNameI(tau.t.name, qelem.I), qelem.V, 255) + // first.blst_p1_add_or_double(first, prod) + // doAssert(blst_p1_on_curve(first).bool) + Types.G1Point memory first; + for (uint256 i = 0; i=0.8.0 <=0.8.13; + +import "../../elliptic-curve-solidity/contracts/EllipticCurve.sol"; + + +/** + * @title BN256G1 Curve Library + * @dev Library providing arithmetic operations over G1 in bn256. + * Provides additional methods like pairing and pairing_batch + * Heavily influenced by https://github.com/PhilippSchindler/ethdkg + * Calls to assembly are public and not external because assembly cannot be applied on calldata + * @author Witnet Foundation + */ + +library BN256G1 { + + // Generator coordinate `x` of the EC curve + uint256 public constant GX = 1; + // Generator coordinate `y` of the EC curve + uint256 public constant GY = 2; + // Constant `a` of EC equation + uint256 internal constant AA = 0; + // Constant `b` of EC equation + uint256 internal constant BB = 3; + // Prime number of the curve + uint256 internal constant PP = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + // Order of the curve + uint256 internal constant NN = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; + + // This is 0xf1f5883e65f820d099915c908786b9d3f58714d70a38f4c22ca2bc723a70f263, the last mulitple of the modulus before 2^256 + uint256 internal constant LAST_MULTIPLE_OF_PP_LOWER_THAN_2_256 = 0xf1f5883e65f820d099915c908786b9d3f58714d70a38f4c22ca2bc723a70f263; + + + /// @dev computes P + Q + /// @param input: 4 values of 256 bits each + /// *) x-coordinate of point P + /// *) y-coordinate of point P + /// *) x-coordinate of point Q + /// *) y-coordinate of point Q + /// @return An array with x and y coordinates of P+Q. + function _add(uint256[4] memory input) internal returns (uint256, uint256) { + bool success; + uint256[2] memory result; + assembly { + // 0x06 id of the bn256Add precompile + // 0 number of ether to transfer + // 128 size of call parameters, i.e. 128 bytes total + // 64 size of call return value, i.e. 64 bytes / 512 bit for a BN256 curve point + success := call(not(0), 0x06, 0, input, 128, result, 64) + } + require(success, "bn256 addition failed"); + + return (result[0], result[1]); + } + + /// @dev computes P*k. + /// @param input: 3 values of 256 bits each: + /// *) x-coordinate of point P + /// *) y-coordinate of point P + /// *) scalar k. + /// @return An array with x and y coordinates of P*k. + function _multiply(uint256[3] memory input) internal returns (uint256, uint256) { + bool success; + uint256[2] memory result; + assembly { + // 0x07 id of the bn256ScalarMul precompile + // 0 number of ether to transfer + // 96 size of call parameters, i.e. 96 bytes total (256 bit for x, 256 bit for y, 256 bit for scalar) + // 64 size of call return value, i.e. 64 bytes / 512 bit for a BN256 curve point + success := call(not(0), 0x07, 0, input, 96, result, 64) + } + require(success, "elliptic curve multiplication failed"); + return (result[0], result[1]); + } + + /// @dev Checks if P is on G1 using the EllipticCurve library. + /// @param point: 2 values of 256 bits each: + /// *) x-coordinate of point P + /// *) y-coordinate of point P + /// @return true if P is in G1. + function _isOnCurve(uint[2] memory point) internal pure returns (bool) { + // checks if the given point is a valid point from the first elliptic curve group + // uses the EllipticCurve library + return EllipticCurve.isOnCurve( + point[0], + point[1], + AA, + BB, + PP); + } + + /// @dev Checks if e(P, Q) = e (R,S). + /// @param input: 12 values of 256 bits each: + /// *) x-coordinate of point P + /// *) y-coordinate of point P + /// *) x real coordinate of point Q + /// *) x imaginary coordinate of point Q + /// *) y real coordinate of point Q + /// *) y imaginary coordinate of point Q + /// *) x-coordinate of point R + /// *) y-coordinate of point R + /// *) x real coordinate of point S + /// *) x imaginary coordinate of point S + /// *) y real coordinate of point S + /// *) y imaginary coordinate of point S + /// @return true if e(P, Q) = e (R,S). + function _bn256CheckPairing(uint256[12] memory input) internal returns (bool) { + uint256[1] memory result; + bool success; + assembly { + // 0x08 id of the bn256CheckPairing precompile (checking the elliptic curve pairings) + // 0 number of ether to transfer + // 0 since we have an array of fixed length, our input starts in 0 + // 384 size of call parameters, i.e. 12*256 bits == 384 bytes + // 32 size of result (one 32 byte boolean!) + success := call(sub(gas(), 2000), 0x08, 0, input, 384, result, 32) + } + require(success, "elliptic curve pairing failed"); + return result[0] == 1; + } + + /// @dev Checks if e(P, Q) = e (R,S)*e(T,U)... + /// @param input: A modulo 6 length array of values of 256 bits each: + /// *) x-coordinate of point P + /// *) y-coordinate of point P + /// *) x real coordinate of point Q + /// *) x imaginary coordinate of point Q + /// *) y real coordinate of point Q + /// *) y imaginary coordinate of point Q + /// *) x-coordinate of point R + /// *) y-coordinate of point R + /// *) x real coordinate of point S + /// *) x imaginary coordinate of point S + /// *) y real coordinate of point S + /// *) y imaginary coordinate of point S + /// *) and so forth with additional pairing checks + /// @return true if e(input[0,1], input[2,3,4,5]) = e(input[6,7], input[8,9,10,11])*e(input[12,13], input[14,15,16,17])... + function _bn256CheckPairingBatch(uint256[] memory input) internal returns (bool) { + uint256[1] memory result; + bool success; + require(input.length % 6 == 0, "Incorrect input length"); + uint256 inLen = input.length * 32; + assembly { + // 0x08 id of the bn256CheckPairing precompile (checking the elliptic curve pairings) + // 0 number of ether to transfer + // add(input, 0x20) since we have an unbounded array, the first 256 bits refer to its length + // 384 size of call parameters, i.e. 12*256 bits == 384 bytes + // 32 size of result (one 32 byte boolean!) + success := call(sub(gas(), 2000), 0x08, 0, add(input, 0x20), inLen, result, 32) + } + require(success, "elliptic curve pairing failed"); + return result[0] == 1; + } + + /// @dev Function to transform compressed bytes into a x and a y coordinate in the curve. + /// @param _point The point bytes + /// @return The coordinates `x` and `y` in an array + function _fromCompressed(bytes memory _point) internal pure returns (uint256, uint256) { + require(_point.length == 33, "invalid encoding"); + uint8 sign; + uint256 x; + assembly { + sign := mload(add(_point, 1)) + x := mload(add(_point, 33)) + } + + return ( + x, _deriveY( + sign, + x) + ); + } + + /// @dev Function to convert a `Hash(msg|DATA)` to a point in the curve as defined in [VRF-draft-04](https://tools.ietf.org/pdf/draft-irtf-cfrg-vrf-04). + /// @param _message The message used for computing the VRF + /// @return The hash point in affine coordinates + function _hashToTryAndIncrement(bytes memory _message) internal pure returns (uint, uint) { + // Find a valid EC point + // Loop over counter ctr starting at 0x00 and do hash + for (uint8 ctr = 0; ctr < 256; ctr++) { + // Counter update + // c[cLength-1] = byte(ctr); + bytes32 sha = sha256(abi.encodePacked(_message, ctr)); + // Step 4: arbitraty string to point and check if it is on curve + uint hPointX = uint256(sha); + // Avoid hashes that are above the last multiple of _PP, otherwise odds are biased + if (hPointX >= LAST_MULTIPLE_OF_PP_LOWER_THAN_2_256) { + continue; + } + // Do the modulus to avoid excesive iterations of the loop + hPointX = hPointX % PP; + uint hPointY = _deriveY(2, hPointX); + // we do not use the subsidized one as it appears to consume more gas + if (_isOnCurve([hPointX,hPointY])) { + // Step 5 (omitted): calculate H (cofactor is 1 on bn256g1) + // If H is not "INVALID" and cofactor > 1, set H = cofactor * H + return (hPointX, hPointY); + } + } + revert("No valid point was found"); + } + + /// @dev Function to derive the `y` coordinate given the `x` coordinate and the parity byte (`0x03` for odd `y` and `0x04` for even `y`). + /// @param _yByte The parity byte following the ec point compressed format + /// @param _x The coordinate `x` of the point + /// @return The coordinate `y` of the point + function _deriveY(uint8 _yByte, uint256 _x) internal pure returns (uint256) { + return EllipticCurve.deriveY( + _yByte, + _x, + AA, + BB, + PP); + } + +} \ No newline at end of file diff --git a/contracts/ecc/vendor/witnet/bls-solidity/contracts/BN256G2.sol b/contracts/ecc/vendor/witnet/bls-solidity/contracts/BN256G2.sol new file mode 100644 index 0000000..f7e5fc7 --- /dev/null +++ b/contracts/ecc/vendor/witnet/bls-solidity/contracts/BN256G2.sol @@ -0,0 +1,764 @@ +// SPDX-License-Identifier: MIT +// solium-disable security/no-assign-params + +pragma solidity >=0.8.0 <=0.8.13; + + +/** + * @title Elliptic curve operations on twist points on bn256 (G2) + * @dev Adaptation of https://github.com/musalbas/solidity-BN256G2 to 0.6.0 + */ +library BN256G2 { + uint256 internal constant FIELD_MODULUS = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; + uint256 internal constant TWISTBX = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5; + uint256 internal constant TWISTBY = 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2; + uint internal constant PTXX = 0; + uint internal constant PTXY = 1; + uint internal constant PTYX = 2; + uint internal constant PTYY = 3; + uint internal constant PTZX = 4; + uint internal constant PTZY = 5; + + // This is the generator negated, to use for pairing + uint256 public constant G2_NEG_X_RE = 0x198E9393920D483A7260BFB731FB5D25F1AA493335A9E71297E485B7AEF312C2; + uint256 public constant G2_NEG_X_IM = 0x1800DEEF121F1E76426A00665E5C4479674322D4F75EDADD46DEBD5CD992F6ED; + uint256 public constant G2_NEG_Y_RE = 0x275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec; + uint256 public constant G2_NEG_Y_IM = 0x1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d; + + /** + * @notice Add two twist points + * @param pt1xx Coefficient 1 of x on point 1 + * @param pt1xy Coefficient 2 of x on point 1 + * @param pt1yx Coefficient 1 of y on point 1 + * @param pt1yy Coefficient 2 of y on point 1 + * @param pt2xx Coefficient 1 of x on point 2 + * @param pt2xy Coefficient 2 of x on point 2 + * @param pt2yx Coefficient 1 of y on point 2 + * @param pt2yy Coefficient 2 of y on point 2 + * @return (pt3xx, pt3xy, pt3yx, pt3yy) + */ + function ecTwistAdd( + uint256 pt1xx, uint256 pt1xy, + uint256 pt1yx, uint256 pt1yy, + uint256 pt2xx, uint256 pt2xy, + uint256 pt2yx, uint256 pt2yy + ) + internal view returns (uint256, uint256, uint256, uint256) + { + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + if (!(pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0)) { + require( + _isOnCurve( + pt2xx, + pt2xy, + pt2yx, + pt2yy + ), + "point not in curve"); + } + return ( + pt2xx, + pt2xy, + pt2yx, + pt2yy); + } else if (pt2xx == 0 && pt2xy == 0 && pt2yx == 0 && pt2yy == 0) { + require( + _isOnCurve( + pt1xx, + pt1xy, + pt1yx, + pt1yy), + "point not in curve"); + return ( + pt1xx, + pt1xy, + pt1yx, + pt1yy); + } + + require( + _isOnCurve( + pt1xx, + pt1xy, + pt1yx, + pt1yy + ), + "point not in curve"); + + require( + _isOnCurve( + pt2xx, + pt2xy, + pt2yx, + pt2yy + ), + "point not in curve"); + + uint256[6] memory pt3 = ecTwistAddJacobian( + pt1xx, + pt1xy, + pt1yx, + pt1yy, + 1, + 0, + pt2xx, + pt2xy, + pt2yx, + pt2yy, + 1, + 0 + ); + + return _fromJacobian( + pt3[PTXX], + pt3[PTXY], + pt3[PTYX], + pt3[PTYY], + pt3[PTZX], + pt3[PTZY] + ); + } + + /** + * @notice Multiply a twist point by a scalar + * @param s Scalar to multiply by + * @param pt1xx Coefficient 1 of x + * @param pt1xy Coefficient 2 of x + * @param pt1yx Coefficient 1 of y + * @param pt1yy Coefficient 2 of y + * @return (pt2xx, pt2xy, pt2yx, pt2yy) + */ + function ecTwistMul( + uint256 s, + uint256 pt1xx, uint256 pt1xy, + uint256 pt1yx, uint256 pt1yy + ) internal view returns (uint256, uint256, uint256, uint256) + { + uint256 pt1zx = 1; + if (pt1xx == 0 && pt1xy == 0 && pt1yx == 0 && pt1yy == 0) { + pt1xx = 1; + pt1yx = 1; + pt1zx = 0; + } else { + require( + _isOnCurve( + pt1xx, + pt1xy, + pt1yx, + pt1yy + ),"point not in curve"); + } + uint256[6] memory pt2 = _ecTwistMulJacobian( + s, + pt1xx, pt1xy, + pt1yx, pt1yy, + pt1zx, 0 + ); + + return _fromJacobian( + pt2[PTXX], pt2[PTXY], + pt2[PTYX], pt2[PTYY], + pt2[PTZX], pt2[PTZY] + ); + } + + /** + * @notice Get the field modulus + * @return The field modulus + */ + function getFieldModulus() external pure returns (uint256) { + return FIELD_MODULUS; + } + + /** + * @notice a-b mod n + * @param a First operand + * @param b Second operand + * @param n modulus + * @return The result of the operation + */ + function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) { + return addmod(a, n - b, n); + } + + /** + * @notice FQ2*FQ2 multiplication operation + * @param xx First FQ2 operands first coordinate + * @param xy First FQ2 operands second coordinate + * @param yx Second FQ2 operands first coordinate + * @param yy Second FQ2 operands second coordinate + * @return [xx*yx-xy*yy, xx*yy+xy*yx] + */ + function _fq2mul( + uint256 xx, uint256 xy, + uint256 yx, uint256 yy + ) + internal + pure + returns (uint256, uint256) + { + return ( + submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS), + addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS) + ); + } + + /** + * @notice Fq2*k multiplication operation + * @param xx FQ2 operands first coordinate + * @param xy FQ2 operands second coordinate + * @param k scalar to multiply with + * @return [xx*k, xy*k] + */ + function _fq2muc( + uint256 xx, uint256 xy, + uint256 k + ) + internal + pure + returns (uint256, uint256) + { + return ( + mulmod(xx, k, FIELD_MODULUS), + mulmod(xy, k, FIELD_MODULUS) + ); + } + + /** + * @notice FQ2+FQ2 addition operation + * @param xx First FQ2 operands first coordinate + * @param xy First FQ2 operands second coordinate + * @param yx Second FQ2 operands first coordinate + * @param yy Second FQ2 operands second coordinate + * @return [xx+yx, xy+yy]f + */ + function _fq2add( + uint256 xx, uint256 xy, + uint256 yx, uint256 yy + ) + internal + pure + returns(uint256, uint256) + { + return ( + addmod(xx, yx, FIELD_MODULUS), + addmod(xy, yy, FIELD_MODULUS) + ); + } + + /** + * @notice FQ2-FQ2 substraction operation + * @param xx First FQ2 operands first coordinate + * @param xy First FQ2 operands second coordinate + * @param yx Second FQ2 operands first coordinate + * @param yy Second FQ2 operands second coordinate + * @return [xx-yx, xy-yy] + */ + function _fq2sub( + uint256 xx, uint256 xy, + uint256 yx, uint256 yy + ) + internal + pure + returns (uint256, uint256) + { + return ( + submod(xx, yx, FIELD_MODULUS), + submod(xy, yy, FIELD_MODULUS) + ); + } + + /** + * @notice FQ2/FQ2 division operation + * @param xx First FQ2 operands first coordinate + * @param xy First FQ2 operands second coordinate + * @param yx Second FQ2 operands first coordinate + * @param yy Second FQ2 operands second coordinate + * @return [xx, xy] * Inv([yx, yy]) + */ + function _fq2div( + uint256 xx, uint256 xy, + uint256 yx, uint256 yy + ) + internal + view + returns (uint256, uint256) + { + (yx, yy) = _fq2inv(yx, yy); + return _fq2mul( + xx, + xy, + yx, + yy); + } + + /** + * @notice 1/FQ2 inverse operation + * @param x FQ2 operands first coordinate + * @param y FQ2 operands second coordinate + * @return Inv([xx, xy]) + */ + function _fq2inv(uint256 x, uint256 y) internal view returns (uint256, uint256) { + uint256 inv = _modInv(addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), FIELD_MODULUS); + return ( + mulmod(x, inv, FIELD_MODULUS), + FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS) + ); + } + + /** + * @notice Checks if FQ2 is on G2 + * @param xx First FQ2 operands first coordinate + * @param xy First FQ2 operands second coordinate + * @param yx Second FQ2 operands first coordinate + * @param yy Second FQ2 operands second coordinate + * @return True if the FQ2 is on G2 + */ + function _isOnCurve( + uint256 xx, uint256 xy, + uint256 yx, uint256 yy + ) + internal + pure + returns (bool) + { + uint256 yyx; + uint256 yyy; + uint256 xxxx; + uint256 xxxy; + (yyx, yyy) = _fq2mul( + yx, + yy, + yx, + yy); + (xxxx, xxxy) = _fq2mul( + xx, + xy, + xx, + xy); + (xxxx, xxxy) = _fq2mul( + xxxx, + xxxy, + xx, + xy); + (yyx, yyy) = _fq2sub( + yyx, + yyy, + xxxx, + xxxy); + (yyx, yyy) = _fq2sub( + yyx, + yyy, + TWISTBX, + TWISTBY); + return yyx == 0 && yyy == 0; + } + + /** + * @notice Calculates the modular inverse of a over n + * @param a The operand to calcualte the inverse of + * @param n The modulus + * @return result Inv(a)modn + **/ + function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) { + bool success; + assembly { + let freemem := mload(0x40) + mstore(freemem, 0x20) + mstore(add(freemem,0x20), 0x20) + mstore(add(freemem,0x40), 0x20) + mstore(add(freemem,0x60), a) + mstore(add(freemem,0x80), sub(n, 2)) + mstore(add(freemem,0xA0), n) + success := staticcall(sub(gas(), 2000), 5, freemem, 0xC0, freemem, 0x20) + result := mload(freemem) + } + require(success, "error calculating the modular inverse"); + } + + /** + * @notice Converts a point from jacobian to affine + * @param pt1xx First point x real coordinate + * @param pt1xy First point x imaginary coordinate + * @param pt1yx First point y real coordinate + * @param pt1yy First point y imaginary coordinate + * @param pt1zx First point z real coordinate + * @param pt1zy First point z imaginary coordinate + * @return pt2xx (x real affine coordinate) + pt2xy (x imaginary affine coordinate) + pt2yx (y real affine coordinate) + pt1zy (y imaginary affine coordinate) + **/ + function _fromJacobian( + uint256 pt1xx, uint256 pt1xy, + uint256 pt1yx, uint256 pt1yy, + uint256 pt1zx, uint256 pt1zy + ) + internal view returns (uint256, uint256, uint256, uint256) + { + uint256 invzx; + uint256 invzy; + uint256[4] memory pt2; + (invzx, invzy) = _fq2inv(pt1zx, pt1zy); + (pt2[0], pt2[1]) = _fq2mul( + pt1xx, + pt1xy, + invzx, + invzy); + (pt2[2], pt2[3]) = _fq2mul( + pt1yx, + pt1yy, + invzx, + invzy); + return( + pt2[0], + pt2[1], + pt2[2], + pt2[3]); + } + +/** +* @notice Adds two points in jacobian coordinates +* @param pt1xx First point x real coordinate +* @param pt1xy First point x imaginary coordinate +* @param pt1yx First point y real coordinate +* @param pt1yy First point y imaginary coordinate +* @param pt1zx First point z real coordinate +* @param pt1zy First point z imaginary coordinate +* @param pt2xx Second point x real coordinate +* @param pt2xy Second point x imaginary coordinate +* @param pt2yx Second point y real coordinate +* @param pt2yy Second point y imaginary coordinate +* @param pt2zx Second point z real coordinate +* @param pt2zy Second point z imaginary coordinate +* @return pt3 = pt1+pt2 in jacobian +**/ + function ecTwistAddJacobian( + uint256 pt1xx, uint256 pt1xy, + uint256 pt1yx, uint256 pt1yy, + uint256 pt1zx, uint256 pt1zy, + uint256 pt2xx, uint256 pt2xy, + uint256 pt2yx, uint256 pt2yy, + uint256 pt2zx, uint256 pt2zy) + internal + pure + returns (uint256[6] memory pt3) + { + if (pt1zx == 0 && pt1zy == 0) { + ( + pt3[PTXX], pt3[PTXY], + pt3[PTYX], pt3[PTYY], + pt3[PTZX], pt3[PTZY] + ) = ( + pt2xx, pt2xy, + pt2yx, pt2yy, + pt2zx, pt2zy + ); + return pt3; + } else if (pt2zx == 0 && pt2zy == 0) { + ( + pt3[PTXX], pt3[PTXY], + pt3[PTYX], pt3[PTYY], + pt3[PTZX], pt3[PTZY] + ) = ( + pt1xx, pt1xy, + pt1yx, pt1yy, + pt1zx, pt1zy + ); + return pt3; + } + + (pt2yx, pt2yy) = _fq2mul( + pt2yx, + pt2yy, + pt1zx, + pt1zy); // U1 = y2 * z1 + (pt3[PTYX], pt3[PTYY]) = _fq2mul( + pt1yx, + pt1yy, + pt2zx, + pt2zy); // U2 = y1 * z2 + (pt2xx, pt2xy) = _fq2mul( + pt2xx, + pt2xy, + pt1zx, + pt1zy); // V1 = x2 * z1 + (pt3[PTZX], pt3[PTZY]) = _fq2mul( + pt1xx, + pt1xy, + pt2zx, + pt2zy); // V2 = x1 * z2 + + if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) { + if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) { + ( + pt3[PTXX], pt3[PTXY], + pt3[PTYX], pt3[PTYY], + pt3[PTZX], pt3[PTZY] + ) = _ecTwistDoubleJacobian( + pt1xx, + pt1xy, + pt1yx, + pt1yy, + pt1zx, + pt1zy); + return pt3; + } + ( + pt3[PTXX], pt3[PTXY], + pt3[PTYX], pt3[PTYY], + pt3[PTZX], pt3[PTZY] + ) = ( + 1, 0, + 1, 0, + 0, 0 + ); + return pt3; + } + + (pt2zx, pt2zy) = _fq2mul( + pt1zx, + pt1zy, + pt2zx, + pt2zy); // W = z1 * z2 + (pt1xx, pt1xy) = _fq2sub( + pt2yx, + pt2yy, + pt3[PTYX], + pt3[PTYY]); // U = U1 - U2 + (pt1yx, pt1yy) = _fq2sub( + pt2xx, + pt2xy, + pt3[PTZX], + pt3[PTZY]); // V = V1 - V2 + (pt1zx, pt1zy) = _fq2mul( + pt1yx, + pt1yy, + pt1yx, + pt1yy); // V_squared = V * V + (pt2yx, pt2yy) = _fq2mul( + pt1zx, + pt1zy, + pt3[PTZX], + pt3[PTZY]); // V_squared_times_V2 = V_squared * V2 + (pt1zx, pt1zy) = _fq2mul( + pt1zx, + pt1zy, + pt1yx, + pt1yy); // V_cubed = V * V_squared + (pt3[PTZX], pt3[PTZY]) = _fq2mul( + pt1zx, + pt1zy, + pt2zx, + pt2zy); // newz = V_cubed * W + (pt2xx, pt2xy) = _fq2mul( + pt1xx, + pt1xy, + pt1xx, + pt1xy); // U * U + (pt2xx, pt2xy) = _fq2mul( + pt2xx, + pt2xy, + pt2zx, + pt2zy); // U * U * W + (pt2xx, pt2xy) = _fq2sub( + pt2xx, + pt2xy, + pt1zx, + pt1zy); // U * U * W - V_cubed + (pt2zx, pt2zy) = _fq2muc(pt2yx, pt2yy, 2); // 2 * V_squared_times_V2 + (pt2xx, pt2xy) = _fq2sub( + pt2xx, + pt2xy, + pt2zx, + pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2 + (pt3[PTXX], pt3[PTXY]) = _fq2mul( + pt1yx, + pt1yy, + pt2xx, + pt2xy); // newx = V * A + (pt1yx, pt1yy) = _fq2sub( + pt2yx, + pt2yy, + pt2xx, + pt2xy); // V_squared_times_V2 - A + (pt1yx, pt1yy) = _fq2mul( + pt1xx, + pt1xy, + pt1yx, + pt1yy); // U * (V_squared_times_V2 - A) + (pt1xx, pt1xy) = _fq2mul( + pt1zx, + pt1zy, + pt3[PTYX], + pt3[PTYY]); // V_cubed * U2 + (pt3[PTYX], pt3[PTYY]) = _fq2sub( + pt1yx, + pt1yy, + pt1xx, + pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2 + } + + /** + * @notice Doubls a point in jacobian coordinates + * @param pt1xx Point x real coordinate + * @param pt1xy Point x imaginary coordinate + * @param pt1yx Point y real coordinate + * @param pt1yy Point y imaginary coordinate + * @param pt1zx Point z real coordinate + * @param pt1zy Point z imaginary coordinate + * @return pt2xx, pt2xy, pt2yx, pt2yy, pt2zx, pt2zy the coordinates of pt2 = 2*pt1 + **/ + function _ecTwistDoubleJacobian( + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) + internal + pure + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + uint256[6] memory pt2; + (pt2[0], pt2[1]) = _fq2muc(pt1xx, pt1xy, 3); // 3 * x + (pt2[0], pt2[1]) = _fq2mul( + pt2[0], + pt2[1], + pt1xx, + pt1xy); // W = 3 * x * x + (pt1zx, pt1zy) = _fq2mul( + pt1yx, + pt1yy, + pt1zx, + pt1zy); // S = y * z + (pt2[2], pt2[3]) = _fq2mul( + pt1xx, + pt1xy, + pt1yx, + pt1yy); // x * y + (pt2[2], pt2[3]) = _fq2mul( + pt2[2], + pt2[3], + pt1zx, + pt1zy); // B = x * y * S + (pt1xx, pt1xy) = _fq2mul( + pt2[0], + pt2[1], + pt2[0], + pt2[1]); // W * W + (pt2[4], pt2[5]) = _fq2muc(pt2[2], pt2[3], 8); // 8 * B + (pt1xx, pt1xy) = _fq2sub( + pt1xx, + pt1xy, + pt2[4], + pt2[5]); // H = W * W - 8 * B + (pt2[4], pt2[5]) = _fq2mul( + pt1zx, + pt1zy, + pt1zx, + pt1zy); // S_squared = S * S + (pt2[2], pt2[3]) = _fq2muc(pt2[2], pt2[3], 4); // 4 * B + (pt2[2], pt2[3]) = _fq2sub( + pt2[2], + pt2[3], + pt1xx, + pt1xy); // 4 * B - H + (pt2[2], pt2[3]) = _fq2mul( + pt2[2], + pt2[3], + pt2[0], + pt2[1]); // W * (4 * B - H) + (pt2[0], pt2[1]) = _fq2muc(pt1yx, pt1yy, 8); // 8 * y + (pt2[0], pt2[1]) = _fq2mul( + pt2[0], + pt2[1], + pt1yx, + pt1yy); // 8 * y * y + (pt2[0], pt2[1]) = _fq2mul( + pt2[0], + pt2[1], + pt2[4], + pt2[5]); // 8 * y * y * S_squared + (pt2[2], pt2[3]) = _fq2sub( + pt2[2], + pt2[3], + pt2[0], + pt2[1]); // newy = W * (4 * B - H) - 8 * y * y * S_squared + (pt2[0], pt2[1]) = _fq2muc(pt1xx, pt1xy, 2); // 2 * H + (pt2[0], pt2[1]) = _fq2mul( + pt2[0], + pt2[1], + pt1zx, + pt1zy); // newx = 2 * H * S + (pt2[4], pt2[5]) = _fq2mul( + pt1zx, + pt1zy, + pt2[4], + pt2[5]); // S * S_squared + (pt2[4], pt2[5]) = _fq2muc(pt2[4], pt2[5], 8); // newz = 8 * S * S_squared + + return (pt2[0], pt2[1], pt2[2], pt2[3], pt2[4], pt2[5]); + } + + /** + * @notice Doubls a point in jacobian coordinates + * @param d scalar to multiply the point with + * @param pt1xx Point x real coordinate + * @param pt1xy Point x imaginary coordinate + * @param pt1yx Point y real coordinate + * @param pt1yy Point y imaginary coordinate + * @param pt1zx Point z real coordinate + * @param pt1zy Point z imaginary coordinate + * @return a point representing pt2 = d*pt1 in jacobian coordinates + **/ + function _ecTwistMulJacobian( + uint256 d, + uint256 pt1xx, + uint256 pt1xy, + uint256 pt1yx, + uint256 pt1yy, + uint256 pt1zx, + uint256 pt1zy + ) internal pure returns (uint256[6] memory) + { + uint[6] memory pt2; + while (d != 0) { + if ((d & 1) != 0) { + pt2 = ecTwistAddJacobian( + pt2[PTXX], + pt2[PTXY], + pt2[PTYX], + pt2[PTYY], + pt2[PTZX], + pt2[PTZY], + pt1xx, + pt1xy, + pt1yx, + pt1yy, + pt1zx, + pt1zy + ); + } + (pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy) = _ecTwistDoubleJacobian( + pt1xx, + pt1xy, + pt1yx, + pt1yy, + pt1zx, + pt1zy + ); + + d = d / 2; + } + return pt2; + } +} \ No newline at end of file diff --git a/contracts/ecc/vendor/witnet/elliptic-curve-solidity/contracts/EllipticCurve.sol b/contracts/ecc/vendor/witnet/elliptic-curve-solidity/contracts/EllipticCurve.sol new file mode 100644 index 0000000..8ec8d14 --- /dev/null +++ b/contracts/ecc/vendor/witnet/elliptic-curve-solidity/contracts/EllipticCurve.sol @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: MIT +// taken from: https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol +pragma solidity >=0.8.0 <=0.8.13; + + +/** + * @title Elliptic Curve Library + * @dev Library providing arithmetic operations over elliptic curves. + * This library does not check whether the inserted points belong to the curve + * `isOnCurve` function should be used by the library user to check the aforementioned statement. + * @author Witnet Foundation + */ +library EllipticCurve { + + // Pre-computed constant for 2 ** 255 + uint256 constant private U255_MAX_PLUS_1 = 57896044618658097711785492504343953926634992332820282019728792003956564819968; + + /// @dev Modular euclidean inverse of a number (mod p). + /// @param _x The number + /// @param _pp The modulus + /// @return q such that x*q = 1 (mod _pp) + function invMod(uint256 _x, uint256 _pp) internal pure returns (uint256) { + require(_x != 0 && _x != _pp && _pp != 0, "Invalid number"); + uint256 q = 0; + uint256 newT = 1; + uint256 r = _pp; + uint256 t; + while (_x != 0) { + t = r / _x; + (q, newT) = (newT, addmod(q, (_pp - mulmod(t, newT, _pp)), _pp)); + (r, _x) = (_x, r - t * _x); + } + + return q; + } + + /// @dev Modular exponentiation, b^e % _pp. + /// Source: https://github.com/androlo/standard-contracts/blob/master/contracts/src/crypto/ECCMath.sol + /// @param _base base + /// @param _exp exponent + /// @param _pp modulus + /// @return r such that r = b**e (mod _pp) + function expMod(uint256 _base, uint256 _exp, uint256 _pp) internal pure returns (uint256) { + require(_pp!=0, "Modulus is zero"); + + if (_base == 0) + return 0; + if (_exp == 0) + return 1; + + uint256 r = 1; + uint256 bit = U255_MAX_PLUS_1; + assembly { + for { } gt(bit, 0) { }{ + r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, bit)))), _pp) + r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 2))))), _pp) + r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 4))))), _pp) + r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 8))))), _pp) + bit := div(bit, 16) + } + } + + return r; + } + + /// @dev Converts a point (x, y, z) expressed in Jacobian coordinates to affine coordinates (x', y', 1). + /// @param _x coordinate x + /// @param _y coordinate y + /// @param _z coordinate z + /// @param _pp the modulus + /// @return (x', y') affine coordinates + function toAffine( + uint256 _x, + uint256 _y, + uint256 _z, + uint256 _pp) + internal pure returns (uint256, uint256) + { + uint256 zInv = invMod(_z, _pp); + uint256 zInv2 = mulmod(zInv, zInv, _pp); + uint256 x2 = mulmod(_x, zInv2, _pp); + uint256 y2 = mulmod(_y, mulmod(zInv, zInv2, _pp), _pp); + + return (x2, y2); + } + + /// @dev Derives the y coordinate from a compressed-format point x [[SEC-1]](https://www.secg.org/SEC1-Ver-1.0.pdf). + /// @param _prefix parity byte (0x02 even, 0x03 odd) + /// @param _x coordinate x + /// @param _aa constant of curve + /// @param _bb constant of curve + /// @param _pp the modulus + /// @return y coordinate y + function deriveY( + uint8 _prefix, + uint256 _x, + uint256 _aa, + uint256 _bb, + uint256 _pp) + internal pure returns (uint256) + { + require(_prefix == 0x02 || _prefix == 0x03, "Invalid compressed EC point prefix"); + + // x^3 + ax + b + uint256 y2 = addmod(mulmod(_x, mulmod(_x, _x, _pp), _pp), addmod(mulmod(_x, _aa, _pp), _bb, _pp), _pp); + y2 = expMod(y2, (_pp + 1) / 4, _pp); + // uint256 cmp = yBit ^ y_ & 1; + uint256 y = (y2 + _prefix) % 2 == 0 ? y2 : _pp - y2; + + return y; + } + + /// @dev Check whether point (x,y) is on curve defined by a, b, and _pp. + /// @param _x coordinate x of P1 + /// @param _y coordinate y of P1 + /// @param _aa constant of curve + /// @param _bb constant of curve + /// @param _pp the modulus + /// @return true if x,y in the curve, false else + function isOnCurve( + uint _x, + uint _y, + uint _aa, + uint _bb, + uint _pp) + internal pure returns (bool) + { + if (0 == _x || _x >= _pp || 0 == _y || _y >= _pp) { + return false; + } + // y^2 + uint lhs = mulmod(_y, _y, _pp); + // x^3 + uint rhs = mulmod(mulmod(_x, _x, _pp), _x, _pp); + if (_aa != 0) { + // x^3 + a*x + rhs = addmod(rhs, mulmod(_x, _aa, _pp), _pp); + } + if (_bb != 0) { + // x^3 + a*x + b + rhs = addmod(rhs, _bb, _pp); + } + + return lhs == rhs; + } + + /// @dev Calculate inverse (x, -y) of point (x, y). + /// @param _x coordinate x of P1 + /// @param _y coordinate y of P1 + /// @param _pp the modulus + /// @return (x, -y) + function ecInv( + uint256 _x, + uint256 _y, + uint256 _pp) + internal pure returns (uint256, uint256) + { + return (_x, (_pp - _y) % _pp); + } + + /// @dev Add two points (x1, y1) and (x2, y2) in affine coordinates. + /// @param _x1 coordinate x of P1 + /// @param _y1 coordinate y of P1 + /// @param _x2 coordinate x of P2 + /// @param _y2 coordinate y of P2 + /// @param _aa constant of the curve + /// @param _pp the modulus + /// @return (qx, qy) = P1+P2 in affine coordinates + function ecAdd( + uint256 _x1, + uint256 _y1, + uint256 _x2, + uint256 _y2, + uint256 _aa, + uint256 _pp) + internal pure returns(uint256, uint256) + { + uint x = 0; + uint y = 0; + uint z = 0; + + // Double if x1==x2 else add + if (_x1==_x2) { + // y1 = -y2 mod p + if (addmod(_y1, _y2, _pp) == 0) { + return(0, 0); + } else { + // P1 = P2 + (x, y, z) = jacDouble( + _x1, + _y1, + 1, + _aa, + _pp); + } + } else { + (x, y, z) = jacAdd( + _x1, + _y1, + 1, + _x2, + _y2, + 1, + _pp); + } + // Get back to affine + return toAffine( + x, + y, + z, + _pp); + } + + /// @dev Substract two points (x1, y1) and (x2, y2) in affine coordinates. + /// @param _x1 coordinate x of P1 + /// @param _y1 coordinate y of P1 + /// @param _x2 coordinate x of P2 + /// @param _y2 coordinate y of P2 + /// @param _aa constant of the curve + /// @param _pp the modulus + /// @return (qx, qy) = P1-P2 in affine coordinates + function ecSub( + uint256 _x1, + uint256 _y1, + uint256 _x2, + uint256 _y2, + uint256 _aa, + uint256 _pp) + internal pure returns(uint256, uint256) + { + // invert square + (uint256 x, uint256 y) = ecInv(_x2, _y2, _pp); + // P1-square + return ecAdd( + _x1, + _y1, + x, + y, + _aa, + _pp); + } + + /// @dev Multiply point (x1, y1, z1) times d in affine coordinates. + /// @param _k scalar to multiply + /// @param _x coordinate x of P1 + /// @param _y coordinate y of P1 + /// @param _aa constant of the curve + /// @param _pp the modulus + /// @return (qx, qy) = d*P in affine coordinates + function ecMul( + uint256 _k, + uint256 _x, + uint256 _y, + uint256 _aa, + uint256 _pp) + internal pure returns(uint256, uint256) + { + // Jacobian multiplication + (uint256 x1, uint256 y1, uint256 z1) = jacMul( + _k, + _x, + _y, + 1, + _aa, + _pp); + // Get back to affine + return toAffine( + x1, + y1, + z1, + _pp); + } + + /// @dev Adds two points (x1, y1, z1) and (x2 y2, z2). + /// @param _x1 coordinate x of P1 + /// @param _y1 coordinate y of P1 + /// @param _z1 coordinate z of P1 + /// @param _x2 coordinate x of square + /// @param _y2 coordinate y of square + /// @param _z2 coordinate z of square + /// @param _pp the modulus + /// @return (qx, qy, qz) P1+square in Jacobian + function jacAdd( + uint256 _x1, + uint256 _y1, + uint256 _z1, + uint256 _x2, + uint256 _y2, + uint256 _z2, + uint256 _pp) + internal pure returns (uint256, uint256, uint256) + { + if (_x1==0 && _y1==0) + return (_x2, _y2, _z2); + if (_x2==0 && _y2==0) + return (_x1, _y1, _z1); + + // We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5 + uint[4] memory zs; // z1^2, z1^3, z2^2, z2^3 + zs[0] = mulmod(_z1, _z1, _pp); + zs[1] = mulmod(_z1, zs[0], _pp); + zs[2] = mulmod(_z2, _z2, _pp); + zs[3] = mulmod(_z2, zs[2], _pp); + + // u1, s1, u2, s2 + zs = [ + mulmod(_x1, zs[2], _pp), + mulmod(_y1, zs[3], _pp), + mulmod(_x2, zs[0], _pp), + mulmod(_y2, zs[1], _pp) + ]; + + // In case of zs[0] == zs[2] && zs[1] == zs[3], double function should be used + require(zs[0] != zs[2] || zs[1] != zs[3], "Use jacDouble function instead"); + + uint[4] memory hr; + //h + hr[0] = addmod(zs[2], _pp - zs[0], _pp); + //r + hr[1] = addmod(zs[3], _pp - zs[1], _pp); + //h^2 + hr[2] = mulmod(hr[0], hr[0], _pp); + // h^3 + hr[3] = mulmod(hr[2], hr[0], _pp); + // qx = -h^3 -2u1h^2+r^2 + uint256 qx = addmod(mulmod(hr[1], hr[1], _pp), _pp - hr[3], _pp); + qx = addmod(qx, _pp - mulmod(2, mulmod(zs[0], hr[2], _pp), _pp), _pp); + // qy = -s1*z1*h^3+r(u1*h^2 -x^3) + uint256 qy = mulmod(hr[1], addmod(mulmod(zs[0], hr[2], _pp), _pp - qx, _pp), _pp); + qy = addmod(qy, _pp - mulmod(zs[1], hr[3], _pp), _pp); + // qz = h*z1*z2 + uint256 qz = mulmod(hr[0], mulmod(_z1, _z2, _pp), _pp); + return(qx, qy, qz); + } + + /// @dev Doubles a points (x, y, z). + /// @param _x coordinate x of P1 + /// @param _y coordinate y of P1 + /// @param _z coordinate z of P1 + /// @param _aa the a scalar in the curve equation + /// @param _pp the modulus + /// @return (qx, qy, qz) 2P in Jacobian + function jacDouble( + uint256 _x, + uint256 _y, + uint256 _z, + uint256 _aa, + uint256 _pp) + internal pure returns (uint256, uint256, uint256) + { + if (_z == 0) + return (_x, _y, _z); + + // We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5 + // Note: there is a bug in the paper regarding the m parameter, M=3*(x1^2)+a*(z1^4) + // x, y, z at this point represent the squares of _x, _y, _z + uint256 x = mulmod(_x, _x, _pp); //x1^2 + uint256 y = mulmod(_y, _y, _pp); //y1^2 + uint256 z = mulmod(_z, _z, _pp); //z1^2 + + // s + uint s = mulmod(4, mulmod(_x, y, _pp), _pp); + // m + uint m = addmod(mulmod(3, x, _pp), mulmod(_aa, mulmod(z, z, _pp), _pp), _pp); + + // x, y, z at this point will be reassigned and rather represent qx, qy, qz from the paper + // This allows to reduce the gas cost and stack footprint of the algorithm + // qx + x = addmod(mulmod(m, m, _pp), _pp - addmod(s, s, _pp), _pp); + // qy = -8*y1^4 + M(S-T) + y = addmod(mulmod(m, addmod(s, _pp - x, _pp), _pp), _pp - mulmod(8, mulmod(y, y, _pp), _pp), _pp); + // qz = 2*y1*z1 + z = mulmod(2, mulmod(_y, _z, _pp), _pp); + + return (x, y, z); + } + + /// @dev Multiply point (x, y, z) times d. + /// @param _d scalar to multiply + /// @param _x coordinate x of P1 + /// @param _y coordinate y of P1 + /// @param _z coordinate z of P1 + /// @param _aa constant of curve + /// @param _pp the modulus + /// @return (qx, qy, qz) d*P1 in Jacobian + function jacMul( + uint256 _d, + uint256 _x, + uint256 _y, + uint256 _z, + uint256 _aa, + uint256 _pp) + internal pure returns (uint256, uint256, uint256) + { + // Early return in case that `_d == 0` + if (_d == 0) { + return (_x, _y, _z); + } + + uint256 remaining = _d; + uint256 qx = 0; + uint256 qy = 0; + uint256 qz = 1; + + // Double and add algorithm + while (remaining != 0) { + if ((remaining & 1) != 0) { + (qx, qy, qz) = jacAdd( + qx, + qy, + qz, + _x, + _y, + _z, + _pp); + } + remaining = remaining / 2; + (_x, _y, _z) = jacDouble( + _x, + _y, + _z, + _aa, + _pp); + } + return (qx, qy, qz); + } +} diff --git a/contracts/ecc/verifiers/Bn254Verifier.sol b/contracts/ecc/verifiers/Bn254Verifier.sol deleted file mode 100644 index e334c35..0000000 --- a/contracts/ecc/verifiers/Bn254Verifier.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.0 <=0.8.13; - -import "../curves/Bn254.sol"; -import "../Verifier.sol"; - -library Bn254Verifier { - - - function _verifyProof(Verifier.Proof memory proof) internal view returns (bool) { - // var first: blst_p1 - // for qelem in q : - // var prod: blst_p1 - // prod.blst_p1_mult(hashNameI(tau.t.name, qelem.I), qelem.V, 255) - // first.blst_p1_add_or_double(first, prod) - // doAssert(blst_p1_on_curve(first).bool) - Curve.G1Point memory first; - for (uint256 i = 0; i