Skip to content

Commit

Permalink
verifyBlobs util (Layr-Labs#862)
Browse files Browse the repository at this point in the history
  • Loading branch information
0x0aa0 authored Nov 18, 2024
1 parent ab13bd7 commit fbe6d3b
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 4 deletions.
86 changes: 82 additions & 4 deletions contracts/src/libraries/EigenDARollupUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ library EigenDARollupUtils {
* @param blobVerificationProof the relevant data needed to prove inclusion of the blob and that the trust assumptions were as expected
*/
function verifyBlob(
IEigenDAServiceManager.BlobHeader calldata blobHeader,
IEigenDAServiceManager.BlobHeader memory blobHeader,
IEigenDAServiceManager eigenDAServiceManager,
BlobVerificationProof calldata blobVerificationProof
) external view {
BlobVerificationProof memory blobVerificationProof
) internal view {
require(
EigenDAHasher.hashBatchMetadata(blobVerificationProof.batchMetadata)
== eigenDAServiceManager.batchIdToBatchMetadataHash(blobVerificationProof.batchId),
Expand Down Expand Up @@ -97,6 +97,84 @@ library EigenDARollupUtils {
);
}

/**
* @notice Verifies the inclusion of a blob within a batch confirmed in `eigenDAServiceManager` and its trust assumptions
* @param blobHeaders the headers of the blobs containing relevant attributes of the blobs
* @param eigenDAServiceManager the contract in which the batch was confirmed
* @param blobVerificationProofs the relevant data needed to prove inclusion of the blobs and that the trust assumptions were as expected
*/
function verifyBlobs(
IEigenDAServiceManager.BlobHeader[] memory blobHeaders,
IEigenDAServiceManager eigenDAServiceManager,
BlobVerificationProof[] memory blobVerificationProofs
) internal view {
require(blobHeaders.length == blobVerificationProofs.length, "EigenDARollupUtils.verifyBlobs: blobHeaders and blobVerificationProofs must have the same length");

bytes memory quorumAdversaryThresholdPercentages = eigenDAServiceManager.quorumAdversaryThresholdPercentages();
uint256 quorumNumbersRequiredBitmap = BitmapUtils.orderedBytesArrayToBitmap(eigenDAServiceManager.quorumNumbersRequired());

for (uint i = 0; i < blobHeaders.length; i++) {
require(
EigenDAHasher.hashBatchMetadata(blobVerificationProofs[i].batchMetadata)
== eigenDAServiceManager.batchIdToBatchMetadataHash(blobVerificationProofs[i].batchId),
"EigenDARollupUtils.verifyBlob: batchMetadata does not match stored metadata"
);

require(
Merkle.verifyInclusionKeccak(
blobVerificationProofs[i].inclusionProof,
blobVerificationProofs[i].batchMetadata.batchHeader.blobHeadersRoot,
keccak256(abi.encodePacked(EigenDAHasher.hashBlobHeader(blobHeaders[i]))),
blobVerificationProofs[i].blobIndex
),
"EigenDARollupUtils.verifyBlob: inclusion proof is invalid"
);

// bitmap of quorum numbers in all quorumBlobParams
uint256 confirmedQuorumsBitmap;

// require that the security param in each blob is met
for (uint j = 0; j < blobHeaders[i].quorumBlobParams.length; j++) {
// make sure that the quorumIndex matches the given quorumNumber
require(uint8(blobVerificationProofs[i].batchMetadata.batchHeader.quorumNumbers[uint8(blobVerificationProofs[i].quorumIndices[i])]) == blobHeaders[i].quorumBlobParams[i].quorumNumber,
"EigenDARollupUtils.verifyBlob: quorumNumber does not match"
);

// make sure that the adversaryThresholdPercentage is less than the given confirmationThresholdPercentage
require(blobHeaders[i].quorumBlobParams[i].adversaryThresholdPercentage
< blobHeaders[i].quorumBlobParams[i].confirmationThresholdPercentage,
"EigenDARollupUtils.verifyBlob: adversaryThresholdPercentage is not valid"
);

// make sure that the adversaryThresholdPercentage is at least the given quorumAdversaryThresholdPercentage
uint8 _adversaryThresholdPercentage = uint8(quorumAdversaryThresholdPercentages[blobHeaders[i].quorumBlobParams[j].quorumNumber]);
if(_adversaryThresholdPercentage > 0){
require(blobHeaders[i].quorumBlobParams[j].adversaryThresholdPercentage >= _adversaryThresholdPercentage,
"EigenDARollupUtils.verifyBlob: adversaryThresholdPercentage is not met"
);
}

// make sure that the stake signed for is greater than the given confirmationThresholdPercentage
require(uint8(blobVerificationProofs[i].batchMetadata.batchHeader.signedStakeForQuorums[uint8(blobVerificationProofs[i].quorumIndices[j])])
>= blobHeaders[i].quorumBlobParams[j].confirmationThresholdPercentage,
"EigenDARollupUtils.verifyBlob: confirmationThresholdPercentage is not met"
);

// mark confirmed quorum in the bitmap
confirmedQuorumsBitmap = BitmapUtils.setBit(confirmedQuorumsBitmap, blobHeaders[i].quorumBlobParams[j].quorumNumber);
}

// check that required quorums are a subset of the confirmed quorums
require(
BitmapUtils.isSubsetOf(
quorumNumbersRequiredBitmap,
confirmedQuorumsBitmap
),
"EigenDARollupUtils.verifyBlob: required quorums are not a subset of the confirmed quorums"
);
}
}

/**
* @notice gets the adversary threshold percentage for a given quorum
* @param eigenDAServiceManager the contract in which the batch was confirmed
Expand All @@ -106,7 +184,7 @@ library EigenDARollupUtils {
function getQuorumAdversaryThreshold(
IEigenDAServiceManager eigenDAServiceManager,
uint256 quorumNumber
) public view returns(uint8 adversaryThresholdPercentage) {
) internal view returns(uint8 adversaryThresholdPercentage) {
if(eigenDAServiceManager.quorumAdversaryThresholdPercentages().length > quorumNumber){
adversaryThresholdPercentage = uint8(eigenDAServiceManager.quorumAdversaryThresholdPercentages()[quorumNumber]);
}
Expand Down
8 changes: 8 additions & 0 deletions contracts/test/harnesses/EigenDABlobUtilsHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ contract EigenDABlobUtilsHarness is Test {
) external view {
EigenDARollupUtils.verifyBlob(blobHeader, eigenDAServiceManager, blobVerificationProof);
}

function verifyBlobs(
IEigenDAServiceManager.BlobHeader[] calldata blobHeaders,
IEigenDAServiceManager eigenDAServiceManager,
EigenDARollupUtils.BlobVerificationProof[] calldata blobVerificationProofs
) external view {
EigenDARollupUtils.verifyBlobs(blobHeaders, eigenDAServiceManager, blobVerificationProofs);
}
}
52 changes: 52 additions & 0 deletions contracts/test/unit/EigenDABlobUtils.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,58 @@ contract EigenDABlobUtilsUnit is BLSMockAVSDeployer {
emit log_named_uint("gas used", gasBefore - gasAfter);
}

function testVerifyBlobs_TwoBlobs(uint256 pseudoRandomNumber) public {
uint256 numQuorumBlobParams = 2;
IEigenDAServiceManager.BlobHeader[] memory blobHeader = new IEigenDAServiceManager.BlobHeader[](2);
blobHeader[0] = _generateRandomBlobHeader(pseudoRandomNumber, numQuorumBlobParams);
uint256 anotherPseudoRandomNumber = uint256(keccak256(abi.encodePacked(pseudoRandomNumber)));
blobHeader[1] = _generateRandomBlobHeader(anotherPseudoRandomNumber, numQuorumBlobParams);

IEigenDAServiceManager.BatchHeader memory batchHeader;
bytes memory firstBlobHash = abi.encodePacked(blobHeader[0].hashBlobHeader());
bytes memory secondBlobHash = abi.encodePacked(blobHeader[1].hashBlobHeader());
batchHeader.blobHeadersRoot = keccak256(abi.encodePacked(keccak256(firstBlobHash), keccak256(secondBlobHash)));
// add dummy quorum numbers and quorum threshold percentages making sure confirmationThresholdPercentage = adversaryThresholdPercentage + defaultCodingRatioPercentage
for (uint i = 0; i < blobHeader[1].quorumBlobParams.length; i++) {
batchHeader.quorumNumbers = abi.encodePacked(batchHeader.quorumNumbers, blobHeader[1].quorumBlobParams[i].quorumNumber);
batchHeader.signedStakeForQuorums = abi.encodePacked(batchHeader.signedStakeForQuorums, blobHeader[1].quorumBlobParams[i].adversaryThresholdPercentage + defaultCodingRatioPercentage);
}
batchHeader.referenceBlockNumber = uint32(block.number);

// add dummy batch metadata
IEigenDAServiceManager.BatchMetadata memory batchMetadata;
batchMetadata.batchHeader = batchHeader;
batchMetadata.signatoryRecordHash = keccak256(abi.encodePacked("signatoryRecordHash"));
batchMetadata.confirmationBlockNumber = defaultConfirmationBlockNumber;

stdstore
.target(address(eigenDAServiceManager))
.sig("batchIdToBatchMetadataHash(uint32)")
.with_key(defaultBatchId)
.checked_write(batchMetadata.hashBatchMetadata());

EigenDARollupUtils.BlobVerificationProof[] memory blobVerificationProofs = new EigenDARollupUtils.BlobVerificationProof[](2);
blobVerificationProofs[0].batchId = defaultBatchId;
blobVerificationProofs[1].batchId = defaultBatchId;
blobVerificationProofs[0].batchMetadata = batchMetadata;
blobVerificationProofs[1].batchMetadata = batchMetadata;
blobVerificationProofs[0].inclusionProof = abi.encodePacked(keccak256(secondBlobHash));
blobVerificationProofs[1].inclusionProof = abi.encodePacked(keccak256(firstBlobHash));
blobVerificationProofs[0].blobIndex = 0;
blobVerificationProofs[1].blobIndex = 1;
blobVerificationProofs[0].quorumIndices = new bytes(batchHeader.quorumNumbers.length);
blobVerificationProofs[1].quorumIndices = new bytes(batchHeader.quorumNumbers.length);
for (uint i = 0; i < batchHeader.quorumNumbers.length; i++) {
blobVerificationProofs[0].quorumIndices[i] = bytes1(uint8(i));
blobVerificationProofs[1].quorumIndices[i] = bytes1(uint8(i));
}

uint256 gasBefore = gasleft();
eigenDABlobUtilsHarness.verifyBlobs(blobHeader, eigenDAServiceManager, blobVerificationProofs);
uint256 gasAfter = gasleft();
emit log_named_uint("gas used", gasBefore - gasAfter);
}

function testVerifyBlob_InvalidMetadataHash(uint256 pseudoRandomNumber) public {
uint256 numQuorumBlobParams = pseudoRandomNumber % 192;
IEigenDAServiceManager.BlobHeader[] memory blobHeader = new IEigenDAServiceManager.BlobHeader[](2);
Expand Down

0 comments on commit fbe6d3b

Please sign in to comment.