Skip to content

Commit cc81736

Browse files
authored
chore: CertificateVerifier clarifications (#1552)
**Motivation:** Add common functions to `BN254CertificateVerifier`. **Modifications:** - Add `getTotalStakeWeights` to `BN254CertificateVerifier` - Add usage patterns documentation **Result:** Clearer docs
1 parent b1f5f47 commit cc81736

File tree

8 files changed

+248
-83
lines changed

8 files changed

+248
-83
lines changed

docs/multichain/destination/CertificateVerifier.md

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Libraries and Mixins:
1212
| -------- | -------- | -------- |
1313
| [`ECDSA.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.0/contracts/utils/cryptography/ECDSA.sol) | ECDSACertificateVerifier | ECDSA signature recovery |
1414
| [`SignatureUtilsMixin.sol`](../../../src/contracts/mixins/SignatureUtilsMixin.sol) | ECDSACertificateVerifier | EIP-712 and signature validation |
15-
| [`BN254.sol`](../../../src/contracts/libraries/BN254.sol) | BN254CertificateVerifier | BN254 curve operations |
15+
| [`BN254.sol`](../../../src/contracts/libraries/BN254.sol) | BN254CertificateVerifier | BN254CertificateVerifier |
1616
| [`BN254SignatureVerifier.sol`](../../../src/contracts/libraries/BN254SignatureVerifier.sol) | BN254CertificateVerifier | BLS signature verification |
1717
| [`Merkle.sol`](../../../src/contracts/libraries/Merkle.sol) | BN254CertificateVerifier | Merkle proof verification |
1818
| [`SemVerMixin.sol`](../../../src/contracts/mixins/SemVerMixin.sol) | BN254CertificateVerifier | Semantic versioning |
@@ -24,7 +24,7 @@ Libraries and Mixins:
2424

2525
The CertificateVerifier contracts are responsible for verifying certificates from an offchain task, on-chain. The operatorSet tables are configured by the [`OperatorTableUpdater`](./OperatorTableUpdater.md) and updated in the `CertificateVerifier` by an offchain process. These contracts support two signature schemes: ECDSA for individual signatures and BN254 for aggregated signatures.
2626

27-
Both verifiers implement staleness checks based on a `maxStalenessPeriod` to ensure certificates are not verified against outdated operator information.
27+
Certificates can be created at any time, but must contain a `referenceTimestamp`, which informs the contract of which operator table for the `operatorSet` to use. Both certificate verifiers implement staleness checks based on a `maxStalenessPeriod` to ensure certificates are not verified against outdated operator information.
2828

2929
**Note: Setting a max staleness period to 0 enables certificates to be confirmed against any `referenceTimestamp`. In addition, setting a `maxStalenessPeriod` that is greater than 0 and less than the frequency of table updates (daily on testnet, weekly on mainnet) is impossible due bounds enfroced by the [`CrossChainRegistry`](../source/CrossChainRegistry.md#parameterization).** See the [staleness period](#staleness-period) in the appendix for some examples.
3030

@@ -106,8 +106,7 @@ For the `msgHash`, it is up to the off-chain AVS software to add relevant metada
106106
```solidity
107107
/**
108108
* @notice A Certificate used to verify a set of ECDSA signatures
109-
* @param referenceTimestamp the timestamp at which the certificate was
110-
* created, which MUST correspond to a reference timestamp of the operator table update
109+
* @param referenceTimestamp a reference timestamp that corresponds to an operator table update
111110
* @param messageHash the hash of the message that was signed by the operators.
112111
* The messageHash should be calculated using `calculateCertificateDigest`
113112
* @param sig the concatenated signature of each signing operator, in ascending order of signer address
@@ -121,10 +120,12 @@ struct ECDSACertificate {
121120
122121
/**
123122
* @notice verifies a certificate
123+
* @param operatorSet the operatorSet that the certificate is for
124124
* @param cert a certificate
125-
* @return signedStakes amount of stake that signed the certificate for each stake
126-
* type. Each index corresponds to a stake type in the `weights` array in the `ECDSAOperatorInfo`
125+
* @return totalSignedStakeWeights total stake weight that signed the certificate for each stake type. Each
126+
* index corresponds to a stake type in the `weights` array in the `ECDSAOperatorInfo`
127127
* @return signers array of addresses that signed the certificate
128+
* @dev This function DOES NOT support smart contact signatures
128129
*/
129130
function verifyCertificate(
130131
OperatorSet calldata operatorSet,
@@ -361,8 +362,7 @@ The contract supports 3 verification patterns:
361362
```solidity
362363
/**
363364
* @notice A BN254 Certificate
364-
* @param referenceTimestamp the timestamp at which the certificate was created,
365-
* which MUST correspond to a reference timestamp of the operator table update
365+
* @param referenceTimestamp a reference timestamp that corresponds to an operator table update
366366
* @param messageHash the hash of the message that was signed by operators and used to verify the aggregated signature
367367
* @param signature the G1 signature of the message
368368
* @param apk the G2 aggregate public key
@@ -535,3 +535,109 @@ The operator table is updated every 10 days. The staleness period is 5 days. The
535535
4. Day 7: A new certificate is generated. However, this will fail as the `referenceTimestamp` would still be Day 1 given that was the latest table update
536536

537537
Note that we cannot re-generate a certificate on Day 7. This is why we prevent the `stalenessPeriod` from being less than 10 days in the `CrossChainRegistry`.
538+
539+
## Consumption Patterns
540+
541+
### Introspection
542+
543+
Both the `BN254CertificateVerifier` and `ECDSACertificateVerifier` share the following view functions
544+
545+
```solidity
546+
/**
547+
* @notice The latest reference timestamp of the operator table for a given operatorSet. This value is
548+
* updated each time an operator table is updated
549+
* @param operatorSet The operatorSet to get the latest reference timestamp of
550+
* @return The latest reference timestamp, 0 if the operatorSet has never been updated
551+
*/
552+
function latestReferenceTimestamp(
553+
OperatorSet memory operatorSet
554+
) external view returns (uint32);
555+
556+
/**
557+
* @notice Whether the operator table has been updated for a given reference timestamp
558+
* @param operatorSet The operatorSet to check
559+
* @param referenceTimestamp The reference timestamp to check
560+
* @return Whether the reference timestamp has been updated
561+
* @dev The reference timestamp is set when the operator table is updated
562+
*/
563+
function isReferenceTimestampSet(
564+
OperatorSet memory operatorSet,
565+
uint32 referenceTimestamp
566+
) external view returns (bool);
567+
568+
/**
569+
* @notice Get the total stake weights for all operators at a given reference timestamp
570+
* @param operatorSet The operator set to calculate stakes for
571+
* @param referenceTimestamp The reference timestamp
572+
* @return The sum of stake weights for each stake type, empty if the operatorSet has not been updated for the given reference timestamp
573+
* @dev For ECDSA, this function *reverts* if the reference timestamp is not set or the number of operators is 0
574+
* @dev For BN254, this function returns empty array if the reference timestamp is not set or the number of operators is 0
575+
*/
576+
function getTotalStakeWeights(
577+
OperatorSet memory operatorSet,
578+
uint32 referenceTimestamp
579+
) external view returns (uint256[] memory);
580+
581+
/**
582+
* @notice Get the number of operators at a given reference timestamp
583+
* @param operatorSet The operator set to get the number of operators for
584+
* @param referenceTimestamp The reference timestamp
585+
* @return The number of operators
586+
* @dev Returns 0 if the reference timestamp is not set or the number of operators is 0
587+
*/
588+
function getOperatorCount(
589+
OperatorSet memory operatorSet,
590+
uint32 referenceTimestamp
591+
) external view returns (uint256);
592+
```
593+
594+
The `getTotalStakeWeights` function should be read by consumers before passing in expected proportional or nominal amounts into the `verifyCertificateProportion` or `verifyCertificateNominal` respectively.
595+
596+
The `latestReferenceTimestamp` should be called by AVSs offchain aggregator to pass in a `referenceTimestasmp` into the `Certificate`
597+
598+
To retrieve the operators and their weights from an operatorSet, the AVS offchain aggregator can call the following function on the operatorSet's `OperatorTableCalculator`, which can be retrieved from the `CrossChainRegistry`.
599+
600+
```solidity
601+
/**
602+
* @notice Get the operator stake weights for a given operatorSet
603+
* @param operatorSet The operatorSet to get the stake weights for
604+
* @return operators The addresses of the operators in the operatorSet
605+
* @return weights The stake weights for each operator in the operatorSet, this is a 2D array where the first index is the operator
606+
* and the second index is the stake weight
607+
*/
608+
function getOperatorSetWeights(
609+
OperatorSet calldata operatorSet
610+
) external view returns (address[] memory operators, uint256[][] memory weights);
611+
```
612+
613+
### End to End Verification
614+
615+
The below diagram describes an end to end verification process for verifying a certificate with nominal thresholds. Solid lines are on-chain write interactions. Dashed lines are read operations, either on- or off- chain.
616+
617+
```mermaid
618+
sequenceDiagram
619+
participant Transporter as EigenLabs Transporter
620+
participant OUT as OperatorTableUpdater
621+
participant OTC as OperatorTableCalculator
622+
participant CV as CertificateVerifier
623+
participant Aggregator as AVS Aggregator
624+
participant Registry as CrossChainRegistry
625+
participant Consumer as AVS Consumer
626+
627+
Transporter->>OUT: 1. updateOperatorTable()
628+
OUT->>CV: updateOperatorTable()
629+
630+
Aggregator-->>CV: 2. Get latestReferenceTimestamp()
631+
Aggregator-->>Registry: 3. getOperatorTableCalculator(operatorSet)
632+
Aggregator-->>OTC: 4. getOperatorSetWeights() at referenceTimestamp
633+
634+
Aggregator-->>Aggregator: 5. create task & collect operator signatures
635+
Aggregator-->>Aggregator: 6. generate certificate with latestReferenceTimestamp & signatures
636+
637+
Consumer-->>Aggregator: 7. retrieve certificate
638+
Consumer-->>CV: 8. getTotalStakeWeights()
639+
Consumer-->>Consumer: 9. adjust nominal verification thresholds
640+
Consumer->>CV: 10. verifyCertificateNominal()
641+
```
642+
643+

src/contracts/interfaces/IBN254CertificateVerifier.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ interface IBN254CertificateVerifierTypes is IOperatorTableCalculatorTypes {
2323

2424
/**
2525
* @notice A BN254 Certificate
26-
* @param referenceTimestamp the timestamp at which the certificate was created,
27-
* which MUST correspond to a reference timestamp of the operator table update
26+
* @param referenceTimestamp a reference timestamp that corresponds to an operator table update
2827
* @param messageHash the hash of the message that was signed by operators and used to verify the aggregated signature
2928
* @param signature the G1 signature of the message
3029
* @param apk the G2 aggregate public key

src/contracts/interfaces/IBaseCertificateVerifier.sol

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,46 @@ interface IBaseCertificateVerifier is
6666
* updated each time an operator table is updated
6767
* @param operatorSet The operatorSet to get the latest reference timestamp of
6868
* @return The latest reference timestamp, 0 if the operatorSet has never been updated
69+
* @dev The latest reference timestamp is set when the operator table is updated
6970
*/
7071
function latestReferenceTimestamp(
7172
OperatorSet memory operatorSet
7273
) external view returns (uint32);
7374

7475
/**
75-
* @notice Whether the reference timestamp has been updated for a given operatorSet
76+
* @notice Whether the operator table has been updated for a given reference timestamp
7677
* @param operatorSet The operatorSet to check
7778
* @param referenceTimestamp The reference timestamp to check
7879
* @return Whether the reference timestamp has been updated
80+
* @dev The reference timestamp is set when the operator table is updated
7981
*/
8082
function isReferenceTimestampSet(
8183
OperatorSet memory operatorSet,
8284
uint32 referenceTimestamp
8385
) external view returns (bool);
86+
87+
/**
88+
* @notice Get the total stake weights for all operators at a given reference timestamp
89+
* @param operatorSet The operator set to calculate stakes for
90+
* @param referenceTimestamp The reference timestamp
91+
* @return The sum of stake weights for each stake type, empty if the operatorSet has not been updated for the given reference timestamp
92+
* @dev For ECDSA, this function *reverts* if the reference timestamp is not set or the number of operators is 0
93+
* @dev For BN254, this function returns empty array if the reference timestamp is not set or the number of operators is 0
94+
*/
95+
function getTotalStakeWeights(
96+
OperatorSet memory operatorSet,
97+
uint32 referenceTimestamp
98+
) external view returns (uint256[] memory);
99+
100+
/**
101+
* @notice Get the number of operators at a given reference timestamp
102+
* @param operatorSet The operator set to get the number of operators for
103+
* @param referenceTimestamp The reference timestamp
104+
* @return The number of operators
105+
* @dev Returns 0 if the reference timestamp is not set or the number of operators is 0
106+
*/
107+
function getOperatorCount(
108+
OperatorSet memory operatorSet,
109+
uint32 referenceTimestamp
110+
) external view returns (uint256);
84111
}

src/contracts/interfaces/IECDSACertificateVerifier.sol

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ interface IECDSACertificateVerifierErrors {
1717
interface IECDSACertificateVerifierTypes is IOperatorTableCalculatorTypes {
1818
/**
1919
* @notice A Certificate used to verify a set of ECDSA signatures
20-
* @param referenceTimestamp the timestamp at which the certificate was
21-
* created, which MUST correspond to a reference timestamp of the operator table update
20+
* @param referenceTimestamp a reference timestamp that corresponds to an operator table update
2221
* @param messageHash the hash of the message that was signed by the operators. The messageHash
2322
* MUST be calculated using `calculateCertificateDigest`
2423
* @param sig the concatenated signature of each signing operator, in ascending order of signer address
@@ -64,16 +63,15 @@ interface IECDSACertificateVerifier is
6463
* @notice verifies a certificate
6564
* @param operatorSet the operatorSet that the certificate is for
6665
* @param cert a certificate
67-
* @return signedStakes total stake weight that signed the certificate for each stake type. Each
66+
* @return totalSignedStakeWeights total stake weight that signed the certificate for each stake type. Each
6867
* index corresponds to a stake type in the `weights` array in the `ECDSAOperatorInfo`
6968
* @return signers array of addresses that signed the certificate
70-
* @dev Signed stakes are the total stake weight that has been signed for each stake type
7169
* @dev This function DOES NOT support smart contact signatures
7270
*/
7371
function verifyCertificate(
7472
OperatorSet calldata operatorSet,
7573
ECDSACertificate memory cert
76-
) external view returns (uint256[] memory signedStakes, address[] memory signers);
74+
) external view returns (uint256[] memory totalSignedStakeWeights, address[] memory signers);
7775

7876
/**
7977
* @notice verifies a certificate and makes sure that the signed stakes meet
@@ -133,28 +131,6 @@ interface IECDSACertificateVerifier is
133131
uint256 operatorIndex
134132
) external view returns (ECDSAOperatorInfo memory);
135133

136-
/**
137-
* @notice Get the total number of operators for a given reference timestamp
138-
* @param operatorSet The operator set
139-
* @param referenceTimestamp The reference timestamp
140-
* @return The number of operators, 0 if the operatorSet has not been updated for the given reference timestamp
141-
*/
142-
function getOperatorCount(
143-
OperatorSet calldata operatorSet,
144-
uint32 referenceTimestamp
145-
) external view returns (uint32);
146-
147-
/**
148-
* @notice Get the total stake weights for all operators at a given reference timestamp
149-
* @param operatorSet The operator set to calculate stakes for
150-
* @param referenceTimestamp The reference timestamp
151-
* @return totalStakes The sum of stake weights for each stake type, empty if the operatorSet has not been updated for the given reference timestamp
152-
*/
153-
function getTotalStakeWeights(
154-
OperatorSet calldata operatorSet,
155-
uint32 referenceTimestamp
156-
) external view returns (uint256[] memory);
157-
158134
/**
159135
* @notice Override domainSeparator to not include chainId
160136
* @return The domain separator hash without chainId

src/contracts/multichain/BN254CertificateVerifier.sol

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,24 @@ contract BN254CertificateVerifier is Initializable, BN254CertificateVerifierStor
326326
return _referenceTimestampsSet[operatorSetKey][referenceTimestamp];
327327
}
328328

329+
///@inheritdoc IBaseCertificateVerifier
330+
function getTotalStakeWeights(
331+
OperatorSet memory operatorSet,
332+
uint32 referenceTimestamp
333+
) external view returns (uint256[] memory) {
334+
bytes32 operatorSetKey = operatorSet.key();
335+
return _operatorSetInfos[operatorSetKey][referenceTimestamp].totalWeights;
336+
}
337+
338+
/// @inheritdoc IBaseCertificateVerifier
339+
function getOperatorCount(
340+
OperatorSet memory operatorSet,
341+
uint32 referenceTimestamp
342+
) external view returns (uint256) {
343+
bytes32 operatorSetKey = operatorSet.key();
344+
return _operatorSetInfos[operatorSetKey][referenceTimestamp].numOperators;
345+
}
346+
329347
///@inheritdoc IBN254CertificateVerifier
330348
function trySignatureVerification(
331349
bytes32 msgHash,

0 commit comments

Comments
 (0)