Skip to content

Commit 624a68b

Browse files
authored
refactor: ECDSACertificateVerifier testing (#1478)
**Motivation:** Update `ECDSACertificateVerifier` to follow testing patterns. **Modifications:** - Update tests - Add functions to interface **Result:** Consistent patterns.
1 parent 5734f8c commit 624a68b

File tree

9 files changed

+1226
-432
lines changed

9 files changed

+1226
-432
lines changed

pkg/bindings/BN254CertificateVerifier/binding.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bindings/ECDSACertificateVerifier/binding.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bindings/ECDSACertificateVerifierStorage/binding.go

Lines changed: 63 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bindings/IECDSACertificateVerifier/binding.go

Lines changed: 63 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/bindings/OperatorTableUpdater/binding.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/contracts/interfaces/IECDSACertificateVerifier.sol

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,24 @@ interface IECDSACertificateVerifier is IECDSACertificateVerifierEvents, IBaseCer
130130
OperatorSet calldata operatorSet,
131131
uint32 referenceTimestamp
132132
) external view returns (uint256[] memory);
133+
134+
/**
135+
* @notice Override domainSeparator to not include chainId
136+
* @return The domain separator hash without chainId
137+
* @dev This function overrides the base domainSeparator to not include chainId
138+
*/
139+
function domainSeparator() external view returns (bytes32);
140+
141+
/**
142+
* @notice Calculate the EIP-712 digest for a certificate
143+
* @param referenceTimestamp The reference timestamp
144+
* @param messageHash The message hash
145+
* @return The EIP-712 digest
146+
* @dev This function is public to allow offchain tools to calculate the same digest
147+
* @dev Note: This does not support smart contract based signatures for multichain
148+
*/
149+
function calculateCertificateDigest(
150+
uint32 referenceTimestamp,
151+
bytes32 messageHash
152+
) external view returns (bytes32);
133153
}

src/contracts/multichain/ECDSACertificateVerifier.sol

Lines changed: 113 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -38,56 +38,10 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
3838
}
3939

4040
/**
41-
* @notice Override domainSeparator to not include chainId
42-
* @return The domain separator hash without chainId
41+
*
42+
* EXTERNAL FUNCTIONS
43+
*
4344
*/
44-
function domainSeparator() public view override returns (bytes32) {
45-
return keccak256(
46-
abi.encode(
47-
EIP712_DOMAIN_TYPEHASH_NO_CHAINID,
48-
keccak256(bytes("EigenLayer")),
49-
keccak256(bytes(_majorVersion())),
50-
address(this)
51-
)
52-
);
53-
}
54-
55-
/**
56-
* @notice Calculate the EIP-712 digest for a certificate
57-
* @param referenceTimestamp The reference timestamp
58-
* @param messageHash The message hash
59-
* @return The EIP-712 digest
60-
* @dev This function is public to allow offchain tools to calculate the same digest
61-
* @dev Note: This does not support smart contract based signatures for multichain
62-
*/
63-
function calculateCertificateDigest(uint32 referenceTimestamp, bytes32 messageHash) public view returns (bytes32) {
64-
bytes32 structHash = keccak256(abi.encode(ECDSA_CERTIFICATE_TYPEHASH, referenceTimestamp, messageHash));
65-
return _calculateSignableDigest(structHash);
66-
}
67-
68-
///@inheritdoc IBaseCertificateVerifier
69-
function getOperatorSetOwner(
70-
OperatorSet memory operatorSet
71-
) external view returns (address) {
72-
bytes32 operatorSetKey = operatorSet.key();
73-
return _operatorSetOwners[operatorSetKey];
74-
}
75-
76-
///@inheritdoc IBaseCertificateVerifier
77-
function maxOperatorTableStaleness(
78-
OperatorSet memory operatorSet
79-
) external view returns (uint32) {
80-
bytes32 operatorSetKey = operatorSet.key();
81-
return _maxStalenessPeriods[operatorSetKey];
82-
}
83-
84-
///@inheritdoc IBaseCertificateVerifier
85-
function latestReferenceTimestamp(
86-
OperatorSet memory operatorSet
87-
) external view returns (uint32) {
88-
bytes32 operatorSetKey = operatorSet.key();
89-
return _latestReferenceTimestamps[operatorSetKey];
90-
}
9145

9246
///@inheritdoc IECDSACertificateVerifier
9347
function updateOperatorTable(
@@ -158,62 +112,6 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
158112
return true;
159113
}
160114

161-
/// @inheritdoc IECDSACertificateVerifier
162-
function getOperatorInfos(
163-
OperatorSet memory operatorSet,
164-
uint32 referenceTimestamp
165-
) external view returns (ECDSAOperatorInfo[] memory) {
166-
bytes32 operatorSetKey = operatorSet.key();
167-
uint32 numOperators = uint32(_numOperators[operatorSetKey][referenceTimestamp]);
168-
ECDSAOperatorInfo[] memory operatorInfos = new ECDSAOperatorInfo[](numOperators);
169-
170-
for (uint32 i = 0; i < numOperators; i++) {
171-
operatorInfos[i] = _operatorInfos[operatorSetKey][referenceTimestamp][i];
172-
}
173-
174-
return operatorInfos;
175-
}
176-
177-
/// @inheritdoc IECDSACertificateVerifier
178-
function getOperatorInfo(
179-
OperatorSet memory operatorSet,
180-
uint32 referenceTimestamp,
181-
uint32 operatorIndex
182-
) external view returns (ECDSAOperatorInfo memory) {
183-
bytes32 operatorSetKey = operatorSet.key();
184-
require(operatorIndex < _numOperators[operatorSetKey][referenceTimestamp], "Operator index out of bounds");
185-
return _operatorInfos[operatorSetKey][referenceTimestamp][operatorIndex];
186-
}
187-
188-
/// @inheritdoc IECDSACertificateVerifier
189-
function getOperatorCount(
190-
OperatorSet memory operatorSet,
191-
uint32 referenceTimestamp
192-
) external view returns (uint32) {
193-
bytes32 operatorSetKey = operatorSet.key();
194-
return uint32(_numOperators[operatorSetKey][referenceTimestamp]);
195-
}
196-
197-
/// @inheritdoc IECDSACertificateVerifier
198-
function getTotalStakes(
199-
OperatorSet calldata operatorSet,
200-
uint32 referenceTimestamp
201-
) public view returns (uint256[] memory) {
202-
bytes32 operatorSetKey = operatorSet.key();
203-
require(_latestReferenceTimestamps[operatorSetKey] == referenceTimestamp, ReferenceTimestampDoesNotExist());
204-
uint256 operatorCount = _numOperators[operatorSetKey][referenceTimestamp];
205-
require(operatorCount > 0, ReferenceTimestampDoesNotExist());
206-
uint256 stakeTypesCount = _operatorInfos[operatorSetKey][referenceTimestamp][0].weights.length;
207-
uint256[] memory totalStakes = new uint256[](stakeTypesCount);
208-
for (uint256 i = 0; i < operatorCount; i++) {
209-
uint256[] memory weights = _operatorInfos[operatorSetKey][referenceTimestamp][uint32(i)].weights;
210-
for (uint256 j = 0; j < weights.length && j < stakeTypesCount; j++) {
211-
totalStakes[j] += weights[j];
212-
}
213-
}
214-
return totalStakes;
215-
}
216-
217115
/**
218116
* @notice Internal function to verify a certificate
219117
* @param cert The certificate to verify
@@ -273,6 +171,12 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
273171
return signedStakes;
274172
}
275173

174+
/**
175+
*
176+
* INTERNAL FUNCTIONS
177+
*
178+
*/
179+
276180
/**
277181
* @notice Parse signatures from the concatenated signature bytes
278182
* @param messageHash The message hash that was signed
@@ -317,4 +221,108 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
317221

318222
return (signers, true);
319223
}
224+
225+
/**
226+
*
227+
* VIEW FUNCTIONS
228+
*
229+
*/
230+
231+
///@inheritdoc IBaseCertificateVerifier
232+
function getOperatorSetOwner(
233+
OperatorSet memory operatorSet
234+
) external view returns (address) {
235+
bytes32 operatorSetKey = operatorSet.key();
236+
return _operatorSetOwners[operatorSetKey];
237+
}
238+
239+
///@inheritdoc IBaseCertificateVerifier
240+
function maxOperatorTableStaleness(
241+
OperatorSet memory operatorSet
242+
) external view returns (uint32) {
243+
bytes32 operatorSetKey = operatorSet.key();
244+
return _maxStalenessPeriods[operatorSetKey];
245+
}
246+
247+
///@inheritdoc IBaseCertificateVerifier
248+
function latestReferenceTimestamp(
249+
OperatorSet memory operatorSet
250+
) external view returns (uint32) {
251+
bytes32 operatorSetKey = operatorSet.key();
252+
return _latestReferenceTimestamps[operatorSetKey];
253+
}
254+
255+
/// @inheritdoc IECDSACertificateVerifier
256+
function getOperatorInfos(
257+
OperatorSet memory operatorSet,
258+
uint32 referenceTimestamp
259+
) external view returns (ECDSAOperatorInfo[] memory) {
260+
bytes32 operatorSetKey = operatorSet.key();
261+
uint32 numOperators = uint32(_numOperators[operatorSetKey][referenceTimestamp]);
262+
ECDSAOperatorInfo[] memory operatorInfos = new ECDSAOperatorInfo[](numOperators);
263+
264+
for (uint32 i = 0; i < numOperators; i++) {
265+
operatorInfos[i] = _operatorInfos[operatorSetKey][referenceTimestamp][i];
266+
}
267+
268+
return operatorInfos;
269+
}
270+
271+
/// @inheritdoc IECDSACertificateVerifier
272+
function getOperatorInfo(
273+
OperatorSet memory operatorSet,
274+
uint32 referenceTimestamp,
275+
uint32 operatorIndex
276+
) external view returns (ECDSAOperatorInfo memory) {
277+
bytes32 operatorSetKey = operatorSet.key();
278+
require(operatorIndex < _numOperators[operatorSetKey][referenceTimestamp], "Operator index out of bounds");
279+
return _operatorInfos[operatorSetKey][referenceTimestamp][operatorIndex];
280+
}
281+
282+
/// @inheritdoc IECDSACertificateVerifier
283+
function getOperatorCount(
284+
OperatorSet memory operatorSet,
285+
uint32 referenceTimestamp
286+
) external view returns (uint32) {
287+
bytes32 operatorSetKey = operatorSet.key();
288+
return uint32(_numOperators[operatorSetKey][referenceTimestamp]);
289+
}
290+
291+
/// @inheritdoc IECDSACertificateVerifier
292+
function getTotalStakes(
293+
OperatorSet calldata operatorSet,
294+
uint32 referenceTimestamp
295+
) public view returns (uint256[] memory) {
296+
bytes32 operatorSetKey = operatorSet.key();
297+
require(_latestReferenceTimestamps[operatorSetKey] == referenceTimestamp, ReferenceTimestampDoesNotExist());
298+
uint256 operatorCount = _numOperators[operatorSetKey][referenceTimestamp];
299+
require(operatorCount > 0, ReferenceTimestampDoesNotExist());
300+
uint256 stakeTypesCount = _operatorInfos[operatorSetKey][referenceTimestamp][0].weights.length;
301+
uint256[] memory totalStakes = new uint256[](stakeTypesCount);
302+
for (uint256 i = 0; i < operatorCount; i++) {
303+
uint256[] memory weights = _operatorInfos[operatorSetKey][referenceTimestamp][uint32(i)].weights;
304+
for (uint256 j = 0; j < weights.length && j < stakeTypesCount; j++) {
305+
totalStakes[j] += weights[j];
306+
}
307+
}
308+
return totalStakes;
309+
}
310+
311+
/// @inheritdoc IECDSACertificateVerifier
312+
function domainSeparator() public view override(IECDSACertificateVerifier, SignatureUtilsMixin) returns (bytes32) {
313+
return keccak256(
314+
abi.encode(
315+
EIP712_DOMAIN_TYPEHASH_NO_CHAINID,
316+
keccak256(bytes("EigenLayer")),
317+
keccak256(bytes(_majorVersion())),
318+
address(this)
319+
)
320+
);
321+
}
322+
323+
/// @inheritdoc IECDSACertificateVerifier
324+
function calculateCertificateDigest(uint32 referenceTimestamp, bytes32 messageHash) public view returns (bytes32) {
325+
bytes32 structHash = keccak256(abi.encode(ECDSA_CERTIFICATE_TYPEHASH, referenceTimestamp, messageHash));
326+
return _calculateSignableDigest(structHash);
327+
}
320328
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.
2+
└── ECDSACertificateVerifier (**** denotes that integration tests are needed to fully validate path)
3+
├── when updateOperatorTable is called
4+
│ ├── given that the caller is not the table updater
5+
│ │ └── it should revert
6+
│ ├── given that the reference timestamp is not greater than the latest
7+
│ │ └── it should revert
8+
│ └── given that all parameters are valid
9+
│ └── it should update operator infos, timestamp, owner, and staleness period & emit event
10+
├── when verifyCertificate is called
11+
│ ├── given that the reference timestamp does not exist
12+
│ │ └── it should revert
13+
│ ├── given that the certificate is stale
14+
│ │ └── it should revert
15+
│ ├── given that the signature is invalid
16+
│ │ └── it should revert
17+
│ ├── given that a signer is not an operator
18+
│ │ └── it should revert
19+
│ ├── given that signatures are not ordered by address
20+
│ │ └── it should revert
21+
│ ├── given that all operators are signers
22+
│ │ └── it should return full stake amounts
23+
│ └── given that some operators are non-signers
24+
│ └── it should deduct non-signer stakes from total
25+
├── when verifyCertificateProportion is called
26+
│ ├── given that array lengths mismatch
27+
│ │ └── it should revert
28+
│ ├── given that the certificate meets thresholds
29+
│ │ └── it should return true
30+
│ └── given that the certificate does not meet thresholds
31+
│ └── it should return false
32+
├── when verifyCertificateNominal is called
33+
│ ├── given that array lengths mismatch
34+
│ │ └── it should revert
35+
│ ├── given that the certificate meets thresholds
36+
│ │ └── it should return true
37+
│ └── given that the certificate does not meet thresholds
38+
│ └── it should return false
39+
└── when view functions are called
40+
├── getOperatorSetOwner
41+
│ └── it should return the correct owner address
42+
├── maxOperatorTableStaleness
43+
│ └── it should return the correct staleness period
44+
├── latestReferenceTimestamp
45+
│ └── it should return the latest timestamp
46+
├── getOperatorInfos
47+
│ └── it should return all operator infos for the given timestamp
48+
├── getOperatorInfo
49+
│ ├── given an out of bounds index
50+
│ │ └── it should revert
51+
│ └── given a valid index
52+
│ └── it should return the correct operator info
53+
├── getOperatorCount
54+
│ └── it should return the correct number of operators
55+
├── getTotalStakes
56+
│ ├── given that reference timestamp does not exist
57+
│ │ └── it should revert
58+
│ ├── given that there are no operators
59+
│ │ └── it should revert
60+
│ └── given valid operators exist
61+
│ └── it should return the sum of all operator weights
62+
├── domainSeparator
63+
│ └── it should return the EIP-712 domain separator
64+
└── calculateCertificateDigest
65+
└── it should return the correct digest for given timestamp and message hash

0 commit comments

Comments
 (0)