-
Notifications
You must be signed in to change notification settings - Fork 2
Alternative validator registration logic #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 12 commits
14e4ba9
c166721
9cb6930
78dd1de
1a80ef7
37f7a26
fc33153
01509c5
ba95a8c
181e7cd
a792458
59e8980
9f5b00d
f0c7b93
67423ee
92c46a5
9ee3f2e
a89bc28
bca0c35
30c7841
cb4bb6c
b4eb50d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,15 @@ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
| pragma solidity >=0.8.0 <0.9.0; | ||
|
|
||
| // EigenLayer Imports | ||
| import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol"; | ||
| import { ISignatureUtils } from "eigenlayer/interfaces/ISignatureUtils.sol"; | ||
| import { BN254 } from "eigenlayer-middleware/libraries/BN254.sol"; | ||
| // Local Imports | ||
| import { IAVSDirectoryExtended } from "../interfaces/EigenLayer/IAVSDirectoryExtended.sol"; | ||
| import "../structs/ValidatorData.sol"; | ||
| import "../structs/OperatorData.sol"; | ||
| import { BeaconChainHelperLib } from "../lib/BeaconChainHelperLib.sol"; | ||
|
|
||
| /** | ||
| * @title IUniFiAVSManager | ||
|
|
@@ -70,6 +74,27 @@ interface IUniFiAVSManager { | |
| /// @notice Thrown when a restaking strategy allowlist update fails | ||
| error RestakingStrategyAllowlistUpdateFailed(); | ||
|
|
||
| /// @notice Thrown when a salt is already used for a registration | ||
| error SaltAlreadyUsed(); | ||
|
|
||
| /// @notice Thrown when a signature is expired | ||
| error SignatureExpired(); | ||
|
|
||
| /// @notice Thrown when the lengths of the input arrays are not equal | ||
| error InvalidArrayLengths(); | ||
WalidOfNow marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// @notice Thrown when a validator proof is invalid | ||
| error InvalidValidatorProof(bytes32 blsPubKeyHash); | ||
|
|
||
| /// @notice Thrown when a validator index is already used | ||
| error ValidatorIndexAlreadyUsed(); | ||
|
|
||
| /// @notice Thrown when an operator is slashed | ||
| error OperatorSlashed(); | ||
|
|
||
| /// @notice Thrown when a validator is not backed by an EigenPod | ||
| error InvalidValidatorType(); | ||
|
|
||
| /** | ||
| * @notice Emitted when a new operator is registered in the UniFi AVS. | ||
| * @param operator The address of the registered operator. | ||
|
|
@@ -152,6 +177,20 @@ interface IUniFiAVSManager { | |
| */ | ||
| event RestakingStrategyAllowlistUpdated(address indexed strategy, bool allowed); | ||
|
|
||
| /** | ||
| * @notice Emitted when a validator is slashed. | ||
| * @param operator The address of the operator managing the validator. | ||
| * @param blsPubKeyHash The BLS public key hash of the slashed validator. | ||
| */ | ||
| event ValidatorSlashed(address indexed operator, bytes32 indexed blsPubKeyHash); | ||
|
|
||
| /** | ||
| * @notice Emitted when the registration delay is set. | ||
| * @param oldDelay The previous registration delay value. | ||
| * @param newDelay The new registration delay value. | ||
| */ | ||
| event RegistrationDelaySet(uint64 oldDelay, uint64 newDelay); | ||
|
|
||
| /** | ||
| * @notice Returns the EigenPodManager contract. | ||
| * @return IEigenPodManager The EigenPodManager contract. | ||
|
|
@@ -232,6 +271,13 @@ interface IUniFiAVSManager { | |
| */ | ||
| function setDeregistrationDelay(uint64 newDelay) external; | ||
WalidOfNow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * @notice Sets a new registration delay for validators. | ||
| * @param newDelay The new registration delay in seconds. | ||
| * @dev Restricted to the DAO | ||
| */ | ||
| function setRegistrationDelay(uint64 newDelay) external; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO interface doesn't need to contain functions that are not meant to to be called by public. It adds unnecessary noise and LOC. I vote fore removing functions like this from the Interface and just keeping them + natspec in the contract
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is callable by DAO. So if our DAO becomes a contract in the future, we would need the interface for it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, but DAO contracts generally do not import interfaces. They deal with target & calldata. e.g. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/Governor.sol |
||
|
|
||
| /** | ||
| * @notice Sets the chain ID for a specific index in the bitmap. | ||
| * @param index The index in the bitmap to set. | ||
|
|
@@ -248,6 +294,29 @@ interface IUniFiAVSManager { | |
| */ | ||
| function setAllowlistRestakingStrategy(address strategy, bool allowed) external; | ||
|
|
||
| /** | ||
| * @notice Registers validators optimistically. | ||
| * @param paramsArray The array of ValidatorRegistrationParams. | ||
| */ | ||
| function registerValidatorsOptimistically(ValidatorRegistrationParams[] calldata paramsArray) external; | ||
|
|
||
| /** | ||
| * @notice Verifies the signatures of validators. | ||
| * @param blsPubKeyHashes The BLS public key hashes of the validators. | ||
| */ | ||
| function verifyValidatorSignatures(bytes32[] calldata blsPubKeyHashes) external; | ||
|
|
||
| /** | ||
| * @notice Slashes validators with invalid pubkey. | ||
| * @param proofs The inclusion proofs for each validator. | ||
| */ | ||
| function slashValidatorsWithInvalidPubkey(BeaconChainHelperLib.InclusionProof[] calldata proofs) external; | ||
|
|
||
| /** | ||
| * @notice Slashes validators with invalid index. | ||
| * @param proofs The inclusion proofs for each validator. | ||
| */ | ||
| function slashValidatorsWithInvalidIndex(BeaconChainHelperLib.InclusionProof[] calldata proofs) external; | ||
| /** | ||
| * @notice Retrieves information about a specific operator. | ||
| * @param operator The address of the operator. | ||
|
|
@@ -290,6 +359,12 @@ interface IUniFiAVSManager { | |
| */ | ||
| function getDeregistrationDelay() external view returns (uint64); | ||
|
|
||
| /** | ||
| * @notice Retrieves the current registration delay for validators. | ||
| * @return The current registration delay in seconds. | ||
| */ | ||
| function getRegistrationDelay() external view returns (uint64); | ||
|
|
||
| /** | ||
| * @notice Converts a bitmap to an array of chain IDs. | ||
| * @param bitmap The bitmap to convert. | ||
|
|
@@ -330,4 +405,18 @@ interface IUniFiAVSManager { | |
|
|
||
| /// @notice Returns the EigenLayer AVSDirectory contract. | ||
| function avsDirectory() external view returns (address); | ||
|
|
||
| /** | ||
| * @notice Returns the BLS message hash for a validator registration. | ||
WalidOfNow marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @param typeHash The type hash for the message. | ||
| * @param operator The address of the operator. | ||
| * @param salt The salt for the message. | ||
| * @param expiry The expiry for the message. | ||
| * @param index The index for the message. | ||
| * @return BN254.G1Point The BLS message hash. | ||
| */ | ||
| function blsMessageHash(bytes32 typeHash, address operator, bytes32 salt, uint256 expiry, uint256 index) | ||
| external | ||
| view | ||
| returns (BN254.G1Point memory); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,96 @@ | ||
| // SPDX-License-Identifier: GPL-3.0 | ||
| pragma solidity >=0.8.0 <0.9.0; | ||
|
|
||
| import { MerkleUtils } from "./MerkleUtils.sol"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in general, 1- MerkleUtils has a merkleize() function to construct a Merkle tree from an array of chunks. which is used for constructing the validator tree hash. It's important that we pass the validator information so we can check them in the sc and contruct the validator tree hash I am experimenting with the proving logic in general and trying to find a good balance between readability and control of the proofs. |
||
|
|
||
| library BeaconChainHelperLib { | ||
| address internal constant _BEACON_ROOT_CONTRACT = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02; | ||
|
|
||
| struct InclusionProof { | ||
| // `Chunks` of the SSZ encoded validator | ||
| bytes32[8] validator; | ||
| // Index of the validator in the beacon state validator list | ||
| uint256 validatorIndex; | ||
| // Proof of inclusion of validator in beacon state validator list | ||
| bytes32[] validatorProof; | ||
| // Root of the validator list in the beacon state | ||
| bytes32 validatorsRoot; | ||
| // Proof of inclusion of validator list in the beacon state | ||
| bytes32[] beaconStateProof; | ||
| // Root of the beacon state | ||
| bytes32 beaconStateRoot; | ||
| // Proof of inclusion of beacon state in the beacon block | ||
| bytes32[] beaconBlockProofForState; | ||
| // Proof of inclusion of the validator index in the beacon block | ||
| bytes32[] beaconBlockProofForProposerIndex; | ||
| // Timestamp of the beacon block | ||
| uint256 timestamp; | ||
| } | ||
|
|
||
| /// @dev The validator pub key failed verification against the pub key hash tree root in the validator chunks | ||
| error InvalidValidatorBLSPubKey(); | ||
| /// @dev The proof that the validator is a part of the validator list is invalid. | ||
| error ValidatorProofFailed(); | ||
| /// @dev The proof that the validator list is a part of the beacon state is invalid. | ||
| error BeaconStateProofFailed(); | ||
| /// @dev The proof that the beacon state is a part of the beacon block is invalid. | ||
| error BeaconBlockProofForStateFailed(); | ||
| /// @dev The proof that the actual validator index is a part of the beacon is invalid. | ||
| error BeaconBlockProofForProposerIndex(); | ||
|
|
||
| function verifyValidator(InclusionProof memory inclusionProof) internal returns (bool) { | ||
| (, bytes32 beaconBlockRoot) = getRootFromTimestamp(inclusionProof.timestamp); | ||
|
||
|
|
||
| // Validator is verified against the validator list in the beacon state | ||
| bytes32 validatorHashTreeRoot = MerkleUtils.merkleize(inclusionProof.validator); | ||
| if ( | ||
| !MerkleUtils.verifyProof( | ||
| inclusionProof.validatorProof, | ||
| inclusionProof.validatorsRoot, | ||
| validatorHashTreeRoot, | ||
| inclusionProof.validatorIndex | ||
| ) | ||
| ) { | ||
| // Revert if the proof that the expected validator is a part of the validator | ||
| // list in beacon state fails | ||
| return false; | ||
| } | ||
|
|
||
| if ( | ||
| !MerkleUtils.verifyProof( | ||
| inclusionProof.beaconStateProof, inclusionProof.beaconStateRoot, inclusionProof.validatorsRoot, 11 | ||
| ) | ||
| ) { | ||
| // Revert if the proof that the validator list is a part of the beacon state fails | ||
| return false; | ||
| } | ||
|
|
||
| // Beacon state is verified against the beacon block | ||
| if ( | ||
| !MerkleUtils.verifyProof( | ||
| inclusionProof.beaconBlockProofForState, beaconBlockRoot, inclusionProof.beaconStateRoot, 3 | ||
| ) | ||
| ) { | ||
| // Revert if the proof for the beacon state being a part of the beacon block fails | ||
| return false; | ||
| } | ||
|
|
||
| // Validator index is verified against the beacon block | ||
| if ( | ||
| !MerkleUtils.verifyProof( | ||
| inclusionProof.beaconBlockProofForProposerIndex, | ||
| beaconBlockRoot, | ||
| MerkleUtils.toLittleEndian(inclusionProof.validatorIndex), | ||
| 1 | ||
| ) | ||
| ) { | ||
| // Revert if the proof that the proposer index is a part of the beacon block fails | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| function verifyProposerAt(uint256 timestamp, uint256 proposerIndex, bytes32[2] memory proof) | ||
| internal | ||
| returns (bool) | ||
|
|
@@ -16,8 +103,8 @@ library BeaconChainHelperLib { | |
|
|
||
| bytes32 slotAndProposerIndexNode = sha256( | ||
| abi.encodePacked( | ||
| abi.encodePacked(to_little_endian_64(uint64(slot)), bytes24(0)), | ||
| abi.encodePacked(to_little_endian_64(uint64(proposerIndex)), bytes24(0)) | ||
| abi.encodePacked(MerkleUtils.to_little_endian_64(uint64(slot)), bytes24(0)), | ||
| abi.encodePacked(MerkleUtils.to_little_endian_64(uint64(proposerIndex)), bytes24(0)) | ||
| ) | ||
| ); | ||
|
|
||
|
|
@@ -28,20 +115,6 @@ library BeaconChainHelperLib { | |
| return root == beaconRootFromChain; | ||
| } | ||
|
|
||
| function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) { | ||
| ret = new bytes(8); | ||
| bytes8 bytesValue = bytes8(value); | ||
| // Byteswapping during copying to bytes. | ||
| ret[0] = bytesValue[7]; | ||
| ret[1] = bytesValue[6]; | ||
| ret[2] = bytesValue[5]; | ||
| ret[3] = bytesValue[4]; | ||
| ret[4] = bytesValue[3]; | ||
| ret[5] = bytesValue[2]; | ||
| ret[6] = bytesValue[1]; | ||
| ret[7] = bytesValue[0]; | ||
| } | ||
|
|
||
| function getRootFromTimestamp(uint256 timestamp) internal returns (bool, bytes32) { | ||
| (bool ret, bytes memory data) = _BEACON_ROOT_CONTRACT.call(bytes.concat(bytes32(timestamp))); | ||
| return (ret, bytes32(data)); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.