Skip to content

Instantly share code, notes, and snippets.

@muellerberndt
Created November 22, 2023 06:13
Show Gist options
  • Save muellerberndt/b8eccc9a91eac757ec918716eaa116eb to your computer and use it in GitHub Desktop.
Save muellerberndt/b8eccc9a91eac757ec918716eaa116eb to your computer and use it in GitHub Desktop.
homework5.sol
// SPDX-License-Identifier: GPL-3.0
import "hardhat/console.sol";
pragma solidity >=0.8.2 <0.9.0;
contract Homework5 {
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
struct G1Point {
uint256 X;
uint256 Y;
}
// Encoding of field elements is: X[0] * z + X[1]
struct G2Point {
uint256[2] X;
uint256[2] Y;
}
G1Point G1Gen = G1Point(1,2);
/*
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
// The prime q in the base field F_q for G1
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
} else {
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
}
}
/*
* @return the sum of two points of G1
*/
function plus(
G1Point memory p1,
G1Point memory p2
) internal view returns (G1Point memory r) {
uint256[4] memory input;
input[0] = p1.X;
input[1] = p1.Y;
input[2] = p2.X;
input[3] = p2.Y;
bool success;
// solium-disable-next-line security/no-inline-assembly
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, "pairing-add-failed");
}
/*
* @return r the product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
* points p.
*/
function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
uint256[3] memory input;
input[0] = p.X;
input[1] = p.Y;
input[2] = s;
bool success;
// solium-disable-next-line security/no-inline-assembly
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, "pairing-mul-failed");
}
function pairing(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2,
G1Point memory d1,
G2Point memory d2
) internal view returns (bool) {
G1Point[4] memory p1 = [a1, b1, c1, d1];
G2Point[4] memory p2 = [a2, b2, c2, d2];
uint256 inputSize = 24;
uint256[] memory input = new uint256[](inputSize);
for (uint256 i = 0; i < 4; i++) {
uint256 j = i * 6;
input[j + 0] = p1[i].X;
input[j + 1] = p1[i].Y;
input[j + 2] = p2[i].X[0];
input[j + 3] = p2[i].X[1];
input[j + 4] = p2[i].Y[0];
input[j + 5] = p2[i].Y[1];
}
/*
for(uint256 i = 0; i < 24; i++ ) {
console.log(input[i]);
}
*/
uint256[1] memory out;
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
require(success, "pairing-opcode-failed");
return out[0] != 0;
}
// Hardcoded EC points
// α1 = 5 * G1
G1Point alpha_1 = G1Point(10744596414106452074759370245733544594153395043370666422502510773307029471145, 848677436511517736191562425154572367705380862894644942948681172815252343932);
// β2 = 6 * G2
G2Point beta_2 = G2Point(
[10191129150170504690859455063377241352678147020731325090942140630855943625622, 12345624066896925082600651626583520268054356403303305150512393106955803260718],
[16727484375212017249697795760885267597317766655549468217180521378213906474374, 13790151551682513054696583104432356791070435696840691503641536676885931241944]
);
// γ2 = 7 * G2
G2Point gamma_2 = G2Point(
[15512671280233143720612069991584289591749188907863576513414377951116606878472, 18551411094430470096460536606940536822990217226529861227533666875800903099477],
[13376798835316611669264291046140500151806347092962367781523498857425536295743, 1711576522631428957817575436337311654689480489843856945284031697403898093784]
);
// δ2 = 8 * G2
G2Point delta_2 = G2Point(
[11166086885672626473267565287145132336823242144708474818695443831501089511977, 1513450333913810775282357068930057790874607011341873340507105465411024430745],
[10576778712883087908382530888778326306865681986179249638025895353796469496812, 20245151454212206884108313452940569906396451322269011731680309881579291004202]
);
/*
Function takes the following arguments:
A1, B2, C1, x1, x2, x3
*/
function pairing_check (
G1Point memory A1,
G2Point memory B2,
G1Point memory C1,
uint256 x1,
uint256 x2,
uint256 x3
) public view returns (bool) {
// X1 = x1 * G1 + x2 * G1 + x3 * G1
G1Point memory X1 = plus(
plus(
scalar_mul(G1Gen, x1),
scalar_mul(G1Gen, x2)
),
scalar_mul(G1Gen, x3)
);
return pairing(
negate(A1),
B2,
alpha_1,
beta_2,
X1,
gamma_2,
C1,
delta_2
);
}
function test_pairing() external view returns (bool) {
// A1 = 22 * G1
G1Point memory A1 = G1Point(15727213640762128376977790067421582934261473041285176203873887513123693207669,19144605879150273414601776380457513460094228635793066771119021730299648624873);
// B2 = 11 * G2
G2Point memory B2 = G2Point([8472151341754925747860535367990505955708751825377817860727104273184244800723, 15624790064206502667756020446826209080711344272800176518784649088946231692936],[1196137947243150610106053819405501111182787323156221967342356892090037828244, 19488077321171448217727198730828487286865984357780136663388739985720647978898]);
// C1 = 16 * G1
G1Point memory C1 = G1Point(10835225521862395592687560951453385602895512958032257955899877380493200080708, 2623520004791921319615054428233368525468155544765295675952919303096698181037);
/*
We should get:
- 22 * 11 + 5 * 6 + 12 * 7 + 16 * 8 = 0
*/
return pairing_check(A1, B2, C1, 3, 4, 5);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment