WIP: reorg to allow for more curves

Current tests are failing because ECMUL is not a valid opcode in hardhat. Exploring alternatives.
This commit is contained in:
Eric Mastro 2022-06-08 15:15:05 +10:00
parent 98c2555036
commit 976ae66cab
No known key found for this signature in database
GPG Key ID: 141E3048D95A4E63
9 changed files with 207 additions and 137 deletions

View File

@ -1,9 +1,12 @@
// 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 "./curves/Bn254Proofs.sol"; import "./ecc/verifiers/Bn254Verifier.sol";
import "./ecc/Verifier.sol";
contract Proofs { contract Proofs {
using Bn254Verifier for Verifier.Proof;
uint256 private immutable period; uint256 private immutable period;
uint256 private immutable timeout; uint256 private immutable timeout;
uint8 private immutable downtime; uint8 private immutable downtime;
@ -131,8 +134,8 @@ contract Proofs {
return _isProofRequired(id, currentPeriod()); return _isProofRequired(id, currentPeriod());
} }
function _submitProof(bytes32 id, bytes calldata proof) internal { function _submitProof(bytes32 id, Verifier.Proof calldata proof) internal {
require(proof.length > 0, "Invalid proof"); // TODO: replace by actual check 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;
emit ProofSubmitted(id, proof); emit ProofSubmitted(id, proof);

View File

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

View File

@ -2,47 +2,48 @@
pragma solidity >=0.8.0 <=0.8.13; pragma solidity >=0.8.0 <=0.8.13;
import "./curves/Bn254.sol"; import "./ecc/curves/Bn254.sol";
import "./ecc/Curve.sol";
contract TestBn254 { contract TestBn254 {
using Bn254 for *; using Bn254 for *;
struct VerifyingKey { struct VerifyingKey {
Bn254.G2Point A; Curve.G2Point A;
Bn254.G1Point B; Curve.G1Point B;
Bn254.G2Point C; Curve.G2Point C;
Bn254.G2Point gamma; Curve.G2Point gamma;
Bn254.G1Point gammaBeta1; Curve.G1Point gammaBeta1;
Bn254.G2Point gammaBeta2; Curve.G2Point gammaBeta2;
Bn254.G2Point Z; Curve.G2Point Z;
Bn254.G1Point[] IC; Curve.G1Point[] IC;
} }
struct Proof { struct Proof {
Bn254.G1Point A; Curve.G1Point A;
Bn254.G1Point A_p; Curve.G1Point A_p;
Bn254.G2Point B; Curve.G2Point B;
Bn254.G1Point B_p; Curve.G1Point B_p;
Bn254.G1Point C; Curve.G1Point C;
Bn254.G1Point C_p; Curve.G1Point C_p;
Bn254.G1Point K; Curve.G1Point K;
Bn254.G1Point H; Curve.G1Point H;
} }
function f() internal view returns (bool) { function f() public view returns (bool) {
Bn254.G1Point memory p1; Curve.G1Point memory p1;
Bn254.G1Point memory p2; Curve.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;
Bn254.G1Point memory explict_sum = Bn254.g1add(p1, p2); Curve.G1Point memory explict_sum = Bn254.g1add(p1, p2);
Bn254.G1Point memory scalar_prod = Bn254.g1mul(p1, 2); Curve.G1Point memory scalar_prod = Bn254.g1mul(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() internal view returns (bool) { function g() public view returns (bool) {
Bn254.G1Point memory x = Bn254.g1add(Bn254.P1(), Bn254.g1neg(Bn254.P1())); Curve.G1Point memory x = Bn254.g1add(Bn254.P1(), Bn254.g1neg(Bn254.P1()));
// should be zero // should be zero
return (x.X == 0 && x.Y == 0); return (x.X == 0 && x.Y == 0);
} }
function testMul() internal view returns (bool) { function testMul() public view returns (bool) {
Bn254.G1Point memory p; Curve.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;
@ -51,15 +52,15 @@ contract TestBn254 {
p.X == 18256332256630856740336504687838346961237861778318632856900758565550522381207 && p.X == 18256332256630856740336504687838346961237861778318632856900758565550522381207 &&
p.Y == 6976682127058094634733239494758371323697222088503263230319702770853579280803; p.Y == 6976682127058094634733239494758371323697222088503263230319702770853579280803;
} }
function pair() internal view returns (bool) { function pair() public view returns (bool) {
Bn254.G2Point memory fiveTimesP2 = Bn254.G2Point( Curve.G2Point memory fiveTimesP2 = Curve.G2Point(
[4540444681147253467785307942530223364530218361853237193970751657229138047649, 20954117799226682825035885491234530437475518021362091509513177301640194298072], [4540444681147253467785307942530223364530218361853237193970751657229138047649, 20954117799226682825035885491234530437475518021362091509513177301640194298072],
[11631839690097995216017572651900167465857396346217730511548857041925508482915, 21508930868448350162258892668132814424284302804699005394342512102884055673846] [11631839690097995216017572651900167465857396346217730511548857041925508482915, 21508930868448350162258892668132814424284302804699005394342512102884055673846]
); );
// The prime p in the base field F_p for G1 // The prime p in the base field F_p for G1
uint p = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint p = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
Bn254.G1Point[] memory g1points = new Bn254.G1Point[](2); Curve.G1Point[] memory g1points = new Curve.G1Point[](2);
Bn254.G2Point[] memory g2points = new Bn254.G2Point[](2); Curve.G2Point[] memory g2points = new Curve.G2Point[](2);
// // check e(5 P1, P2)e(-P1, 5 P2) == 1 // // check e(5 P1, P2)e(-P1, 5 P2) == 1
g1points[0] = Bn254.P1().g1mul(5); g1points[0] = Bn254.P1().g1mul(5);
g1points[1] = Bn254.P1(); g1points[1] = Bn254.P1();
@ -78,30 +79,30 @@ contract TestBn254 {
return true; return true;
} }
function verifyingKey() internal pure returns (VerifyingKey memory vk) { function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.A = Bn254.G2Point([0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7, 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678], [0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d, 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550]); vk.A = Curve.G2Point([0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7, 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678], [0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d, 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550]);
vk.B = Bn254.G1Point(0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84); vk.B = Curve.G1Point(0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84);
vk.C = Bn254.G2Point([0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb, 0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3], [0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713, 0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590]); vk.C = Curve.G2Point([0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb, 0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3], [0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713, 0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590]);
vk.gamma = Bn254.G2Point([0x25f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb1, 0x22acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d], [0x065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf68, 0x06d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb]); vk.gamma = Curve.G2Point([0x25f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb1, 0x22acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d], [0x065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf68, 0x06d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb]);
vk.gammaBeta1 = Bn254.G1Point(0x15794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f21, 0x14db745c6780e9df549864cec19c2daf4531f6ec0c89cc1c7436cc4d8d300c6d); vk.gammaBeta1 = Curve.G1Point(0x15794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f21, 0x14db745c6780e9df549864cec19c2daf4531f6ec0c89cc1c7436cc4d8d300c6d);
vk.gammaBeta2 = Bn254.G2Point([0x1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e, 0x283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39], [0x140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e, 0x0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd4]); vk.gammaBeta2 = Curve.G2Point([0x1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e, 0x283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39], [0x140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e, 0x0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd4]);
vk.Z = Bn254.G2Point([0x217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac29, 0x0a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c], [0x26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a9855, 0x2fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d7]); vk.Z = Curve.G2Point([0x217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac29, 0x0a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c], [0x26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a9855, 0x2fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d7]);
vk.IC = new Bn254.G1Point[](10); vk.IC = new Curve.G1Point[](10);
vk.IC[0] = Bn254.G1Point(0x0aee46a7ea6e80a3675026dfa84019deee2a2dedb1bbe11d7fe124cb3efb4b5a, 0x044747b6e9176e13ede3a4dfd0d33ccca6321b9acd23bf3683a60adc0366ebaf); vk.IC[0] = Curve.G1Point(0x0aee46a7ea6e80a3675026dfa84019deee2a2dedb1bbe11d7fe124cb3efb4b5a, 0x044747b6e9176e13ede3a4dfd0d33ccca6321b9acd23bf3683a60adc0366ebaf);
vk.IC[1] = Bn254.G1Point(0x1e39e9f0f91fa7ff8047ffd90de08785777fe61c0e3434e728fce4cf35047ddc, 0x2e0b64d75ebfa86d7f8f8e08abbe2e7ae6e0a1c0b34d028f19fa56e9450527cb); vk.IC[1] = Curve.G1Point(0x1e39e9f0f91fa7ff8047ffd90de08785777fe61c0e3434e728fce4cf35047ddc, 0x2e0b64d75ebfa86d7f8f8e08abbe2e7ae6e0a1c0b34d028f19fa56e9450527cb);
vk.IC[2] = Bn254.G1Point(0x1c36e713d4d54e3a9644dffca1fc524be4868f66572516025a61ca542539d43f, 0x042dcc4525b82dfb242b09cb21909d5c22643dcdbe98c4d082cc2877e96b24db); vk.IC[2] = Curve.G1Point(0x1c36e713d4d54e3a9644dffca1fc524be4868f66572516025a61ca542539d43f, 0x042dcc4525b82dfb242b09cb21909d5c22643dcdbe98c4d082cc2877e96b24db);
vk.IC[3] = Bn254.G1Point(0x17d5d09b4146424bff7e6fb01487c477bbfcd0cdbbc92d5d6457aae0b6717cc5, 0x02b5636903efbf46db9235bbe74045d21c138897fda32e079040db1a16c1a7a1); vk.IC[3] = Curve.G1Point(0x17d5d09b4146424bff7e6fb01487c477bbfcd0cdbbc92d5d6457aae0b6717cc5, 0x02b5636903efbf46db9235bbe74045d21c138897fda32e079040db1a16c1a7a1);
vk.IC[4] = Bn254.G1Point(0x0f103f14a584d4203c27c26155b2c955f8dfa816980b24ba824e1972d6486a5d, 0x0c4165133b9f5be17c804203af781bcf168da7386620479f9b885ecbcd27b17b); vk.IC[4] = Curve.G1Point(0x0f103f14a584d4203c27c26155b2c955f8dfa816980b24ba824e1972d6486a5d, 0x0c4165133b9f5be17c804203af781bcf168da7386620479f9b885ecbcd27b17b);
vk.IC[5] = Bn254.G1Point(0x232063b584fb76c8d07995bee3a38fa7565405f3549c6a918ddaa90ab971e7f8, 0x2ac9b135a81d96425c92d02296322ad56ffb16299633233e4880f95aafa7fda7); vk.IC[5] = Curve.G1Point(0x232063b584fb76c8d07995bee3a38fa7565405f3549c6a918ddaa90ab971e7f8, 0x2ac9b135a81d96425c92d02296322ad56ffb16299633233e4880f95aafa7fda7);
vk.IC[6] = Bn254.G1Point(0x09b54f111d3b2d1b2fe1ae9669b3db3d7bf93b70f00647e65c849275de6dc7fe, 0x18b2e77c63a3e400d6d1f1fbc6e1a1167bbca603d34d03edea231eb0ab7b14b4); vk.IC[6] = Curve.G1Point(0x09b54f111d3b2d1b2fe1ae9669b3db3d7bf93b70f00647e65c849275de6dc7fe, 0x18b2e77c63a3e400d6d1f1fbc6e1a1167bbca603d34d03edea231eb0ab7b14b4);
vk.IC[7] = Bn254.G1Point(0x0c54b42137b67cc268cbb53ac62b00ecead23984092b494a88befe58445a244a, 0x18e3723d37fae9262d58b548a0575f59d9c3266db7afb4d5739555837f6b8b3e); vk.IC[7] = Curve.G1Point(0x0c54b42137b67cc268cbb53ac62b00ecead23984092b494a88befe58445a244a, 0x18e3723d37fae9262d58b548a0575f59d9c3266db7afb4d5739555837f6b8b3e);
vk.IC[8] = Bn254.G1Point(0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a, 0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e); vk.IC[8] = Curve.G1Point(0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a, 0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e);
vk.IC[9] = Bn254.G1Point(0x111e2e2a5f8828f80ddad08f9f74db56dac1cc16c1cb278036f79a84cf7a116f, 0x1d7d62e192b219b9808faa906c5ced871788f6339e8d91b83ac1343e20a16b30); vk.IC[9] = Curve.G1Point(0x111e2e2a5f8828f80ddad08f9f74db56dac1cc16c1cb278036f79a84cf7a116f, 0x1d7d62e192b219b9808faa906c5ced871788f6339e8d91b83ac1343e20a16b30);
} }
function verify(uint[] memory input, Proof memory proof) internal view returns (uint) { function verify(uint[] memory input, Proof memory proof) internal view returns (uint) {
VerifyingKey memory vk = verifyingKey(); VerifyingKey memory vk = verifyingKey();
require(input.length + 1 == vk.IC.length); require(input.length + 1 == vk.IC.length);
// Compute the linear combination vk_x // Compute the linear combination vk_x
Bn254.G1Point memory vk_x = Bn254.G1Point(0, 0); Curve.G1Point memory vk_x = Curve.G1Point(0, 0);
for (uint i = 0; i < input.length; i++) 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, Bn254.g1mul(vk.IC[i + 1], input[i]));
vk_x = Bn254.g1add(vk_x, vk.IC[0]); vk_x = Bn254.g1add(vk_x, vk.IC[0]);
@ -124,16 +125,16 @@ contract TestBn254 {
function verifyTx() internal returns (bool r) { function verifyTx() internal returns (bool r) {
uint[] memory input = new uint[](9); uint[] memory input = new uint[](9);
Proof memory proof; Proof memory proof;
proof.A = Bn254.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497); proof.A = Curve.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497);
proof.A_p = Bn254.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366); proof.A_p = Curve.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366);
proof.B = Bn254.G2Point( proof.B = Curve.G2Point(
[8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055], [8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055],
[15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]); [15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]);
proof.B_p = Bn254.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950); proof.B_p = Curve.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950);
proof.C = Bn254.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506); proof.C = Curve.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506);
proof.C_p = Bn254.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686); proof.C_p = Curve.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686);
proof.H = Bn254.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758); proof.H = Curve.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758);
proof.K = Bn254.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170); proof.K = Curve.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170);
input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521; input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521;
input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053; input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053;
input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569; input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569;

View File

@ -56,7 +56,7 @@ contract TestProofs is Proofs {
return _getPointer(id); return _getPointer(id);
} }
function submitProof(bytes32 id, bytes calldata proof) public { function submitProof(bytes32 id, Verifier.Proof calldata proof) public {
_submitProof(id, proof); _submitProof(id, proof);
} }

16
contracts/ecc/Curve.sol Normal file
View File

@ -0,0 +1,16 @@
// 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;
}
}

View File

@ -0,0 +1,25 @@
// 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

@ -4,6 +4,8 @@
pragma solidity >=0.8.0 <=0.8.13; pragma solidity >=0.8.0 <=0.8.13;
import "../Curve.sol";
library Bn254 { library Bn254 {
// p = p(u) = 36u^4 + 36u^3 + 24u^2 + 6u + 1 // p = p(u) = 36u^4 + 36u^3 + 24u^2 + 6u + 1
uint256 internal constant FIELD_ORDER = uint256 internal constant FIELD_ORDER =
@ -20,16 +22,16 @@ library Bn254 {
uint256 internal constant CURVE_A = uint256 internal constant CURVE_A =
0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52; 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52;
struct G1Point { // struct Curve.G1Point {
uint256 X; // uint256 X;
uint256 Y; // uint256 Y;
} // }
// Encoding of field elements is: X[0] * z + X[1] // // Encoding of field elements is: X[0] * z + X[1]
struct G2Point { // struct Curve.G2Point {
uint256[2] X; // uint256[2] X;
uint256[2] Y; // uint256[2] Y;
} // }
// (P+1) / 4 // (P+1) / 4
function A() internal pure returns (uint256) { function A() internal pure returns (uint256) {
@ -49,11 +51,11 @@ library Bn254 {
} }
/// @return the generator of G1 /// @return the generator of G1
function P1() internal pure returns (G1Point memory) { function P1() internal pure returns (Curve.G1Point memory) {
return G1Point(1, 2); return Curve.G1Point(1, 2);
} }
function HashToPoint(uint256 s) internal view returns (G1Point memory g) { function HashToPoint(uint256 s) internal view returns (Curve.G1Point memory g) {
uint256 beta = 0; uint256 beta = 0;
uint256 y = 0; uint256 y = 0;
@ -65,7 +67,7 @@ library Bn254 {
// y^2 == beta // y^2 == beta
if (beta == mulmod(y, y, FIELD_ORDER)) { if (beta == mulmod(y, y, FIELD_ORDER)) {
return G1Point(x, y); return Curve.G1Point(x, y);
} }
x = addmod(x, 1, FIELD_ORDER); x = addmod(x, 1, FIELD_ORDER);
@ -134,9 +136,9 @@ library Bn254 {
} }
/// @return the generator of G2 /// @return the generator of G2
function P2() internal pure returns (G2Point memory) { function P2() internal pure returns (Curve.G2Point memory) {
return return
G2Point( Curve.G2Point(
[ [
11559732032986387107991004021392285783925812861821192530917403151452391805634, 11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781 10857046999023057135944570762232829481370756359578518086990519993285655852781
@ -149,18 +151,18 @@ library Bn254 {
} }
/// @return the negation of p, i.e. p.add(p.negate()) should be zero. /// @return the negation of p, i.e. p.add(p.negate()) should be zero.
function g1neg(G1Point memory p) internal pure returns (G1Point memory) { function g1neg(Curve.G1Point memory p) internal pure returns (Curve.G1Point memory) {
// The prime q in the base field F_q for G1 // The prime q in the base field F_q for G1
uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
if (p.X == 0 && p.Y == 0) return G1Point(0, 0); if (p.X == 0 && p.Y == 0) return Curve.G1Point(0, 0);
return G1Point(p.X, q - (p.Y % q)); return Curve.G1Point(p.X, q - (p.Y % q));
} }
/// @return r the sum of two points of G1 /// @return r the sum of two points of G1
function g1add(G1Point memory p1, G1Point memory p2) function g1add(Curve.G1Point memory p1, Curve.G1Point memory p2)
internal internal
view view
returns (G1Point memory r) returns (Curve.G1Point memory r)
{ {
uint256[4] memory input; uint256[4] memory input;
input[0] = p1.X; input[0] = p1.X;
@ -181,10 +183,10 @@ library Bn254 {
/// @return r the product of a point on G1 and a scalar, i.e. /// @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. /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p.
function g1mul(G1Point memory p, uint256 s) function g1mul(Curve.G1Point memory p, uint256 s)
internal internal
view view
returns (G1Point memory r) returns (Curve.G1Point memory r)
{ {
uint256[3] memory input; uint256[3] memory input;
input[0] = p.X; input[0] = p.X;
@ -206,7 +208,7 @@ library Bn254 {
/// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
/// For example pairing([P1(), P1().negate()], [P2(), P2()]) should /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
/// return true. /// return true.
function pairing(G1Point[] memory p1, G2Point[] memory p2) function pairing(Curve.G1Point[] memory p1, Curve.G2Point[] memory p2)
internal internal
view view
returns (bool) returns (bool)
@ -246,13 +248,13 @@ library Bn254 {
/// Convenience method for a pairing check for two pairs. /// Convenience method for a pairing check for two pairs.
function pairingProd2( function pairingProd2(
G1Point memory a1, Curve.G1Point memory a1,
G2Point memory a2, Curve.G2Point memory a2,
G1Point memory b1, Curve.G1Point memory b1,
G2Point memory b2 Curve.G2Point memory b2
) internal view returns (bool) { ) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](2); Curve.G1Point[] memory p1 = new Curve.G1Point[](2);
G2Point[] memory p2 = new G2Point[](2); Curve.G2Point[] memory p2 = new Curve.G2Point[](2);
p1[0] = a1; p1[0] = a1;
p1[1] = b1; p1[1] = b1;
p2[0] = a2; p2[0] = a2;
@ -262,15 +264,15 @@ library Bn254 {
/// Convenience method for a pairing check for three pairs. /// Convenience method for a pairing check for three pairs.
function pairingProd3( function pairingProd3(
G1Point memory a1, Curve.G1Point memory a1,
G2Point memory a2, Curve.G2Point memory a2,
G1Point memory b1, Curve.G1Point memory b1,
G2Point memory b2, Curve.G2Point memory b2,
G1Point memory c1, Curve.G1Point memory c1,
G2Point memory c2 Curve.G2Point memory c2
) internal view returns (bool) { ) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](3); Curve.G1Point[] memory p1 = new Curve.G1Point[](3);
G2Point[] memory p2 = new G2Point[](3); Curve.G2Point[] memory p2 = new Curve.G2Point[](3);
p1[0] = a1; p1[0] = a1;
p1[1] = b1; p1[1] = b1;
p1[2] = c1; p1[2] = c1;
@ -282,17 +284,17 @@ library Bn254 {
/// Convenience method for a pairing check for four pairs. /// Convenience method for a pairing check for four pairs.
function pairingProd4( function pairingProd4(
G1Point memory a1, Curve.G1Point memory a1,
G2Point memory a2, Curve.G2Point memory a2,
G1Point memory b1, Curve.G1Point memory b1,
G2Point memory b2, Curve.G2Point memory b2,
G1Point memory c1, Curve.G1Point memory c1,
G2Point memory c2, Curve.G2Point memory c2,
G1Point memory d1, Curve.G1Point memory d1,
G2Point memory d2 Curve.G2Point memory d2
) internal view returns (bool) { ) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](4); Curve.G1Point[] memory p1 = new Curve.G1Point[](4);
G2Point[] memory p2 = new G2Point[](4); Curve.G2Point[] memory p2 = new Curve.G2Point[](4);
p1[0] = a1; p1[0] = a1;
p1[1] = b1; p1[1] = b1;
p1[2] = c1; p1[2] = c1;
@ -304,7 +306,7 @@ library Bn254 {
return pairing(p1, p2); return pairing(p1, p2);
} }
function isOnCurve(Bn254.G1Point memory g1) internal pure returns (bool) { function isOnCurve(Curve.G1Point memory g1) internal pure returns (bool) {
uint256 aa = Bn254.A(); uint256 aa = Bn254.A();
uint256 bb = Bn254.B(); uint256 bb = Bn254.B();
uint256 pp = Bn254.P(); uint256 pp = Bn254.P();

View File

@ -2,43 +2,28 @@
pragma solidity >=0.8.0 <=0.8.13; pragma solidity >=0.8.0 <=0.8.13;
import "./Bn254.sol"; import "../curves/Bn254.sol";
import "../Verifier.sol";
library Bn254Proofs { library Bn254Verifier {
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)
Bn254.G1Point sigma;
// TODO: should `u` be bounded?
Bn254.G1Point[] u;
bytes name;
Bn254.G2Point publicKey;
}
function verifyProof(Proof memory proof) internal view returns (bool) { function _verifyProof(Verifier.Proof memory proof) internal view returns (bool) {
// var first: blst_p1 // var first: blst_p1
// for qelem in q : // for qelem in q :
// var prod: blst_p1 // var prod: blst_p1
// prod.blst_p1_mult(hashNameI(tau.t.name, qelem.I), qelem.V, 255) // prod.blst_p1_mult(hashNameI(tau.t.name, qelem.I), qelem.V, 255)
// first.blst_p1_add_or_double(first, prod) // first.blst_p1_add_or_double(first, prod)
// doAssert(blst_p1_on_curve(first).bool) // doAssert(blst_p1_on_curve(first).bool)
Bn254.G1Point memory first; Curve.G1Point memory first;
for (uint256 i = 0; i<proof.q.length; i++) { for (uint256 i = 0; i<proof.q.length; i++) {
QElement memory qelem = proof.q[i]; Verifier.QElement memory qelem = proof.q[i];
bytes32 namei = sha256(abi.encodePacked(proof.name, qelem.i)); bytes32 namei = sha256(abi.encodePacked(proof.name, qelem.i));
// Step 4: arbitraty string to point and check if it is on curve // Step 4: arbitraty string to point and check if it is on curve
uint256 hPointX = uint256(namei); uint256 hPointX = uint256(namei);
Bn254.G1Point memory h = Bn254.HashToPoint(hPointX); Curve.G1Point memory h = Bn254.HashToPoint(hPointX);
// TODO: Where does 255 get used??? // TODO: Where does 255 get used???
Bn254.G1Point memory prod = Bn254.g1mul(h, uint256(qelem.v)); Curve.G1Point memory prod = Bn254.g1mul(h, uint256(qelem.v));
first = Bn254.g1add(first, prod); first = Bn254.g1add(first, prod);
require(Bn254.isOnCurve(first), "must be on Bn254 curve"); require(Bn254.isOnCurve(first), "must be on Bn254 curve");
} }
@ -49,18 +34,18 @@ library Bn254Proofs {
// prod.blst_p1_mult(us[j], mus[j], 255) // prod.blst_p1_mult(us[j], mus[j], 255)
// second.blst_p1_add_or_double(second, prod) // second.blst_p1_add_or_double(second, prod)
// doAssert(blst_p1_on_curve(second).bool) // doAssert(blst_p1_on_curve(second).bool)
Bn254.G1Point[] memory us = proof.u; Curve.G1Point[] memory us = proof.u;
Bn254.G1Point memory second; Curve.G1Point memory second;
for (uint256 j = 0; j<us.length; j++) { for (uint256 j = 0; j<us.length; j++) {
// TODO: Where does 255 get used??? // TODO: Where does 255 get used???
Bn254.G1Point memory prod = Bn254.g1mul(us[j], proof.mus[j]); Curve.G1Point memory prod = Bn254.g1mul(us[j], proof.mus[j]);
second = Bn254.g1add(second, prod); second = Bn254.g1add(second, prod);
require(Bn254.isOnCurve(second), "must be on Bn254 curve"); require(Bn254.isOnCurve(second), "must be on Bn254 curve");
} }
// var sum: blst_p1 // var sum: blst_p1
// sum.blst_p1_add_or_double(first, second) // sum.blst_p1_add_or_double(first, second)
Bn254.G1Point memory sum = Bn254.g1add(first, second); Curve.G1Point memory sum = Bn254.g1add(first, second);
// var g{.noInit.}: blst_p2 // var g{.noInit.}: blst_p2
// g.blst_p2_from_affine(BLS12_381_G2) // g.blst_p2_from_affine(BLS12_381_G2)

38
test/Bn254.test.js Normal file
View File

@ -0,0 +1,38 @@
const { expect } = require("chai")
const { ethers } = require("hardhat")
const {
snapshot,
revert,
ensureMinimumBlockHeight,
currentTime,
advanceTime,
advanceTimeTo,
} = require("./evm")
const { periodic } = require("./time")
describe("Bn254", function () {
let bn254
beforeEach(async function () {
await snapshot()
await ensureMinimumBlockHeight(256)
const Bn254 = await ethers.getContractFactory("TestBn254")
bn254 = await Bn254.deploy()
})
afterEach(async function () {
await revert()
})
it("explicit sum and scalar prod are the same", async function () {
expect(await bn254.f()).to.be.true
})
it("adding point to negation of itself should be zero", async function () {
expect(await bn254.g()).to.be.true
})
it("multiplication along BN254 emits expected values", async function () {
expect(await bn254.testMul()).to.be.true
})
})