Skip to content

Commit

Permalink
chore: remove unused bitmap functions
Browse files Browse the repository at this point in the history
  • Loading branch information
8sunyuan committed Jan 5, 2024
1 parent 9e4fc83 commit 77aa80b
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 223 deletions.
2 changes: 1 addition & 1 deletion src/BLSSignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ contract BLSSignatureChecker is IBLSSignatureChecker {
uint256[] memory nonSignerQuorumBitmaps = new uint256[](nonSignerStakesAndSignature.nonSignerPubkeys.length);
{
// the bitmap of the quorumNumbers
uint256 signingQuorumBitmap = BitmapUtils.bytesArrayToBitmap(quorumNumbers);
uint256 signingQuorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers);

for (uint i = 0; i < nonSignerStakesAndSignature.nonSignerPubkeys.length; i++) {
nonSignerPubkeyHashes[i] = nonSignerStakesAndSignature.nonSignerPubkeys[i].hashG1Point();
Expand Down
154 changes: 0 additions & 154 deletions src/libraries/BitmapUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,6 @@ library BitmapUtils {
*/
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.
Expand Down Expand Up @@ -110,122 +72,6 @@ library BitmapUtils {
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
Expand Down
12 changes: 0 additions & 12 deletions test/harnesses/BitmapUtilsWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import "src/libraries/BitmapUtils.sol";

// wrapper around the BitmapUtils library that exposes the internal functions
contract BitmapUtilsWrapper {
function bytesArrayToBitmap(bytes calldata bytesArray) external pure returns (uint256) {
return BitmapUtils.bytesArrayToBitmap(bytesArray);
}

function orderedBytesArrayToBitmap(bytes calldata orderedBytesArray) external pure returns (uint256) {
return BitmapUtils.orderedBytesArrayToBitmap(orderedBytesArray);
}
Expand All @@ -21,14 +17,6 @@ contract BitmapUtilsWrapper {
return BitmapUtils.bitmapToBytesArray(bitmap);
}

function orderedBytesArrayToBitmap_Yul(bytes calldata orderedBytesArray) external pure returns (uint256) {
return BitmapUtils.orderedBytesArrayToBitmap_Yul(orderedBytesArray);
}

function bytesArrayToBitmap_Yul(bytes calldata bytesArray) external pure returns (uint256) {
return BitmapUtils.bytesArrayToBitmap_Yul(bytesArray);
}

function countNumOnes(uint256 n) external pure returns (uint16) {
return BitmapUtils.countNumOnes(n);
}
Expand Down
64 changes: 8 additions & 56 deletions test/unit/BitmapUtils.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,29 +122,30 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests {
// ensure that the bitmap encoding of an empty bytes array is an empty bitmap (function doesn't revert and approriately returns uint256(0))
function test_EmptyArrayEncoding() public {
bytes memory emptyBytesArray;
uint256 returnedBitMap = bitmapUtilsWrapper.bytesArrayToBitmap(emptyBytesArray);
uint256 returnedBitMap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(emptyBytesArray);
assertEq(returnedBitMap, 0, "BitmapUtilsUnitTests.testEmptyArrayEncoding: empty array not encoded to empty bitmap");
}

// ensure that the bitmap encoding of a single uint8 (i.e. a single byte) matches the expected output
function testFuzz_SingleByteEncoding(uint8 fuzzedNumber) public {
bytes1 singleByte = bytes1(fuzzedNumber);
bytes memory bytesArray = abi.encodePacked(singleByte);
uint256 returnedBitMap = bitmapUtilsWrapper.bytesArrayToBitmap(bytesArray);
uint256 returnedBitMap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(bytesArray);
uint256 bitMask = uint256(1 << fuzzedNumber);
assertEq(returnedBitMap, bitMask, "BitmapUtilsUnitTests.testSingleByteEncoding: non-equivalence");
}

// ensure that the bitmap encoding of a two uint8's (i.e. a two byte array) matches the expected output
function testFuzz_TwoByteEncoding(uint8 firstFuzzedNumber, uint8 secondFuzzedNumber) public {
cheats.assume(secondFuzzedNumber > firstFuzzedNumber);
bytes1 firstSingleByte = bytes1(firstFuzzedNumber);
bytes1 secondSingleByte = bytes1(secondFuzzedNumber);
bytes memory bytesArray = abi.encodePacked(firstSingleByte, secondSingleByte);
if (firstFuzzedNumber == secondFuzzedNumber) {
cheats.expectRevert(bytes("BitmapUtils.bytesArrayToBitmap: repeat entry in bytesArray"));
bitmapUtilsWrapper.bytesArrayToBitmap(bytesArray);
cheats.expectRevert(bytes("BitmapUtils.orderedBytesArrayToBitmap: repeat entry in bytesArray"));
bitmapUtilsWrapper.orderedBytesArrayToBitmap(bytesArray);
} else {
uint256 returnedBitMap = bitmapUtilsWrapper.bytesArrayToBitmap(bytesArray);
uint256 returnedBitMap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(bytesArray);
uint256 firstBitMask = uint256(1 << firstFuzzedNumber);
uint256 secondBitMask = uint256(1 << secondFuzzedNumber);
uint256 combinedBitMask = firstBitMask | secondBitMask;
Expand All @@ -157,7 +158,7 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests {
function testFuzz_BytesArrayToBitmapToBytesArray(bytes memory originalBytesArray) public {
// filter down to only ordered inputs
cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray));
uint256 bitmap = bitmapUtilsWrapper.bytesArrayToBitmap(originalBytesArray);
uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray);
bytes memory returnedBytesArray = bitmapUtilsWrapper.bitmapToBytesArray(bitmap);
assertEq(
keccak256(abi.encodePacked(originalBytesArray)),
Expand All @@ -166,20 +167,6 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests {
);
}

// ensure that converting bytes array => bitmap => bytes array returns the original bytes array (i.e. is lossless and artifactless)
// note that this only works on ordered arrays, because unordered arrays will be returned ordered
function testFuzz_BytesArrayToBitmapToBytesArray_Yul(bytes memory originalBytesArray) public {
// filter down to only ordered inputs
cheats.assume(bitmapUtilsWrapper.isArrayStrictlyAscendingOrdered(originalBytesArray));
uint256 bitmap = bitmapUtilsWrapper.bytesArrayToBitmap_Yul(originalBytesArray);
bytes memory returnedBytesArray = bitmapUtilsWrapper.bitmapToBytesArray(bitmap);
assertEq(
keccak256(abi.encodePacked(originalBytesArray)),
keccak256(abi.encodePacked(returnedBytesArray)),
"BitmapUtilsUnitTests.testBytesArrayToBitmapToBytesArray_Yul: output does not match input"
);
}

// ensure that converting bytes array => bitmap => bytes array returns the original bytes array (i.e. is lossless and artifactless)
// note that this only works on ordered arrays
function testFuzz_BytesArrayToBitmapToBytesArray_OrderedVersion(bytes memory originalBytesArray) public {
Expand Down Expand Up @@ -216,19 +203,6 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests {
);
}

// testing one function for a specific input. used for comparing gas costs
function test_BytesArrayToBitmap_OrderedVersion_Yul_SpecificInput() public {
bytes memory originalBytesArray = abi.encodePacked(
bytes1(uint8(5)), bytes1(uint8(6)), bytes1(uint8(7)), bytes1(uint8(8)), bytes1(uint8(9)), bytes1(uint8(10)), bytes1(uint8(11)), bytes1(uint8(12))
);
uint256 gasLeftBefore = gasleft();
uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap_Yul(originalBytesArray);
uint256 gasLeftAfter = gasleft();
uint256 gasSpent = gasLeftBefore - gasLeftAfter;
assertEq(bitmap, 8160);
emit log_named_uint("gasSpent", gasSpent);
}

// testing one function for a specific input. used for comparing gas costs
function test_BytesArrayToBitmap_OrderedVersion_SpecificInput() public {
bytes memory originalBytesArray = abi.encodePacked(
Expand All @@ -248,36 +222,14 @@ contract BitmapUtilsUnitTests_bytesArrayToBitmap is BitmapUtilsUnitTests {
bytes1(uint8(5)), bytes1(uint8(6)), bytes1(uint8(7)), bytes1(uint8(8)), bytes1(uint8(9)), bytes1(uint8(10)), bytes1(uint8(11)), bytes1(uint8(12))
);
uint256 gasLeftBefore = gasleft();
uint256 bitmap = bitmapUtilsWrapper.bytesArrayToBitmap(originalBytesArray);
uint256 gasLeftAfter = gasleft();
uint256 gasSpent = gasLeftBefore - gasLeftAfter;
assertEq(bitmap, 8160);
emit log_named_uint("gasSpent", gasSpent);
}

// testing one function for a specific input. used for comparing gas costs
function test_BytesArrayToBitmap_Yul_SpecificInput() public {
bytes memory originalBytesArray = abi.encodePacked(
bytes1(uint8(5)), bytes1(uint8(6)), bytes1(uint8(7)), bytes1(uint8(8)), bytes1(uint8(9)), bytes1(uint8(10)), bytes1(uint8(11)), bytes1(uint8(12))
);
uint256 gasLeftBefore = gasleft();
uint256 bitmap = bitmapUtilsWrapper.bytesArrayToBitmap_Yul(originalBytesArray);
uint256 bitmap = bitmapUtilsWrapper.orderedBytesArrayToBitmap(originalBytesArray);
uint256 gasLeftAfter = gasleft();
uint256 gasSpent = gasLeftBefore - gasLeftAfter;
assertEq(bitmap, 8160);
emit log_named_uint("gasSpent", gasSpent);
}
}

contract BitmapUtilsUnitTests_bitmapToBytesArray is BitmapUtilsUnitTests {
// ensure that converting bitmap => bytes array => bitmap is returns the original bitmap (i.e. is lossless and artifactless)
function testFuzz_BitMapToBytesArrayToBitmap(uint256 originalBitmap) public {
bytes memory bytesArray = bitmapUtilsWrapper.bitmapToBytesArray(originalBitmap);
uint256 returnedBitMap = bitmapUtilsWrapper.bytesArrayToBitmap(bytesArray);
assertEq(returnedBitMap, originalBitmap, "BitmapUtilsUnitTests.testBitMapToArrayToBitmap: output does not match input");
}
}

contract BitmapUtilsUnitTests_isArrayStrictlyAscendingOrdered is BitmapUtilsUnitTests {
function test_DifferentBytesArrayOrdering() public {
// Descending order and duplicate element bytes arrays should return false
Expand Down

0 comments on commit 77aa80b

Please sign in to comment.