diff --git a/foundry.toml b/foundry.toml index 6a48adda..f8f557c1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,4 +6,7 @@ fs_permissions = [{ access = "read-write", path = "./" }] ffi = true +# The number of optimizer runs +optimizer_runs = 100 + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/script/AVSContractsDeploy.s.sol b/script/AVSContractsDeploy.s.sol index d3386dd3..8ec6c200 100644 --- a/script/AVSContractsDeploy.s.sol +++ b/script/AVSContractsDeploy.s.sol @@ -19,10 +19,8 @@ import "eigenlayer-contracts/src/contracts/pods/EigenPodManager.sol"; import "eigenlayer-contracts/src/contracts/pods/DelayedWithdrawalRouter.sol"; import "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -import "src/BLSPublicKeyCompendium.sol"; import "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; -import "eigenlayer-contracts/src/test/mocks/ETHDepositMock.sol"; import "eigenlayer-contracts/src/test/mocks/ERC20Mock.sol"; import "forge-std/Script.sol"; diff --git a/script/DeploySharedContracts.s.sol b/script/DeploySharedContracts.s.sol index d139f8fb..56535aec 100644 --- a/script/DeploySharedContracts.s.sol +++ b/script/DeploySharedContracts.s.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "../src/BLSPublicKeyCompendium.sol"; import "../src/OperatorStateRetriever.sol"; import "forge-std/Script.sol"; @@ -15,26 +14,24 @@ import "forge-std/Test.sol"; contract DeploySharedContracts is Script, Test { Vm cheats = Vm(HEVM_ADDRESS); - BLSPublicKeyCompendium public blsPublicKeyCompendium; OperatorStateRetriever public blsOperatorStateRetriever; function run() external { vm.startBroadcast(); - blsPublicKeyCompendium = new BLSPublicKeyCompendium(); blsOperatorStateRetriever = new OperatorStateRetriever(); vm.stopBroadcast(); string memory deployed_addresses = "addresses"; - vm.serializeAddress( + // vm.serializeAddress( + // deployed_addresses, + // "blsOperatorStateRetriever", + // address(blsOperatorStateRetriever) + // ); + string memory finalJson = vm.serializeAddress( deployed_addresses, "blsOperatorStateRetriever", address(blsOperatorStateRetriever) ); - string memory finalJson = vm.serializeAddress( - deployed_addresses, - "blsPublicKeyCompendium", - address(blsPublicKeyCompendium) - ); vm.writeJson(finalJson, outputFileName()); } diff --git a/src/BLSApkRegistry.sol b/src/BLSApkRegistry.sol index 14093fa0..a732a44a 100644 --- a/src/BLSApkRegistry.sol +++ b/src/BLSApkRegistry.sol @@ -3,7 +3,6 @@ pragma solidity =0.8.12; import {BLSApkRegistryStorage} from "src/BLSApkRegistryStorage.sol"; -import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {BN254} from "src/libraries/BN254.sol"; @@ -20,11 +19,10 @@ contract BLSApkRegistry is BLSApkRegistryStorage { _; } - /// @notice Sets the (immutable) `registryCoordinator` and `pubkeyCompendium` addresses + /// @notice Sets the (immutable) `registryCoordinator` address constructor( - IRegistryCoordinator _registryCoordinator, - IBLSPublicKeyCompendium _pubkeyCompendium - ) BLSApkRegistryStorage(_registryCoordinator, _pubkeyCompendium) {} + IRegistryCoordinator _registryCoordinator + ) BLSApkRegistryStorage(_registryCoordinator) {} /******************************************************************************* EXTERNAL FUNCTIONS - REGISTRY COORDINATOR @@ -34,7 +32,6 @@ contract BLSApkRegistry is BLSApkRegistryStorage { * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. * @param operator The address of the operator to register. * @param quorumNumbers The quorum numbers the operator is registering for, where each byte is an 8 bit integer quorumNumber. - * @return pubkeyHash of the operator's pubkey * @dev access restricted to the RegistryCoordinator * @dev Preconditions (these are assumed, not validated in this contract): * 1) `quorumNumbers` has no duplicates @@ -45,16 +42,15 @@ contract BLSApkRegistry is BLSApkRegistryStorage { function registerOperator( address operator, bytes memory quorumNumbers - ) public virtual onlyRegistryCoordinator returns (bytes32) { - // Get the operator's pubkey from the compendium. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator); + ) public virtual onlyRegistryCoordinator { + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey); // Return pubkeyHash, which will become the operator's unique id emit OperatorAddedToQuorums(operator, quorumNumbers); - return pubkeyHash; } /** @@ -73,8 +69,8 @@ contract BLSApkRegistry is BLSApkRegistryStorage { address operator, bytes memory quorumNumbers ) public virtual onlyRegistryCoordinator { - // Get the operator's pubkey from the compendium. Reverts if they have not registered a key - (BN254.G1Point memory pubkey, ) = pubkeyCompendium.getRegisteredPubkey(operator); + // Get the operator's pubkey. Reverts if they have not registered a key + (BN254.G1Point memory pubkey, ) = getRegisteredPubkey(operator); // Update each quorum's aggregate pubkey _processQuorumApkUpdate(quorumNumbers, pubkey.negate()); @@ -95,6 +91,58 @@ contract BLSApkRegistry is BLSApkRegistryStorage { })); } + /** + * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. + * @param operator is the operator for whom the key is being registered + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership + */ + function registerBLSPublicKey( + address operator, + PubkeyRegistrationParams calldata params, + BN254.G1Point calldata pubkeyRegistrationMessageHash + ) external onlyRegistryCoordinator returns (bytes32 operatorId) { + bytes32 pubkeyHash = BN254.hashG1Point(params.pubkeyG1); + require( + pubkeyHash != ZERO_PK_HASH, "BLSApkRegistry.registerBLSPublicKey: cannot register zero pubkey" + ); + require( + operatorToPubkeyHash[operator] == bytes32(0), + "BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey" + ); + require( + pubkeyHashToOperator[pubkeyHash] == address(0), + "BLSApkRegistry.registerBLSPublicKey: public key already registered" + ); + + // gamma = h(sigma, P, P', H(m)) + uint256 gamma = uint256(keccak256(abi.encodePacked( + params.pubkeyRegistrationSignature.X, + params.pubkeyRegistrationSignature.Y, + params.pubkeyG1.X, + params.pubkeyG1.Y, + params.pubkeyG2.X, + params.pubkeyG2.Y, + pubkeyRegistrationMessageHash.X, + pubkeyRegistrationMessageHash.Y + ))) % BN254.FR_MODULUS; + + // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') + require(BN254.pairing( + params.pubkeyRegistrationSignature.plus(params.pubkeyG1.scalar_mul(gamma)), + BN254.negGeneratorG2(), + pubkeyRegistrationMessageHash.plus(BN254.generatorG1().scalar_mul(gamma)), + params.pubkeyG2 + ), "BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); + + operatorToPubkey[operator] = params.pubkeyG1; + operatorToPubkeyHash[operator] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = operator; + + emit NewPubkeyRegistration(operator, params.pubkeyG1, params.pubkeyG2); + return pubkeyHash; + } + /******************************************************************************* INTERNAL FUNCTIONS *******************************************************************************/ @@ -148,6 +196,21 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /******************************************************************************* VIEW FUNCTIONS *******************************************************************************/ + /** + * @notice Returns the pubkey and pubkey hash of an operator + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { + BN254.G1Point memory pubkey = operatorToPubkey[operator]; + bytes32 pubkeyHash = operatorToPubkeyHash[operator]; + + require( + pubkeyHash != bytes32(0), + "BLSApkRegistry.getRegisteredPubkey: operator is not registered" + ); + + return (pubkey, pubkeyHash); + } /** * @notice Returns the indices of the quorumApks index at `blockNumber` for the provided `quorumNumbers` @@ -212,11 +275,12 @@ contract BLSApkRegistry is BLSApkRegistryStorage { /// @notice Returns the operator address for the given `pubkeyHash` function getOperatorFromPubkeyHash(bytes32 pubkeyHash) public view returns (address) { - return pubkeyCompendium.pubkeyHashToOperator(pubkeyHash); + return pubkeyHashToOperator[pubkeyHash]; } + /// @notice returns the ID used to identify the `operator` within this AVS + /// @dev Returns zero in the event that the `operator` has never registered for the AVS function getOperatorId(address operator) public view returns (bytes32) { - (, bytes32 pubkeyHash) = pubkeyCompendium.getRegisteredPubkey(operator); - return pubkeyHash; + return operatorToPubkeyHash[operator]; } } diff --git a/src/BLSApkRegistryStorage.sol b/src/BLSApkRegistryStorage.sol index 1629d708..4676a6f5 100644 --- a/src/BLSApkRegistryStorage.sol +++ b/src/BLSApkRegistryStorage.sol @@ -3,30 +3,38 @@ pragma solidity =0.8.12; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; -import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol"; import {BN254} from "src/libraries/BN254.sol"; abstract contract BLSApkRegistryStorage is Initializable, IBLSApkRegistry { + /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) + bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; + /// @notice the registry coordinator contract IRegistryCoordinator public immutable registryCoordinator; - /// @notice the BLSPublicKeyCompendium contract against which pubkey ownership is checked - IBLSPublicKeyCompendium public immutable pubkeyCompendium; + // storage for individual pubkeys + /// @notice maps operator address to pubkey hash + mapping(address => bytes32) public operatorToPubkeyHash; + /// @notice maps pubkey hash to operator address + mapping(bytes32 => address) public pubkeyHashToOperator; + /// @notice maps operator address to pubkeyG1 + mapping(address => BN254.G1Point) public operatorToPubkey; + + // storage for aggregate pubkeys (APKs) /// @notice maps quorumNumber => historical aggregate pubkey updates mapping(uint8 => ApkUpdate[]) public apkHistory; /// @notice maps quorumNumber => current aggregate pubkey of quorum mapping(uint8 => BN254.G1Point) public currentApk; - constructor(IRegistryCoordinator _registryCoordinator, IBLSPublicKeyCompendium _pubkeyCompendium) { + constructor(IRegistryCoordinator _registryCoordinator) { registryCoordinator = _registryCoordinator; - pubkeyCompendium = _pubkeyCompendium; // disable initializers so that the implementation contract cannot be initialized _disableInitializers(); } // storage gap for upgradeability - uint256[48] private __GAP; + uint256[45] private __GAP; } diff --git a/src/BLSPublicKeyCompendium.sol b/src/BLSPublicKeyCompendium.sol deleted file mode 100644 index f9aad7f1..00000000 --- a/src/BLSPublicKeyCompendium.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import {IBLSPublicKeyCompendium} from "src/interfaces/IBLSPublicKeyCompendium.sol"; -import {BN254} from "src/libraries/BN254.sol"; - -/** - * @title A shared contract for EigenLayer operators to register their BLS public keys. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -contract BLSPublicKeyCompendium is IBLSPublicKeyCompendium { - using BN254 for BN254.G1Point; - - /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - - /// @notice maps operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice maps pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - /// @notice maps operator address to pubkeyG1 - mapping(address => BN254.G1Point) public operatorToPubkey; - - /******************************************************************************* - EXTERNAL FUNCTIONS - *******************************************************************************/ - - /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. - * @param signedMessageHash is the registration message hash signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator - * @param pubkeyG2 is the corresponding G2 public key of the operator - */ - function registerBLSPublicKey( - BN254.G1Point memory signedMessageHash, - BN254.G1Point memory pubkeyG1, - BN254.G2Point memory pubkeyG2 - ) external { - bytes32 pubkeyHash = BN254.hashG1Point(pubkeyG1); - require( - pubkeyHash != ZERO_PK_HASH, "BLSPublicKeyCompendium.registerBLSPublicKey: cannot register zero pubkey" - ); - require( - operatorToPubkeyHash[msg.sender] == bytes32(0), - "BLSPublicKeyCompendium.registerBLSPublicKey: operator already registered pubkey" - ); - require( - pubkeyHashToOperator[pubkeyHash] == address(0), - "BLSPublicKeyCompendium.registerBLSPublicKey: public key already registered" - ); - - // H(m) - BN254.G1Point memory messageHash = getMessageHash(msg.sender); - - // gamma = h(sigma, P, P', H(m)) - uint256 gamma = uint256(keccak256(abi.encodePacked( - signedMessageHash.X, - signedMessageHash.Y, - pubkeyG1.X, - pubkeyG1.Y, - pubkeyG2.X, - pubkeyG2.Y, - messageHash.X, - messageHash.Y - ))) % BN254.FR_MODULUS; - - // e(sigma + P * gamma, [-1]_2) = e(H(m) + [1]_1 * gamma, P') - require(BN254.pairing( - signedMessageHash.plus(pubkeyG1.scalar_mul(gamma)), - BN254.negGeneratorG2(), - messageHash.plus(BN254.generatorG1().scalar_mul(gamma)), - pubkeyG2 - ), "BLSPublicKeyCompendium.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match"); - - operatorToPubkey[msg.sender] = pubkeyG1; - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; - - emit NewPubkeyRegistration(msg.sender, pubkeyG1, pubkeyG2); - } - - /******************************************************************************* - VIEW FUNCTIONS - *******************************************************************************/ - - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey - */ - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - - /** - * @notice Returns the message hash that an operator must sign to register their BLS public key. - * @param operator is the address of the operator registering their BLS public key - */ - function getMessageHash(address operator) public view returns (BN254.G1Point memory) { - return BN254.hashToG1(keccak256(abi.encodePacked( - operator, - address(this), - block.chainid, - "EigenLayer_BN254_Pubkey_Registration" - ))); - } -} diff --git a/src/RegistryCoordinator.sol b/src/RegistryCoordinator.sol index 714285bb..b86f9dc6 100644 --- a/src/RegistryCoordinator.sol +++ b/src/RegistryCoordinator.sol @@ -19,6 +19,7 @@ import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title A `RegistryCoordinator` that has three registries: @@ -38,10 +39,13 @@ contract RegistryCoordinator is ISignatureUtils { using BitmapUtils for *; + using BN254 for BN254.G1Point; /// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract bytes32 public constant OPERATOR_CHURN_APPROVAL_TYPEHASH = keccak256("OperatorChurnApproval(bytes32 registeringOperatorId,OperatorKickParam[] operatorKickParams)OperatorKickParam(address operator,bytes32[] operatorIdsToSwap)"); + /// @notice The EIP-712 typehash used for registering BLS public keys + bytes32 public constant PUBKEY_REGISTRATION_TYPEHASH = keccak256("BN254PubkeyRegistration(address operator)"); /// @notice The maximum value of a quorum bitmap uint256 internal constant MAX_QUORUM_BITMAP = type(uint192).max; /// @notice The basis point denominator @@ -157,35 +161,40 @@ contract RegistryCoordinator is * operator capacity, this method will fail. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for * @param socket is the socket of the operator + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @dev the `params` input param is ignored if the caller has previously registered a public key * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager */ function registerOperator( bytes calldata quorumNumbers, string calldata socket, + IBLSApkRegistry.PubkeyRegistrationParams calldata params, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { - bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + /** + * IF the operator has never registered a pubkey before, THEN register their pubkey + * OTHERWISE, simply ignore the provided `params` input + */ + bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params); // Register the operator in each of the registry contracts - RegisterResults memory results = _registerOperator({ + uint32[] memory numOperatorsPerQuorum = _registerOperator({ operator: msg.sender, operatorId: operatorId, quorumNumbers: quorumNumbers, socket: socket, operatorSignature: operatorSignature - }); + }).numOperatorsPerQuorum; for (uint256 i = 0; i < quorumNumbers.length; i++) { uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; - + /** * The new operator count for each quorum may not exceed the configured maximum * If it does, use `registerOperatorWithChurn` instead. */ require( - results.numOperatorsPerQuorum[i] <= operatorSetParams.maxOperatorCount, + numOperatorsPerQuorum[i] <= _quorumParams[quorumNumber].maxOperatorCount, "RegistryCoordinator.registerOperator: operator count exceeds maximum" ); } @@ -195,21 +204,28 @@ contract RegistryCoordinator is * @notice Registers msg.sender as an operator for one or more quorums. If any quorum reaches its maximum operator * capacity, `operatorKickParams` is used to replace an old operator with the new one. * @param quorumNumbers is an ordered byte array containing the quorum numbers being registered for + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership * @param operatorKickParams are used to determine which operator is removed to maintain quorum capacity as the * operator registers for quorums. * @param churnApproverSignature is the signature of the churnApprover on the operator kick params * @param operatorSignature is the signature of the operator used by the AVS to register the operator in the delegation manager + * @dev the `params` input param is ignored if the caller has previously registered a public key */ function registerOperatorWithChurn( bytes calldata quorumNumbers, string calldata socket, + IBLSApkRegistry.PubkeyRegistrationParams calldata params, OperatorKickParam[] calldata operatorKickParams, SignatureWithSaltAndExpiry memory churnApproverSignature, SignatureWithSaltAndExpiry memory operatorSignature ) external onlyWhenNotPaused(PAUSED_REGISTER_OPERATOR) { require(operatorKickParams.length == quorumNumbers.length, "RegistryCoordinator.registerOperatorWithChurn: input length mismatch"); - bytes32 operatorId = blsApkRegistry.getOperatorId(msg.sender); + /** + * IF the operator has never registered a pubkey before, THEN register their pubkey + * OTHERWISE, simply ignore the provided `params` input + */ + bytes32 operatorId = _getOrCreateOperatorId(msg.sender, params); // Verify the churn approver's signature for the registering operator and kick params _verifyChurnApproverSignature({ @@ -228,9 +244,8 @@ contract RegistryCoordinator is }); for (uint256 i = 0; i < quorumNumbers.length; i++) { - uint8 quorumNumber = uint8(quorumNumbers[i]); - - OperatorSetParam memory operatorSetParams = _quorumParams[quorumNumber]; + // reference: uint8 quorumNumber = uint8(quorumNumbers[i]); + OperatorSetParam memory operatorSetParams = _quorumParams[uint8(quorumNumbers[i])]; /** * If the new operator count for any quorum exceeds the maximum, validate @@ -238,7 +253,7 @@ contract RegistryCoordinator is */ if (results.numOperatorsPerQuorum[i] > operatorSetParams.maxOperatorCount) { _validateChurn({ - quorumNumber: quorumNumber, + quorumNumber: uint8(quorumNumbers[i]), totalQuorumStake: results.totalStakes[i], newOperator: msg.sender, newOperatorStake: results.operatorStakes[i], @@ -417,7 +432,7 @@ contract RegistryCoordinator is /** * @notice Sets the metadata URI for the AVS * @param _metadataURI is the metadata URI for the AVS - * @dev only callable by the service manager owner + * @dev only callable by the owner */ function setMetadataURI(string memory _metadataURI) external onlyOwner { delegationManager.updateAVSMetadataURI(_metadataURI); @@ -443,7 +458,7 @@ contract RegistryCoordinator is bytes calldata quorumNumbers, string memory socket, SignatureWithSaltAndExpiry memory operatorSignature - ) internal virtual returns (RegisterResults memory) { + ) internal virtual returns (RegisterResults memory results) { /** * Get bitmap of quorums to register for and operator's current bitmap. Validate that: * - we're trying to register for at least 1 quorum @@ -483,17 +498,23 @@ contract RegistryCoordinator is /** * Register the operator with the BLSApkRegistry, StakeRegistry, and IndexRegistry */ - bytes32 registeredId = blsApkRegistry.registerOperator(operator, quorumNumbers); - require(registeredId == operatorId, "RegistryCoordinator._registerOperator: operatorId mismatch"); - (uint96[] memory operatorStakes, uint96[] memory totalStakes) = + blsApkRegistry.registerOperator(operator, quorumNumbers); + (results.operatorStakes, results.totalStakes) = stakeRegistry.registerOperator(operator, operatorId, quorumNumbers); - uint32[] memory numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); + results.numOperatorsPerQuorum = indexRegistry.registerOperator(operatorId, quorumNumbers); - return RegisterResults({ - numOperatorsPerQuorum: numOperatorsPerQuorum, - operatorStakes: operatorStakes, - totalStakes: totalStakes - }); + return results; + } + + function _getOrCreateOperatorId( + address operator, + IBLSApkRegistry.PubkeyRegistrationParams calldata params + ) internal returns (bytes32 operatorId) { + operatorId = blsApkRegistry.getOperatorId(operator); + if (operatorId == 0) { + operatorId = blsApkRegistry.registerBLSPublicKey(operator, params, pubkeyRegistrationMessageHash(operator)); + } + return operatorId; } function _validateChurn( @@ -838,6 +859,18 @@ contract RegistryCoordinator is return _hashTypedDataV4(keccak256(abi.encode(OPERATOR_CHURN_APPROVAL_TYPEHASH, registeringOperatorId, operatorKickParams, salt, expiry))); } + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { + return BN254.hashToG1( + _hashTypedDataV4( + keccak256(abi.encode(PUBKEY_REGISTRATION_TYPEHASH, operator)) + ) + ); + } + /// @dev need to override function here since its defined in both these contracts function owner() public diff --git a/src/interfaces/IBLSApkRegistry.sol b/src/interfaces/IBLSApkRegistry.sol index 58cdc10b..62a3e6b1 100644 --- a/src/interfaces/IBLSApkRegistry.sol +++ b/src/interfaces/IBLSApkRegistry.sol @@ -10,29 +10,45 @@ import {BN254} from "src/libraries/BN254.sol"; * @author Layr Labs, Inc. */ interface IBLSApkRegistry is IRegistry { + // STRUCTS + /// @notice Data structure used to track the history of the Aggregate Public Key of all operators + struct ApkUpdate { + // first 24 bytes of keccak256(apk_x0, apk_x1, apk_y0, apk_y1) + bytes24 apkHash; + // block number at which the update occurred + uint32 updateBlockNumber; + // block number at which the next update occurred + uint32 nextUpdateBlockNumber; + } + + /** + * @notice Struct used when registering a new public key + * @param signedMessageHash is the registration message hash signed by the private key of the operator + * @param pubkeyG1 is the corresponding G1 public key of the operator + * @param pubkeyG2 is the corresponding G2 public key of the operator + */ + struct PubkeyRegistrationParams { + BN254.G1Point pubkeyRegistrationSignature; + BN254.G1Point pubkeyG1; + BN254.G2Point pubkeyG2; + } + // EVENTS - // Emitted when a new operator pubkey is registered for a set of quorums + /// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`. + event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2); + + // @notice Emitted when a new operator pubkey is registered for a set of quorums event OperatorAddedToQuorums( address operator, bytes quorumNumbers ); - // Emitted when an operator pubkey is removed from a set of quorums + // @notice Emitted when an operator pubkey is removed from a set of quorums event OperatorRemovedFromQuorums( address operator, bytes quorumNumbers ); - /// @notice Data structure used to track the history of the Aggregate Public Key of all operators - struct ApkUpdate { - // first 24 bytes of keccak256(apk_x0, apk_x1, apk_y0, apk_y1) - bytes24 apkHash; - // block number at which the update occurred - uint32 updateBlockNumber; - // block number at which the next update occurred - uint32 nextUpdateBlockNumber; - } - /** * @notice Registers the `operator`'s pubkey for the specified `quorumNumbers`. * @param operator The address of the operator to register. @@ -44,7 +60,7 @@ interface IBLSApkRegistry is IRegistry { * 3) `quorumNumbers` is ordered in ascending order * 4) the operator is not already registered */ - function registerOperator(address operator, bytes calldata quorumNumbers) external returns(bytes32); + function registerOperator(address operator, bytes calldata quorumNumbers) external; /** * @notice Deregisters the `operator`'s pubkey for the specified `quorumNumbers`. @@ -66,6 +82,37 @@ interface IBLSApkRegistry is IRegistry { */ function initializeQuorum(uint8 quorumNumber) external; + /** + * @notice mapping from operator address to pubkey hash. + * Returns *zero* if the `operator` has never registered, and otherwise returns the hash of the public key of the operator. + */ + function operatorToPubkeyHash(address operator) external view returns (bytes32); + + /** + * @notice mapping from pubkey hash to operator address. + * Returns *zero* if no operator has ever registered the public key corresponding to `pubkeyHash`, + * and otherwise returns the (unique) registered operator who owns the BLS public key that is the preimage of `pubkeyHash`. + */ + function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address); + + /** + * @notice Called by the RegistryCoordinator register an operator as the owner of a BLS public key. + * @param operator is the operator for whom the key is being registered + * @param params contains the G1 & G2 public keys of the operator, and a signature proving their ownership + * @param pubkeyRegistrationMessageHash is a hash that the operator must sign to prove key ownership + */ + function registerBLSPublicKey( + address operator, + PubkeyRegistrationParams calldata params, + BN254.G1Point calldata pubkeyRegistrationMessageHash + ) external returns (bytes32 operatorId); + + /** + * @notice Returns the pubkey and pubkey hash of an operator + * @dev Reverts if the operator has not registered a valid pubkey + */ + function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); + /// @notice Returns the current APK for the provided `quorumNumber ` function getApk(uint8 quorumNumber) external view returns (BN254.G1Point memory); @@ -87,5 +134,7 @@ interface IBLSApkRegistry is IRegistry { */ function getApkHashAtBlockNumberAndIndex(uint8 quorumNumber, uint32 blockNumber, uint256 index) external view returns (bytes24); + /// @notice returns the ID used to identify the `operator` within this AVS. + /// @dev Returns zero in the event that the `operator` has never registered for the AVS function getOperatorId(address operator) external view returns (bytes32); } diff --git a/src/interfaces/IBLSPublicKeyCompendium.sol b/src/interfaces/IBLSPublicKeyCompendium.sol deleted file mode 100644 index b7ab1a8e..00000000 --- a/src/interfaces/IBLSPublicKeyCompendium.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity >=0.5.0; - -import {BN254} from "src/libraries/BN254.sol"; - -/** - * @title Minimal interface for the `BLSPublicKeyCompendium` contract. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -interface IBLSPublicKeyCompendium { - - // EVENTS - /// @notice Emitted when `operator` registers with the public keys `pubkeyG1` and `pubkeyG2`. - event NewPubkeyRegistration(address indexed operator, BN254.G1Point pubkeyG1, BN254.G2Point pubkeyG2); - - /** - * @notice mapping from operator address to pubkey hash. - * Returns *zero* if the `operator` has never registered, and otherwise returns the hash of the public key of the operator. - */ - function operatorToPubkeyHash(address operator) external view returns (bytes32); - - /** - * @notice mapping from pubkey hash to operator address. - * Returns *zero* if no operator has ever registered the public key corresponding to `pubkeyHash`, - * and otherwise returns the (unique) registered operator who owns the BLS public key that is the preimage of `pubkeyHash`. - */ - function pubkeyHashToOperator(bytes32 pubkeyHash) external view returns (address); - - /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. - * @param signedMessageHash is the registration message hash signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator - * @param pubkeyG2 is the corresponding G2 public key of the operator - */ - function registerBLSPublicKey(BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2) external; - - /** - * @notice Returns the pubkey and pubkey hash of an operator - * @dev Reverts if the operator has not registered a valid pubkey - */ - function getRegisteredPubkey(address operator) external view returns (BN254.G1Point memory, bytes32); - - /** - * @notice Returns the message hash that an operator must sign to register their BLS public key. - * @param operator is the address of the operator registering their BLS public key - */ - function getMessageHash(address operator) external view returns (BN254.G1Point memory); -} diff --git a/src/interfaces/IRegistryCoordinator.sol b/src/interfaces/IRegistryCoordinator.sol index 02b72a5e..7e84b491 100644 --- a/src/interfaces/IRegistryCoordinator.sol +++ b/src/interfaces/IRegistryCoordinator.sol @@ -4,6 +4,7 @@ pragma solidity =0.8.12; import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; import {IStakeRegistry} from "src/interfaces/IStakeRegistry.sol"; import {IIndexRegistry} from "src/interfaces/IIndexRegistry.sol"; +import {BN254} from "src/libraries/BN254.sol"; /** * @title Interface for a contract that coordinates between various registries for an AVS. @@ -138,6 +139,12 @@ interface IRegistryCoordinator { /// @notice Returns the number of registries function numRegistries() external view returns (uint256); + /** + * @notice Returns the message hash that an operator must sign to register their BLS public key. + * @param operator is the address of the operator registering their BLS public key + */ + function pubkeyRegistrationMessageHash(address operator) external view returns (BN254.G1Point memory); + /// @notice returns the blocknumber the quorum was last updated all at once for all operators function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256); diff --git a/test/ffi/BLSPubKeyCompendiumFFI.t.sol b/test/ffi/BLSPubKeyCompendiumFFI.t.sol index c3a5216d..3bb75118 100644 --- a/test/ffi/BLSPubKeyCompendiumFFI.t.sol +++ b/test/ffi/BLSPubKeyCompendiumFFI.t.sol @@ -1,49 +1,51 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.12; -import "src/BLSPublicKeyCompendium.sol"; +import "src/BLSApkRegistry.sol"; import "test/ffi/util/G2Operations.sol"; +import {IBLSApkRegistry} from "src/interfaces/IBLSApkRegistry.sol"; -contract BLSPublicKeyCompendiumFFITests is G2Operations { +contract BLSApkRegistryFFITests is G2Operations { using BN254 for BN254.G1Point; using Strings for uint256; Vm cheats = Vm(HEVM_ADDRESS); - BLSPublicKeyCompendium compendium; + BLSApkRegistry blsApkRegistry; + IRegistryCoordinator registryCoordinator; uint256 privKey; - BN254.G1Point pubKeyG1; - BN254.G2Point pubKeyG2; - BN254.G1Point signedMessageHash; + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; address alice = address(0x69); function setUp() public { - compendium = new BLSPublicKeyCompendium(); + blsApkRegistry = new BLSApkRegistry(registryCoordinator); } function testRegisterBLSPublicKey(uint256 _privKey) public { cheats.assume(_privKey != 0); _setKeys(_privKey); - signedMessageHash = _signMessage(alice); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); - vm.prank(alice); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); + vm.prank(address(registryCoordinator)); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, registryCoordinator.pubkeyRegistrationMessageHash(alice)); - assertEq(compendium.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); - assertEq(compendium.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); + assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1), + "pubkey hash not stored correctly"); + assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1)), alice, + "operator address not stored correctly"); } function _setKeys(uint256 _privKey) internal { privKey = _privKey; - pubKeyG1 = BN254.generatorG1().scalar_mul(_privKey); - pubKeyG2 = G2Operations.mul(_privKey); + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(_privKey); + pubkeyRegistrationParams.pubkeyG2 = G2Operations.mul(_privKey); } function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = compendium.getMessageHash(signer); + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); return BN254.scalar_mul(messageHash, privKey); } } diff --git a/test/harnesses/BLSApkRegistryHarness.sol b/test/harnesses/BLSApkRegistryHarness.sol new file mode 100644 index 00000000..a888986e --- /dev/null +++ b/test/harnesses/BLSApkRegistryHarness.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity =0.8.12; + +import "src/BLSApkRegistry.sol"; + +// wrapper around the BLSApkRegistry contract that exposes internal functionality, for unit testing _other functionality_. +contract BLSApkRegistryHarness is BLSApkRegistry { + + constructor( + IRegistryCoordinator _registryCoordinator + ) BLSApkRegistry(_registryCoordinator) {} + + function setBLSPublicKey(address account, BN254.G1Point memory pk) external { + + bytes32 pubkeyHash = BN254.hashG1Point(pk); + // store updates + operatorToPubkeyHash[account] = pubkeyHash; + pubkeyHashToOperator[pubkeyHash] = account; + operatorToPubkey[account] = pk; + } +} diff --git a/test/harnesses/RegistryCoordinatorHarness.sol b/test/harnesses/RegistryCoordinatorHarness.t.sol similarity index 93% rename from test/harnesses/RegistryCoordinatorHarness.sol rename to test/harnesses/RegistryCoordinatorHarness.t.sol index 246ea705..f3ce41a7 100644 --- a/test/harnesses/RegistryCoordinatorHarness.sol +++ b/test/harnesses/RegistryCoordinatorHarness.t.sol @@ -3,8 +3,10 @@ pragma solidity =0.8.12; import "src/RegistryCoordinator.sol"; +import "forge-std/Test.sol"; + // wrapper around the RegistryCoordinator contract that exposes the internal functions for unit testing. -contract RegistryCoordinatorHarness is RegistryCoordinator { +contract RegistryCoordinatorHarness is RegistryCoordinator, Test { constructor( IDelegationManager _delegationManager, ISlasher _slasher, diff --git a/test/integration/CoreRegistration.t.sol b/test/integration/CoreRegistration.t.sol index 1df04dfc..fa16bbb3 100644 --- a/test/integration/CoreRegistration.t.sol +++ b/test/integration/CoreRegistration.t.sol @@ -57,7 +57,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Set operator address operator = cheats.addr(operatorPrivateKey); - pubkeyCompendium.setBLSPublicKey(operator, defaultPubKey); + blsApkRegistry.setBLSPublicKey(operator, defaultPubKey); // Register operator to EigenLayer cheats.prank(operator); @@ -91,7 +91,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); // Check operator is registered IDelegationManager.OperatorAVSRegistrationStatus operatorStatus = delegationManager.avsOperatorStatus(address(registryCoordinator), operator); @@ -152,7 +152,7 @@ contract Test_CoreRegistration is MockAVSDeployer { // Register operator cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, operatorSignature); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, operatorSignature); } function _getOperatorSignature( diff --git a/test/mocks/BLSPublicKeyCompendiumMock.sol b/test/mocks/BLSPublicKeyCompendiumMock.sol deleted file mode 100644 index fa5c7867..00000000 --- a/test/mocks/BLSPublicKeyCompendiumMock.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "src/interfaces/IBLSPublicKeyCompendium.sol"; -import "src/libraries/BN254.sol"; -/** - * @title A shared contract for EigenLayer operators to register their BLS public keys. - * @author Layr Labs, Inc. - * @notice Terms of Service: https://docs.eigenlayer.xyz/overview/terms-of-service - */ -contract BLSPublicKeyCompendiumMock is IBLSPublicKeyCompendium{ - - /// @notice the hash of the zero pubkey aka BN254.G1Point(0,0) - bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - - /// @notice maps operator address to pubkey hash - mapping(address => bytes32) public operatorToPubkeyHash; - /// @notice maps pubkey hash to operator address - mapping(bytes32 => address) public pubkeyHashToOperator; - /// @notice maps operator address to pubkeyG1 - mapping(address => BN254.G1Point) public operatorToPubkey; - - /** - * @notice Called by an operator to register themselves as the owner of a BLS public key and reveal their G1 and G2 public key. - * @param signedMessageHash is the registration message hash signed by the private key of the operator - * @param pubkeyG1 is the corresponding G1 public key of the operator - * @param pubkeyG2 is the corresponding G2 public key of the operator - */ - function registerBLSPublicKey(BN254.G1Point memory signedMessageHash, BN254.G1Point memory pubkeyG1, BN254.G2Point memory pubkeyG2) external { - } - - function registerPublicKey(BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[msg.sender] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = msg.sender; - operatorToPubkey[msg.sender] = pk; - } - - function setBLSPublicKey(address account, BN254.G1Point memory pk) external { - - bytes32 pubkeyHash = BN254.hashG1Point(pk); - // store updates - operatorToPubkeyHash[account] = pubkeyHash; - pubkeyHashToOperator[pubkeyHash] = account; - operatorToPubkey[account] = pk; - } - - function getRegisteredPubkey(address operator) public view returns (BN254.G1Point memory, bytes32) { - BN254.G1Point memory pubkey = operatorToPubkey[operator]; - bytes32 pubkeyHash = operatorToPubkeyHash[operator]; - - require( - pubkeyHash != bytes32(0), - "BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered" - ); - - return (pubkey, pubkeyHash); - } - - function getMessageHash(address operator) external view returns (BN254.G1Point memory) {} -} diff --git a/test/mocks/MiddlewareRegistryMock.sol b/test/mocks/MiddlewareRegistryMock.sol deleted file mode 100644 index 64d1fe5d..00000000 --- a/test/mocks/MiddlewareRegistryMock.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; -import "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; - -contract MiddlewareRegistryMock{ - IStrategyManager public strategyManager; - ISlasher public slasher; - - - constructor( - IStrategyManager _strategyManager - ) { - strategyManager = _strategyManager; - slasher = _strategyManager.slasher(); - } - - function registerOperator(address operator, uint32 serveUntil) public { - // require(slasher.canSlash(operator, address(serviceManager)), "Not opted into slashing"); - - } - - function deregisterOperator(address operator) public { - } - - function isActiveOperator(address operator) external pure returns (bool) { - if (operator != address(0)) { - return true; - } else { - return false; - } - } - -} diff --git a/test/mocks/RegistryCoordinatorMock.sol b/test/mocks/RegistryCoordinatorMock.sol index f6ca981d..fb68a38f 100644 --- a/test/mocks/RegistryCoordinatorMock.sol +++ b/test/mocks/RegistryCoordinatorMock.sol @@ -59,6 +59,12 @@ contract RegistryCoordinatorMock is IRegistryCoordinator { function deregisterOperator(bytes calldata quorumNumbers, bytes calldata) external {} + function pubkeyRegistrationMessageHash(address operator) public view returns (BN254.G1Point memory) { + return BN254.hashToG1( + keccak256(abi.encode(operator)) + ); + } + function quorumUpdateBlockNumber(uint8 quorumNumber) external view returns (uint256) {} function owner() external view returns (address) {} diff --git a/test/mocks/StakeRegistryMock.sol b/test/mocks/StakeRegistryMock.sol index 411b1f9e..3e614940 100644 --- a/test/mocks/StakeRegistryMock.sol +++ b/test/mocks/StakeRegistryMock.sol @@ -200,7 +200,7 @@ contract StakeRegistryMock is IStakeRegistry { bytes calldata quorumNumbers ) external returns (uint192) {} - function getMockOperatorId(address operator) external returns(bytes32) { + function getMockOperatorId(address operator) external pure returns(bytes32) { return bytes32(uint256(keccak256(abi.encodePacked(operator, "operatorId")))); } } diff --git a/test/unit/BLSApkRegistryUnit.t.sol b/test/unit/BLSApkRegistryUnit.t.sol index fe82c373..81c02654 100644 --- a/test/unit/BLSApkRegistryUnit.t.sol +++ b/test/unit/BLSApkRegistryUnit.t.sol @@ -3,9 +3,7 @@ pragma solidity =0.8.12; import "forge-std/Test.sol"; -import "src/BLSApkRegistry.sol"; -import "src/interfaces/IRegistryCoordinator.sol"; -import "test/mocks/BLSPublicKeyCompendiumMock.sol"; +import "test/harnesses/BLSApkRegistryHarness.sol"; import "test/mocks/RegistryCoordinatorMock.sol"; @@ -18,12 +16,18 @@ contract BLSApkRegistryUnitTests is Test { bytes32 internal constant ZERO_PK_HASH = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5"; - BLSApkRegistry public blsApkRegistry; - BLSPublicKeyCompendiumMock public pkCompendium; + BLSApkRegistryHarness public blsApkRegistry; RegistryCoordinatorMock public registryCoordinator; BN254.G1Point internal defaultPubKey = BN254.G1Point(18260007818883133054078754218619977578772505796600400998181738095793040006897,3432351341799135763167709827653955074218841517684851694584291831827675065899); + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; + + address alice = address(1); + address bob = address(2); + + uint256 privKey = 69; + uint8 internal defaultQuorumNumber = 0; // Track initialized quorums so we can filter these out when fuzzing @@ -31,8 +35,16 @@ contract BLSApkRegistryUnitTests is Test { function setUp() external { registryCoordinator = new RegistryCoordinatorMock(); - pkCompendium = new BLSPublicKeyCompendiumMock(); - blsApkRegistry = new BLSApkRegistry(registryCoordinator, pkCompendium); + blsApkRegistry = new BLSApkRegistryHarness(registryCoordinator); + + pubkeyRegistrationParams.pubkeyG1 = BN254.generatorG1().scalar_mul(privKey); + + //privKey*G2 + pubkeyRegistrationParams.pubkeyG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; + pubkeyRegistrationParams.pubkeyG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; + pubkeyRegistrationParams.pubkeyG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; + pubkeyRegistrationParams.pubkeyG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; + // Initialize a quorum _initializeQuorum(defaultQuorumNumber); @@ -40,7 +52,6 @@ contract BLSApkRegistryUnitTests is Test { function testConstructorArgs() public view { require(blsApkRegistry.registryCoordinator() == registryCoordinator, "registryCoordinator not set correctly"); - require(blsApkRegistry.pubkeyCompendium() == pkCompendium, "pubkeyCompendium not set correctly"); } function testCallRegisterOperatorFromNonCoordinatorAddress(address nonCoordinatorAddress) public { @@ -63,7 +74,7 @@ contract BLSApkRegistryUnitTests is Test { function testOperatorDoesNotOwnPubKeyRegister() public { cheats.startPrank(address(registryCoordinator)); - cheats.expectRevert(bytes("BLSPublicKeyCompendium.getRegisteredPubkey: operator is not registered")); + cheats.expectRevert(bytes("BLSApkRegistry.getRegisteredPubkey: operator is not registered")); blsApkRegistry.registerOperator(defaultOperator, new bytes(1)); cheats.stopPrank(); } @@ -73,21 +84,19 @@ contract BLSApkRegistryUnitTests is Test { BN254.G1Point memory pubkey = BN254.hashToG1(x); bytes32 pkHash = BN254.hashG1Point(pubkey); - cheats.startPrank(operator); - pkCompendium.registerPublicKey(pubkey); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(operator, pubkey); cheats.stopPrank(); //register for one quorum bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(address(registryCoordinator)); - bytes32 registeredpkHash = blsApkRegistry.registerOperator(operator, quorumNumbers); - cheats.stopPrank(); - + cheats.prank(address(registryCoordinator)); + blsApkRegistry.registerOperator(operator, quorumNumbers); + (, bytes32 registeredpkHash) = blsApkRegistry.getRegisteredPubkey(operator); require(registeredpkHash == pkHash, "registeredpkHash not set correctly"); - emit log("ehey"); return pkHash; } @@ -110,8 +119,8 @@ contract BLSApkRegistryUnitTests is Test { quorumApksBefore[i] = blsApkRegistry.getApk(uint8(quorumNumbers[i])); } - cheats.prank(defaultOperator); - pkCompendium.registerPublicKey(defaultPubKey); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); cheats.prank(address(registryCoordinator)); blsApkRegistry.registerOperator(defaultOperator, quorumNumbers); @@ -135,8 +144,8 @@ contract BLSApkRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(operator); - pkCompendium.registerPublicKey(negatedQuorumApk); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(operator, negatedQuorumApk); cheats.stopPrank(); cheats.startPrank(address(registryCoordinator)); @@ -182,8 +191,8 @@ contract BLSApkRegistryUnitTests is Test { bytes memory quorumNumbers = new bytes(1); quorumNumbers[0] = bytes1(defaultQuorumNumber); - cheats.startPrank(defaultOperator); - pkCompendium.registerPublicKey(quorumApksBefore); + // use harnessed function to directly set the pubkey, bypassing the ordinary checks + blsApkRegistry.setBLSPublicKey(defaultOperator, quorumApksBefore); cheats.stopPrank(); cheats.prank(address(registryCoordinator)); @@ -277,4 +286,63 @@ contract BLSApkRegistryUnitTests is Test { cheats.stopPrank(); } + + // TODO: better organize / integrate tests migrated from `BLSPublicKeyCompendium` unit tests + function testRegisterBLSPublicKey() public { + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); + vm.prank(address(registryCoordinator)); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); + + assertEq(blsApkRegistry.operatorToPubkeyHash(alice), BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1), + "pubkey hash not stored correctly"); + assertEq(blsApkRegistry.pubkeyHashToOperator(BN254.hashG1Point(pubkeyRegistrationParams.pubkeyG1)), alice, + "operator address not stored correctly"); + } + + function testRegisterBLSPublicKey_NoMatch_Reverts() public { + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(alice); + BN254.G1Point memory badPubkeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys + + pubkeyRegistrationParams.pubkeyG1 = badPubkeyG1; + + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); + vm.prank(address(registryCoordinator)); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); + } + + function testRegisterBLSPublicKey_BadSig_Reverts() public { + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(bob); // sign with wrong private key + + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); + vm.prank(address(registryCoordinator)); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); + } + + function testRegisterBLSPublicKey_OpRegistered_Reverts() public { + testRegisterBLSPublicKey(); // register alice + + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(alice); + vm.prank(address(registryCoordinator)); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: operator already registered pubkey")); + blsApkRegistry.registerBLSPublicKey(alice, pubkeyRegistrationParams, messageHash); + } + + function testRegisterBLSPublicKey_PkRegistered_Reverts() public { + testRegisterBLSPublicKey(); + pubkeyRegistrationParams.pubkeyRegistrationSignature = _signMessage(bob); // same private key different operator + + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(bob); + vm.prank(address(registryCoordinator)); + vm.expectRevert(bytes("BLSApkRegistry.registerBLSPublicKey: public key already registered")); + blsApkRegistry.registerBLSPublicKey(bob, pubkeyRegistrationParams, messageHash); + } + + function _signMessage(address signer) internal view returns(BN254.G1Point memory) { + BN254.G1Point memory messageHash = registryCoordinator.pubkeyRegistrationMessageHash(signer); + return BN254.scalar_mul(messageHash, privKey); + } + } diff --git a/test/unit/BLSPublicKeyCompendiumUnit.t.sol b/test/unit/BLSPublicKeyCompendiumUnit.t.sol deleted file mode 100644 index 31ba0a95..00000000 --- a/test/unit/BLSPublicKeyCompendiumUnit.t.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity =0.8.12; - -import "forge-std/Test.sol"; -import "src/BLSPublicKeyCompendium.sol"; - -contract BLSPublicKeyCompendiumUnitTests is Test { - using BN254 for BN254.G1Point; - - BLSPublicKeyCompendium compendium; - uint256 privKey = 69; - - BN254.G1Point pubKeyG1; - BN254.G2Point pubKeyG2; - BN254.G1Point signedMessageHash; - - address alice = address(1); - address bob = address(2); - - function setUp() public { - compendium = new BLSPublicKeyCompendium(); - - pubKeyG1 = BN254.generatorG1().scalar_mul(privKey); - - //privKey*G2 - pubKeyG2.X[1] = 19101821850089705274637533855249918363070101489527618151493230256975900223847; - pubKeyG2.X[0] = 5334410886741819556325359147377682006012228123419628681352847439302316235957; - pubKeyG2.Y[1] = 354176189041917478648604979334478067325821134838555150300539079146482658331; - pubKeyG2.Y[0] = 4185483097059047421902184823581361466320657066600218863748375739772335928910; - } - - function testRegisterBLSPublicKey() public { - signedMessageHash = _signMessage(alice); - vm.prank(alice); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - - assertEq(compendium.operatorToPubkeyHash(alice), BN254.hashG1Point(pubKeyG1), "pubkey hash not stored correctly"); - assertEq(compendium.pubkeyHashToOperator(BN254.hashG1Point(pubKeyG1)), alice, "operator address not stored correctly"); - } - - function testRegisterBLSPublicKey_NoMatch_Reverts() public { - signedMessageHash = _signMessage(alice); - BN254.G1Point memory badPubKeyG1 = BN254.generatorG1().scalar_mul(420); // mismatch public keys - - vm.prank(alice); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - compendium.registerBLSPublicKey(signedMessageHash, badPubKeyG1, pubKeyG2); - } - - function testRegisterBLSPublicKey_BadSig_Reverts() public { - signedMessageHash = _signMessage(bob); // sign with wrong private key - - vm.prank(alice); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: either the G1 signature is wrong, or G1 and G2 private key do not match")); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - } - - function testRegisterBLSPublicKey_OpRegistered_Reverts() public { - testRegisterBLSPublicKey(); // register alice - - vm.prank(alice); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: operator already registered pubkey")); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - } - - function testRegisterBLSPublicKey_PkRegistered_Reverts() public { - testRegisterBLSPublicKey(); - signedMessageHash = _signMessage(bob); // same private key different operator - - vm.prank(bob); - vm.expectRevert(bytes("BLSPublicKeyCompendium.registerBLSPublicKey: public key already registered")); - compendium.registerBLSPublicKey(signedMessageHash, pubKeyG1, pubKeyG2); - } - - function _signMessage(address signer) internal view returns(BN254.G1Point memory) { - BN254.G1Point memory messageHash = compendium.getMessageHash(signer); - return BN254.scalar_mul(messageHash, privKey); - } - -} diff --git a/test/unit/RegistryCoordinatorUnit.t.sol b/test/unit/RegistryCoordinatorUnit.t.sol index 3f20e23d..e602e30f 100644 --- a/test/unit/RegistryCoordinatorUnit.t.sol +++ b/test/unit/RegistryCoordinatorUnit.t.sol @@ -124,7 +124,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.startPrank(defaultOperator); cheats.expectRevert(bytes("Pausable: index is paused")); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } function testRegisterOperatorWithCoordinator_EmptyQuorumNumbers_Reverts() public { @@ -133,7 +133,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.expectRevert("RegistryCoordinator._registerOperator: bitmap cannot be 0"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(emptyQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } function testRegisterOperatorWithCoordinator_QuorumNumbersTooLarge_Reverts() public { @@ -141,9 +141,10 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; quorumNumbersTooLarge[0] = 0xC0; + cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbersTooLarge, defaultSocket, pubkeyRegistrationParams, emptySig); } function testRegisterOperatorWithCoordinator_QuorumNotCreated_Reverts() public { @@ -152,9 +153,10 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySig; quorumNumbersNotCreated[0] = 0x0B; + cheats.prank(defaultOperator); cheats.expectRevert("BitmapUtils.orderedBytesArrayToBitmap: bitmap exceeds max value"); - registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbersNotCreated, defaultSocket, pubkeyRegistrationParams, emptySig); } function testRegisterOperatorWithCoordinatorForSingleQuorum_Valid() public { @@ -175,7 +177,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); @@ -228,7 +230,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint256 gasAfter = gasleft(); emit log_named_uint("gasUsed", gasBefore - gasAfter); emit log_named_uint("numQuorums", quorumNumbers.length); @@ -263,7 +265,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); bytes memory newQuorumNumbers = new bytes(1); newQuorumNumbers[0] = bytes1(defaultQuorumNumber+1); @@ -279,7 +281,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { emit QuorumIndexUpdate(defaultOperatorId, uint8(newQuorumNumbers[0]), 0); cheats.roll(nextRegistrationBlockNumber); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(newQuorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers) | BitmapUtils.orderedBytesArrayToBitmap(newQuorumNumbers); @@ -332,13 +334,13 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { address operatorToRegister = _incrementAddress(defaultOperator, numOperators); BN254.G1Point memory operatorToRegisterPubKey = BN254.hashToG1(keccak256(abi.encodePacked(pseudoRandomNumber, numOperators))); - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, defaultStake); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator.registerOperator: operator count exceeds maximum"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } function testRegisterOperatorWithCoordinator_RegisteredOperatorForSameQuorums_Reverts() public { @@ -352,12 +354,12 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); cheats.prank(defaultOperator); cheats.roll(nextRegistrationBlockNumber); cheats.expectRevert("RegistryCoordinator._registerOperator: operator already registered for some quorums being registered for"); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); } function testDeregisterOperatorWithCoordinator_WhenPaused_Reverts() public { @@ -415,7 +417,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -466,7 +468,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { cheats.roll(registrationBlockNumber); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -594,7 +596,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { registryCoordinator.getQuorumBitmapUpdateByIndex(defaultOperatorId, 0); // re-register the operator - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); // check success of registration uint256 quorumBitmap = BitmapUtils.orderedBytesArrayToBitmap(quorumNumbers); @@ -671,7 +673,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); uint96 registeringStake = defaultKickBIPsOfOperatorStake * defaultStake; stakeRegistry.setOperatorWeight(defaultQuorumNumber, operatorToRegister, registeringStake); @@ -698,7 +700,8 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { uint256 gasBefore = gasleft(); registryCoordinator.registerOperatorWithChurn( quorumNumbers, - defaultSocket, + defaultSocket, + pubkeyRegistrationParams, operatorKickParams, signatureWithExpiry, emptyAVSRegSig @@ -749,7 +752,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: incoming operator has insufficient stake for churn"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry, emptyAVSRegSig); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithExpiry, + emptyAVSRegSig + ); } function testRegisterOperatorWithCoordinatorWithKicks_LessThanKickBIPsOfTotalStake_Reverts(uint256 pseudoRandomNumber) public { @@ -773,7 +783,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp + 10); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._validateChurn: cannot kick operator with more than kickBIPsOfTotalStake"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithExpiry, emptyAVSRegSig); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithExpiry, + emptyAVSRegSig + ); } function testRegisterOperatorWithCoordinatorWithKicks_InvalidSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -797,7 +814,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { signatureWithSaltAndExpiry.salt = defaultSalt; cheats.prank(operatorToRegister); cheats.expectRevert("ECDSA: invalid signature"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithSaltAndExpiry, + emptyAVSRegSig + ); } function testRegisterOperatorWithCoordinatorWithKicks_ExpiredSignatures_Reverts(uint256 pseudoRandomNumber) public { @@ -819,7 +843,14 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { ISignatureUtils.SignatureWithSaltAndExpiry memory signatureWithSaltAndExpiry = _signOperatorChurnApproval(operatorToRegisterId, operatorKickParams, defaultSalt, block.timestamp - 1); cheats.prank(operatorToRegister); cheats.expectRevert("RegistryCoordinator._verifyChurnApproverSignature: churnApprover signature expired"); - registryCoordinator.registerOperatorWithChurn(quorumNumbers, defaultSocket, operatorKickParams, signatureWithSaltAndExpiry, emptyAVSRegSig); + registryCoordinator.registerOperatorWithChurn( + quorumNumbers, + defaultSocket, + pubkeyRegistrationParams, + operatorKickParams, + signatureWithSaltAndExpiry, + emptyAVSRegSig + ); } function testEjectOperatorFromCoordinator_AllQuorums_Valid() public { @@ -831,7 +862,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); cheats.expectEmit(true, true, true, true, address(blsApkRegistry)); emit OperatorRemovedFromQuorums(defaultOperator, quorumNumbers); @@ -867,7 +898,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { } cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); // eject from only first quorum bytes memory quorumNumbersToEject = new bytes(1); @@ -905,7 +936,7 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { stakeRegistry.setOperatorWeight(uint8(quorumNumbers[0]), defaultOperator, defaultStake); cheats.prank(defaultOperator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySig); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySig); cheats.expectRevert("RegistryCoordinator.onlyEjector: caller is not the ejector"); cheats.prank(defaultOperator); @@ -972,6 +1003,6 @@ contract RegistryCoordinatorUnit is MockAVSDeployer { }); } - pubkeyCompendium.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); + blsApkRegistry.setBLSPublicKey(operatorToRegister, operatorToRegisterPubKey); } } diff --git a/test/unit/StakeRegistryUnit.t.sol b/test/unit/StakeRegistryUnit.t.sol index c0ae719b..766e5fe3 100644 --- a/test/unit/StakeRegistryUnit.t.sol +++ b/test/unit/StakeRegistryUnit.t.sol @@ -25,7 +25,7 @@ import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; -import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; +import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; import "forge-std/Test.sol"; diff --git a/test/utils/MockAVSDeployer.sol b/test/utils/MockAVSDeployer.sol index 45ea8c4c..118cbd03 100644 --- a/test/utils/MockAVSDeployer.sol +++ b/test/utils/MockAVSDeployer.sol @@ -7,16 +7,14 @@ import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.so import {Slasher} from "eigenlayer-contracts/src/contracts/core/Slasher.sol"; import {ISlasher} from "eigenlayer-contracts/src/contracts/interfaces/ISlasher.sol"; import {PauserRegistry} from "eigenlayer-contracts/src/contracts/permissions/PauserRegistry.sol"; -import {IStrategyManager} from "eigenlayer-contracts/src/contracts/interfaces/IStrategyManager.sol"; import {IStrategy} from "eigenlayer-contracts/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "eigenlayer-contracts/src/contracts/interfaces/ISignatureUtils.sol"; import {BitmapUtils} from "src/libraries/BitmapUtils.sol"; import {BN254} from "src/libraries/BN254.sol"; -import {BLSPublicKeyCompendium} from "src/BLSPublicKeyCompendium.sol"; import {OperatorStateRetriever} from "src/OperatorStateRetriever.sol"; import {RegistryCoordinator} from "src/RegistryCoordinator.sol"; -import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.sol"; +import {RegistryCoordinatorHarness} from "test/harnesses/RegistryCoordinatorHarness.t.sol"; import {BLSApkRegistry} from "src/BLSApkRegistry.sol"; import {StakeRegistry} from "src/StakeRegistry.sol"; import {IndexRegistry} from "src/IndexRegistry.sol"; @@ -28,10 +26,8 @@ import {IRegistryCoordinator} from "src/interfaces/IRegistryCoordinator.sol"; import {StrategyManagerMock} from "eigenlayer-contracts/src/test/mocks/StrategyManagerMock.sol"; import {EigenPodManagerMock} from "eigenlayer-contracts/src/test/mocks/EigenPodManagerMock.sol"; -import {OwnableMock} from "eigenlayer-contracts/src/test/mocks/OwnableMock.sol"; import {DelegationManagerMock} from "eigenlayer-contracts/src/test/mocks/DelegationManagerMock.sol"; -import {SlasherMock} from "eigenlayer-contracts/src/test/mocks/SlasherMock.sol"; -import {BLSPublicKeyCompendiumMock} from "test/mocks/BLSPublicKeyCompendiumMock.sol"; +import {BLSApkRegistryHarness} from "test/harnesses/BLSApkRegistryHarness.sol"; import {EmptyContract} from "eigenlayer-contracts/src/test/mocks/EmptyContract.sol"; import {StakeRegistryHarness} from "test/harnesses/StakeRegistryHarness.sol"; @@ -50,7 +46,6 @@ contract MockAVSDeployer is Test { Slasher public slasherImplementation; EmptyContract public emptyContract; - BLSPublicKeyCompendiumMock public pubkeyCompendium; RegistryCoordinatorHarness public registryCoordinatorImplementation; StakeRegistryHarness public stakeRegistryImplementation; @@ -60,7 +55,7 @@ contract MockAVSDeployer is Test { OperatorStateRetriever public operatorStateRetriever; RegistryCoordinatorHarness public registryCoordinator; StakeRegistryHarness public stakeRegistry; - IBLSApkRegistry public blsApkRegistry; + BLSApkRegistryHarness public blsApkRegistry; IIndexRegistry public indexRegistry; StrategyManagerMock public strategyManagerMock; @@ -97,6 +92,8 @@ contract MockAVSDeployer is Test { uint32 registrationBlockNumber = 100; uint32 blocksBetweenRegistrations = 10; + IBLSApkRegistry.PubkeyRegistrationParams pubkeyRegistrationParams; + struct OperatorMetadata { uint256 quorumBitmap; address operator; @@ -142,10 +139,6 @@ contract MockAVSDeployer is Test { eigenPodManagerMock, slasher ); - - pubkeyCompendium = new BLSPublicKeyCompendiumMock(); - pubkeyCompendium.setBLSPublicKey(defaultOperator, defaultPubKey); - cheats.stopPrank(); cheats.startPrank(registryCoordinatorOwner); @@ -177,7 +170,7 @@ contract MockAVSDeployer is Test { ) ); - blsApkRegistry = BLSApkRegistry( + blsApkRegistry = BLSApkRegistryHarness( address( new TransparentUpgradeableProxy( address(emptyContract), @@ -188,6 +181,7 @@ contract MockAVSDeployer is Test { ); cheats.stopPrank(); + cheats.startPrank(proxyAdminOwner); stakeRegistryImplementation = new StakeRegistryHarness( @@ -200,9 +194,8 @@ contract MockAVSDeployer is Test { address(stakeRegistryImplementation) ); - blsApkRegistryImplementation = new BLSApkRegistry( - registryCoordinator, - BLSPublicKeyCompendium(address(pubkeyCompendium)) + blsApkRegistryImplementation = new BLSApkRegistryHarness( + registryCoordinator ); proxyAdmin.upgrade( @@ -219,6 +212,9 @@ contract MockAVSDeployer is Test { address(indexRegistryImplementation) ); + // set the public key for an operator, using harnessed function to bypass checks + blsApkRegistry.setBLSPublicKey(defaultOperator, defaultPubKey); + // setup the dummy minimum stake for quorum uint96[] memory minimumStakeForQuorum = new uint96[](numQuorumsToAdd); for (uint256 i = 0; i < minimumStakeForQuorum.length; i++) { @@ -290,7 +286,7 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - pubkeyCompendium.setBLSPublicKey(operator, pubKey); + blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { @@ -299,7 +295,7 @@ contract MockAVSDeployer is Test { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry); } /** @@ -309,7 +305,7 @@ contract MockAVSDeployer is Test { // quorumBitmap can only have 192 least significant bits quorumBitmap &= MAX_QUORUM_BITMAP; - pubkeyCompendium.setBLSPublicKey(operator, pubKey); + blsApkRegistry.setBLSPublicKey(operator, pubKey); bytes memory quorumNumbers = BitmapUtils.bitmapToBytesArray(quorumBitmap); for (uint i = 0; i < quorumNumbers.length; i++) { @@ -318,7 +314,7 @@ contract MockAVSDeployer is Test { ISignatureUtils.SignatureWithSaltAndExpiry memory emptySignatureAndExpiry; cheats.prank(operator); - registryCoordinator.registerOperator(quorumNumbers, defaultSocket, emptySignatureAndExpiry); + registryCoordinator.registerOperator(quorumNumbers, defaultSocket, pubkeyRegistrationParams, emptySignatureAndExpiry); } function _registerRandomOperators(uint256 pseudoRandomNumber) internal returns(OperatorMetadata[] memory, uint256[][] memory) { diff --git a/test/utils/Operators.sol b/test/utils/Operators.sol index 910165e7..5bc607f7 100644 --- a/test/utils/Operators.sol +++ b/test/utils/Operators.sol @@ -16,15 +16,15 @@ contract Operators is Test { return string.concat(".operators[", string.concat(vm.toString(index), "].")); } - function getNumOperators() public returns(uint256) { + function getNumOperators() public view returns(uint256) { return stdJson.readUint(operatorConfigJson, ".numOperators"); } - function getOperatorAddress(uint256 index) public returns(address) { + function getOperatorAddress(uint256 index) public view returns(address) { return stdJson.readAddress(operatorConfigJson, string.concat(operatorPrefix(index), "Address")); } - function getOperatorSchnorrSignature(uint256 index) public returns(uint256, BN254.G1Point memory) { + function getOperatorSchnorrSignature(uint256 index) public view returns(uint256, BN254.G1Point memory) { uint256 s = readUint(operatorConfigJson, index, "SField"); BN254.G1Point memory pubkey = BN254.G1Point({ X: readUint(operatorConfigJson, index, "RPoint.X"), @@ -33,11 +33,11 @@ contract Operators is Test { return (s, pubkey); } - function getOperatorSecretKey(uint256 index) public returns(uint256) { + function getOperatorSecretKey(uint256 index) public view returns(uint256) { return readUint(operatorConfigJson, index, "SecretKey"); } - function getOperatorPubkeyG1(uint256 index) public returns(BN254.G1Point memory) { + function getOperatorPubkeyG1(uint256 index) public view returns(BN254.G1Point memory) { BN254.G1Point memory pubkey = BN254.G1Point({ X: readUint(operatorConfigJson, index, "PubkeyG1.X"), Y: readUint(operatorConfigJson, index, "PubkeyG1.Y") @@ -45,7 +45,7 @@ contract Operators is Test { return pubkey; } - function getOperatorPubkeyG2(uint256 index) public returns(BN254.G2Point memory) { + function getOperatorPubkeyG2(uint256 index) public view returns(BN254.G2Point memory) { BN254.G2Point memory pubkey = BN254.G2Point({ X: [ readUint(operatorConfigJson, index, "PubkeyG2.X.A1"), @@ -59,7 +59,7 @@ contract Operators is Test { return pubkey; } - function readUint(string memory json, uint256 index, string memory key) public returns (uint256) { + function readUint(string memory json, uint256 index, string memory key) public pure returns (uint256) { return stringToUint(stdJson.readString(json, string.concat(operatorPrefix(index), key))); } diff --git a/test/utils/Owners.sol b/test/utils/Owners.sol index 5f93d50b..0a0643e7 100644 --- a/test/utils/Owners.sol +++ b/test/utils/Owners.sol @@ -17,11 +17,11 @@ contract Owners is Test { return string.concat(".owners[", string.concat(vm.toString(index), "].")); } - function getNumOperators() public returns(uint256) { + function getNumOperators() public view returns(uint256) { return stdJson.readUint(ownersConfigJson, ".numOwners"); } - function getOwnerAddress(uint256 index) public returns(address) { + function getOwnerAddress(uint256 index) public view returns(address) { return stdJson.readAddress(ownersConfigJson, string.concat(ownerPrefix(index), "Address")); } diff --git a/test/utils/ProofParsing.sol b/test/utils/ProofParsing.sol index f73f74b8..ddc22259 100644 --- a/test/utils/ProofParsing.sol +++ b/test/utils/ProofParsing.sol @@ -29,55 +29,55 @@ contract ProofParsing is Test{ proofConfigJson = vm.readFile(path); } - function getSlot() public returns(uint256) { + function getSlot() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".slot"); } - function getValidatorIndex() public returns(uint256) { + function getValidatorIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".validatorIndex"); } - function getValidatorPubkeyHash() public returns(bytes32) { + function getValidatorPubkeyHash() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".ValidatorFields[0]"); } - function getWithdrawalIndex() public returns(uint256) { + function getWithdrawalIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".withdrawalIndex"); } - function getBlockRootIndex() public returns(uint256) { + function getBlockRootIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".blockHeaderRootIndex"); } - function getHistoricalSummaryIndex() public returns(uint256) { + function getHistoricalSummaryIndex() public view returns(uint256) { return stdJson.readUint(proofConfigJson, ".historicalSummaryIndex"); } - function getBeaconStateRoot() public returns(bytes32) { + function getBeaconStateRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".beaconStateRoot"); } - function getBlockRoot() public returns(bytes32) { + function getBlockRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".blockHeaderRoot"); } - function getSlotRoot() public returns(bytes32) { + function getSlotRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".slotRoot"); } - function getBalanceRoot() public returns(bytes32) { + function getBalanceRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".balanceRoot"); } - function getTimestampRoot() public returns(bytes32) { + function getTimestampRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".timestampRoot"); } - function getExecutionPayloadRoot() public returns(bytes32) { + function getExecutionPayloadRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".executionPayloadRoot"); } - function getLatestBlockRoot() public returns(bytes32) { + function getLatestBlockRoot() public view returns(bytes32) { return stdJson.readBytes32(proofConfigJson, ".latestBlockHeaderRoot"); } function getExecutionPayloadProof () public returns(bytes32[7] memory) {