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

CertVerifier updates #1207

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
16 changes: 11 additions & 5 deletions contracts/src/core/EigenDACertVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ contract EigenDACertVerifier is IEigenDACertVerifier {
* @param batchHeader The batch header of the blob
* @param blobInclusionInfo The inclusion proof for the blob cert
* @param nonSignerStakesAndSignature The nonSignerStakesAndSignature to verify the blob cert against
* @param signedQuorumNumbers The signed quorum numbers corresponding to the nonSignerStakesAndSignature
*/
function verifyDACertV2(
BatchHeaderV2 calldata batchHeader,
BlobInclusionInfo calldata blobInclusionInfo,
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature,
bytes memory signedQuorumNumbers
) external view {
EigenDACertVerificationUtils._verifyDACertV2ForQuorums(
eigenDAThresholdRegistry,
Expand All @@ -112,7 +114,8 @@ contract EigenDACertVerifier is IEigenDACertVerifier {
blobInclusionInfo,
nonSignerStakesAndSignature,
securityThresholdsV2,
quorumNumbersRequiredV2
quorumNumbersRequiredV2,
signedQuorumNumbers
);
}

Expand Down Expand Up @@ -145,11 +148,13 @@ contract EigenDACertVerifier is IEigenDACertVerifier {
* @param batchHeader The batch header of the blob
* @param blobInclusionInfo The inclusion proof for the blob cert
* @param nonSignerStakesAndSignature The nonSignerStakesAndSignature to verify the blob cert against
* @param signedQuorumNumbers The signed quorum numbers corresponding to the nonSignerStakesAndSignature
*/
function verifyDACertV2ForZKProof(
BatchHeaderV2 calldata batchHeader,
BlobInclusionInfo calldata blobInclusionInfo,
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature,
bytes memory signedQuorumNumbers
Copy link
Contributor

Choose a reason for hiding this comment

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

intention is to let proxy to set those value for now, and later pulled from rollup config?

Copy link
Contributor

Choose a reason for hiding this comment

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

looks like it is not the case, the signedQuorumNumbers is actually the all quorum for all blobs a batch, I think it makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's what it currently is, but it doesn't NEED to be. As we discussed with Robert the GetBlobStatus endpoint could eventually be optimized to return a different signature for each blob request. I vote to rename signedQuorumNumbers to sigmaQuorumNumbers so that the sigma name matches the sigma inside the NonSignerStakesAndSignature (sigma = signature)

) external view returns (bool) {
try EigenDACertVerificationUtils.verifyDACertV2ForQuorumsExternal(
eigenDAThresholdRegistry,
Expand All @@ -159,7 +164,8 @@ contract EigenDACertVerifier is IEigenDACertVerifier {
blobInclusionInfo,
nonSignerStakesAndSignature,
securityThresholdsV2,
quorumNumbersRequiredV2
quorumNumbersRequiredV2,
signedQuorumNumbers
) {
return true;
} catch {
Expand All @@ -175,7 +181,7 @@ contract EigenDACertVerifier is IEigenDACertVerifier {
*/
function getNonSignerStakesAndSignature(
SignedBatch calldata signedBatch
) external view returns (NonSignerStakesAndSignature memory) {
) external view returns (NonSignerStakesAndSignature memory, bytes memory) {
return EigenDACertVerificationUtils._getNonSignerStakesAndSignature(
operatorStateRetriever,
registryCoordinator,
Expand Down
22 changes: 20 additions & 2 deletions contracts/src/interfaces/IEigenDACertVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ interface IEigenDACertVerifier is IEigenDAThresholdRegistry {
* @param batchHeader The batch header of the blob
* @param blobInclusionInfo The inclusion proof for the blob cert
* @param nonSignerStakesAndSignature The nonSignerStakesAndSignature to verify the blob cert against
* @param signedQuorumNumbers The signed quorum numbers corresponding to the nonSignerStakesAndSignature
*/
function verifyDACertV2(
BatchHeaderV2 calldata batchHeader,
BlobInclusionInfo calldata blobInclusionInfo,
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature,
bytes memory signedQuorumNumbers
) external view;

/**
Expand All @@ -49,13 +51,29 @@ interface IEigenDACertVerifier is IEigenDAThresholdRegistry {
BlobInclusionInfo calldata blobInclusionInfo
) external view;

/**
* @notice Thin try/catch wrapper around verifyDACertV2 that returns false instead of panicing
* @dev The Steel library (https://github.com/risc0/risc0-ethereum/tree/main/crates/steel)
* currently has a limitation that it can only create zk proofs for functions that return a value
* @param batchHeader The batch header of the blob
* @param blobInclusionInfo The inclusion proof for the blob cert
* @param nonSignerStakesAndSignature The nonSignerStakesAndSignature to verify the blob cert against
* @param signedQuorumNumbers The signed quorum numbers corresponding to the nonSignerStakesAndSignature
*/
function verifyDACertV2ForZKProof(
BatchHeaderV2 calldata batchHeader,
BlobInclusionInfo calldata blobInclusionInfo,
NonSignerStakesAndSignature calldata nonSignerStakesAndSignature,
bytes memory signedQuorumNumbers
) external view returns (bool);

/**
* @notice Returns the nonSignerStakesAndSignature for a given blob cert and signed batch
* @param signedBatch The signed batch to get the nonSignerStakesAndSignature for
*/
function getNonSignerStakesAndSignature(
SignedBatch calldata signedBatch
) external view returns (NonSignerStakesAndSignature memory);
) external view returns (NonSignerStakesAndSignature memory, bytes memory);
Copy link
Contributor

Choose a reason for hiding this comment

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

can you document what the second bytes param is being returned is

Copy link
Contributor

Choose a reason for hiding this comment

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

ideally by giving it a name here (can we do this?)


/**
* @notice Verifies the security parameters for a blob cert
Expand Down
71 changes: 49 additions & 22 deletions contracts/src/libraries/EigenDACertVerificationUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ library EigenDACertVerificationUtils {
BlobInclusionInfo memory blobInclusionInfo,
NonSignerStakesAndSignature memory nonSignerStakesAndSignature,
SecurityThresholds memory securityThresholds,
bytes memory requiredQuorumNumbers
bytes memory requiredQuorumNumbers,
bytes memory signedQuorumNumbers
) internal view {
require(
Merkle.verifyInclusionKeccak(
Expand All @@ -184,7 +185,7 @@ library EigenDACertVerificationUtils {
bytes32 signatoryRecordHash
) = signatureVerifier.checkSignatures(
EigenDAHasher.hashBatchHeaderV2(batchHeader),
blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers,
signedQuorumNumbers,
Copy link
Contributor

Choose a reason for hiding this comment

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

what was the issue exactly when quorumNumbers of the blob is strict subset of those in the batch? Guessing the signedQuorumNumbers we're passing in here are those that have actually signed on the batch, whereas the quorumNumbers in the blobHeader are those that were requested?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it right that signedQuorumNumber is based on quorum's from a batch, which contains all quorum for all blobs.

Copy link
Contributor

Choose a reason for hiding this comment

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

would be good to put a comment

Copy link
Contributor

Choose a reason for hiding this comment

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

Right now yes, but doesn't necessarily have to be that way. sigma and this sigmaQuorumNumbers could be a subset of the actual batch signers, as long as it contains the quorums that the blob requested. see #1207 (comment)

batchHeader.referenceBlockNumber,
nonSignerStakesAndSignature
);
Expand All @@ -201,7 +202,7 @@ library EigenDACertVerificationUtils {

uint256 confirmedQuorumsBitmap;

for (uint i = 0; i < blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers.length; i++) {
for (uint i = 0; i < signedQuorumNumbers.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

think we still only want to check the ones in the blobHeader here? Since those are the only quorums that the requester paid for and cares about

require(
quorumStakeTotals.signedStakeForQuorum[i] * THRESHOLD_DENOMINATOR >=
quorumStakeTotals.totalStakeForQuorum[i] * securityThresholds.confirmationThreshold,
Expand All @@ -210,16 +211,24 @@ library EigenDACertVerificationUtils {

confirmedQuorumsBitmap = BitmapUtils.setBit(
confirmedQuorumsBitmap,
uint8(blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers[i])
uint8(signedQuorumNumbers[i])
);
}

require(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a comment above this saying something like:
"We check that the quorums that are part of the batch signatures contain both 1) the quorums requested by the particular blob, and 2) the eigenda required quorums"

BitmapUtils.isSubsetOf(
BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers),
BitmapUtils.orderedBytesArrayToBitmap(blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers),
confirmedQuorumsBitmap
),
"EigenDACertVerificationUtils._verifyDACertV2ForQuorums: required quorums are not a subset of the confirmed quorums"
"EigenDACertVerificationUtils._verifyDACertV2ForQuorums: blob quorums are not a subset of the confirmed quorums"
Copy link
Contributor

Choose a reason for hiding this comment

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

The error message would be a lot more informative if it contained the actual quorum numbers that have not been signed. Is it possible to check each quorum one by one instead of checking the subset and having a generic error msg?

);

require(
BitmapUtils.isSubsetOf(
BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers),
BitmapUtils.orderedBytesArrayToBitmap(blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers)
),
"EigenDACertVerificationUtils._verifyDACertV2ForQuorums: required quorums are not a subset of the blob quorums"
);
}

Expand All @@ -232,7 +241,8 @@ library EigenDACertVerificationUtils {
BlobInclusionInfo memory _blobInclusionInfo,
NonSignerStakesAndSignature memory _nonSignerStakesAndSignature,
SecurityThresholds memory _securityThresholds,
bytes memory _requiredQuorumNumbers
bytes memory _requiredQuorumNumbers,
bytes memory _signedQuorumNumbers
) external view {
EigenDACertVerificationUtils._verifyDACertV2ForQuorums(
_eigenDAThresholdRegistry,
Expand All @@ -242,7 +252,8 @@ library EigenDACertVerificationUtils {
_blobInclusionInfo,
_nonSignerStakesAndSignature,
_securityThresholds,
_requiredQuorumNumbers
_requiredQuorumNumbers,
_signedQuorumNumbers
);
}

Expand All @@ -254,7 +265,8 @@ library EigenDACertVerificationUtils {
BlobInclusionInfo memory blobInclusionInfo,
NonSignerStakesAndSignature memory nonSignerStakesAndSignature,
SecurityThresholds[] memory securityThresholds,
bytes memory requiredQuorumNumbers
bytes memory requiredQuorumNumbers,
bytes memory signedQuorumNumbers
) internal view {
require(
securityThresholds.length == blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers.length,
Expand All @@ -276,7 +288,7 @@ library EigenDACertVerificationUtils {
bytes32 signatoryRecordHash
) = signatureVerifier.checkSignatures(
EigenDAHasher.hashBatchHeaderV2(batchHeader),
blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers,
signedQuorumNumbers,
batchHeader.referenceBlockNumber,
nonSignerStakesAndSignature
);
Expand All @@ -289,7 +301,7 @@ library EigenDACertVerificationUtils {
uint256 confirmedQuorumsBitmap;
VersionedBlobParams memory blobParams = eigenDAThresholdRegistry.getBlobParams(blobInclusionInfo.blobCertificate.blobHeader.version);

for (uint i = 0; i < blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers.length; i++) {
for (uint i = 0; i < signedQuorumNumbers.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

same comment as above. Why do we have this same code duplicated in both functions btw? Can we refactor into an internal function shared by both? Otherwise someone might update one and forget to update the other one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also do we still need the function _verifyDACertV2ForQuorumsForThresholds? Seems to be for allowing different thresholds for each quorum? This isn't possible right now right?
Can we document this in the natspec for this function

_verifyDACertSecurityParams(
blobParams,
securityThresholds[i]
Expand All @@ -303,16 +315,24 @@ library EigenDACertVerificationUtils {

confirmedQuorumsBitmap = BitmapUtils.setBit(
confirmedQuorumsBitmap,
uint8(blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers[i])
uint8(signedQuorumNumbers[i])
);
}

require(
BitmapUtils.isSubsetOf(
BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers),
BitmapUtils.orderedBytesArrayToBitmap(blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers),
confirmedQuorumsBitmap
),
"EigenDACertVerificationUtils._verifyDACertV2ForQuorums: required quorums are not a subset of the confirmed quorums"
"EigenDACertVerificationUtils._verifyDACertV2ForQuorums: blob quorums are not a subset of the confirmed quorums"
);

require(
BitmapUtils.isSubsetOf(
BitmapUtils.orderedBytesArrayToBitmap(requiredQuorumNumbers),
BitmapUtils.orderedBytesArrayToBitmap(blobInclusionInfo.blobCertificate.blobHeader.quorumNumbers)
),
"EigenDACertVerificationUtils._verifyDACertV2ForQuorums: required quorums are not a subset of the blob quorums"
);
}

Expand All @@ -327,7 +347,10 @@ library EigenDACertVerificationUtils {
SecurityThresholds memory securityThresholds,
bytes memory requiredQuorumNumbers
) internal view {
NonSignerStakesAndSignature memory nonSignerStakesAndSignature = _getNonSignerStakesAndSignature(
(
NonSignerStakesAndSignature memory nonSignerStakesAndSignature,
bytes memory signedQuorumNumbers
) = _getNonSignerStakesAndSignature(
operatorStateRetriever,
registryCoordinator,
signedBatch
Expand All @@ -341,7 +364,8 @@ library EigenDACertVerificationUtils {
blobInclusionInfo,
nonSignerStakesAndSignature,
securityThresholds,
requiredQuorumNumbers
requiredQuorumNumbers,
signedQuorumNumbers
);
}

Expand All @@ -356,7 +380,10 @@ library EigenDACertVerificationUtils {
SecurityThresholds[] memory securityThresholds,
bytes memory requiredQuorumNumbers
) internal view {
NonSignerStakesAndSignature memory nonSignerStakesAndSignature = _getNonSignerStakesAndSignature(
(
NonSignerStakesAndSignature memory nonSignerStakesAndSignature,
bytes memory signedQuorumNumbers
) = _getNonSignerStakesAndSignature(
operatorStateRetriever,
registryCoordinator,
signedBatch
Expand All @@ -370,29 +397,29 @@ library EigenDACertVerificationUtils {
blobInclusionInfo,
nonSignerStakesAndSignature,
securityThresholds,
requiredQuorumNumbers
requiredQuorumNumbers,
signedQuorumNumbers
);
}

function _getNonSignerStakesAndSignature(
OperatorStateRetriever operatorStateRetriever,
IRegistryCoordinator registryCoordinator,
SignedBatch memory signedBatch
) internal view returns (NonSignerStakesAndSignature memory nonSignerStakesAndSignature) {
) internal view returns (NonSignerStakesAndSignature memory nonSignerStakesAndSignature, bytes memory signedQuorumNumbers) {
bytes32[] memory nonSignerOperatorIds = new bytes32[](signedBatch.attestation.nonSignerPubkeys.length);
for (uint i = 0; i < signedBatch.attestation.nonSignerPubkeys.length; ++i) {
nonSignerOperatorIds[i] = BN254.hashG1Point(signedBatch.attestation.nonSignerPubkeys[i]);
}

bytes memory quorumNumbers;
for (uint i = 0; i < signedBatch.attestation.quorumNumbers.length; ++i) {
quorumNumbers = abi.encodePacked(quorumNumbers, uint8(signedBatch.attestation.quorumNumbers[i]));
signedQuorumNumbers = abi.encodePacked(signedQuorumNumbers, uint8(signedBatch.attestation.quorumNumbers[i]));
}

OperatorStateRetriever.CheckSignaturesIndices memory checkSignaturesIndices = operatorStateRetriever.getCheckSignaturesIndices(
registryCoordinator,
signedBatch.batchHeader.referenceBlockNumber,
quorumNumbers,
signedQuorumNumbers,
nonSignerOperatorIds
);

Expand Down
14 changes: 6 additions & 8 deletions contracts/test/unit/EigenDACertVerifierV2Unit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ contract EigenDACertVerifierV2Unit is MockEigenDADeployer {

eigenDACertVerifier.verifyDACertV2FromSignedBatch(signedBatch, blobInclusionInfo);

eigenDACertVerifier.verifyDACertV2(signedBatch.batchHeader, blobInclusionInfo, nonSignerStakesAndSignature);

NonSignerStakesAndSignature memory _nonSignerStakesAndSignature = eigenDACertVerifier.getNonSignerStakesAndSignature(signedBatch);
eigenDACertVerifier.verifyDACertV2(signedBatch.batchHeader, blobInclusionInfo, _nonSignerStakesAndSignature);
(NonSignerStakesAndSignature memory _nonSignerStakesAndSignature, bytes memory signedQuorumNumbers) = eigenDACertVerifier.getNonSignerStakesAndSignature(signedBatch);
eigenDACertVerifier.verifyDACertV2(signedBatch.batchHeader, blobInclusionInfo, _nonSignerStakesAndSignature, signedQuorumNumbers);
}

function test_verifyDACertV2ZK_True(uint256 pseudoRandomNumber) public {
Expand All @@ -61,8 +59,8 @@ contract EigenDACertVerifierV2Unit is MockEigenDADeployer {

_registerRelayKeys();

NonSignerStakesAndSignature memory _nonSignerStakesAndSignature = eigenDACertVerifier.getNonSignerStakesAndSignature(signedBatch);
bool zk = eigenDACertVerifier.verifyDACertV2ForZKProof(signedBatch.batchHeader, blobInclusionInfo, _nonSignerStakesAndSignature);
(NonSignerStakesAndSignature memory _nonSignerStakesAndSignature, bytes memory signedQuorumNumbers) = eigenDACertVerifier.getNonSignerStakesAndSignature(signedBatch);
bool zk = eigenDACertVerifier.verifyDACertV2ForZKProof(signedBatch.batchHeader, blobInclusionInfo, _nonSignerStakesAndSignature, signedQuorumNumbers);
assert(zk);
}

Expand All @@ -83,8 +81,8 @@ contract EigenDACertVerifierV2Unit is MockEigenDADeployer {
nonSignerStakesAndSignature.totalStakeIndices = nssas.totalStakeIndices;
nonSignerStakesAndSignature.nonSignerStakeIndices = nssas.nonSignerStakeIndices;

NonSignerStakesAndSignature memory _nonSignerStakesAndSignature = eigenDACertVerifier.getNonSignerStakesAndSignature(signedBatch);
bool zk = eigenDACertVerifier.verifyDACertV2ForZKProof(signedBatch.batchHeader, blobInclusionInfo, _nonSignerStakesAndSignature);
(NonSignerStakesAndSignature memory _nonSignerStakesAndSignature, bytes memory signedQuorumNumbers) = eigenDACertVerifier.getNonSignerStakesAndSignature(signedBatch);
bool zk = eigenDACertVerifier.verifyDACertV2ForZKProof(signedBatch.batchHeader, blobInclusionInfo, _nonSignerStakesAndSignature, signedQuorumNumbers);
assert(!zk);
}

Expand Down
Loading