chore: clean up

- Move bulk of verification to curves/Bn254Proofs.sol
- remove all unneeded structs and add Proof struct
- bring in point `isOnCurve` check so that we can use solidity compiler 0.8.x
- remove all other Bn254 library deps
- changed proof type from bool to Proof
- add Bn254 test file (uncalled yet)

TODO:
- update Proofs test such that verifyProof test is called (need to generate proof using https://github.com/status-im/nim-codex/pull/76)
- call Bn254 tests from test harness
This commit is contained in:
Eric Mastro 2022-06-08 15:06:53 +10:00
parent b03a415fb9
commit 98c2555036
No known key found for this signature in database
GPG Key ID: 141E3048D95A4E63
6 changed files with 20090 additions and 2136 deletions

View File

@ -1,9 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <=0.8.13;
pragma solidity >=0.8.0 <=0.8.13;
import "bls-solidity/contracts/BN256G1.sol";
import "./ecc/AltBn254.sol";
import "elliptic-curve-solidity/contracts/EllipticCurve.sol";
import "./curves/Bn254Proofs.sol";
contract Proofs {
uint256 private immutable period;
@ -14,7 +12,7 @@ contract Proofs {
uint256 __period,
uint256 __timeout,
uint8 __downtime
) public {
) {
require(block.number > 256, "Insufficient block height");
period = __period;
timeout = __timeout;
@ -133,259 +131,6 @@ contract Proofs {
return _isProofRequired(id, currentPeriod());
}
struct BnFr {
// in mratsim/constantine, given the following:
//
// func wordsRequired*(bits: int): int {.compileTime.} =
// ## Compute the number of limbs required
// # from the **announced** bit length
// (bits + WordBitWidth - 1) div WordBitWidth
//
// type
// SecretWord* = distinct uint64
//
// BigInt*[bits: static int] = object
// limbs*: array[bits.wordsRequired, SecretWord]
//
// Fr*[C: static Curve] = object => Fr[C: BN254_Snarks]
// mres*: matchingOrderBigInt(C) => mres*: BigInt[254]
//
// For the BN254_Snarks curve,
// orderBitwidth = 254
// wordsRequired = 4 (output of the wordsRequired func above)
//
// We can then conclude:
//
// type
// Fr*[C: BN254_Snarks] = object
// mres*: BigInt[254]
// limbs*: array[4, uint64]
//
// This matches FR = distinct BNU256 in nim-bncurve:
// type
// BNU256* = array[4, uint64]
//
// Note: for BLS curves (in nim-blscurve),
// type
// blst_fp = array[0..5, uint64]
// uint64[4] ls; // this is what the data type SHOULD be
uint ls; // but using a uint256 has the same number of bytes, but
// doesn't require conversion
}
struct BnFp {
// see notes from BnFr, the only difference is that we use the
// ordered bit width in BnFr, as opposed to the bit width for Fr.
// Both are 254 bits wide, so the data structure is identical.
// uint64[4] ls; // this is what the data type SHOULD be
uint ls; // but using a uint256 has the same number of bytes, but
// doesn't require conversion
}
struct BnFp2 {
// In mratsim/constantine, given the following:
//
// type
//
// QuadraticExt*[F] = object
// coords*: array[2, F]
//
// Fp2*[C: static Curve] =
// QuadraticExt[Fp[C]] => QuadraticExt[Fp[BN254_Snarks]]
//
// equates to
// Fp2*[C: static Curve] =
// QuadraticExt[Fp[BN254_Snarks]]
// coords: array[2, Fp[BN254_Snarks]]
BnFp[2] fp;
}
// blst_fp6* {.byref.} = object
// fp2*: array[3, blst_fp2]
struct BnFp6 {
BnFp2[3] fp2;
}
// blst_fp12* {.byref.} = object
// fp6*: array[2, blst_fp6]
struct BnFp12 {
BnFp6[2] fp6;
}
struct BnP1 {
BnFp x;
BnFp y;
// BnFp z; // for some reason, BN254G1 doesn't accept z in operations on
// jacobian coords, and removing an unused field is easier
// trying to instantiate a struct with an unused field.
}
struct BnP1Affine {
BnFp x;
BnFp y;
}
// blst_p2* {.byref.} = object
// x*: blst_fp2
// y*: blst_fp2
// z*: blst_fp2
struct BnP2 {
BnFp2 x;
BnFp2 y;
// BnFp2 z; // for some reason, BN254G1 doesn't accept z in operations on
// jacobian coords, and removing an unused field is easier
// trying to instantiate a struct with an unused field.
}
// blst_p2_affine* {.byref.} = object
// x*: blst_fp2
// y*: blst_fp2
struct BnP2Affine {
BnFp2 x;
BnFp2 y;
}
struct BnScalar {
// Taken from nim-blscurve/bls_abi, cannot find an analogue in
// mratsim/constantine nor nim-bncurve
//
// # blst_scalar
// # = typedesc[array[0..31, byte]]
// array[typeof(256)(typeof(256)(256 / typeof(256)(8))), byte]
bytes32 ls;
}
struct TauZero {
// bytes[512] name; // array[512, byte], byte is alias for uint8
bytes name;
int64 n;
Curve.G1Point[] u; // seq[blst_p1]
}
struct Tau {
TauZero t;
// bytes32[96] signature;
Curve.G1Point signature;
}
// x', y' affine coordinates, result of EllipticCurve.ecMul
// e.g. https://github.com/witnet/elliptic-curve-solidity/blob/master/examples/Secp256k1.sol
struct PublicKey {
Curve.G1Point signkey;
Curve.G2Point key;
}
struct QElement {
int64 i;
BnScalar v;
}
function isEmpty(bytes32[96] memory array) internal pure returns (bool) {
for(uint i; i< array.length; i++){
if(i > 0) {
return true;
}
}
return false;
}
// Example of BLS signature verification
// Taken from: https://ethereum.stackexchange.com/a/59315
function _verifySignature(
Curve.G1Point memory sig,
Curve.G2Point memory key,
uint hashedMsg) internal view returns (bool)
{
// TODO: Is it ok to use the PublicKey.key (G2) here?
// Curve.G2Point memory v = Curve.G2Point(
// [18523194229674161632574346342370534213928970227736813349975332190798837787897, 5725452645840548248571879966249653216818629536104756116202892528545334967238],
// [3816656720215352836236372430537606984911914992659540439626020770732736710924, 677280212051826798882467475639465784259337739185938192379192340908771705870]
// );
Curve.G1Point memory h = Curve.HashToPoint(hashedMsg);
return Curve.pairingProd2(Curve.g1neg(sig), Curve.P2(), h, key);
}
function _isOnCurve(Curve.G1Point memory g1) internal view returns (bool) {
return EllipticCurve.isOnCurve(
g1.X,
g1.Y,
Curve.A(),
Curve.B(),
Curve.P());
}
function _verifyProof(
Tau memory tau,
QElement[] memory q,
BnFr[10] memory mus,
// Possibly 48 bytes long, csaba?
// If only 48 bytes, how can it be a G1Point? (x, y both only 24 bytes?)
Curve.G1Point memory sigma,
PublicKey memory spk) internal returns (bool) {
// TODO: is this needed in solidity?
// require(!isEmpty(tau.signature), "Signature cannot be empty");
// TODO: $tau.t -- how to do this in solidity?
// TODO: what's the second G2 point needed, PublicKey.signkey is a G1
// point?? Is using the PublicKey.key sufficient?
uint hashedMsg = uint(sha256(abi.encodePacked(tau.t)));
require(_verifySignature(tau.signature, spk.key, hashedMsg),
"invalid signature");
// 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 (uint i = 0; i<q.length; i++) {
QElement memory qelem = q[i];
bytes32 namei = sha256(abi.encodePacked(tau.t.name, qelem.i));
// Step 4: arbitraty string to point and check if it is on curve
uint hPointX = uint256(namei);
Curve.G1Point memory h = Curve.HashToPoint(hPointX);
// TODO: Where does 255 get used???
// TODO: Can we convert qelem.v.ls from a 32 byte array to a uint256 without worry?
// TODO: It's very hard to know if multiply takes jacobian or affine coords...???
Curve.G1Point memory prod = Curve.g1mul(h, uint(qelem.v.ls));
first = Curve.g1add(first, prod);
require(_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 = tau.t.u;
Curve.G1Point memory second;
for (uint j = 0; j<us.length; j++) {
// TODO: Where does 255 get used???
Curve.G1Point memory prod = Curve.g1mul(us[j], mus[j].ls);
second = Curve.g1add(second, prod);
require(_isOnCurve(second), "must be on BN254 curve");
}
// var sum: blst_p1
// sum.blst_p1_add_or_double(first, second)
Curve.G1Point memory sum = Curve.g1add(first, second);
// var g{.noInit.}: blst_p2
// g.blst_p2_from_affine(BLS12_381_G2)
// TODO: do we need to convert Curve.P2() to/from affine???
// return verifyPairings(sum, spk.key, sigma, g)
return Curve.pairingProd2(sum, spk.key, sigma, Curve.P2());
}
function _willProofBeRequired(bytes32 id) internal view returns (bool) {
bool isRequired;
uint8 pointer;
(isRequired, pointer) = _getProofRequirement(id, currentPeriod());
return isRequired && pointer < downtime;
}
function _submitProof(bytes32 id, bytes calldata proof) internal {
require(proof.length > 0, "Invalid proof"); // TODO: replace by actual check
require(!received[id][currentPeriod()], "Proof already submitted");

153
contracts/TestBn254.sol Normal file
View File

@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <=0.8.13;
import "./curves/Bn254.sol";
contract TestBn254 {
using Bn254 for *;
struct VerifyingKey {
Bn254.G2Point A;
Bn254.G1Point B;
Bn254.G2Point C;
Bn254.G2Point gamma;
Bn254.G1Point gammaBeta1;
Bn254.G2Point gammaBeta2;
Bn254.G2Point Z;
Bn254.G1Point[] IC;
}
struct Proof {
Bn254.G1Point A;
Bn254.G1Point A_p;
Bn254.G2Point B;
Bn254.G1Point B_p;
Bn254.G1Point C;
Bn254.G1Point C_p;
Bn254.G1Point K;
Bn254.G1Point H;
}
function f() internal view returns (bool) {
Bn254.G1Point memory p1;
Bn254.G1Point memory p2;
p1.X = 1; p1.Y = 2;
p2.X = 1; p2.Y = 2;
Bn254.G1Point memory explict_sum = Bn254.g1add(p1, p2);
Bn254.G1Point memory scalar_prod = Bn254.g1mul(p1, 2);
return (explict_sum.X == scalar_prod.X &&
explict_sum.Y == scalar_prod.Y);
}
function g() internal view returns (bool) {
Bn254.G1Point memory x = Bn254.g1add(Bn254.P1(), Bn254.g1neg(Bn254.P1()));
// should be zero
return (x.X == 0 && x.Y == 0);
}
function testMul() internal view returns (bool) {
Bn254.G1Point memory p;
// @TODO The points here are reported to be not well-formed
p.X = 14125296762497065001182820090155008161146766663259912659363835465243039841726;
p.Y = 16229134936871442251132173501211935676986397196799085184804749187146857848057;
p = Bn254.g1mul(p, 13986731495506593864492662381614386532349950841221768152838255933892789078521);
return
p.X == 18256332256630856740336504687838346961237861778318632856900758565550522381207 &&
p.Y == 6976682127058094634733239494758371323697222088503263230319702770853579280803;
}
function pair() internal view returns (bool) {
Bn254.G2Point memory fiveTimesP2 = Bn254.G2Point(
[4540444681147253467785307942530223364530218361853237193970751657229138047649, 20954117799226682825035885491234530437475518021362091509513177301640194298072],
[11631839690097995216017572651900167465857396346217730511548857041925508482915, 21508930868448350162258892668132814424284302804699005394342512102884055673846]
);
// The prime p in the base field F_p for G1
uint p = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
Bn254.G1Point[] memory g1points = new Bn254.G1Point[](2);
Bn254.G2Point[] memory g2points = new Bn254.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 = Bn254.G2Point([0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7, 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678], [0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d, 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550]);
vk.B = Bn254.G1Point(0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84);
vk.C = Bn254.G2Point([0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb, 0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3], [0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713, 0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590]);
vk.gamma = Bn254.G2Point([0x25f83c8b6ab9de74e7da488ef02645c5a16a6652c3c71a15dc37fe3a5dcb7cb1, 0x22acdedd6308e3bb230d226d16a105295f523a8a02bfc5e8bd2da135ac4c245d], [0x065bbad92e7c4e31bf3757f1fe7362a63fbfee50e7dc68da116e67d600d9bf68, 0x06d302580dc0661002994e7cd3a7f224e7ddc27802777486bf80f40e4ca3cfdb]);
vk.gammaBeta1 = Bn254.G1Point(0x15794ab061441e51d01e94640b7e3084a07e02c78cf3103c542bc5b298669f21, 0x14db745c6780e9df549864cec19c2daf4531f6ec0c89cc1c7436cc4d8d300c6d);
vk.gammaBeta2 = Bn254.G2Point([0x1f39e4e4afc4bc74790a4a028aff2c3d2538731fb755edefd8cb48d6ea589b5e, 0x283f150794b6736f670d6a1033f9b46c6f5204f50813eb85c8dc4b59db1c5d39], [0x140d97ee4d2b36d99bc49974d18ecca3e7ad51011956051b464d9e27d46cc25e, 0x0764bb98575bd466d32db7b15f582b2d5c452b36aa394b789366e5e3ca5aabd4]);
vk.Z = Bn254.G2Point([0x217cee0a9ad79a4493b5253e2e4e3a39fc2df38419f230d341f60cb064a0ac29, 0x0a3d76f140db8418ba512272381446eb73958670f00cf46f1d9e64cba057b53c], [0x26f64a8ec70387a13e41430ed3ee4a7db2059cc5fc13c067194bcc0cb49a9855, 0x2fd72bd9edb657346127da132e5b82ab908f5816c826acb499e22f2412d1a2d7]);
vk.IC = new Bn254.G1Point[](10);
vk.IC[0] = Bn254.G1Point(0x0aee46a7ea6e80a3675026dfa84019deee2a2dedb1bbe11d7fe124cb3efb4b5a, 0x044747b6e9176e13ede3a4dfd0d33ccca6321b9acd23bf3683a60adc0366ebaf);
vk.IC[1] = Bn254.G1Point(0x1e39e9f0f91fa7ff8047ffd90de08785777fe61c0e3434e728fce4cf35047ddc, 0x2e0b64d75ebfa86d7f8f8e08abbe2e7ae6e0a1c0b34d028f19fa56e9450527cb);
vk.IC[2] = Bn254.G1Point(0x1c36e713d4d54e3a9644dffca1fc524be4868f66572516025a61ca542539d43f, 0x042dcc4525b82dfb242b09cb21909d5c22643dcdbe98c4d082cc2877e96b24db);
vk.IC[3] = Bn254.G1Point(0x17d5d09b4146424bff7e6fb01487c477bbfcd0cdbbc92d5d6457aae0b6717cc5, 0x02b5636903efbf46db9235bbe74045d21c138897fda32e079040db1a16c1a7a1);
vk.IC[4] = Bn254.G1Point(0x0f103f14a584d4203c27c26155b2c955f8dfa816980b24ba824e1972d6486a5d, 0x0c4165133b9f5be17c804203af781bcf168da7386620479f9b885ecbcd27b17b);
vk.IC[5] = Bn254.G1Point(0x232063b584fb76c8d07995bee3a38fa7565405f3549c6a918ddaa90ab971e7f8, 0x2ac9b135a81d96425c92d02296322ad56ffb16299633233e4880f95aafa7fda7);
vk.IC[6] = Bn254.G1Point(0x09b54f111d3b2d1b2fe1ae9669b3db3d7bf93b70f00647e65c849275de6dc7fe, 0x18b2e77c63a3e400d6d1f1fbc6e1a1167bbca603d34d03edea231eb0ab7b14b4);
vk.IC[7] = Bn254.G1Point(0x0c54b42137b67cc268cbb53ac62b00ecead23984092b494a88befe58445a244a, 0x18e3723d37fae9262d58b548a0575f59d9c3266db7afb4d5739555837f6b8b3e);
vk.IC[8] = Bn254.G1Point(0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a, 0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e);
vk.IC[9] = Bn254.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
Bn254.G1Point memory vk_x = Bn254.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;
}
event Verified(string);
function verifyTx() internal returns (bool r) {
uint[] memory input = new uint[](9);
Proof memory proof;
proof.A = Bn254.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497);
proof.A_p = Bn254.G1Point(7742452358972543465462254569134860944739929848367563713587808717088650354556, 7324522103398787664095385319014038380128814213034709026832529060148225837366);
proof.B = Bn254.G2Point(
[8176651290984905087450403379100573157708110416512446269839297438960217797614, 15588556568726919713003060429893850972163943674590384915350025440408631945055],
[15347511022514187557142999444367533883366476794364262773195059233657571533367, 4265071979090628150845437155927259896060451682253086069461962693761322642015]);
proof.B_p = Bn254.G1Point(2979746655438963305714517285593753729335852012083057917022078236006592638393, 6470627481646078059765266161088786576504622012540639992486470834383274712950);
proof.C = Bn254.G1Point(6851077925310461602867742977619883934042581405263014789956638244065803308498, 10336382210592135525880811046708757754106524561907815205241508542912494488506);
proof.C_p = Bn254.G1Point(12491625890066296859584468664467427202390981822868257437245835716136010795448, 13818492518017455361318553880921248537817650587494176379915981090396574171686);
proof.H = Bn254.G1Point(12091046215835229523641173286701717671667447745509192321596954139357866668225, 14446807589950902476683545679847436767890904443411534435294953056557941441758);
proof.K = Bn254.G1Point(21341087976609916409401737322664290631992568431163400450267978471171152600502, 2942165230690572858696920423896381470344658299915828986338281196715687693170);
input[0] = 13986731495506593864492662381614386532349950841221768152838255933892789078521;
input[1] = 622860516154313070522697309645122400675542217310916019527100517240519630053;
input[2] = 11094488463398718754251685950409355128550342438297986977413505294941943071569;
input[3] = 6627643779954497813586310325594578844876646808666478625705401786271515864467;
input[4] = 2957286918163151606545409668133310005545945782087581890025685458369200827463;
input[5] = 1384290496819542862903939282897996566903332587607290986044945365745128311081;
input[6] = 5613571677741714971687805233468747950848449704454346829971683826953541367271;
input[7] = 9643208548031422463313148630985736896287522941726746581856185889848792022807;
input[8] = 18066496933330839731877828156604;
if (verify(input, proof) == 0) {
emit Verified("Transaction successfully verified.");
return true;
} else {
return false;
}
}
}

View File

@ -1,15 +1,10 @@
// This file is MIT Licensed.
// SPDX-License-Identifier: MIT
//
// From: https://gist.githubusercontent.com/chriseth/f9be9d9391efc5beb9704255a8e2989d/raw/4d0fb90847df1d4e04d507019031888df8372239/snarktest.solidity
//
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// From: https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol
pragma solidity >=0.6.0 <=0.8.13;
pragma solidity >=0.8.0 <=0.8.13;
library Curve {
library Bn254 {
// p = p(u) = 36u^4 + 36u^3 + 24u^2 + 6u + 1
uint256 internal constant FIELD_ORDER =
0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
@ -58,7 +53,7 @@ library Curve {
return G1Point(1, 2);
}
function HashToPoint(uint256 s) internal view returns (G1Point memory) {
function HashToPoint(uint256 s) internal view returns (G1Point memory g) {
uint256 beta = 0;
uint256 y = 0;
@ -154,9 +149,7 @@ library Curve {
}
/// @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(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
@ -165,7 +158,9 @@ library Curve {
/// @return r the sum of two points of G1
function g1add(G1Point memory p1, G1Point memory p2)
internal view returns (G1Point memory r)
internal
view
returns (G1Point memory r)
{
uint256[4] memory input;
input[0] = p1.X;
@ -187,7 +182,9 @@ library Curve {
/// @return r the product of a point on G1 and a scalar, i.e.
/// p == p.mul(1) and p.add(p) == p.mul(2) for all points p.
function g1mul(G1Point memory p, uint256 s)
internal view returns (G1Point memory r)
internal
view
returns (G1Point memory r)
{
uint256[3] memory input;
input[0] = p.X;
@ -306,4 +303,30 @@ library Curve {
p2[3] = d2;
return pairing(p1, p2);
}
function isOnCurve(Bn254.G1Point memory g1) internal pure returns (bool) {
uint256 aa = Bn254.A();
uint256 bb = Bn254.B();
uint256 pp = Bn254.P();
// Implementation borrowed from the witnet/elliptic-curve-solidity lib:
// https://github.com/witnet/elliptic-curve-solidity/blob/b6886bb08333ccf6883ac42827d62c1bfdb37d44/contracts/EllipticCurve.sol#L113-L145
if (0 == g1.X || g1.X == pp || 0 == g1.Y || g1.Y == pp) {
return false;
}
// y^2
uint256 lhs = mulmod(g1.Y, g1.Y, pp);
// x^3
uint256 rhs = mulmod(mulmod(g1.X, g1.X, pp), g1.X, pp);
if (aa != 0) {
// x^3 + a*x
rhs = addmod(rhs, mulmod(g1.X, aa, pp), pp);
}
if (bb != 0) {
// x^3 + a*x + b
rhs = addmod(rhs, bb, pp);
}
return lhs == rhs;
}
}

View File

@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <=0.8.13;
import "./Bn254.sol";
library Bn254Proofs {
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) {
// 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)
Bn254.G1Point memory first;
for (uint256 i = 0; i<proof.q.length; i++) {
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);
Bn254.G1Point memory h = Bn254.HashToPoint(hPointX);
// TODO: Where does 255 get used???
Bn254.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)
Bn254.G1Point[] memory us = proof.u;
Bn254.G1Point memory second;
for (uint256 j = 0; j<us.length; j++) {
// TODO: Where does 255 get used???
Bn254.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)
Bn254.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());
}
}

21684
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,6 @@
"@nomiclabs/hardhat-ethers": "^2.0.5",
"@nomiclabs/hardhat-waffle": "^2.0.3",
"@openzeppelin/contracts": "^4.5.0",
"bls-solidity": "github:witnet/bls-solidity#5a27f14",
"elliptic-curve-solidity": "github:witnet/elliptic-curve-solidity#b6886bb",
"chai": "^4.3.6",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.6.0",