mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
387 lines
14 KiB
Solidity
387 lines
14 KiB
Solidity
// Sources flattened with hardhat v2.22.2 https://hardhat.org
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only AND MIT
|
|
|
|
// File contracts/libraries/BN254.sol
|
|
|
|
// Original license: SPDX_License_Identifier: MIT
|
|
// several functions are taken or adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol (MIT license):
|
|
// 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.
|
|
|
|
// The remainder of the code in this library is written by LayrLabs Inc. and is also under an MIT license
|
|
|
|
pragma solidity ^0.8.12;
|
|
|
|
/**
|
|
* @title Library for operations on the BN254 elliptic curve.
|
|
* @author Layr Labs, Inc.
|
|
* @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service
|
|
* @notice Contains BN254 parameters, common operations (addition, scalar mul, pairing), and BLS signature functionality.
|
|
*/
|
|
library BN254 {
|
|
// modulus for the underlying field F_p of the elliptic curve
|
|
uint internal constant FP_MODULUS = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
|
// modulus for the underlying field F_r of the elliptic curve
|
|
uint internal constant FR_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
|
|
|
struct G1Point {
|
|
uint X;
|
|
uint Y;
|
|
}
|
|
|
|
// Encoding of field elements is: X[1] * i + X[0]
|
|
struct G2Point {
|
|
uint[2] X;
|
|
uint[2] Y;
|
|
}
|
|
|
|
function generatorG1() internal pure returns (G1Point memory) {
|
|
return G1Point(1, 2);
|
|
}
|
|
|
|
// generator of group G2
|
|
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
|
|
uint internal constant G2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
|
|
uint internal constant G2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
|
|
uint internal constant G2y1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
|
|
uint internal constant G2y0 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
|
|
|
|
/// @notice returns the G2 generator
|
|
/// @dev mind the ordering of the 1s and 0s!
|
|
/// this is because of the (unknown to us) convention used in the bn254 pairing precompile contract
|
|
/// "Elements a * i + b of F_p^2 are encoded as two elements of F_p, (a, b)."
|
|
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-197.md#encoding
|
|
function generatorG2() internal pure returns (G2Point memory) {
|
|
return G2Point([G2x1, G2x0], [G2y1, G2y0]);
|
|
}
|
|
|
|
// negation of the generator of group G2
|
|
/// @dev Generator point in F_q2 is of the form: (x0 + ix1, y0 + iy1).
|
|
uint internal constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
|
|
uint internal constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
|
|
uint internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
|
|
uint internal constant nG2y0 = 13392588948715843804641432497768002650278120570034223513918757245338268106653;
|
|
|
|
function negGeneratorG2() internal pure returns (G2Point memory) {
|
|
return G2Point([nG2x1, nG2x0], [nG2y1, nG2y0]);
|
|
}
|
|
|
|
bytes32 internal constant powersOfTauMerkleRoot =
|
|
0x22c998e49752bbb1918ba87d6d59dd0e83620a311ba91dd4b2cc84990b31b56f;
|
|
|
|
/**
|
|
* @param p Some point in G1.
|
|
* @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, FP_MODULUS - (p.Y % FP_MODULUS));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return r the sum of two points of G1
|
|
*/
|
|
function plus(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
|
|
uint[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, 0x80, r, 0x40)
|
|
// Use "invalid" to make gas estimation work
|
|
switch success
|
|
case 0 {
|
|
invalid()
|
|
}
|
|
}
|
|
|
|
require(success, "ec-add-failed");
|
|
}
|
|
|
|
/**
|
|
* @notice an optimized ecMul implementation that takes O(log_2(s)) ecAdds
|
|
* @param p the point to multiply
|
|
* @param s the scalar to multiply by
|
|
* @dev this function is only safe to use if the scalar is 9 bits or less
|
|
*/
|
|
function scalar_mul_tiny(BN254.G1Point memory p, uint16 s) internal view returns (BN254.G1Point memory) {
|
|
require(s < 2 ** 9, "scalar-too-large");
|
|
|
|
// if s is 1 return p
|
|
if (s == 1) {
|
|
return p;
|
|
}
|
|
|
|
// the accumulated product to return
|
|
BN254.G1Point memory acc = BN254.G1Point(0, 0);
|
|
// the 2^n*p to add to the accumulated product in each iteration
|
|
BN254.G1Point memory p2n = p;
|
|
// value of most significant bit
|
|
uint16 m = 1;
|
|
// index of most significant bit
|
|
uint8 i = 0;
|
|
|
|
//loop until we reach the most significant bit
|
|
while (s >= m) {
|
|
unchecked {
|
|
// if the current bit is 1, add the 2^n*p to the accumulated product
|
|
if ((s >> i) & 1 == 1) {
|
|
acc = plus(acc, p2n);
|
|
}
|
|
// double the 2^n*p for the next iteration
|
|
p2n = plus(p2n, p2n);
|
|
|
|
// increment the index and double the value of the most significant bit
|
|
m <<= 1;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
// return the accumulated product
|
|
return acc;
|
|
}
|
|
|
|
/**
|
|
* @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, uint s) internal view returns (G1Point memory r) {
|
|
uint[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, 0x60, r, 0x40)
|
|
// Use "invalid" to make gas estimation work
|
|
switch success
|
|
case 0 {
|
|
invalid()
|
|
}
|
|
}
|
|
require(success, "ec-mul-failed");
|
|
}
|
|
|
|
/**
|
|
* @return The result of computing the pairing check
|
|
* e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1
|
|
* For example,
|
|
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
|
|
*/
|
|
function pairing(
|
|
G1Point memory a1,
|
|
G2Point memory a2,
|
|
G1Point memory b1,
|
|
G2Point memory b2
|
|
) internal view returns (bool) {
|
|
G1Point[2] memory p1 = [a1, b1];
|
|
G2Point[2] memory p2 = [a2, b2];
|
|
|
|
uint[12] memory input;
|
|
|
|
for (uint i = 0; i < 2; i++) {
|
|
uint 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];
|
|
}
|
|
|
|
uint[1] memory out;
|
|
bool success;
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
assembly {
|
|
success := staticcall(sub(gas(), 2000), 8, input, mul(12, 0x20), out, 0x20)
|
|
// Use "invalid" to make gas estimation work
|
|
switch success
|
|
case 0 {
|
|
invalid()
|
|
}
|
|
}
|
|
|
|
require(success, "pairing-opcode-failed");
|
|
|
|
return out[0] != 0;
|
|
}
|
|
|
|
/**
|
|
* @notice This function is functionally the same as pairing(), however it specifies a gas limit
|
|
* the user can set, as a precompile may use the entire gas budget if it reverts.
|
|
*/
|
|
function safePairing(
|
|
G1Point memory a1,
|
|
G2Point memory a2,
|
|
G1Point memory b1,
|
|
G2Point memory b2,
|
|
uint pairingGas
|
|
) internal view returns (bool, bool) {
|
|
G1Point[2] memory p1 = [a1, b1];
|
|
G2Point[2] memory p2 = [a2, b2];
|
|
|
|
uint[12] memory input;
|
|
|
|
for (uint i = 0; i < 2; i++) {
|
|
uint 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];
|
|
}
|
|
|
|
uint[1] memory out;
|
|
bool success;
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
assembly {
|
|
success := staticcall(pairingGas, 8, input, mul(12, 0x20), out, 0x20)
|
|
}
|
|
|
|
//Out is the output of the pairing precompile, either 0 or 1 based on whether the two pairings are equal.
|
|
//Success is true if the precompile actually goes through (aka all inputs are valid)
|
|
|
|
return (success, out[0] != 0);
|
|
}
|
|
|
|
/// @return hashedG1 the keccak256 hash of the G1 Point
|
|
/// @dev used for BLS signatures
|
|
function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32 hashedG1) {
|
|
assembly {
|
|
mstore(0, mload(pk))
|
|
mstore(0x20, mload(add(0x20, pk)))
|
|
hashedG1 := keccak256(0, 0x40)
|
|
}
|
|
}
|
|
|
|
/// @return the keccak256 hash of the G2 Point
|
|
/// @dev used for BLS signatures
|
|
function hashG2Point(BN254.G2Point memory pk) internal pure returns (bytes32) {
|
|
return keccak256(abi.encodePacked(pk.X[0], pk.X[1], pk.Y[0], pk.Y[1]));
|
|
}
|
|
|
|
/**
|
|
* @notice adapted from https://github.com/HarryR/solcrypto/blob/master/contracts/altbn128.sol
|
|
*/
|
|
function hashToG1(bytes32 _x) internal view returns (G1Point memory) {
|
|
uint beta = 0;
|
|
uint y = 0;
|
|
|
|
uint x = uint(_x) % FP_MODULUS;
|
|
|
|
while (true) {
|
|
(beta, y) = findYFromX(x);
|
|
|
|
// y^2 == beta
|
|
if (beta == mulmod(y, y, FP_MODULUS)) {
|
|
return G1Point(x, y);
|
|
}
|
|
|
|
x = addmod(x, 1, FP_MODULUS);
|
|
}
|
|
return G1Point(0, 0);
|
|
}
|
|
|
|
/**
|
|
* Given X, find Y
|
|
*
|
|
* where y = sqrt(x^3 + b)
|
|
*
|
|
* Returns: (x^3 + b), y
|
|
*/
|
|
function findYFromX(uint x) internal view returns (uint, uint) {
|
|
// beta = (x^3 + b) % p
|
|
uint beta = addmod(mulmod(mulmod(x, x, FP_MODULUS), x, FP_MODULUS), 3, FP_MODULUS);
|
|
|
|
// y^2 = x^3 + b
|
|
// this acts like: y = sqrt(beta) = beta^((p+1) / 4)
|
|
uint y = expMod(beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS);
|
|
|
|
return (beta, y);
|
|
}
|
|
|
|
function expMod(uint _base, uint _exponent, uint _modulus) internal view returns (uint retval) {
|
|
bool success;
|
|
uint[1] memory output;
|
|
uint[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, "BN254.expMod: call failure");
|
|
return output[0];
|
|
}
|
|
}
|
|
|
|
|
|
// File contracts/interface/IDASigners.sol
|
|
|
|
// Original license: SPDX_License_Identifier: LGPL-3.0-only
|
|
|
|
pragma solidity >=0.8.0 <0.9.0;
|
|
|
|
interface IDASigners {
|
|
/*=== struct ===*/
|
|
struct SignerDetail {
|
|
string socket;
|
|
BN254.G1Point pkG1;
|
|
BN254.G2Point pkG2;
|
|
}
|
|
|
|
/*=== event ===*/
|
|
event NewSigner(address indexed signer, BN254.G1Point pkG1, BN254.G2Point pkG2);
|
|
event SocketUpdated(address indexed signer, string socket);
|
|
|
|
/*=== function ===*/
|
|
function epochNumber() external view returns (uint);
|
|
|
|
function getSigners(uint epoch) external view returns (address[] memory accounts, SignerDetail[] memory details);
|
|
|
|
function registerSigner(SignerDetail memory _signer, BN254.G1Point memory _signature) external;
|
|
|
|
function checkSignatures(
|
|
BN254.G1Point memory _hash,
|
|
uint epoch,
|
|
bytes memory signerBitmap,
|
|
BN254.G2Point memory _aggPkG2,
|
|
BN254.G1Point memory _signature
|
|
) external view returns (bool);
|
|
} |