Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion pkg/bindings/BN254CertificateVerifier/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/ECDSACertificateVerifier/binding.go

Large diffs are not rendered by default.

64 changes: 63 additions & 1 deletion pkg/bindings/ECDSACertificateVerifierStorage/binding.go

Large diffs are not rendered by default.

64 changes: 63 additions & 1 deletion pkg/bindings/IECDSACertificateVerifier/binding.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/bindings/OperatorTableUpdater/binding.go

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions src/contracts/interfaces/IECDSACertificateVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,24 @@ interface IECDSACertificateVerifier is IECDSACertificateVerifierEvents, IBaseCer
OperatorSet calldata operatorSet,
uint32 referenceTimestamp
) external view returns (uint256[] memory);

/**
* @notice Override domainSeparator to not include chainId
* @return The domain separator hash without chainId
* @dev This function overrides the base domainSeparator to not include chainId
*/
function domainSeparator() external view returns (bytes32);

/**
* @notice Calculate the EIP-712 digest for a certificate
* @param referenceTimestamp The reference timestamp
* @param messageHash The message hash
* @return The EIP-712 digest
* @dev This function is public to allow offchain tools to calculate the same digest
* @dev Note: This does not support smart contract based signatures for multichain
*/
function calculateCertificateDigest(
uint32 referenceTimestamp,
bytes32 messageHash
) external view returns (bytes32);
}
218 changes: 113 additions & 105 deletions src/contracts/multichain/ECDSACertificateVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,56 +38,10 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
}

/**
* @notice Override domainSeparator to not include chainId
* @return The domain separator hash without chainId
*
* EXTERNAL FUNCTIONS
*
*/
function domainSeparator() public view override returns (bytes32) {
return keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH_NO_CHAINID,
keccak256(bytes("EigenLayer")),
keccak256(bytes(_majorVersion())),
address(this)
)
);
}

/**
* @notice Calculate the EIP-712 digest for a certificate
* @param referenceTimestamp The reference timestamp
* @param messageHash The message hash
* @return The EIP-712 digest
* @dev This function is public to allow offchain tools to calculate the same digest
* @dev Note: This does not support smart contract based signatures for multichain
*/
function calculateCertificateDigest(uint32 referenceTimestamp, bytes32 messageHash) public view returns (bytes32) {
bytes32 structHash = keccak256(abi.encode(ECDSA_CERTIFICATE_TYPEHASH, referenceTimestamp, messageHash));
return _calculateSignableDigest(structHash);
}

///@inheritdoc IBaseCertificateVerifier
function getOperatorSetOwner(
OperatorSet memory operatorSet
) external view returns (address) {
bytes32 operatorSetKey = operatorSet.key();
return _operatorSetOwners[operatorSetKey];
}

///@inheritdoc IBaseCertificateVerifier
function maxOperatorTableStaleness(
OperatorSet memory operatorSet
) external view returns (uint32) {
bytes32 operatorSetKey = operatorSet.key();
return _maxStalenessPeriods[operatorSetKey];
}

///@inheritdoc IBaseCertificateVerifier
function latestReferenceTimestamp(
OperatorSet memory operatorSet
) external view returns (uint32) {
bytes32 operatorSetKey = operatorSet.key();
return _latestReferenceTimestamps[operatorSetKey];
}

///@inheritdoc IECDSACertificateVerifier
function updateOperatorTable(
Expand Down Expand Up @@ -158,62 +112,6 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
return true;
}

/// @inheritdoc IECDSACertificateVerifier
function getOperatorInfos(
OperatorSet memory operatorSet,
uint32 referenceTimestamp
) external view returns (ECDSAOperatorInfo[] memory) {
bytes32 operatorSetKey = operatorSet.key();
uint32 numOperators = uint32(_numOperators[operatorSetKey][referenceTimestamp]);
ECDSAOperatorInfo[] memory operatorInfos = new ECDSAOperatorInfo[](numOperators);

for (uint32 i = 0; i < numOperators; i++) {
operatorInfos[i] = _operatorInfos[operatorSetKey][referenceTimestamp][i];
}

return operatorInfos;
}

/// @inheritdoc IECDSACertificateVerifier
function getOperatorInfo(
OperatorSet memory operatorSet,
uint32 referenceTimestamp,
uint32 operatorIndex
) external view returns (ECDSAOperatorInfo memory) {
bytes32 operatorSetKey = operatorSet.key();
require(operatorIndex < _numOperators[operatorSetKey][referenceTimestamp], "Operator index out of bounds");
return _operatorInfos[operatorSetKey][referenceTimestamp][operatorIndex];
}

/// @inheritdoc IECDSACertificateVerifier
function getOperatorCount(
OperatorSet memory operatorSet,
uint32 referenceTimestamp
) external view returns (uint32) {
bytes32 operatorSetKey = operatorSet.key();
return uint32(_numOperators[operatorSetKey][referenceTimestamp]);
}

/// @inheritdoc IECDSACertificateVerifier
function getTotalStakes(
OperatorSet calldata operatorSet,
uint32 referenceTimestamp
) public view returns (uint256[] memory) {
bytes32 operatorSetKey = operatorSet.key();
require(_latestReferenceTimestamps[operatorSetKey] == referenceTimestamp, ReferenceTimestampDoesNotExist());
uint256 operatorCount = _numOperators[operatorSetKey][referenceTimestamp];
require(operatorCount > 0, ReferenceTimestampDoesNotExist());
uint256 stakeTypesCount = _operatorInfos[operatorSetKey][referenceTimestamp][0].weights.length;
uint256[] memory totalStakes = new uint256[](stakeTypesCount);
for (uint256 i = 0; i < operatorCount; i++) {
uint256[] memory weights = _operatorInfos[operatorSetKey][referenceTimestamp][uint32(i)].weights;
for (uint256 j = 0; j < weights.length && j < stakeTypesCount; j++) {
totalStakes[j] += weights[j];
}
}
return totalStakes;
}

/**
* @notice Internal function to verify a certificate
* @param cert The certificate to verify
Expand Down Expand Up @@ -273,6 +171,12 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor
return signedStakes;
}

/**
*
* INTERNAL FUNCTIONS
*
*/

/**
* @notice Parse signatures from the concatenated signature bytes
* @param messageHash The message hash that was signed
Expand Down Expand Up @@ -317,4 +221,108 @@ contract ECDSACertificateVerifier is Initializable, ECDSACertificateVerifierStor

return (signers, true);
}

/**
*
* VIEW FUNCTIONS
*
*/

///@inheritdoc IBaseCertificateVerifier
function getOperatorSetOwner(
OperatorSet memory operatorSet
) external view returns (address) {
bytes32 operatorSetKey = operatorSet.key();
return _operatorSetOwners[operatorSetKey];
}

///@inheritdoc IBaseCertificateVerifier
function maxOperatorTableStaleness(
OperatorSet memory operatorSet
) external view returns (uint32) {
bytes32 operatorSetKey = operatorSet.key();
return _maxStalenessPeriods[operatorSetKey];
}

///@inheritdoc IBaseCertificateVerifier
function latestReferenceTimestamp(
OperatorSet memory operatorSet
) external view returns (uint32) {
bytes32 operatorSetKey = operatorSet.key();
return _latestReferenceTimestamps[operatorSetKey];
}

/// @inheritdoc IECDSACertificateVerifier
function getOperatorInfos(
OperatorSet memory operatorSet,
uint32 referenceTimestamp
) external view returns (ECDSAOperatorInfo[] memory) {
bytes32 operatorSetKey = operatorSet.key();
uint32 numOperators = uint32(_numOperators[operatorSetKey][referenceTimestamp]);
ECDSAOperatorInfo[] memory operatorInfos = new ECDSAOperatorInfo[](numOperators);

for (uint32 i = 0; i < numOperators; i++) {
operatorInfos[i] = _operatorInfos[operatorSetKey][referenceTimestamp][i];
}

return operatorInfos;
}

/// @inheritdoc IECDSACertificateVerifier
function getOperatorInfo(
OperatorSet memory operatorSet,
uint32 referenceTimestamp,
uint32 operatorIndex
) external view returns (ECDSAOperatorInfo memory) {
bytes32 operatorSetKey = operatorSet.key();
require(operatorIndex < _numOperators[operatorSetKey][referenceTimestamp], "Operator index out of bounds");
return _operatorInfos[operatorSetKey][referenceTimestamp][operatorIndex];
}

/// @inheritdoc IECDSACertificateVerifier
function getOperatorCount(
OperatorSet memory operatorSet,
uint32 referenceTimestamp
) external view returns (uint32) {
bytes32 operatorSetKey = operatorSet.key();
return uint32(_numOperators[operatorSetKey][referenceTimestamp]);
}

/// @inheritdoc IECDSACertificateVerifier
function getTotalStakes(
OperatorSet calldata operatorSet,
uint32 referenceTimestamp
) public view returns (uint256[] memory) {
bytes32 operatorSetKey = operatorSet.key();
require(_latestReferenceTimestamps[operatorSetKey] == referenceTimestamp, ReferenceTimestampDoesNotExist());
uint256 operatorCount = _numOperators[operatorSetKey][referenceTimestamp];
require(operatorCount > 0, ReferenceTimestampDoesNotExist());
uint256 stakeTypesCount = _operatorInfos[operatorSetKey][referenceTimestamp][0].weights.length;
uint256[] memory totalStakes = new uint256[](stakeTypesCount);
for (uint256 i = 0; i < operatorCount; i++) {
uint256[] memory weights = _operatorInfos[operatorSetKey][referenceTimestamp][uint32(i)].weights;
for (uint256 j = 0; j < weights.length && j < stakeTypesCount; j++) {
totalStakes[j] += weights[j];
}
}
return totalStakes;
}

/// @inheritdoc IECDSACertificateVerifier
function domainSeparator() public view override(IECDSACertificateVerifier, SignatureUtilsMixin) returns (bytes32) {
return keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH_NO_CHAINID,
keccak256(bytes("EigenLayer")),
keccak256(bytes(_majorVersion())),
address(this)
)
);
}

/// @inheritdoc IECDSACertificateVerifier
function calculateCertificateDigest(uint32 referenceTimestamp, bytes32 messageHash) public view returns (bytes32) {
bytes32 structHash = keccak256(abi.encode(ECDSA_CERTIFICATE_TYPEHASH, referenceTimestamp, messageHash));
return _calculateSignableDigest(structHash);
}
}
65 changes: 65 additions & 0 deletions src/test/tree/ECDSACertificateVerifierUnit.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.
└── ECDSACertificateVerifier (**** denotes that integration tests are needed to fully validate path)
├── when updateOperatorTable is called
│ ├── given that the caller is not the table updater
│ │ └── it should revert
│ ├── given that the reference timestamp is not greater than the latest
│ │ └── it should revert
│ └── given that all parameters are valid
│ └── it should update operator infos, timestamp, owner, and staleness period & emit event
├── when verifyCertificate is called
│ ├── given that the reference timestamp does not exist
│ │ └── it should revert
│ ├── given that the certificate is stale
│ │ └── it should revert
│ ├── given that the signature is invalid
│ │ └── it should revert
│ ├── given that a signer is not an operator
│ │ └── it should revert
│ ├── given that signatures are not ordered by address
│ │ └── it should revert
│ ├── given that all operators are signers
│ │ └── it should return full stake amounts
│ └── given that some operators are non-signers
│ └── it should deduct non-signer stakes from total
├── when verifyCertificateProportion is called
│ ├── given that array lengths mismatch
│ │ └── it should revert
│ ├── given that the certificate meets thresholds
│ │ └── it should return true
│ └── given that the certificate does not meet thresholds
│ └── it should return false
├── when verifyCertificateNominal is called
│ ├── given that array lengths mismatch
│ │ └── it should revert
│ ├── given that the certificate meets thresholds
│ │ └── it should return true
│ └── given that the certificate does not meet thresholds
│ └── it should return false
└── when view functions are called
├── getOperatorSetOwner
│ └── it should return the correct owner address
├── maxOperatorTableStaleness
│ └── it should return the correct staleness period
├── latestReferenceTimestamp
│ └── it should return the latest timestamp
├── getOperatorInfos
│ └── it should return all operator infos for the given timestamp
├── getOperatorInfo
│ ├── given an out of bounds index
│ │ └── it should revert
│ └── given a valid index
│ └── it should return the correct operator info
├── getOperatorCount
│ └── it should return the correct number of operators
├── getTotalStakes
│ ├── given that reference timestamp does not exist
│ │ └── it should revert
│ ├── given that there are no operators
│ │ └── it should revert
│ └── given valid operators exist
│ └── it should return the sum of all operator weights
├── domainSeparator
│ └── it should return the EIP-712 domain separator
└── calculateCertificateDigest
└── it should return the correct digest for given timestamp and message hash
Loading