-
Notifications
You must be signed in to change notification settings - Fork 187
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
Blob verification changes #321
Changes from 8 commits
9b62333
5846612
e23a416
de26613
a45d573
425ecee
01e0078
4fd381b
e77e762
c5f5ea7
e10147c
a4e17cb
167a096
00f9428
a30f1ef
ab1e30c
557a79c
022719f
39aefa3
a749f99
a33b415
7878dbb
4cb62d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ interface IEigenDAServiceManager is IServiceManager { | |
struct QuorumBlobParam { | ||
uint8 quorumNumber; | ||
uint8 adversaryThresholdPercentage; | ||
uint8 quorumThresholdPercentage; | ||
uint8 confirmationThresholdPercentage; | ||
uint32 chunkLength; // the length of the chunks in the quorum | ||
} | ||
|
||
|
@@ -45,7 +45,7 @@ interface IEigenDAServiceManager is IServiceManager { | |
struct BatchHeader { | ||
bytes32 blobHeadersRoot; | ||
bytes quorumNumbers; // each byte is a different quorum number | ||
bytes quorumThresholdPercentages; // every bytes is an amount less than 100 specifying the percentage of stake | ||
bytes signedStakeForQuorums; // every bytes is an amount less than 100 specifying the percentage of stake | ||
// the must have signed in the corresponding quorum in `quorumNumbers` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "must have signed" -> "have actually signed"? The former is the threshold, whereas this looks the actual signed stake |
||
uint32 referenceBlockNumber; | ||
} | ||
|
@@ -54,19 +54,9 @@ interface IEigenDAServiceManager is IServiceManager { | |
struct BatchMetadata { | ||
BatchHeader batchHeader; // the header of the data store | ||
bytes32 signatoryRecordHash; // the hash of the signatory record | ||
uint96 fee; // the amount of paymentToken paid for the datastore | ||
uint32 confirmationBlockNumber; // the block number at which the batch was confirmed | ||
} | ||
|
||
// Relevant metadata for a given datastore | ||
struct BatchMetadataWithSignatoryRecord { | ||
bytes32 batchHeaderHash; // the header hash of the data store | ||
uint32 referenceBlockNumber; // the block number at which stakes | ||
bytes32[] nonSignerPubkeyHashes; // the pubkeyHashes of all of the nonSigners | ||
uint96 fee; // the amount of paymentToken paid for the datastore | ||
uint32 blockNumber; // the block number at which the datastore was confirmed | ||
} | ||
|
||
// FUNCTIONS | ||
|
||
/// @notice mapping between the batchId to the hash of the metadata of the corresponding Batch | ||
|
@@ -94,4 +84,13 @@ interface IEigenDAServiceManager is IServiceManager { | |
|
||
/// @notice The maximum amount of blocks in the past that the service will consider stake amounts to still be 'valid'. | ||
function BLOCK_STALE_MEASURE() external view returns (uint32); | ||
|
||
/// @notice Returns the bytes array of quotaAdversaryThresholdPercentages | ||
0x0aa0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
function quorumAdversaryThresholdPercentages() external view returns (bytes memory); | ||
|
||
/// @notice Returns the bytes array of quotaAdversaryThresholdPercentages | ||
0x0aa0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
function quorumConfirmationThresholdPercentages() external view returns (bytes memory); | ||
|
||
/// @notice Returns the bytes array of quorumsNumbersRequired | ||
function quorumNumbersRequired() external view returns (bytes memory); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import {Merkle} from "eigenlayer-core/contracts/libraries/Merkle.sol"; | |
import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; | ||
import {EigenDAHasher} from "./EigenDAHasher.sol"; | ||
import {IEigenDAServiceManager} from "../interfaces/IEigenDAServiceManager.sol"; | ||
import {BitmapUtils} from "eigenlayer-middleware/libraries/BitmapUtils.sol"; | ||
|
||
/** | ||
* @title Library of functions to be used by smart contracts wanting to prove blobs on EigenDA and open KZG commitments. | ||
|
@@ -20,7 +21,7 @@ library EigenDARollupUtils { | |
uint8 blobIndex; | ||
IEigenDAServiceManager.BatchMetadata batchMetadata; | ||
bytes inclusionProof; | ||
bytes quorumThresholdIndexes; | ||
bytes quorumIndices; | ||
} | ||
|
||
/** | ||
|
@@ -50,25 +51,64 @@ library EigenDARollupUtils { | |
"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 i = 0; i < blobHeader.quorumBlobParams.length; i++) { | ||
// make sure that the quorumIndex matches the given quorumNumber | ||
require(uint8(blobVerificationProof.batchMetadata.batchHeader.quorumNumbers[uint8(blobVerificationProof.quorumThresholdIndexes[i])]) == blobHeader.quorumBlobParams[i].quorumNumber, | ||
require(uint8(blobVerificationProof.batchMetadata.batchHeader.quorumNumbers[uint8(blobVerificationProof.quorumIndices[i])]) == blobHeader.quorumBlobParams[i].quorumNumber, | ||
"EigenDARollupUtils.verifyBlob: quorumNumber does not match" | ||
); | ||
|
||
// make sure that the adversaryThresholdPercentage is less than the given quorumThresholdPercentage | ||
// make sure that the adversaryThresholdPercentage is less than the given confirmationThresholdPercentage | ||
require(blobHeader.quorumBlobParams[i].adversaryThresholdPercentage | ||
< blobHeader.quorumBlobParams[i].quorumThresholdPercentage, | ||
< blobHeader.quorumBlobParams[i].confirmationThresholdPercentage, | ||
"EigenDARollupUtils.verifyBlob: adversaryThresholdPercentage is not valid" | ||
); | ||
|
||
// make sure that the stake signed for is greater than the given quorumThresholdPercentage | ||
require(uint8(blobVerificationProof.batchMetadata.batchHeader.quorumThresholdPercentages[uint8(blobVerificationProof.quorumThresholdIndexes[i])]) | ||
>= blobHeader.quorumBlobParams[i].quorumThresholdPercentage, | ||
"EigenDARollupUtils.verifyBlob: quorumThresholdPercentage is not met" | ||
// make sure that the adversaryThresholdPercentage is at least the given quorumAdversaryThresholdPercentage | ||
uint8 _adversaryThresholdPercentage = getQuorumAdversaryThreshold(eigenDAServiceManager, blobHeader.quorumBlobParams[i].quorumNumber); | ||
if(_adversaryThresholdPercentage > 0){ | ||
require(blobHeader.quorumBlobParams[i].adversaryThresholdPercentage >= _adversaryThresholdPercentage, | ||
"EigenDARollupUtils.verifyBlob: adversaryThresholdPercentage is not met" | ||
); | ||
} | ||
|
||
// make sure that the stake signed for is greater than the given confirmationThresholdPercentage | ||
require(uint8(blobVerificationProof.batchMetadata.batchHeader.signedStakeForQuorums[uint8(blobVerificationProof.quorumIndices[i])]) | ||
>= blobHeader.quorumBlobParams[i].confirmationThresholdPercentage, | ||
"EigenDARollupUtils.verifyBlob: confirmationThresholdPercentage is not met" | ||
); | ||
|
||
// mark confirmed quorum in the bitmap | ||
confirmedQuorumsBitmap = BitmapUtils.setBit(confirmedQuorumsBitmap, blobHeader.quorumBlobParams[i].quorumNumber); | ||
} | ||
|
||
// check that required quorums are a subset of the confirmed quorums | ||
require( | ||
BitmapUtils.isSubsetOf( | ||
BitmapUtils.orderedBytesArrayToBitmap( | ||
eigenDAServiceManager.quorumNumbersRequired() | ||
), | ||
confirmedQuorumsBitmap | ||
), | ||
"EigenDARollupUtils.verifyBlob: confirmed quorums are not a subset of the required quorums" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: error message is reversed, should be "required quorums are not a subset of the confirmed quorums" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch |
||
); | ||
} | ||
|
||
/** | ||
* @notice gets the adversary threshold percentage for a given quorum | ||
* @param eigenDAServiceManager the contract in which the batch was confirmed | ||
* @param quorumNumber the quorum number to get the adversary threshold percentage for | ||
* @dev returns 0 if the quorumNumber is not found | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no clause setting to 0 below for invalid quorumNumber. Does it implicit do so in Sol? Is it a good practice to set it explicitly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes because adversaryThresholdPercentage is defined as 0 by default with return it will return zero if a quorum is not found |
||
*/ | ||
function getQuorumAdversaryThreshold( | ||
IEigenDAServiceManager eigenDAServiceManager, | ||
uint256 quorumNumber | ||
) public view returns(uint8 adversaryThresholdPercentage) { | ||
if(eigenDAServiceManager.quorumAdversaryThresholdPercentages().length > quorumNumber){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should it be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be > I think. If only quorum 0 and 1 are set we want to make sure those numbers are less than the length of the byte array (2) |
||
adversaryThresholdPercentage = uint8(eigenDAServiceManager.quorumAdversaryThresholdPercentages()[quorumNumber]); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
|
||
pragma solidity ^0.8.9; | ||
|
||
import {EigenDARollupUtils} from "../libraries/EigenDARollupUtils.sol"; | ||
|
@@ -8,87 +7,60 @@ import {IEigenDAServiceManager} from "../interfaces/IEigenDAServiceManager.sol"; | |
import {BN254} from "eigenlayer-middleware/libraries/BN254.sol"; | ||
|
||
struct Commitment { | ||
address validator; // validator who posted the commitment | ||
address confirmer; // confirmer who posted the commitment | ||
uint32 dataLength; // length of the data | ||
BN254.G1Point polynomialCommitment; // commitment to the polynomial | ||
} | ||
|
||
/** | ||
* @title MockRollup | ||
* @author Layr Labs, Inc. | ||
* @notice This contract is used to emulate a rollup contract for the purpose of testing the rollup interface. | ||
*/ | ||
contract MockRollup { | ||
|
||
IEigenDAServiceManager public eigenDAServiceManager; // EigenDASM contract | ||
BN254.G1Point public tau; //power of tau | ||
uint256 public illegalValue; // special "illegal" value that should not be included in blob | ||
uint256 public stakeRequired; // amount of stake required to register as a validator | ||
|
||
///@notice mapping of validators who have registered | ||
mapping(address => bool) public validators; | ||
///@notice mapping of validators who have been blacklisted | ||
mapping(address => bool) public blacklist; | ||
///@notice mapping of timestamps to commitments | ||
mapping(uint256 => Commitment) public commitments; | ||
|
||
constructor(IEigenDAServiceManager _eigenDAServiceManager, BN254.G1Point memory _tau, uint256 _illegalValue, uint256 _stakeRequired) { | ||
constructor(IEigenDAServiceManager _eigenDAServiceManager, BN254.G1Point memory _tau) { | ||
eigenDAServiceManager = _eigenDAServiceManager; | ||
tau = _tau; | ||
illegalValue = _illegalValue; | ||
stakeRequired = _stakeRequired; | ||
} | ||
|
||
///@notice registers msg.sender as validator by putting up 1 ether of stake | ||
function registerValidator() external payable { | ||
require(msg.value == stakeRequired, "MockRollup.registerValidator: Must send stake required to register"); | ||
require(!validators[msg.sender], "MockRollup.registerValidator: Validator already registered"); | ||
require(!blacklist[msg.sender], "MockRollup.registerValidator: Validator blacklisted"); | ||
validators[msg.sender] = true; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can anyone call mockrollup postcommitment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes |
||
/** | ||
* @notice a function for validators to post a commitment to a blob on behalf of the rollup | ||
* @notice a function for a confirmer to post a commitment to a blob and verfiy it on EigenDA | ||
* @param blobHeader the blob header | ||
* @param blobVerificationProof the blob verification proof | ||
*/ | ||
function postCommitment( | ||
IEigenDAServiceManager.BlobHeader memory blobHeader, | ||
EigenDARollupUtils.BlobVerificationProof memory blobVerificationProof | ||
) external { | ||
require(validators[msg.sender], "MockRollup.postCommitment: Validator not registered"); | ||
require(commitments[block.timestamp].validator == address(0), "MockRollup.postCommitment: Commitment already posted"); | ||
// require commitment has not already been posted | ||
require(commitments[block.timestamp].confirmer == address(0), "MockRollup.postCommitment: Commitment already posted"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should there be a check on who can call confirmer? or should the comment be not a confirmer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the check to simplify so that we can just use any address to verify the blobs |
||
|
||
// verify that the blob was included in the batch | ||
EigenDARollupUtils.verifyBlob(blobHeader, eigenDAServiceManager, blobVerificationProof); | ||
|
||
// store the commitment | ||
commitments[block.timestamp] = Commitment(msg.sender, blobHeader.dataLength, blobHeader.commitment); | ||
} | ||
|
||
/** | ||
* @notice a function for users to challenge a commitment that contains the illegal value | ||
* @notice a function for users to challenge a commitment against a provided value | ||
* @param timestamp the timestamp of the commitment being challenged | ||
* @param point the point on the polynomial to evaluate | ||
* @param proof revelvant KZG proof | ||
* @param challengeValue The value expected upon opening the commitment | ||
*/ | ||
function challengeCommitment(uint256 timestamp, uint256 point, BN254.G2Point memory proof) external { | ||
function challengeCommitment(uint256 timestamp, uint256 point, BN254.G2Point memory proof, uint256 challengeValue) external returns (bool) { | ||
Commitment memory commitment = commitments[timestamp]; | ||
require(commitment.validator != address(0), "MockRollup.challengeCommitment: Commitment not posted"); | ||
// require the commitment exists | ||
require(commitment.confirmer != address(0), "MockRollup.challengeCommitment: Commitment not posted"); | ||
|
||
// point on the polynomial must be less than the length of the data stored | ||
require(point < commitment.dataLength, "MockRollup.challengeCommitment: Point must be less than data length"); | ||
|
||
// verify that the commitment contains the illegal value | ||
require(EigenDARollupUtils.openCommitment(point, illegalValue, tau, commitment.polynomialCommitment, proof), "MockRollup.challengeCommitment: Does not evaluate to illegal value"); | ||
|
||
// blacklist the validator | ||
validators[commitment.validator] = false; | ||
blacklist[commitment.validator] = true; | ||
|
||
// send validators stake to the user who challenged the commitment | ||
(bool success, ) = msg.sender.call{value: 1 ether}(""); | ||
require(success); | ||
|
||
// verify that the commitment contains the challenge value | ||
return EigenDARollupUtils.openCommitment(point, challengeValue, tau, commitment.polynomialCommitment, proof); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very cryptic. Can we comment on what this means?