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 // SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <=0.8.13; pragma solidity >=0.8.0 <=0.8.13;
import "./ecc/verifiers/Bn254Verifier.sol"; import "./ecc/curves/Bn254.sol";
import "./ecc/Verifier.sol"; import "./ecc/Types.sol";
contract Proofs { contract Proofs {
using Bn254Verifier for Verifier.Proof; using Bn254 for Types.Proof;
uint256 private immutable period; uint256 private immutable period;
uint256 private immutable timeout; uint256 private immutable timeout;
@ -134,7 +134,7 @@ contract Proofs {
return _isProofRequired(id, currentPeriod()); 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(proof._verifyProof(), "Invalid proof");
require(!received[id][currentPeriod()], "Proof already submitted"); require(!received[id][currentPeriod()], "Proof already submitted");
received[id][currentPeriod()] = true; received[id][currentPeriod()] = true;

View File

@ -86,7 +86,7 @@ contract Storage is Collateral, Marketplace, Proofs {
return _getPointer(id); return _getPointer(id);
} }
function submitProof(bytes32 contractId, Verifier.Proof calldata proof) public { function submitProof(bytes32 contractId, Types.Proof calldata proof) public {
_submitProof(contractId, proof); _submitProof(contractId, proof);
} }

View File

@ -3,147 +3,150 @@
pragma solidity >=0.8.0 <=0.8.13; pragma solidity >=0.8.0 <=0.8.13;
import "./ecc/curves/Bn254.sol"; import "./ecc/curves/Bn254.sol";
import "./ecc/Curve.sol"; import "./ecc/Types.sol";
contract TestBn254 { contract TestBn254 {
using Bn254 for *; using Bn254 for *;
struct VerifyingKey { struct VerifyingKey {
Curve.G2Point A; Types.G2Point A;
Curve.G1Point B; Types.G1Point B;
Curve.G2Point C; Types.G2Point C;
Curve.G2Point gamma; Types.G2Point gamma;
Curve.G1Point gammaBeta1; Types.G1Point gammaBeta1;
Curve.G2Point gammaBeta2; Types.G2Point gammaBeta2;
Curve.G2Point Z; Types.G2Point Z;
Curve.G1Point[] IC; Types.G1Point[] IC;
} }
struct Proof { struct Proof {
Curve.G1Point A; Types.G1Point A;
Curve.G1Point A_p; Types.G1Point A_p;
Curve.G2Point B; Types.G2Point B;
Curve.G1Point B_p; Types.G1Point B_p;
Curve.G1Point C; Types.G1Point C;
Curve.G1Point C_p; Types.G1Point C_p;
Curve.G1Point K; Types.G1Point K;
Curve.G1Point H; Types.G1Point H;
} }
function f() public view returns (bool) { function f() public returns (bool) {
Curve.G1Point memory p1; Types.G1Point memory p1;
Curve.G1Point memory p2; Types.G1Point memory p2;
p1.X = 1; p1.Y = 2; p1.x = 1; p1.y = 2;
p2.X = 1; p2.Y = 2; p2.x = 1; p2.y = 2;
Curve.G1Point memory explict_sum = Bn254.g1add(p1, p2); Types.G1Point memory explict_sum = Bn254._add(p1, p2);
Curve.G1Point memory scalar_prod = Bn254.g1mul(p1, 2); Types.G1Point memory scalar_prod = Bn254._multiply(p1, 2);
return (explict_sum.X == scalar_prod.X && return (explict_sum.x == scalar_prod.x &&
explict_sum.Y == scalar_prod.Y); explict_sum.y == scalar_prod.y);
} }
function g() public view returns (bool) { function g() public returns (bool) {
Curve.G1Point memory x = Bn254.g1add(Bn254.P1(), Bn254.g1neg(Bn254.P1())); Types.G1Point memory x = Bn254._add(Bn254._p1Generator(), Bn254._negate(Bn254._p1Generator()));
// should be zero // should be zero
return (x.X == 0 && x.Y == 0); return (x.x == 0 && x.y == 0);
} }
function testMul() public view returns (bool) { function testMul() public returns (bool) {
Curve.G1Point memory p; Types.G1Point memory p;
// @TODO The points here are reported to be not well-formed // @TODO The points here are reported to be not well-formed
p.X = 14125296762497065001182820090155008161146766663259912659363835465243039841726; p.x = 14125296762497065001182820090155008161146766663259912659363835465243039841726;
p.Y = 16229134936871442251132173501211935676986397196799085184804749187146857848057; p.y = 16229134936871442251132173501211935676986397196799085184804749187146857848057;
p = Bn254.g1mul(p, 13986731495506593864492662381614386532349950841221768152838255933892789078521); p = Bn254._multiply(p, 13986731495506593864492662381614386532349950841221768152838255933892789078521);
return return
p.X == 18256332256630856740336504687838346961237861778318632856900758565550522381207 && p.x == 18256332256630856740336504687838346961237861778318632856900758565550522381207 &&
p.Y == 6976682127058094634733239494758371323697222088503263230319702770853579280803; p.y == 6976682127058094634733239494758371323697222088503263230319702770853579280803;
} }
function pair() public view returns (bool) { function verifyProof(Types.Proof memory p) public returns (bool) {
Curve.G2Point memory fiveTimesP2 = Curve.G2Point( return p._verifyProof();
[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 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) { // function verifyTx() public view returns (bool r) {
uint[] memory input = new uint[](9); // uint[] memory input = new uint[](9);
Proof memory proof; // Proof memory proof;
proof.A = Curve.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497); // proof.A = Types.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497);
proof.A_p = Curve.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366); // proof.A_p = Types.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366);
proof.B = Curve.G2Point( // proof.B = Types.G2Point(
[8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055], // [8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055],
[15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]); // [15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]);
proof.B_p = Curve.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950); // proof.B_p = Types.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950);
proof.C = Curve.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506); // proof.C = Types.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506);
proof.C_p = Curve.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686); // proof.C_p = Types.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686);
proof.H = Curve.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758); // proof.H = Types.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758);
proof.K = Curve.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170); // proof.K = Types.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170);
input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521; // input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521;
input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053; // input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053;
input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569; // input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569;
input[3] = 6627643779954497813586310325594578844876646808666478625705401786271515864467; // input[3] = 6627643779954497813586310325594578844876646808666478625705401786271515864467;
input[4] = 2957286918163151606545409668133310005545945782087581890025685458369200827463; // input[4] = 2957286918163151606545409668133310005545945782087581890025685458369200827463;
input[5] = 1384290496819542862903939282897996566903332587607290986044945365745128311081; // input[5] = 1384290496819542862903939282897996566903332587607290986044945365745128311081;
input[6] = 5613571677741714971687805233468747950848449704454346829971683826953541367271; // input[6] = 5613571677741714971687805233468747950848449704454346829971683826953541367271;
input[7] = 9643208548031422463313148630985736896287522941726746581856185889848792022807; // input[7] = 9643208548031422463313148630985736896287522941726746581856185889848792022807;
input[8] = 18066496933330839731877828156604; // input[8] = 18066496933330839731877828156604;
return _verify(input, proof) == 0; // 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); return _getPointer(id);
} }
function submitProof(bytes32 id, Verifier.Proof calldata proof) public { function submitProof(bytes32 id, Types.Proof calldata proof) public {
_submitProof(id, proof); _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 // 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 { 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 /// @return the generator of G1
function P1() internal pure returns (Curve.G1Point memory) { function _p1Generator() internal pure returns (Types.G1Point memory) {
return Curve.G1Point(1, 2); return Types.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];
} }
/// @return the generator of G2 /// @return the generator of G2
function P2() internal pure returns (Curve.G2Point memory) { function _p2Generator() internal pure returns (Types.G2Point memory) {
return return
Curve.G2Point( Types.G2Point(
[ [
11559732032986387107991004021392285783925812861821192530917403151452391805634, 11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781 10857046999023057135944570762232829481370756359578518086990519993285655852781
@ -150,185 +31,188 @@ library Bn254 {
); );
} }
/// @return the negation of p, i.e. p.add(p.negate()) should be zero. /// @dev computes P + Q
function g1neg(Curve.G1Point memory p) internal pure returns (Curve.G1Point memory) { /// @param p: G1 point p
// The prime q in the base field F_q for G1 /// @param q: G1 point q
uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; /// @return G1 point with x and y coordinates of P+Q.
if (p.X == 0 && p.Y == 0) return Curve.G1Point(0, 0); function _add(Types.G1Point memory p, Types.G1Point memory q)
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)
internal internal
view returns (Types.G1Point memory)
returns (Curve.G1Point memory r)
{ {
uint256[4] memory input; (uint256 x, uint256 y) = BN256G1._add([p.x, p.y, q.x, q.y]);
input[0] = p1.X; return Types.G1Point(x, y);
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);
} }
/// @return r the product of a point on G1 and a scalar, i.e. /// @dev computes P*k.
/// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. /// @param p: G1 point p
function g1mul(Curve.G1Point memory p, uint256 s) /// @param k: scalar k.
/// @return G1 point with x and y coordinates of P*k.
function _multiply(Types.G1Point memory p, uint256 k)
internal internal
view returns (Types.G1Point memory)
returns (Curve.G1Point memory r)
{ {
uint256[3] memory input; (uint256 x, uint256 y) = BN256G1._multiply([p.x, p.y, k]);
input[0] = p.X; return Types.G1Point(x, y);
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);
} }
/// @return the result of computing the pairing check
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 /// @dev Check whether point (x,y) is on curve BN254.
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should /// @param p1 G1 point
/// return true. /// @return true if x,y in the curve, false else
function pairing(Curve.G1Point[] memory p1, Curve.G2Point[] memory p2) function _isOnCurve(Types.G1Point memory p1)
internal internal
view pure
returns (bool) returns (bool)
{ {
require(p1.length == p2.length); return BN256G1._isOnCurve([p1.x, p1.y]);
uint256 elements = p1.length; }
uint256 inputSize = elements * 6; /**
uint256[] memory input = new uint256[](inputSize); * @notice Checks if FQ2 is on G2
for (uint256 i = 0; i < elements; i++) { * @param p1 G2 Point
input[i * 6 + 0] = p1[i].X; * @return True if the FQ2 is on G2
input[i * 6 + 1] = p1[i].Y; */
input[i * 6 + 2] = p2[i].X[0]; function _isOnCurve(Types.G2Point memory p1)
input[i * 6 + 3] = p2[i].X[1]; internal
input[i * 6 + 4] = p2[i].Y[0]; pure
input[i * 6 + 5] = p2[i].Y[1]; returns (bool)
} {
uint256[1] memory out; return BN256G2._isOnCurve(p1.x[0], p1.y[0], p1.x[1], p1.y[1]);
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;
} }
/// Convenience method for a pairing check for two pairs. /// @dev Derives the y coordinate from a compressed-format point x [[SEC-1]](https://www.secg.org/SEC1-Ver-1.0.pdf).
function pairingProd2( /// @param prefix parity byte (0x02 even, 0x03 odd)
Curve.G1Point memory a1, /// @param x coordinate x
Curve.G2Point memory a2, /// @return y coordinate y
Curve.G1Point memory b1, function _deriveY(uint8 prefix, uint256 x)
Curve.G2Point memory b2 internal
) internal view returns (bool) { pure
Curve.G1Point[] memory p1 = new Curve.G1Point[](2); returns (uint256)
Curve.G2Point[] memory p2 = new Curve.G2Point[](2); {
p1[0] = a1; return BN256G1._deriveY(prefix, x);
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
} }
/// Convenience method for a pairing check for three pairs. /// @dev Calculate inverse (x, -y) of point (x, y).
function pairingProd3( /// @param p1 G1 point
Curve.G1Point memory a1, /// @return (x, -y)
Curve.G2Point memory a2, function _negate(Types.G1Point memory p1)
Curve.G1Point memory b1, internal
Curve.G2Point memory b2, pure
Curve.G1Point memory c1, returns (Types.G1Point memory)
Curve.G2Point memory c2 {
) internal view returns (bool) { (uint256 x, uint256 y) = EllipticCurve.ecInv(p1.x, p1.y, BN256G1.PP);
Curve.G1Point[] memory p1 = new Curve.G1Point[](3); return Types.G1Point(x, y);
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);
} }
/// Convenience method for a pairing check for four pairs. /// @dev Substract two points (x1, y1) and (x2, y2) in affine coordinates.
function pairingProd4( /// @param p1 G1 point P1
Curve.G1Point memory a1, /// @param p2 G1 point P2
Curve.G2Point memory a2, /// @return (qx, qy) = P1-P2 in affine coordinates
Curve.G1Point memory b1, function _subtract(Types.G1Point memory p1, Types.G1Point memory p2)
Curve.G2Point memory b2, internal
Curve.G1Point memory c1, pure
Curve.G2Point memory c2, returns(Types.G1Point memory)
Curve.G1Point memory d1, {
Curve.G2Point memory d2 (uint256 x, uint256 y) = EllipticCurve.ecSub(
) internal view returns (bool) { p1.x,
Curve.G1Point[] memory p1 = new Curve.G1Point[](4); p1.y,
Curve.G2Point[] memory p2 = new Curve.G2Point[](4); p2.x,
p1[0] = a1; p2.y,
p1[1] = b1; BN256G1.AA,
p1[2] = c1; BN256G1.PP);
p1[3] = d1;
p2[0] = a2; return Types.G1Point(x, y);
p2[1] = b2;
p2[2] = c2;
p2[3] = d2;
return pairing(p1, p2);
} }
function isOnCurve(Curve.G1Point memory g1) internal pure returns (bool) { /// @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).
uint256 aa = Bn254.A(); /// @param _message The message used for computing the VRF
uint256 bb = Bn254.B(); /// @return The hash point in affine coordinates
uint256 pp = Bn254.P(); 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: /// @dev Checks if e(P, Q) = e (R,S).
// https://github.com/witnet/elliptic-curve-solidity/blob/b6886bb08333ccf6883ac42827d62c1bfdb37d44/contracts/EllipticCurve.sol#L113-L145 /// @param p: G1 point P
if (0 == g1.X || g1.X == pp || 0 == g1.Y || g1.Y == pp) { /// @param q: G2 point Q
return false; /// @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 // let us = tau.t.u
uint256 lhs = mulmod(g1.Y, g1.Y, pp); // var second: blst_p1
// x^3 // for j in 0 ..< len(us) :
uint256 rhs = mulmod(mulmod(g1.X, g1.X, pp), g1.X, pp); // var prod: blst_p1
if (aa != 0) { // prod.blst_p1_mult(us[j], mus[j], 255)
// x^3 + a*x // second.blst_p1_add_or_double(second, prod)
rhs = addmod(rhs, mulmod(g1.X, aa, pp), pp); // doAssert(blst_p1_on_curve(second).bool)
} Types.G1Point[] memory us = proof.u;
if (bb != 0) { Types.G1Point memory second;
// x^3 + a*x + b for (uint256 j = 0; j<us.length; j++) {
rhs = addmod(rhs, bb, pp); // 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 () { 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 expect(await bn254.f()).to.be.true
}) })
@ -29,13 +32,38 @@ describe("Bn254", function () {
expect(await bn254.g()).to.be.true expect(await bn254.g()).to.be.true
}) })
it("points should be paired correctly", async function () { it("fails when first point is not on Bn254 curve", async function () {
expect(await bn254.pair()).to.be.true 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 () { // it("points should be paired correctly", async function () {
let result = await bn254.verifyTx() // expect(await bn254.pair()).to.be.true
console.log("verify result: " + JSON.stringify(result, null, 2)) // })
expect(await bn254.verifyTx()).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 }, { i: -3, v: 3 },
], ],
mus: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], mus: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
sigma: { X: 1, Y: 2 }, sigma: { x: 1, y: 2 },
u: [ u: [
{ X: 1, Y: 2 }, { x: 1, y: 2 },
{ X: 2, Y: 2 }, { x: 2, y: 2 },
{ X: 3, Y: 3 }, { x: 3, y: 3 },
], ],
name: ethers.utils.toUtf8Bytes("test"), name: ethers.utils.toUtf8Bytes("test"),
publicKey: { publicKey: {
X: [1, 2], x: [1, 2],
Y: [1, 2], y: [1, 2],
}, },
} }
await expect(verifier.verifyProof(proof)).to.be.revertedWith( await expect(verifier.verifyProof(proof)).to.be.revertedWith(
"must be on Bn254 curve" "elliptic curve multiplication failed"
) )
}) })
}) })