Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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