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
This commit is contained in:
Eric Mastro 2022-06-08 15:16:08 +10:00
parent 32ff556691
commit c0be4e9b1d
No known key found for this signature in database
GPG Key ID: 141E3048D95A4E63
15 changed files with 1797 additions and 567 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
// }
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

40
contracts/ecc/Types.sol Normal file
View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<proof.q.length; i++) {
Types.QElement memory qelem = proof.q[i];
bytes32 namei = sha256(abi.encodePacked(proof.name, qelem.i));
// Step 4: arbitraty string to point and check if it is on curve
// uint256 hPointX = abi.encodePacked(namei);
Types.G1Point memory h = _hashToPoint(abi.encodePacked(namei));
// TODO: Where does 255 get used???
Types.G1Point memory prod = _multiply(h, uint256(qelem.v));
first = _add(first, prod);
require(_isOnCurve(first), "must be on Bn254 curve");
}
// y^2
uint256 lhs = mulmod(g1.Y, g1.Y, pp);
// x^3
uint256 rhs = mulmod(mulmod(g1.X, g1.X, pp), g1.X, pp);
if (aa != 0) {
// x^3 + a*x
rhs = addmod(rhs, mulmod(g1.X, aa, pp), pp);
}
if (bb != 0) {
// x^3 + a*x + b
rhs = addmod(rhs, bb, pp);
// let us = tau.t.u
// var second: blst_p1
// for j in 0 ..< len(us) :
// var prod: blst_p1
// prod.blst_p1_mult(us[j], mus[j], 255)
// second.blst_p1_add_or_double(second, prod)
// doAssert(blst_p1_on_curve(second).bool)
Types.G1Point[] memory us = proof.u;
Types.G1Point memory second;
for (uint256 j = 0; j<us.length; j++) {
// TODO: Where does 255 get used???
Types.G1Point memory prod = _multiply(us[j], proof.mus[j]);
second = _add(second, prod);
require(_isOnCurve(second), "must be on Bn254 curve");
}
return lhs == rhs;
// var sum: blst_p1
// sum.blst_p1_add_or_double(first, second)
Types.G1Point memory sum = _add(first, second);
// var g{.noInit.}: blst_p2
// g.blst_p2_from_affine(BLS12_381_G2)
// TODO: do we need to convert Bn254._p2Generator() to/from affine???
// return verifyPairings(sum, spk.key, sigma, g)
return Bn254._checkPairing(sum, proof.publicKey, proof.sigma, Bn254._p2Generator());
}
}

View File

@ -0,0 +1,218 @@
// SPDX-License-Identifier: MIT
pragma solidity >=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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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<proof.q.length; i++) {
Verifier.QElement memory qelem = proof.q[i];
bytes32 namei = sha256(abi.encodePacked(proof.name, qelem.i));
// Step 4: arbitraty string to point and check if it is on curve
uint256 hPointX = uint256(namei);
Curve.G1Point memory h = Bn254.HashToPoint(hPointX);
// TODO: Where does 255 get used???
Curve.G1Point memory prod = Bn254.g1mul(h, uint256(qelem.v));
first = Bn254.g1add(first, prod);
require(Bn254.isOnCurve(first), "must be on Bn254 curve");
}
// let us = tau.t.u
// var second: blst_p1
// for j in 0 ..< len(us) :
// var prod: blst_p1
// prod.blst_p1_mult(us[j], mus[j], 255)
// second.blst_p1_add_or_double(second, prod)
// doAssert(blst_p1_on_curve(second).bool)
Curve.G1Point[] memory us = proof.u;
Curve.G1Point memory second;
for (uint256 j = 0; j<us.length; j++) {
// TODO: Where does 255 get used???
Curve.G1Point memory prod = Bn254.g1mul(us[j], proof.mus[j]);
second = Bn254.g1add(second, prod);
require(Bn254.isOnCurve(second), "must be on Bn254 curve");
}
// var sum: blst_p1
// sum.blst_p1_add_or_double(first, second)
Curve.G1Point memory sum = Bn254.g1add(first, second);
// var g{.noInit.}: blst_p2
// g.blst_p2_from_affine(BLS12_381_G2)
// TODO: do we need to convert Bn254.P2() to/from affine???
// return verifyPairings(sum, spk.key, sigma, g)
return Bn254.pairingProd2(sum, proof.publicKey, proof.sigma, Bn254.P2());
}
}

View File

@ -22,6 +22,9 @@ describe("Bn254", function () {
})
it("explicit sum and scalar prod are the same", async function () {
let fRes = await bn254.f()
console.log("f result: ")
console.log(JSON.stringify(fRes, null, 2))
expect(await bn254.f()).to.be.true
})
@ -29,13 +32,38 @@ describe("Bn254", function () {
expect(await bn254.g()).to.be.true
})
it("points should be paired correctly", async function () {
expect(await bn254.pair()).to.be.true
it("fails when first point is not on Bn254 curve", async function () {
let proof = {
q: [
{ i: -1, v: 1 },
{ i: -2, v: 2 },
{ i: -3, v: 3 },
],
mus: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
sigma: { x: 1, y: 2 },
u: [
{ x: 1, y: 2 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
],
name: ethers.utils.toUtf8Bytes("test"),
publicKey: {
x: [1, 2],
y: [1, 2],
},
}
await expect(bn254.verifyProof(proof)).to.be.revertedWith(
"elliptic curve multiplication failed"
)
})
it("can verify proof", async function () {
let result = await bn254.verifyTx()
console.log("verify result: " + JSON.stringify(result, null, 2))
expect(await bn254.verifyTx()).to.be.true
})
// it("points should be paired correctly", async function () {
// expect(await bn254.pair()).to.be.true
// })
// it("can verify proof", async function () {
// let result = await bn254.verifyTx()
// console.log("verify result: " + JSON.stringify(result, null, 2))
// expect(await bn254.verifyTx()).to.be.true
// })
})

View File

@ -29,20 +29,20 @@ describe("Bn254Verifier", function () {
{ i: -3, v: 3 },
],
mus: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
sigma: { X: 1, Y: 2 },
sigma: { x: 1, y: 2 },
u: [
{ X: 1, Y: 2 },
{ X: 2, Y: 2 },
{ X: 3, Y: 3 },
{ x: 1, y: 2 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
],
name: ethers.utils.toUtf8Bytes("test"),
publicKey: {
X: [1, 2],
Y: [1, 2],
x: [1, 2],
y: [1, 2],
},
}
await expect(verifier.verifyProof(proof)).to.be.revertedWith(
"must be on Bn254 curve"
"elliptic curve multiplication failed"
)
})
})