From 5929f8090d48c98a7b9ac56c36378335e4009456 Mon Sep 17 00:00:00 2001
From: Alex <18387287+wadealexc@users.noreply.github.com>
Date: Tue, 7 Nov 2023 11:22:29 -0500
Subject: [PATCH] chore: move BitmapUtils and BN254 libraries to middleware
 (#55)

* chore: move BitmapUtils and BN254 libraries to middleware

* fix: actually commit migrated files

* chore: update submodule
---
 lib/eigenlayer-contracts                      |   2 +-
 src/BLSOperatorStateRetriever.sol             |   3 +-
 src/BLSPubkeyRegistry.sol                     |   3 +-
 src/BLSPubkeyRegistryStorage.sol              |   3 +-
 src/BLSPublicKeyCompendium.sol                |   3 +-
 src/BLSRegistryCoordinatorWithIndices.sol     |   5 +-
 src/BLSSignatureChecker.sol                   |   7 +-
 src/IndexRegistry.sol                         |   1 -
 src/ServiceManagerBase.sol                    |  10 +-
 src/StakeRegistry.sol                         |   4 +-
 src/interfaces/IBLSPubkeyRegistry.sol         |   2 +-
 src/interfaces/IBLSPublicKeyCompendium.sol    |   2 +-
 src/interfaces/IBLSRegistry.sol               |   2 +-
 .../IBLSRegistryCoordinatorWithIndices.sol    |   2 +-
 src/interfaces/IBLSSignatureChecker.sol       |   2 +-
 src/libraries/BN254.sol                       | 346 ++++++++++++++++++
 src/libraries/BitmapUtils.sol                 | 283 ++++++++++++++
 test/ffi/BLSSignatureCheckerFFI.t.sol         |   4 +-
 test/ffi/util/G2Operations.sol                |   2 +-
 test/harnesses/BitmapUtilsWrapper.sol         |   2 +-
 test/mocks/BLSPublicKeyCompendiumMock.sol     |   2 +-
 test/unit/StakeRegistryUnit.t.sol             |   2 +-
 test/utils/BLSMockAVSDeployer.sol             |   4 +-
 test/utils/MockAVSDeployer.sol                |   4 +-
 test/utils/Operators.sol                      |   2 +-
 test/utils/ProofParsing.sol                   |   2 +-
 26 files changed, 671 insertions(+), 33 deletions(-)
 create mode 100644 src/libraries/BN254.sol
 create mode 100644 src/libraries/BitmapUtils.sol

diff --git a/lib/eigenlayer-contracts b/lib/eigenlayer-contracts
index 1a8f17fa..bccae530 160000
--- a/lib/eigenlayer-contracts
+++ b/lib/eigenlayer-contracts
@@ -1 +1 @@
-Subproject commit 1a8f17fad68a1cd09ba2adf0df0ba8a903410f34
+Subproject commit bccae530de8bcbf17cfd3e0a04c7b8b13679d34d
diff --git a/src/BLSOperatorStateRetriever.sol b/src/BLSOperatorStateRetriever.sol
index 85656e4c..7b891923 100644
--- a/src/BLSOperatorStateRetriever.sol
+++ b/src/BLSOperatorStateRetriever.sol
@@ -5,7 +5,8 @@ import "src/interfaces/IStakeRegistry.sol";
 import "src/interfaces/IBLSPubkeyRegistry.sol";
 import "src/interfaces/IIndexRegistry.sol";
 import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
+
+import "src/libraries/BitmapUtils.sol";
 
 /**
  * @title BLSOperatorStateRetriever with view functions that allow to retrieve the state of an AVSs registry system.
diff --git a/src/BLSPubkeyRegistry.sol b/src/BLSPubkeyRegistry.sol
index 34875bd6..f98af12b 100644
--- a/src/BLSPubkeyRegistry.sol
+++ b/src/BLSPubkeyRegistry.sol
@@ -1,8 +1,9 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity =0.8.12;
 
+import "src/libraries/BN254.sol";
+
 import "src/BLSPubkeyRegistryStorage.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
 
 contract BLSPubkeyRegistry is BLSPubkeyRegistryStorage {
     using BN254 for BN254.G1Point;
diff --git a/src/BLSPubkeyRegistryStorage.sol b/src/BLSPubkeyRegistryStorage.sol
index 22d4751a..10e2fe51 100644
--- a/src/BLSPubkeyRegistryStorage.sol
+++ b/src/BLSPubkeyRegistryStorage.sol
@@ -4,7 +4,8 @@ pragma solidity =0.8.12;
 import "src/interfaces/IBLSPubkeyRegistry.sol";
 import "src/interfaces/IRegistryCoordinator.sol";
 import "src/interfaces/IBLSPublicKeyCompendium.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+
+import "src/libraries/BN254.sol";
 
 import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
 
diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol
index a5a95159..88d47163 100644
--- a/src/BLSPublicKeyCompendium.sol
+++ b/src/BLSPublicKeyCompendium.sol
@@ -2,7 +2,8 @@
 pragma solidity =0.8.12;
 
 import "src/interfaces/IBLSPublicKeyCompendium.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+
+import "src/libraries/BN254.sol";
 
 /**
  * @title A shared contract for EigenLayer operators to register their BLS public keys.
diff --git a/src/BLSRegistryCoordinatorWithIndices.sol b/src/BLSRegistryCoordinatorWithIndices.sol
index 45787e0f..7b49eadf 100644
--- a/src/BLSRegistryCoordinatorWithIndices.sol
+++ b/src/BLSRegistryCoordinatorWithIndices.sol
@@ -6,9 +6,7 @@ import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
 
 import "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
 import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
 import "eigenlayer-contracts/src/contracts/libraries/EIP1271SignatureUtils.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
 import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol";
 
 import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol";
@@ -20,6 +18,9 @@ import "src/interfaces/IStakeRegistry.sol";
 import "src/interfaces/IIndexRegistry.sol";
 import "src/interfaces/IRegistryCoordinator.sol";
 
+import "src/libraries/BitmapUtils.sol";
+import "src/libraries/BN254.sol";
+
 
 
 /**
diff --git a/src/BLSSignatureChecker.sol b/src/BLSSignatureChecker.sol
index 734f9148..86d44680 100644
--- a/src/BLSSignatureChecker.sol
+++ b/src/BLSSignatureChecker.sol
@@ -1,13 +1,14 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity =0.8.12;
 
-import "src/interfaces/IBLSSignatureChecker.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
 import "src/interfaces/IRegistryCoordinator.sol";
 import "src/interfaces/IStakeRegistry.sol";
 import "src/interfaces/IBLSPubkeyRegistry.sol";
 import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol";
+import "src/interfaces/IBLSSignatureChecker.sol";
+
+import "src/libraries/BitmapUtils.sol";
+import "src/libraries/BN254.sol";
 
 /**
  * @title Used for checking BLS aggregate signatures from the operators of a `BLSRegistry`.
diff --git a/src/IndexRegistry.sol b/src/IndexRegistry.sol
index 2453e509..3c814e98 100644
--- a/src/IndexRegistry.sol
+++ b/src/IndexRegistry.sol
@@ -2,7 +2,6 @@
 pragma solidity =0.8.12;
 
 import "src/IndexRegistryStorage.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
 
 /**
  * @title A `Registry` that keeps track of an ordered list of operators for each quorum
diff --git a/src/ServiceManagerBase.sol b/src/ServiceManagerBase.sol
index 2e1f1cbe..ae300c43 100644
--- a/src/ServiceManagerBase.sol
+++ b/src/ServiceManagerBase.sol
@@ -3,13 +3,15 @@ pragma solidity ^0.8.9;
 
 import "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol";
 import "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol";
-import "src/BLSSignatureChecker.sol";
-import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol";
-import "src/interfaces/IServiceManager.sol";
 
-import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol";
 import "eigenlayer-contracts/src/contracts/interfaces/IPauserRegistry.sol";
 import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol";
+import "eigenlayer-contracts/src/contracts/permissions/Pausable.sol";
+
+import "src/interfaces/IBLSRegistryCoordinatorWithIndices.sol";
+import "src/interfaces/IServiceManager.sol";
+
+import "src/BLSSignatureChecker.sol";
 
 /**
  * @title Base implementation of `IServiceManager` interface, designed to be inherited from by more complex ServiceManagers.
diff --git a/src/StakeRegistry.sol b/src/StakeRegistry.sol
index 1dd6d9d0..5d32852a 100644
--- a/src/StakeRegistry.sol
+++ b/src/StakeRegistry.sol
@@ -1,10 +1,12 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity =0.8.12;
 
-import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
 import "src/interfaces/IServiceManager.sol";
 import "src/interfaces/IStakeRegistry.sol";
 import "src/interfaces/IRegistryCoordinator.sol";
+
+import "src/libraries/BitmapUtils.sol";
+
 import "src/StakeRegistryStorage.sol";
 import {VoteWeigherBase} from "src/VoteWeigherBase.sol";
 
diff --git a/src/interfaces/IBLSPubkeyRegistry.sol b/src/interfaces/IBLSPubkeyRegistry.sol
index 42ec2251..f0fa5ec2 100644
--- a/src/interfaces/IBLSPubkeyRegistry.sol
+++ b/src/interfaces/IBLSPubkeyRegistry.sol
@@ -2,7 +2,7 @@
 pragma solidity =0.8.12;
 
 import {IRegistry} from "src/interfaces/IRegistry.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BN254} from "src/libraries/BN254.sol";
 
 /**
  * @title Minimal interface for a registry that keeps track of aggregate operator public keys for among many quorums.
diff --git a/src/interfaces/IBLSPublicKeyCompendium.sol b/src/interfaces/IBLSPublicKeyCompendium.sol
index 51705d57..3a6d6ff6 100644
--- a/src/interfaces/IBLSPublicKeyCompendium.sol
+++ b/src/interfaces/IBLSPublicKeyCompendium.sol
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity >=0.5.0;
 
-import {BN254}from"eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BN254} from "src/libraries/BN254.sol";
 
 /**
  * @title Minimal interface for the `BLSPublicKeyCompendium` contract.
diff --git a/src/interfaces/IBLSRegistry.sol b/src/interfaces/IBLSRegistry.sol
index 95509aec..8cecc073 100644
--- a/src/interfaces/IBLSRegistry.sol
+++ b/src/interfaces/IBLSRegistry.sol
@@ -2,7 +2,7 @@
 pragma solidity >=0.5.0;
 
 import {IQuorumRegistry} from "./IQuorumRegistry.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BN254} from "src/libraries/BN254.sol";
 
 
 /**
diff --git a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol
index 821f00f1..f8ad8452 100644
--- a/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol
+++ b/src/interfaces/IBLSRegistryCoordinatorWithIndices.sol
@@ -6,7 +6,7 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol";
 import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol";
 import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol";
 import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BN254} from "src/libraries/BN254.sol";
 
 /**
  * @title Minimal interface for the `IBLSStakeRegistryCoordinator` contract.
diff --git a/src/interfaces/IBLSSignatureChecker.sol b/src/interfaces/IBLSSignatureChecker.sol
index 131f9cee..bf098acc 100644
--- a/src/interfaces/IBLSSignatureChecker.sol
+++ b/src/interfaces/IBLSSignatureChecker.sol
@@ -5,7 +5,7 @@ import {IBLSRegistryCoordinatorWithIndices} from "src/interfaces/IBLSRegistryCoo
 import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol";
 import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol";
 import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BN254} from "src/libraries/BN254.sol";
 
 /**
  * @title Used for checking BLS aggregate signatures from the operators of a EigenLayer AVS with the RegistryCoordinator/BLSPubkeyRegistry/StakeRegistry architechture.
diff --git a/src/libraries/BN254.sol b/src/libraries/BN254.sol
new file mode 100644
index 00000000..42703bda
--- /dev/null
+++ b/src/libraries/BN254.sol
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: BUSL-1.1 AND 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 is written by LayrLabs Inc. and is under the BUSL-1.1 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
+    uint256 internal constant FP_MODULUS =
+        21888242871839275222246405745257275088696311157297823662689037894645226208583;
+    // modulus for the underlying field F_r of the elliptic curve
+    uint256 internal constant FR_MODULUS =
+        21888242871839275222246405745257275088548364400416034343698204186575808495617;
+
+    struct G1Point {
+        uint256 X;
+        uint256 Y;
+    }
+
+    // Encoding of field elements is: X[1] * i + X[0]
+    struct G2Point {
+        uint256[2] X;
+        uint256[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).
+    uint256 internal constant G2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
+    uint256 internal constant G2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
+    uint256 internal constant G2y1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
+    uint256 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).
+    uint256 internal constant nG2x1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
+    uint256 internal constant nG2x0 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
+    uint256 internal constant nG2y1 = 17805874995975841540914202342111839520379459829704422454583296818431106115052;
+    uint256 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) {
+        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, 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, 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, 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];
+
+        uint256[12] memory input;
+
+        for (uint256 i = 0; i < 2; 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];
+        }
+
+        uint256[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,
+        uint256 pairingGas
+    ) internal view returns (bool, bool) {
+        G1Point[2] memory p1 = [a1, b1];
+        G2Point[2] memory p2 = [a2, b2];
+
+        uint256[12] memory input;
+
+        for (uint256 i = 0; i < 2; 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];
+        }
+
+        uint256[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 the keccak256 hash of the G1 Point
+    /// @dev used for BLS signatures
+    function hashG1Point(BN254.G1Point memory pk) internal pure returns (bytes32) {
+        return keccak256(abi.encodePacked(pk.X, pk.Y));
+    }
+
+    /// @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) {
+        uint256 beta = 0;
+        uint256 y = 0;
+
+        uint256 x = uint256(_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(uint256 x) internal view returns (uint256, uint256) {
+        // beta = (x^3 + b) % p
+        uint256 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)
+        uint256 y = expMod(beta, 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52, FP_MODULUS);
+
+        return (beta, y);
+    }
+
+    function expMod(uint256 _base, uint256 _exponent, uint256 _modulus) internal view returns (uint256 retval) {
+        bool success;
+        uint256[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];
+    }
+}
diff --git a/src/libraries/BitmapUtils.sol b/src/libraries/BitmapUtils.sol
new file mode 100644
index 00000000..1db53985
--- /dev/null
+++ b/src/libraries/BitmapUtils.sol
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: BUSL-1.1
+
+pragma solidity =0.8.12;
+
+/**
+ * @title Library for Bitmap utilities such as converting between an array of bytes and a bitmap and finding the number of 1s in a bitmap.
+ * @author Layr Labs, Inc.
+ */
+library BitmapUtils {
+    /**
+     * @notice Byte arrays are meant to contain unique bytes.
+     * If the array length exceeds 256, then it's impossible for all entries to be unique.
+     * This constant captures the max allowed array length (inclusive, i.e. 256 is allowed).
+     */
+    uint256 internal constant MAX_BYTE_ARRAY_LENGTH = 256;
+
+    /**
+     * @notice Converts an array of bytes into a bitmap.
+     * @param bytesArray The array of bytes to convert/compress into a bitmap.
+     * @return The resulting bitmap.
+     * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap
+     * @dev This function will also revert if the `bytesArray` input contains any duplicate entries (i.e. duplicate bytes).
+     */
+    function bytesArrayToBitmap(bytes memory bytesArray) internal pure returns (uint256) {
+        // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s)
+        require(bytesArray.length <= MAX_BYTE_ARRAY_LENGTH,
+            "BitmapUtils.bytesArrayToBitmap: bytesArray is too long");
+
+        // return empty bitmap early if length of array is 0
+        if (bytesArray.length == 0) {
+            return uint256(0);
+        }
+
+        // initialize the empty bitmap, to be built inside the loop
+        uint256 bitmap;
+        // initialize an empty uint256 to be used as a bitmask inside the loop
+        uint256 bitMask;
+
+        // perform the 0-th loop iteration with the duplicate check *omitted* (since it is unnecessary / will always pass)
+        // construct a single-bit mask from the numerical value of the 0th byte of the array, and immediately add it to the bitmap
+        bitmap = uint256(1 << uint8(bytesArray[0]));
+
+        // loop through each byte in the array to construct the bitmap
+        for (uint256 i = 1; i < bytesArray.length; ++i) {
+            // construct a single-bit mask from the numerical value of the next byte out of the array
+            bitMask = uint256(1 << uint8(bytesArray[i]));
+            // check that the entry is not a repeat
+            require(bitmap & bitMask == 0, "BitmapUtils.bytesArrayToBitmap: repeat entry in bytesArray");
+            // add the entry to the bitmap
+            bitmap = (bitmap | bitMask);
+        }
+        return bitmap;
+    }
+
+    /**
+     * @notice Converts an ordered array of bytes into a bitmap.
+     * @param orderedBytesArray The array of bytes to convert/compress into a bitmap. Must be in strictly ascending order.
+     * @return The resulting bitmap.
+     * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap.
+     * @dev This function will eventually revert in the event that the `orderedBytesArray` is not properly ordered (in ascending order).
+     * @dev This function will also revert if the `orderedBytesArray` input contains any duplicate entries (i.e. duplicate bytes).
+     */
+    function orderedBytesArrayToBitmap(bytes memory orderedBytesArray) internal pure returns (uint256) {
+        // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s)
+        require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH,
+            "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long");
+
+        // return empty bitmap early if length of array is 0
+        if (orderedBytesArray.length == 0) {
+            return uint256(0);
+        }
+
+        // initialize the empty bitmap, to be built inside the loop
+        uint256 bitmap;
+        // initialize an empty uint256 to be used as a bitmask inside the loop
+        uint256 bitMask;
+
+        // perform the 0-th loop iteration with the ordering check *omitted* (since it is unnecessary / will always pass)
+        // construct a single-bit mask from the numerical value of the 0th byte of the array, and immediately add it to the bitmap
+        bitmap = uint256(1 << uint8(orderedBytesArray[0]));
+
+        // loop through each byte in the array to construct the bitmap
+        for (uint256 i = 1; i < orderedBytesArray.length; ++i) {
+            // construct a single-bit mask from the numerical value of the next byte of the array
+            bitMask = uint256(1 << uint8(orderedBytesArray[i]));
+            // check strictly ascending array ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap)
+            require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered");
+            // add the entry to the bitmap
+            bitmap = (bitmap | bitMask);
+        }
+        return bitmap;
+    }
+
+    /**
+     * @notice Converts an ordered array of bytes into a bitmap. Optimized, Yul-heavy version of `orderedBytesArrayToBitmap`.
+     * @param orderedBytesArray The array of bytes to convert/compress into a bitmap. Must be in strictly ascending order.
+     * @return bitmap The resulting bitmap.
+     * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap.
+     * @dev This function will eventually revert in the event that the `orderedBytesArray` is not properly ordered (in ascending order).
+     * @dev This function will also revert if the `orderedBytesArray` input contains any duplicate entries (i.e. duplicate bytes).
+     */
+    function orderedBytesArrayToBitmap_Yul(bytes calldata orderedBytesArray) internal pure returns (uint256 bitmap) {
+        // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s)
+        require(orderedBytesArray.length <= MAX_BYTE_ARRAY_LENGTH,
+            "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is too long");
+
+        // return empty bitmap early if length of array is 0
+        if (orderedBytesArray.length == 0) {
+            return uint256(0);
+        }
+
+        assembly {
+            // get first entry in bitmap (single byte => single-bit mask)
+            bitmap :=
+                shl(
+                    // extract single byte to get the correct value for the left shift
+                    shr(
+                        248,
+                        calldataload(
+                            orderedBytesArray.offset
+                        )
+                    ),
+                    1
+                )
+            // loop through other entries (byte by byte)
+            for { let i := 1 } lt(i, orderedBytesArray.length) { i := add(i, 1) } {
+                // first construct the single-bit mask by left-shifting a '1'
+                let bitMask := 
+                    shl(
+                        // extract single byte to get the correct value for the left shift
+                        shr(
+                            248,
+                            calldataload(
+                                add(
+                                    orderedBytesArray.offset,
+                                    i
+                                )
+                            )
+                        ),
+                        1
+                    )
+                // check strictly ascending ordering by comparing the mask to the bitmap so far (revert if mask isn't greater than bitmap)
+                // TODO: revert with a good message instead of using `revert(0, 0)`
+                // REFERENCE: require(bitMask > bitmap, "BitmapUtils.orderedBytesArrayToBitmap: orderedBytesArray is not ordered");
+                if iszero(gt(bitMask, bitmap)) { revert(0, 0) }
+                // update the bitmap by adding the single bit in the mask
+                bitmap := or(bitmap, bitMask)
+            }
+        }
+    }
+
+    /**
+     * @notice Converts an array of bytes into a bitmap. Optimized, Yul-heavy version of `bytesArrayToBitmap`.
+     * @param bytesArray The array of bytes to convert/compress into a bitmap.
+     * @return bitmap The resulting bitmap.
+     * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap.
+     * @dev This function will eventually revert in the event that the `bytesArray` is not properly ordered (in ascending order).
+     * @dev This function will also revert if the `bytesArray` input contains any duplicate entries (i.e. duplicate bytes).
+     */
+    function bytesArrayToBitmap_Yul(bytes calldata bytesArray) internal pure returns (uint256 bitmap) {
+        // sanity-check on input. a too-long input would fail later on due to having duplicate entry(s)
+        require(bytesArray.length <= MAX_BYTE_ARRAY_LENGTH,
+            "BitmapUtils.bytesArrayToBitmap: bytesArray is too long");
+
+        // return empty bitmap early if length of array is 0
+        if (bytesArray.length == 0) {
+            return uint256(0);
+        }
+
+        assembly {
+            // get first entry in bitmap (single byte => single-bit mask)
+            bitmap :=
+                shl(
+                    // extract single byte to get the correct value for the left shift
+                    shr(
+                        248,
+                        calldataload(
+                            bytesArray.offset
+                        )
+                    ),
+                    1
+                )
+            // loop through other entries (byte by byte)
+            for { let i := 1 } lt(i, bytesArray.length) { i := add(i, 1) } {
+                // first construct the single-bit mask by left-shifting a '1'
+                let bitMask := 
+                    shl(
+                        // extract single byte to get the correct value for the left shift
+                        shr(
+                            248,
+                            calldataload(
+                                add(
+                                    bytesArray.offset,
+                                    i
+                                )
+                            )
+                        ),
+                        1
+                    )
+                // check against duplicates by comparing the bitmask and bitmap (revert if the bitmap already contains the entry, i.e. bitmap & bitMask != 0)
+                // TODO: revert with a good message instead of using `revert(0, 0)`
+                // REFERENCE: require(bitmap & bitMask == 0, "BitmapUtils.bytesArrayToBitmap: repeat entry in bytesArray");
+                if gt(and(bitmap, bitMask), 0) { revert(0, 0) }
+                // update the bitmap by adding the single bit in the mask
+                bitmap := or(bitmap, bitMask)
+            }
+        }
+    }
+
+    /**
+     * @notice Utility function for checking if a bytes array is strictly ordered, in ascending order.
+     * @param bytesArray the bytes array of interest
+     * @return Returns 'true' if the array is ordered in strictly ascending order, and 'false' otherwise.
+     * @dev This function returns 'true' for the edge case of the `bytesArray` having zero length.
+     * It also returns 'false' early for arrays with length in excess of MAX_BYTE_ARRAY_LENGTH (i.e. so long that they cannot be strictly ordered)
+     */
+    function isArrayStrictlyAscendingOrdered(bytes calldata bytesArray) internal pure returns (bool) {
+        // return 'false' early for too-long (i.e. unorderable) arrays
+        if (bytesArray.length > MAX_BYTE_ARRAY_LENGTH) {
+            return false;
+        }
+
+        // return 'true' early if length of array is 0
+        if (bytesArray.length == 0) {
+            return true;
+        }
+
+        // initialize an empty byte object, to be re-used inside the loop
+        bytes1 singleByte;
+
+        // perform the 0-th loop iteration with the ordering check *omitted* (otherwise it will break with an out-of-bounds error)
+        // pull the 0th byte out of the array
+        singleByte = bytesArray[0];
+
+        // loop through each byte in the array to construct the bitmap
+        for (uint256 i = 1; i < bytesArray.length; ++i) {
+            // check if the entry is *less than or equal to* the previous entry. if it is, then the array isn't strictly ordered!
+            if (uint256(uint8(bytesArray[i])) <= uint256(uint8(singleByte))) {
+                return false;
+            }
+            // pull the next byte out of the array
+            singleByte = bytesArray[i];
+        }
+        return true;
+    }
+
+    /**
+     * @notice Converts a bitmap into an array of bytes.
+     * @param bitmap The bitmap to decompress/convert to an array of bytes.
+     * @return bytesArray The resulting bitmap array of bytes.
+     * @dev Each byte in the input is processed as indicating a single bit to flip in the bitmap
+     */
+    function bitmapToBytesArray(uint256 bitmap) internal pure returns (bytes memory bytesArray) {
+        // initialize an empty uint256 to be used as a bitmask inside the loop
+        uint256 bitMask;
+        // loop through each index in the bitmap to construct the array
+        for (uint256 i = 0; i < 256; ++i) {
+            // construct a single-bit mask for the i-th bit
+            bitMask = uint256(1 << i);
+            // check if the i-th bit is flipped in the bitmap
+            if (bitmap & bitMask != 0) {
+                // if the i-th bit is flipped, then add a byte encoding the value 'i' to the `bytesArray`
+                bytesArray = bytes.concat(bytesArray, bytes1(uint8(i)));
+            }
+        }
+        return bytesArray;
+    }
+
+    /// @return count number of ones in binary representation of `n`
+    function countNumOnes(uint256 n) internal pure returns (uint16) {
+        uint16 count = 0;
+        while (n > 0) {
+            n &= (n - 1);
+            count++;
+        }
+        return count;
+    }
+
+    // @notice returns 'true' if `numberToCheckForInclusion` is in `bitmap` and 'false' otherwise.
+    function numberIsInBitmap(uint256 bitmap, uint8 numberToCheckForInclusion) internal pure returns (bool) {
+        return (((bitmap >> numberToCheckForInclusion) & 1) == 1);
+    }
+}
\ No newline at end of file
diff --git a/test/ffi/BLSSignatureCheckerFFI.t.sol b/test/ffi/BLSSignatureCheckerFFI.t.sol
index 1511dcaa..dce7b281 100644
--- a/test/ffi/BLSSignatureCheckerFFI.t.sol
+++ b/test/ffi/BLSSignatureCheckerFFI.t.sol
@@ -4,9 +4,9 @@ pragma solidity =0.8.12;
 import {G2Operations} from "test/ffi/util/G2Operations.sol";
 import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol";
 import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
 import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol";
-import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
+import {BN254} from "src/libraries/BN254.sol";
+import {BitmapUtils} from "src/libraries/BitmapUtils.sol";
 
 
 contract BLSSignatureCheckerFFITests is MockAVSDeployer, G2Operations {
diff --git a/test/ffi/util/G2Operations.sol b/test/ffi/util/G2Operations.sol
index bc4d67d8..488c30b2 100644
--- a/test/ffi/util/G2Operations.sol
+++ b/test/ffi/util/G2Operations.sol
@@ -3,7 +3,7 @@ pragma solidity =0.8.12;
 
 import "forge-std/Test.sol";
 import "openzeppelin-contracts/contracts/utils/Strings.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import "src/libraries/BN254.sol";
 
 contract G2Operations is Test {
     using Strings for uint256;
diff --git a/test/harnesses/BitmapUtilsWrapper.sol b/test/harnesses/BitmapUtilsWrapper.sol
index f0d3be24..3f4e6d8c 100644
--- a/test/harnesses/BitmapUtilsWrapper.sol
+++ b/test/harnesses/BitmapUtilsWrapper.sol
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity =0.8.12;
 
-import "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
+import "src/libraries/BitmapUtils.sol";
 
 // wrapper around the BitmapUtils library that exposes the internal functions
 contract BitmapUtilsWrapper {
diff --git a/test/mocks/BLSPublicKeyCompendiumMock.sol b/test/mocks/BLSPublicKeyCompendiumMock.sol
index 1373ed4c..ee79d3fe 100644
--- a/test/mocks/BLSPublicKeyCompendiumMock.sol
+++ b/test/mocks/BLSPublicKeyCompendiumMock.sol
@@ -2,7 +2,7 @@
 pragma solidity =0.8.12;
 
 import "src/interfaces/IBLSPublicKeyCompendium.sol";
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import "src/libraries/BN254.sol";
 /**
  * @title A shared contract for EigenLayer operators to register their BLS public keys.
  * @author Layr Labs, Inc.
diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol
index 64599d44..91e7e3fb 100644
--- a/test/unit/StakeRegistryUnit.t.sol
+++ b/test/unit/StakeRegistryUnit.t.sol
@@ -17,7 +17,7 @@ import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol";
 import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol";
 import {IBLSPubkeyRegistry} from "src/interfaces/IBLSPubkeyRegistry.sol";
 
-import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
+import {BitmapUtils} from "src/libraries/BitmapUtils.sol";
 
 import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol";
 import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol";
diff --git a/test/utils/BLSMockAVSDeployer.sol b/test/utils/BLSMockAVSDeployer.sol
index 3e839f1e..5c935814 100644
--- a/test/utils/BLSMockAVSDeployer.sol
+++ b/test/utils/BLSMockAVSDeployer.sol
@@ -3,9 +3,9 @@ pragma solidity =0.8.12;
 
 import {BLSSignatureChecker} from "src/BLSSignatureChecker.sol";
 import {MockAVSDeployer} from "test/utils/MockAVSDeployer.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BN254} from "src/libraries/BN254.sol";
 import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol";
-import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
+import {BitmapUtils} from "src/libraries/BitmapUtils.sol";
 
 contract BLSMockAVSDeployer is MockAVSDeployer {
     using BN254 for BN254.G1Point;
diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol
index 64611832..7f3981d7 100644
--- a/test/utils/MockAVSDeployer.sol
+++ b/test/utils/MockAVSDeployer.sol
@@ -10,8 +10,8 @@ import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/Pau
 import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol";
 import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol";
 import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol";
-import {BitmapUtils} from "eigenlayer-contracts/src/contracts/libraries/BitmapUtils.sol";
-import {BN254} from "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import {BitmapUtils} from "src/libraries/BitmapUtils.sol";
+import {BN254} from "src/libraries/BN254.sol";
 
 import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol";
 import {BLSOperatorStateRetriever} from "src/BLSOperatorStateRetriever.sol";
diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol
index 9f7f6236..910165e7 100644
--- a/test/utils/Operators.sol
+++ b/test/utils/Operators.sol
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity =0.8.12;
 
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import "src/libraries/BN254.sol";
 import "forge-std/Test.sol";
 import "forge-std/StdJson.sol";
 
diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol
index 54ced2d9..f73f74b8 100644
--- a/test/utils/ProofParsing.sol
+++ b/test/utils/ProofParsing.sol
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BUSL-1.1
 pragma solidity =0.8.12;
 
-import "eigenlayer-contracts/src/contracts/libraries/BN254.sol";
+import "src/libraries/BN254.sol";
 import "forge-std/Test.sol";
 import "forge-std/StdJson.sol";