Skip to content

Commit 3e64ea8

Browse files
authored
refactor: cleaner reverts for ECDSACertificateVerifier (#1521)
**Motivation:** `parseSignatures` returned false for several errors, making it harder to debug **Modifications:** - Revert in `_parseSignatures` right when error occurs **Result:** Cleaner code
1 parent 3b08c05 commit 3e64ea8

File tree

3 files changed

+33
-19
lines changed

3 files changed

+33
-19
lines changed

src/contracts/interfaces/IECDSACertificateVerifier.sol

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ import "./IOperatorTableCalculator.sol";
77

88
interface IECDSACertificateVerifierTypes is IOperatorTableCalculatorTypes {
99
// Errors
10+
/// @notice Thrown when the signature length is invalid
1011
error InvalidSignatureLength();
11-
12+
/// @notice Thrown when the signatures are not ordered by signer address
13+
error SignersNotOrdered();
1214
/**
1315
* @notice A ECDSA Certificate
1416
* @param referenceTimestamp the timestamp at which the certificate was created,
1517
* which corresponds to a reference timestamp of the operator table update
1618
* @param messageHash the hash of the message that was signed by operators
1719
* @param sig the concatenated signature of each signing operator
1820
*/
21+
1922
struct ECDSACertificate {
2023
uint32 referenceTimestamp;
2124
bytes32 messageHash;

src/contracts/multichain/ECDSACertificateVerifier.sol

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
141141
bytes32 signableDigest = calculateCertificateDigest(cert.referenceTimestamp, cert.messageHash);
142142

143143
// Parse the signatures
144-
(address[] memory signers, bool validSignatures) = _parseSignatures(signableDigest, cert.sig);
145-
require(validSignatures, VerificationFailed());
144+
address[] memory signers = _parseSignatures(signableDigest, cert.sig);
146145

147146
// Process each recovered signer
148147
for (uint256 i = 0; i < signers.length; i++) {
@@ -186,14 +185,13 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
186185
* @param signableDigest The signable digest that was signed
187186
* @param signatures The concatenated signatures
188187
* @return signers Array of addresses that signed the message
189-
* @return valid Whether all signatures are valid
190188
* @dev Signatures must be ordered by signer address (ascending)
191189
* @dev This does not support smart contract based signatures for multichain
192190
*/
193191
function _parseSignatures(
194192
bytes32 signableDigest,
195193
bytes memory signatures
196-
) internal pure returns (address[] memory signers, bool valid) {
194+
) internal pure returns (address[] memory signers) {
197195
// Each ECDSA signature is 65 bytes: r (32 bytes) + s (32 bytes) + v (1 byte)
198196
require(signatures.length > 0 && signatures.length % 65 == 0, InvalidSignatureLength());
199197

@@ -207,20 +205,16 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
207205
}
208206

209207
// Recover the signer
210-
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(signableDigest, signature);
211-
if (error != ECDSA.RecoverError.NoError || recovered == address(0)) {
212-
return (signers, false);
213-
}
208+
(address recovered, ECDSA.RecoverError err) = ECDSA.tryRecover(signableDigest, signature);
209+
require(err == ECDSA.RecoverError.NoError, InvalidSignature());
214210

215211
// Check that signatures are ordered by signer address
216-
if (i > 0 && recovered <= signers[i - 1]) {
217-
return (signers, false);
218-
}
212+
require(i == 0 || recovered > signers[i - 1], SignersNotOrdered());
219213

220214
signers[i] = recovered;
221215
}
222216

223-
return (signers, true);
217+
return signers;
224218
}
225219

226220
/**

src/test/unit/ECDSACertificateVerifierUnit.t.sol

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,23 @@ contract ECDSACertificateVerifierUnitTests_verifyCertificate is ECDSACertificate
376376
sig: "" // Empty signatures
377377
});
378378

379-
vm.expectRevert(InvalidSignatureLength.selector);
379+
vm.expectRevert(IECDSACertificateVerifierTypes.InvalidSignatureLength.selector);
380+
verifier.verifyCertificate(defaultOperatorSet, cert);
381+
}
382+
383+
function test_revert_invalidSignatureLength() public {
384+
uint32 referenceTimestamp = _initializeOperatorTableBase();
385+
386+
// Create certificate with wrong signature length (not a multiple of 65)
387+
bytes memory invalidLengthSig = new bytes(64); // Should be 65 bytes for ECDSA
388+
389+
IECDSACertificateVerifierTypes.ECDSACertificate memory cert = IECDSACertificateVerifierTypes.ECDSACertificate({
390+
referenceTimestamp: referenceTimestamp,
391+
messageHash: defaultMsgHash,
392+
sig: invalidLengthSig
393+
});
394+
395+
vm.expectRevert(IECDSACertificateVerifierTypes.InvalidSignatureLength.selector);
380396
verifier.verifyCertificate(defaultOperatorSet, cert);
381397
}
382398

@@ -423,8 +439,9 @@ contract ECDSACertificateVerifierUnitTests_verifyCertificate is ECDSACertificate
423439
// Modify the certificate to use a different message hash
424440
cert.messageHash = keccak256("different message");
425441

426-
// Verification should fail
427-
vm.expectRevert(VerificationFailed.selector);
442+
// Verification should fail - expect SignersNotOrdered because signature recovery
443+
// with wrong message hash produces different addresses that break ordering
444+
vm.expectRevert(IECDSACertificateVerifierTypes.SignersNotOrdered.selector);
428445
verifier.verifyCertificate(defaultOperatorSet, cert);
429446
}
430447

@@ -678,7 +695,7 @@ contract ECDSACertificateVerifierUnitTests_verifyCertificate is ECDSACertificate
678695
});
679696

680697
// Should revert because signatures are not ordered by address
681-
vm.expectRevert(VerificationFailed.selector);
698+
vm.expectRevert(IECDSACertificateVerifierTypes.SignersNotOrdered.selector);
682699
verifier.verifyCertificate(defaultOperatorSet, cert);
683700
}
684701

@@ -713,7 +730,7 @@ contract ECDSACertificateVerifierUnitTests_verifyCertificate is ECDSACertificate
713730
});
714731

715732
// Should revert because signature recovery returns an error
716-
vm.expectRevert(VerificationFailed.selector);
733+
vm.expectRevert(ISignatureUtilsMixinErrors.InvalidSignature.selector);
717734
verifier.verifyCertificate(defaultOperatorSet, cert);
718735
}
719736

@@ -748,7 +765,7 @@ contract ECDSACertificateVerifierUnitTests_verifyCertificate is ECDSACertificate
748765
sig: zeroRecoverySignature
749766
});
750767

751-
// Should revert because recovered address is zero
768+
// Should revert because recovered address is zero - now throws VerificationFailed
752769
vm.expectRevert(VerificationFailed.selector);
753770
verifier.verifyCertificate(defaultOperatorSet, cert);
754771
}

0 commit comments

Comments
 (0)