diff --git a/contracts/src/libraries/EigenDARollupUtils.sol b/contracts/src/libraries/EigenDARollupUtils.sol index e89819e0e9..cee88ef897 100644 --- a/contracts/src/libraries/EigenDARollupUtils.sol +++ b/contracts/src/libraries/EigenDARollupUtils.sol @@ -134,7 +134,7 @@ library EigenDARollupUtils { uint256 confirmedQuorumsBitmap; // require that the security param in each blob is met - for (uint j = 0; i < blobHeaders[i].quorumBlobParams.length; j++) { + 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" diff --git a/contracts/test/harnesses/EigenDABlobUtilsHarness.sol b/contracts/test/harnesses/EigenDABlobUtilsHarness.sol index 8a4c7a4582..1a3ceb17c3 100644 --- a/contracts/test/harnesses/EigenDABlobUtilsHarness.sol +++ b/contracts/test/harnesses/EigenDABlobUtilsHarness.sol @@ -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); + } } diff --git a/contracts/test/unit/EigenDABlobUtils.t.sol b/contracts/test/unit/EigenDABlobUtils.t.sol index 5596a82911..aa7afed0fd 100644 --- a/contracts/test/unit/EigenDABlobUtils.t.sol +++ b/contracts/test/unit/EigenDABlobUtils.t.sol @@ -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);