Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

verifyBlobs util #862

Merged
merged 3 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions contracts/src/libraries/EigenDARollupUtils.sol
Original file line number Diff line number Diff line change
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[] calldata blobHeaders,
IEigenDAServiceManager eigenDAServiceManager,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this the first param imo

BlobVerificationProof[] calldata blobVerificationProofs
) external 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]))),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double hash?

blobVerificationProofs[i].blobIndex
),
"EigenDARollupUtils.verifyBlob: inclusion proof is invalid"
);

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't return this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not, why not just point to the indices in quorumBlobParams to verify?


// 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){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this check?

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 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
Loading