Sepolia Testnet

Contract

0xBb5Ed4265dE177516699F59BDdab841ddDe97e5e

Overview

ETH Balance

0 ETH

Token Holdings

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
New Blocks57346332024-04-19 22:37:36297 days ago1713566256IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000298963.00079538
New Blocks57346322024-04-19 22:37:24297 days ago1713566244IN
0xBb5Ed426...ddDe97e5e
0 ETH0.0002993.00083006
New Blocks57346312024-04-19 22:37:12297 days ago1713566232IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000227373.00080802
New Blocks57346302024-04-19 22:37:00297 days ago1713566220IN
0xBb5Ed426...ddDe97e5e
0 ETH0.0002993.00082054
New Blocks57346292024-04-19 22:36:48297 days ago1713566208IN
0xBb5Ed426...ddDe97e5e
0 ETH0.0002993.00079256
New Blocks57346282024-04-19 22:36:36297 days ago1713566196IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299013.00084514
New Blocks57346272024-04-19 22:36:24297 days ago1713566184IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299013.00089533
New Blocks57346262024-04-19 22:36:12297 days ago1713566172IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299023.00095281
New Blocks57346252024-04-19 22:36:00297 days ago1713566160IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299023.00101474
New Blocks57346242024-04-19 22:35:48297 days ago1713566148IN
0xBb5Ed426...ddDe97e5e
0 ETH0.0002993.00110641
New Blocks57346232024-04-19 22:35:36297 days ago1713566136IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299043.00115668
New Blocks57346222024-04-19 22:35:24297 days ago1713566124IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299033.00113296
New Blocks57346202024-04-19 22:35:00297 days ago1713566100IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299023.00132129
New Blocks57346192024-04-19 22:34:48297 days ago1713566088IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299013.00125706
New Blocks57346182024-04-19 22:34:36297 days ago1713566076IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299053.00125024
New Blocks57346172024-04-19 22:34:24297 days ago1713566064IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299053.00128228
New Blocks57346162024-04-19 22:34:12297 days ago1713566052IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299053.00130069
New Blocks57346152024-04-19 22:34:00297 days ago1713566040IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299053.00127727
New Blocks57346142024-04-19 22:33:48297 days ago1713566028IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000298983.00136126
New Blocks57346132024-04-19 22:33:36297 days ago1713566016IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299023.00136799
New Blocks57346122024-04-19 22:33:24297 days ago1713566004IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299033.00146647
New Blocks57346112024-04-19 22:33:12297 days ago1713565992IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299073.00154461
New Blocks57346102024-04-19 22:33:00297 days ago1713565980IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299063.00140829
New Blocks57346092024-04-19 22:32:48297 days ago1713565968IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299073.0014562
New Blocks57346082024-04-19 22:32:36297 days ago1713565956IN
0xBb5Ed426...ddDe97e5e
0 ETH0.000299063.00134799
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x7cc87E6B...fB4776c26
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
HotShot

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 4 : HotShot.sol
pragma solidity ^0.8.0;

import { BN254 } from "bn254/BN254.sol";
import { BLSSig } from "./libraries/BLSSig.sol";

contract HotShot {
    event NewStakingKey(BN254.G2Point stakingKey, uint256 amount, uint256 index);

    uint256 public constant MAX_BLOCKS = 500;
    mapping(uint256 blockHeight => uint256 commitment) public commitments;
    uint256 public blockHeight;

    // Stake table related data structures
    mapping(uint256 index => uint256 amount) private _stakeAmounts;
    BN254.G2Point[] private _stakingKeys;

    event NewBlocks(uint256 firstBlockNumber, uint256 numBlocks);

    error TooManyBlocks(uint256 numBlocks);
    error InvalidQC(uint256 blockNumber);
    error IncorrectBlockNumber(uint256 blockNumber, uint256 expectedBlockNumber);
    error NoKeySelected();
    error NotEnoughStake();

    struct QC {
        uint256 height;
        uint256 blockCommitment;
        // QC validation is currently mocked out, so the rest of the QC data isn't used, and its
        // format is not finalized. For realism of gas usage, we want something of the correct size.
        // The plan for on-chain QC validation is for the contract to only take a few 32-byte words
        // of the QC, with the rest replaced by a short commitment, since the contract doesn't need
        // all the fields of the QC and storing the whole QC in calldata can be expensive (or even
        // run into RPC size limits).
        uint256 pad1;
        uint256 pad2;
    }

    function _verifyQC(QC calldata /* qc */ ) private pure returns (bool) {
        // TODO Check the QC
        // TODO Check the block number
        return true;
    }

    function newBlocks(QC[] calldata qcs) external {
        if (qcs.length > MAX_BLOCKS) {
            revert TooManyBlocks(qcs.length);
        }

        uint256 firstBlockNumber = blockHeight;
        for (uint256 i = 0; i < qcs.length; ++i) {
            if (qcs[i].height != blockHeight) {
                // Fail quickly if this QC is for the wrong block. In particular, this saves the
                // caller some gas in the race condition where two clients try to sequence the same
                // block at the same time, and the first one wins.
                revert IncorrectBlockNumber(qcs[i].height, blockHeight);
            }

            // Check that QC is signed and well-formed.
            if (!_verifyQC(qcs[i])) {
                revert InvalidQC(blockHeight);
            }

            commitments[blockHeight] = qcs[i].blockCommitment;
            blockHeight += 1;
        }

        emit NewBlocks(firstBlockNumber, qcs.length);
    }

    /// @dev Stake table related functions
    /// @notice This function is for testing purposes only. The real sequencer
    ///         contract will perform several checks before adding a new key (e.g.
    ///         validate deposits).
    /// @param stakingKey public key for the BLS scheme
    /// @param amount stake corresponding to the staking key
    function addNewStakingKey(BN254.G2Point memory stakingKey, uint256 amount) public {
        uint256 index = _stakingKeys.length;
        _stakeAmounts[index] = amount;
        _stakingKeys.push(stakingKey);
        emit NewStakingKey(stakingKey, amount, index);
    }

    function getStakingKey(uint256 index) public view returns (BN254.G2Point memory, uint256) {
        return (_stakingKeys[index], _stakeAmounts[index]);
    }
}

File 2 of 4 : BN254.sol
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2022 Espresso Systems (espressosys.com)
// This file is part of the solidity-bn254 library.
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.

//
// Based on:
// - Christian Reitwiessner: https://gist.githubusercontent.com/chriseth/f9be9d9391efc5beb9704255a8e2989d/raw/4d0fb90847df1d4e04d507019031888df8372239/snarktest.solidity
// - Aztec: https://github.com/AztecProtocol/aztec-2-bug-bounty

pragma solidity ^0.8.0;

import { Utils } from "./Utils.sol";

/// @notice Barreto-Naehrig curve over a 254 bit prime field
library BN254 {
    /// @notice type alias for BN254::ScalarField
    type ScalarField is uint256;
    /// @notice type alias for BN254::BaseField
    type BaseField is uint256;

    // use notation from https://datatracker.ietf.org/doc/draft-irtf-cfrg-pairing-friendly-curves/
    //
    // Elliptic curve is defined over a prime field GF(p), with embedding degree k.
    // Short Weierstrass (SW form) is, for a, b \in GF(p^n) for some natural number n > 0:
    //   E: y^2 = x^3 + a * x + b
    //
    // Pairing is defined over cyclic subgroups G1, G2, both of which are of order r.
    // G1 is a subgroup of E(GF(p)), G2 is a subgroup of E(GF(p^k)).
    //
    // BN family are parameterized curves with well-chosen t,
    //   p = 36 * t^4 + 36 * t^3 + 24 * t^2 + 6 * t + 1
    //   r = 36 * t^4 + 36 * t^3 + 18 * t^2 + 6 * t + 1
    // for some integer t.
    // E has the equation:
    //   E: y^2 = x^3 + b
    // where b is a primitive element of multiplicative group (GF(p))^* of order (p-1).
    // A pairing e is defined by taking G1 as a subgroup of E(GF(p)) of order r,
    // G2 as a subgroup of E'(GF(p^2)),
    // and G_T as a subgroup of a multiplicative group (GF(p^12))^* of order r.
    //
    // BN254 is defined over a 254-bit prime order p, embedding degree k = 12.
    uint256 public constant P_MOD =
        21888242871839275222246405745257275088696311157297823662689037894645226208583;
    uint256 public constant R_MOD =
        21888242871839275222246405745257275088548364400416034343698204186575808495617;

    struct G1Point {
        BaseField x;
        BaseField y;
    }

    // G2 group element where x \in Fp2 = c0 + c1 * X
    struct G2Point {
        BaseField x0;
        BaseField x1;
        BaseField y0;
        BaseField y1;
    }

    /// @return the generator of G1
    // solhint-disable-next-line func-name-mixedcase
    function P1() internal pure returns (G1Point memory) {
        return G1Point(BaseField.wrap(1), BaseField.wrap(2));
    }

    /// @return the generator of G2
    // solhint-disable-next-line func-name-mixedcase
    function P2() internal pure returns (G2Point memory) {
        return G2Point({
            x0: BaseField.wrap(0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed),
            x1: BaseField.wrap(0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2),
            y0: BaseField.wrap(0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa),
            y1: BaseField.wrap(0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b)
        });
    }

    /// @notice the neutral/infinity point of G1
    function infinity() internal pure returns (G1Point memory) {
        return G1Point(BaseField.wrap(0), BaseField.wrap(0));
    }

    /// @dev check if a G1 point is Infinity
    /// @notice precompile bn256Add at address(6) takes (0, 0) as Point of Infinity,
    /// some crypto libraries (such as arkwork) uses a boolean flag to mark PoI, and
    /// just use (0, 1) as affine coordinates (not on curve) to represents PoI.
    function isInfinity(G1Point memory point) internal pure returns (bool result) {
        assembly {
            let x := mload(point)
            let y := mload(add(point, 0x20))
            result := and(iszero(x), iszero(y))
        }
    }

    /// @return r the negation of p, i.e. p.add(p.negate()) should be zero.
    function negate(G1Point memory p) internal pure returns (G1Point memory) {
        if (isInfinity(p)) {
            return p;
        }
        return G1Point(p.x, BaseField.wrap(P_MOD - (BaseField.unwrap(p.y) % P_MOD)));
    }

    /// @return res = -fr the negation of scalar field element.
    function negate(ScalarField fr) internal pure returns (ScalarField res) {
        return ScalarField.wrap(R_MOD - (ScalarField.unwrap(fr) % R_MOD));
    }

    /// @notice res = -fq for base field
    function negate(BaseField fq) internal pure returns (BaseField) {
        return BaseField.wrap(P_MOD - (BaseField.unwrap(fq) % P_MOD));
    }

    /// @return r the sum of two points of G1
    function add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) {
        uint256[4] memory input;
        input[0] = BaseField.unwrap(p1.x);
        input[1] = BaseField.unwrap(p1.y);
        input[2] = BaseField.unwrap(p2.x);
        input[3] = BaseField.unwrap(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 { revert(0, 0) }
        }
        require(success, "Bn254: group addition failed!");
    }

    /// @notice add for BaseField
    function add(BaseField a, BaseField b) internal pure returns (BaseField) {
        return BaseField.wrap(addmod(BaseField.unwrap(a), BaseField.unwrap(b), P_MOD));
    }

    /// @notice add for ScalarField
    function add(ScalarField a, ScalarField b) internal pure returns (ScalarField) {
        return ScalarField.wrap(addmod(ScalarField.unwrap(a), ScalarField.unwrap(b), R_MOD));
    }

    /// @notice mul for BaseField
    function mul(BaseField a, BaseField b) internal pure returns (BaseField) {
        return BaseField.wrap(mulmod(BaseField.unwrap(a), BaseField.unwrap(b), P_MOD));
    }

    /// @notice mul for ScalarField
    function mul(ScalarField a, ScalarField b) internal pure returns (ScalarField) {
        return ScalarField.wrap(mulmod(ScalarField.unwrap(a), ScalarField.unwrap(b), R_MOD));
    }

    /// @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 scalarMul(G1Point memory p, ScalarField s) internal view returns (G1Point memory r) {
        uint256[3] memory input;
        input[0] = BaseField.unwrap(p.x);
        input[1] = BaseField.unwrap(p.y);
        input[2] = ScalarField.unwrap(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 { revert(0, 0) }
        }
        require(success, "Bn254: scalar mul failed!");
    }

    /// @dev Multi-scalar Mulitiplication (MSM)
    /// @return r = \Prod{B_i^s_i} where {s_i} are `scalars` and {B_i} are `bases`
    function multiScalarMul(G1Point[] memory bases, ScalarField[] memory scalars)
        internal
        view
        returns (G1Point memory r)
    {
        require(scalars.length == bases.length, "MSM error: length does not match");

        r = scalarMul(bases[0], scalars[0]);
        for (uint256 i = 1; i < scalars.length; i++) {
            r = add(r, scalarMul(bases[i], scalars[i]));
        }
    }

    /// @dev Compute f^-1 for f \in Fr scalar field
    /// @notice credit: Aztec, Spilsbury Holdings Ltd
    function invert(ScalarField fr) internal view returns (ScalarField output) {
        bool success;
        uint256 p = R_MOD;
        assembly {
            let mPtr := mload(0x40)
            mstore(mPtr, 0x20)
            mstore(add(mPtr, 0x20), 0x20)
            mstore(add(mPtr, 0x40), 0x20)
            mstore(add(mPtr, 0x60), fr)
            mstore(add(mPtr, 0x80), sub(p, 2))
            mstore(add(mPtr, 0xa0), p)
            success := staticcall(gas(), 0x05, mPtr, 0xc0, 0x00, 0x20)
            output := mload(0x00)
        }
        require(success, "Bn254: pow precompile failed!");
    }

    /**
     * validate the following:
     *   x < p
     *   y < p
     *   y^2 = x^3 + 3 mod p or Point-of-Infinity
     */
    /// @dev validate G1 point and check if it is on curve
    /// @notice credit: Aztec, Spilsbury Holdings Ltd
    function validateG1Point(G1Point memory point) internal pure {
        bool isWellFormed;
        uint256 p = P_MOD;
        if (isInfinity(point)) {
            return;
        }
        assembly {
            let x := mload(point)
            let y := mload(add(point, 0x20))

            isWellFormed :=
                and(
                    and(lt(x, p), lt(y, p)),
                    eq(mulmod(y, y, p), addmod(mulmod(x, mulmod(x, x, p), p), 3, p))
                )
        }
        require(isWellFormed, "Bn254: invalid G1 point");
    }

    /// @dev Validate scalar field, revert if invalid (namely if fr > r_mod).
    /// @notice Writing this inline instead of calling it might save gas.
    function validateScalarField(ScalarField fr) internal pure {
        bool isValid;
        assembly {
            isValid := lt(fr, R_MOD)
        }
        require(isValid, "Bn254: invalid scalar field");
    }

    /// @dev Evaluate the following pairing product:
    /// @dev e(a1, a2).e(b1, b2) == 1
    /// @dev equality holds for e(a1, a2) == e(-b1, b2) (NOTE: input `b1`=-b1)
    /// @dev caller needs to ensure that a1, a2, b1 and b2 are within proper group
    /// @dev Modified from original credit: Aztec, Spilsbury Holdings Ltd
    function pairingProd2(
        G1Point memory a1,
        G2Point memory a2,
        G1Point memory b1,
        G2Point memory b2
    ) internal view returns (bool) {
        uint256 out;
        bool success;
        assembly {
            let mPtr := mload(0x40)
            mstore(mPtr, mload(a1))
            mstore(add(mPtr, 0x20), mload(add(a1, 0x20)))
            mstore(add(mPtr, 0x40), mload(add(a2, 0x20)))
            mstore(add(mPtr, 0x60), mload(a2))
            mstore(add(mPtr, 0x80), mload(add(a2, 0x60)))
            mstore(add(mPtr, 0xa0), mload(add(a2, 0x40)))

            mstore(add(mPtr, 0xc0), mload(b1))
            mstore(add(mPtr, 0xe0), mload(add(b1, 0x20)))
            mstore(add(mPtr, 0x100), mload(add(b2, 0x20)))
            mstore(add(mPtr, 0x120), mload(b2))
            mstore(add(mPtr, 0x140), mload(add(b2, 0x60)))
            mstore(add(mPtr, 0x160), mload(add(b2, 0x40)))
            success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20)
            out := mload(0x00)
        }
        require(success, "Bn254: Pairing check failed!");
        return (out != 0);
    }

    function fromLeBytesModOrder(bytes memory leBytes) internal pure returns (uint256 ret) {
        for (uint256 i = 0; i < leBytes.length; i++) {
            ret = mulmod(ret, 256, R_MOD);
            ret = addmod(ret, uint256(uint8(leBytes[leBytes.length - 1 - i])), R_MOD);
        }
    }

    /// @dev Check if y-coordinate of G1 point is negative.
    function isYNegative(G1Point memory point) internal pure returns (bool) {
        return (BaseField.unwrap(point.y) << 1) < P_MOD;
    }

    // @dev Perform a modular exponentiation.
    // @return base^exponent (mod modulus)
    // This method is ideal for small exponents (~64 bits or less), as it is cheaper than using the pow precompile
    // @notice credit: credit: Aztec, Spilsbury Holdings Ltd
    function powSmall(uint256 base, uint256 exponent, uint256 modulus)
        internal
        pure
        returns (uint256)
    {
        uint256 result = 1;
        uint256 input = base;
        uint256 count = 1;

        assembly {
            let endpoint := add(exponent, 0x01)
            for { } lt(count, endpoint) { count := add(count, count) } {
                if and(exponent, count) { result := mulmod(result, input, modulus) }
                input := mulmod(input, input, modulus)
            }
        }

        return result;
    }

    function g1Serialize(G1Point memory point) internal pure returns (bytes memory) {
        uint256 mask = 0;

        // Set the 254-th bit to 1 for infinity
        // https://docs.rs/ark-serialize/0.3.0/src/ark_serialize/flags.rs.html#117
        if (isInfinity(point)) {
            mask |= 0x4000000000000000000000000000000000000000000000000000000000000000;
        }

        // Set the 255-th bit to 1 for positive Y
        // https://docs.rs/ark-serialize/0.3.0/src/ark_serialize/flags.rs.html#118
        if (!isYNegative(point)) {
            mask = 0x8000000000000000000000000000000000000000000000000000000000000000;
        }

        return abi.encodePacked(Utils.reverseEndianness(BaseField.unwrap(point.x) | mask));
    }

    function g1Deserialize(bytes32 input) internal view returns (G1Point memory point) {
        uint256 mask = 0x4000000000000000000000000000000000000000000000000000000000000000;
        uint256 xVal = Utils.reverseEndianness(uint256(input));
        bool isQuadraticResidue;
        bool isYPositive;
        if (xVal & mask != 0) {
            // the 254-th bit == 1 for infinity
            point = infinity();
        } else {
            // Set the 255-th bit to 1 for positive Y
            mask = 0x8000000000000000000000000000000000000000000000000000000000000000;
            isYPositive = (xVal & mask != 0);
            // mask off the first two bits of x
            mask = 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
            xVal &= mask;

            // solve for y where E: y^2 = x^3 + 3
            BaseField x = BaseField.wrap(xVal);
            BaseField y = add(mul(mul(x, x), x), BaseField.wrap(3));
            (isQuadraticResidue, y) = quadraticResidue(y);

            require(isQuadraticResidue, "deser fail: not on curve");

            if (isYPositive) {
                y = negate(y);
            }
            point = G1Point(x, y);
        }
    }

    function quadraticResidue(BaseField x)
        internal
        view
        returns (bool isQuadraticResidue, BaseField)
    {
        bool success;
        uint256 a;
        // e = (p+1)/4
        uint256 e = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52;
        uint256 p = P_MOD;

        // we have p == 3 mod 4 therefore
        // a = x^((p+1)/4)
        assembly {
            // credit: Aztec
            let mPtr := mload(0x40)
            mstore(mPtr, 0x20)
            mstore(add(mPtr, 0x20), 0x20)
            mstore(add(mPtr, 0x40), 0x20)
            mstore(add(mPtr, 0x60), x)
            mstore(add(mPtr, 0x80), e)
            mstore(add(mPtr, 0xa0), p)
            success := staticcall(gas(), 0x05, mPtr, 0xc0, 0x00, 0x20)
            a := mload(0x00)
        }
        require(success, "pow precompile call failed!");

        // ensure a < p/2
        if (a << 1 > p) {
            a = p - a;
        }

        // check if a^2 = x, if not x is not a quadratic residue
        e = mulmod(a, a, p);

        isQuadraticResidue = (e == BaseField.unwrap(x));
        return (isQuadraticResidue, BaseField.wrap(a));
    }
}

File 3 of 4 : BLSSig.sol
// SPDX-License-Identifier: Unlicensed

pragma solidity ^0.8.0;

import { BN254 } from "bn254/BN254.sol";

/// @dev test top
/// This library implements the verification of the BLS signature scheme over the BN254 curve
/// following
/// the rust implementation at
// solhint-disable-next-line
/// https://github.com/EspressoSystems/jellyfish/blob/e1e683c287f20160738e6e737295dd8f9e70577a/primitives/src/signatures/bls_over_bn254.rs
library BLSSig {
    error BLSSigVerificationFailed();

    // TODO gas optimization
    function _uint256FromBytesLittleEndian(uint8[] memory input) private pure returns (uint256) {
        uint256 r = 0;
        for (uint256 i = 0; i < input.length; i++) {
            r += 2 ** (8 * i) * input[i];
        }
        return r;
    }

    /// @dev Takes a sequence of bytes and turn in into another sequence of bytes with fixed size.
    /// Equivalent of
    // solhint-disable-next-line
    /// https://github.com/arkworks-rs/algebra/blob/1f7b3c6b215e98fa3130b39d2967f6b43df41e04/ff/src/fields/field_hashers/expander/mod.rs#L37
    /// @param message message to be "expanded"
    /// @return fixed size array of bytes
    function expand(bytes memory message) internal pure returns (bytes memory) {
        uint8 blockSize = 48;
        uint256 bLen = 32; // Output length of sha256 in number of bytes
        bytes1 ell = 0x02; // (n+(bLen-1))/bLen where n=48

        // Final value of buffer must be: z_pad || message || lib_str || 0 || dst_prime

        // z_pad
        bytes memory buffer = new bytes(blockSize);

        // message
        buffer = bytes.concat(buffer, message);

        // lib_str
        buffer = bytes.concat(buffer, hex"00", bytes1(blockSize));

        // 0 separator
        buffer = bytes.concat(buffer, hex"00");

        // dst_prime = [1,1]
        bytes2 dstPrime = 0x0101;

        buffer = bytes.concat(buffer, dstPrime);

        bytes32 b0 = keccak256(buffer);

        buffer = bytes.concat(b0, hex"01", dstPrime);

        bytes32 bi = keccak256(buffer);

        // Building uniform_bytes
        bytes memory uniformBytes = new bytes(blockSize);

        // Copy bi into uniform_bytes
        bytes memory biBytes = bytes.concat(bi);
        for (uint256 i = 0; i < biBytes.length; i++) {
            uniformBytes[i] = biBytes[i];
        }

        bytes memory b0Bytes = bytes.concat(b0);

        // In our case ell=2 so we do not have an outer loop
        // solhint-disable-next-line
        // https://github.com/arkworks-rs/algebra/blob/1f7b3c6b215e98fa3130b39d2967f6b43df41e04/ff/src/fields/field_hashers/expander/mod.rs#L100

        buffer = "";
        for (uint256 j = 0; j < bLen; j++) {
            bytes1 v = bytes1(b0Bytes[j] ^ biBytes[j]);
            buffer = bytes.concat(buffer, v);
        }
        buffer = bytes.concat(buffer, ell, dstPrime);

        bi = keccak256(buffer);
        biBytes = bytes.concat(bi);

        for (uint256 i = 0; i < blockSize - bLen; i++) {
            uniformBytes[bLen + i] = biBytes[i];
        }

        return uniformBytes;
    }

    /// @dev Hash a sequence of bytes to a field element in Fq. Equivalent of
    // solhint-disable-next-line
    /// https://github.com/arkworks-rs/algebra/blob/1f7b3c6b215e98fa3130b39d2967f6b43df41e04/ff/src/fields/field_hashers/mod.rs#L65
    /// @param message input message to be hashed
    /// @return field element in Fq
    function hashToField(bytes memory message) internal pure returns (uint256) {
        bytes memory uniformBytes = expand(message);

        // Reverse uniform_bytes
        uint256 n = uniformBytes.length;
        assert(n == 48);
        bytes memory uniformBytesReverted = new bytes(n);

        for (uint256 i = 0; i < n; i++) {
            uniformBytesReverted[i] = uniformBytes[n - i - 1];
        }

        // solhint-disable-next-line
        // https://github.com/arkworks-rs/algebra/blob/bc991d44c5e579025b7ed56df3d30267a7b9acac/ff/src/fields/prime.rs#L72

        // Do the split
        uint256 numBytesDirectlyToConvert = 31; // Fixed for Fq

        // Process the second slice
        uint8[] memory secondSlice = new uint8[](numBytesDirectlyToConvert);

        for (uint256 i = 0; i < numBytesDirectlyToConvert; i++) {
            secondSlice[i] = uint8(uniformBytesReverted[n - numBytesDirectlyToConvert + i]);
        }

        uint256 res = _uint256FromBytesLittleEndian(secondSlice);

        uint256 windowSize = 256;
        uint256 p = BN254.P_MOD;
        // Handle the first slice
        uint256 arrSize = n - numBytesDirectlyToConvert;
        for (uint256 i = 0; i < arrSize; i++) {
            // Compute field element from a single byte
            uint256 fieldElem = uint256(uint8(uniformBytesReverted[arrSize - i - 1])); // In reverse

            res = mulmod(res, windowSize, p);
            res = addmod(res, fieldElem, p);
        }
        return res;
    }

    /// @dev Hash a sequence of bytes to a group element in BN254.G_1. We use the hash-and-pray
    /// algorithm for now.
    /// Rust implementation can be found at
    // solhint-disable-next-line
    /// https://github.com/EspressoSystems/jellyfish/blob/e1e683c287f20160738e6e737295dd8f9e70577a/primitives/src/signatures/bls_over_bn254.rs#L318
    /// @param input message to be hashed
    /// @return group element in G_1
    function hashToCurve(bytes memory input) internal view returns (BN254.G1Point memory) {
        uint256 x = hashToField(input);

        uint256 p = BN254.P_MOD;

        uint256 b = 3;

        // solhint-disable-next-line var-name-mixedcase
        uint256 Y = mulmod(x, x, p);
        Y = mulmod(Y, x, p);
        Y = addmod(Y, b, p);

        // Check Y is a quadratic residue
        BN254.BaseField y;
        bool isQr;
        (isQr, y) = BN254.quadraticResidue(BN254.BaseField.wrap(Y));

        while (!isQr) {
            x = addmod(x, 1, p);
            Y = mulmod(x, x, p);
            Y = mulmod(Y, x, p);
            Y = addmod(Y, b, p);
            (isQr, y) = BN254.quadraticResidue(BN254.BaseField.wrap(Y));
        }

        return BN254.G1Point(BN254.BaseField.wrap(x), y);
    }

    /// @dev Verify a bls signature. Reverts if the signature is invalid
    /// @param message message to check the signature against
    /// @param sig signature represented as a point in BN254.G_1
    /// @param pk public key represented as a point in BN254.G_2
    function verifyBlsSig(bytes memory message, BN254.G1Point memory sig, BN254.G2Point memory pk)
        internal
        view
    {
        // Check the signature is a valid G1 point
        // Note: checking pk belong to G2 is not possible in practice
        // https://ethresear.ch/t/fast-mathbb-g-2-subgroup-check-in-bn254/13974
        BN254.validateG1Point(sig);

        // Hardcoded suffix "BLS_SIG_BN254G1_XMD:KECCAK_NCTH_NUL_"
        // solhint-disable-next-line
        // https://github.com/EspressoSystems/jellyfish/blob/e1e683c287f20160738e6e737295dd8f9e70577a/primitives/src/constants.rs#L30
        bytes memory csidSuffix = "BLS_SIG_BN254G1_XMD:KECCAK_NCTH_NUL_";

        bytes memory input = bytes.concat(message, csidSuffix);

        BN254.G1Point memory hash = hashToCurve(input);

        if (!BN254.pairingProd2(hash, pk, BN254.negate(sig), BN254.P2())) {
            revert BLSSigVerificationFailed();
        }
    }
}

File 4 of 4 : Utils.sol
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2022 Espresso Systems (espressosys.com)
// This file is part of the solidity-bn254 library.
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

library Utils {
    function reverseEndianness(uint256 input) internal pure returns (uint256 v) {
        v = input;

        // swap bytes
        v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8)
            | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);

        // swap 2-byte long pairs
        v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16)
            | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);

        // swap 4-byte long pairs
        v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32)
            | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);

        // swap 8-byte long pairs
        v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64)
            | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);

        // swap 16-byte long pairs
        v = (v >> 128) | (v << 128);
    }
}

Settings
{
  "remappings": [
    "bn254/=contracts/lib/bn254/src/",
    "ds-test/=contracts/lib/forge-std/lib/ds-test/src/",
    "forge-std/=contracts/lib/forge-std/src/",
    "solidity-bytes-utils/=contracts/lib/solidity-bytes-utils/contracts/",
    "solmate/=contracts/lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"},{"internalType":"uint256","name":"expectedBlockNumber","type":"uint256"}],"name":"IncorrectBlockNumber","type":"error"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"InvalidQC","type":"error"},{"inputs":[],"name":"NoKeySelected","type":"error"},{"inputs":[],"name":"NotEnoughStake","type":"error"},{"inputs":[{"internalType":"uint256","name":"numBlocks","type":"uint256"}],"name":"TooManyBlocks","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"firstBlockNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numBlocks","type":"uint256"}],"name":"NewBlocks","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"BN254.BaseField","name":"x0","type":"uint256"},{"internalType":"BN254.BaseField","name":"x1","type":"uint256"},{"internalType":"BN254.BaseField","name":"y0","type":"uint256"},{"internalType":"BN254.BaseField","name":"y1","type":"uint256"}],"indexed":false,"internalType":"struct BN254.G2Point","name":"stakingKey","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"NewStakingKey","type":"event"},{"inputs":[],"name":"MAX_BLOCKS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"BN254.BaseField","name":"x0","type":"uint256"},{"internalType":"BN254.BaseField","name":"x1","type":"uint256"},{"internalType":"BN254.BaseField","name":"y0","type":"uint256"},{"internalType":"BN254.BaseField","name":"y1","type":"uint256"}],"internalType":"struct BN254.G2Point","name":"stakingKey","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addNewStakingKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blockHeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockHeight","type":"uint256"}],"name":"commitments","outputs":[{"internalType":"uint256","name":"commitment","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getStakingKey","outputs":[{"components":[{"internalType":"BN254.BaseField","name":"x0","type":"uint256"},{"internalType":"BN254.BaseField","name":"x1","type":"uint256"},{"internalType":"BN254.BaseField","name":"y0","type":"uint256"},{"internalType":"BN254.BaseField","name":"y1","type":"uint256"}],"internalType":"struct BN254.G2Point","name":"","type":"tuple"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"height","type":"uint256"},{"internalType":"uint256","name":"blockCommitment","type":"uint256"},{"internalType":"uint256","name":"pad1","type":"uint256"},{"internalType":"uint256","name":"pad2","type":"uint256"}],"internalType":"struct HotShot.QC[]","name":"qcs","type":"tuple[]"}],"name":"newBlocks","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100625760003560e01c80630a321cff1461006757806326833dcc1461007c57806349ce89971461009857806367a21e70146100b8578063f1f45d9914610102578063f44ff71214610115575b600080fd5b61007a61007536600461044e565b61011e565b005b6100856101f481565b6040519081526020015b60405180910390f35b6100856100a63660046104c3565b60006020819052908152604090205481565b6100cb6100c63660046104c3565b610292565b604080518351815260208085015190820152838201519181019190915260609283015192810192909252608082015260a00161008f565b61007a6101103660046104dc565b610326565b61008560015481565b6101f48111156101495760405163e082840b60e01b8152600481018290526024015b60405180910390fd5b60015460005b828110156102525760015484848381811061016c5761016c610562565b90506080020160000135146101be5783838281811061018d5761018d610562565b905060800201600001356001546040516334e423ff60e01b8152600401610140929190918252602082015260400190565b6101dc8484838181106101d3576101d3610562565b90505050600190565b6101ff57600154604051637818671960e01b815260040161014091815260200190565b83838281811061021157610211610562565b9050608002016020013560008060015481526020019081526020016000208190555060018060008282546102459190610578565b909155505060010161014f565b5060408051828152602081018490527f8203a21e4f95f72e5081d5e0929b1a8c52141e123f9a14e1e74b0260fa5f52f191015b60405180910390a1505050565b6102bd6040518060800160405280600081526020016000815260200160008152602001600081525090565b6000600383815481106102d2576102d2610562565b60009182526020808320958352600280825260409384902054845160808101865260049094029097018054845260018101549284019290925281015492820192909252600390910154606082015293915050565b600380546000818152600260209081526040808320869055845460018101865594909252855160049094027fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b810185905586820180517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85c83015587840180517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85d8401556060808a0180517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85e9095019490945585519788529151938701939093529151928501929092529051908301526080820183905260a08201819052907fd72fe1ac57d3e6d51c922ae4d811cc50aa3ad7026283aea637494a073252565a9060c001610285565b6000806020838503121561046157600080fd5b823567ffffffffffffffff8082111561047957600080fd5b818501915085601f83011261048d57600080fd5b81358181111561049c57600080fd5b8660208260071b85010111156104b157600080fd5b60209290920196919550909350505050565b6000602082840312156104d557600080fd5b5035919050565b60008082840360a08112156104f057600080fd5b60808112156104fe57600080fd5b506040516080810181811067ffffffffffffffff8211171561053057634e487b7160e01b600052604160045260246000fd5b604090815284358252602080860135908301528481013590820152606080850135908201529460809093013593505050565b634e487b7160e01b600052603260045260246000fd5b8082018082111561059957634e487b7160e01b600052601160045260246000fd5b9291505056fea164736f6c6343000817000a

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.