diff --git a/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol b/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol index f4a3117d4..c61acaa75 100644 --- a/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol +++ b/bolt-contracts/script/holesky/admin/helpers/RegisterAVS.s.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; -import {BoltEigenLayerMiddlewareV1} from "../../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltEigenLayerMiddlewareV2} from "../../../../src/contracts/BoltEigenLayerMiddlewareV2.sol"; contract RegisterAVS is Script { function run() public { address admin = msg.sender; console.log("Running with admin address:", admin); - BoltEigenLayerMiddlewareV1 middleware = BoltEigenLayerMiddlewareV1(readMiddleware()); + BoltEigenLayerMiddlewareV2 middleware = BoltEigenLayerMiddlewareV2(readMiddleware()); string memory avsURI = "https://boltprotocol.xyz/avs.json"; console.log("Setting AVS metadata URI to:", avsURI); diff --git a/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol b/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol index 14fdb19be..917dde939 100644 --- a/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol +++ b/bolt-contracts/script/holesky/admin/helpers/UpdateSupportedVaults.s.sol @@ -6,11 +6,11 @@ import {Script, console} from "forge-std/Script.sol"; import {INetworkRegistry} from "@symbiotic/interfaces/INetworkRegistry.sol"; import {INetworkMiddlewareService} from "@symbiotic/interfaces/service/INetworkMiddlewareService.sol"; -import {BoltSymbioticMiddlewareV1} from "../../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; +import {BoltSymbioticMiddlewareV2} from "../../../../src/contracts/BoltSymbioticMiddlewareV2.sol"; contract UpdateSupportedVaults is Script { function run() public { - BoltSymbioticMiddlewareV1 middleware = _readSymbioticMiddleware(); + BoltSymbioticMiddlewareV2 middleware = _readSymbioticMiddleware(); address[] memory whitelisted = middleware.getWhitelistedVaults(); address[] memory toWhitelist = _readVaultsToWhitelist(); @@ -64,11 +64,11 @@ contract UpdateSupportedVaults is Script { return vm.parseJsonAddressArray(json, ".symbiotic.supportedVaults"); } - function _readSymbioticMiddleware() public view returns (BoltSymbioticMiddlewareV1) { + function _readSymbioticMiddleware() public view returns (BoltSymbioticMiddlewareV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")); + return BoltSymbioticMiddlewareV2(vm.parseJsonAddress(json, ".symbiotic.middleware")); } } diff --git a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol index c67b7ef06..61cfbde43 100644 --- a/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterEigenLayerOperator.s.sol @@ -9,9 +9,9 @@ import {IStrategyManager} from "@eigenlayer/src/contracts/interfaces/IStrategyMa import {IStrategy, IERC20} from "@eigenlayer/src/contracts/interfaces/IStrategy.sol"; import {ISignatureUtils} from "@eigenlayer/src/contracts/interfaces/ISignatureUtils.sol"; -import {BoltEigenLayerMiddlewareV1} from "../../../src/contracts/BoltEigenLayerMiddlewareV1.sol"; +import {BoltEigenLayerMiddlewareV2} from "../../../src/contracts/BoltEigenLayerMiddlewareV2.sol"; import {IBoltMiddlewareV1} from "../../../src/interfaces/IBoltMiddlewareV1.sol"; -import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; +import {IBoltManagerV2} from "../../../src/interfaces/IBoltManagerV2.sol"; contract RegisterEigenLayerOperator is Script { struct OperatorConfig { @@ -43,7 +43,7 @@ contract RegisterEigenLayerOperator is Script { uint256 operatorSk = vm.envUint("OPERATOR_SK"); address operator = vm.addr(operatorSk); - BoltEigenLayerMiddlewareV1 middleware = _readMiddleware(); + BoltEigenLayerMiddlewareV2 middleware = _readMiddleware(); IAVSDirectory avsDirectory = _readAvsDirectory(); OperatorConfig memory config = _readConfig("config/holesky/operators/eigenlayer/registerIntoBoltAVS.json"); @@ -76,18 +76,18 @@ contract RegisterEigenLayerOperator is Script { address operatorPublicKey = vm.envAddress("OPERATOR_PK"); console.log("Checking operator registration for address", operatorPublicKey); - IBoltManagerV1 boltManager = _readBoltManager(); + IBoltManagerV2 boltManager = _readBoltManager(); bool isRegistered = boltManager.isOperator(operatorPublicKey); console.log("Operator is registered:", isRegistered); require(isRegistered, "Operator is not registered"); } - function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV1) { + function _readMiddleware() public view returns (BoltEigenLayerMiddlewareV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return BoltEigenLayerMiddlewareV1(vm.parseJsonAddress(json, ".eigenLayer.middleware")); + return BoltEigenLayerMiddlewareV2(vm.parseJsonAddress(json, ".eigenLayer.middleware")); } function _readAvsDirectory() public view returns (IAVSDirectory) { @@ -113,11 +113,11 @@ contract RegisterEigenLayerOperator is Script { return IStrategyManager(vm.parseJsonAddress(json, ".eigenLayer.strategyManager")); } - function _readBoltManager() public view returns (IBoltManagerV1) { + function _readBoltManager() public view returns (IBoltManagerV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + return IBoltManagerV2(vm.parseJsonAddress(json, ".bolt.manager")); } function _readConfig( diff --git a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol index d57902e9b..0bcea2005 100644 --- a/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol +++ b/bolt-contracts/script/holesky/operators/RegisterSymbioticOperator.s.sol @@ -3,15 +3,15 @@ pragma solidity 0.8.25; import {Script, console} from "forge-std/Script.sol"; -import {BoltSymbioticMiddlewareV1} from "../../../src/contracts/BoltSymbioticMiddlewareV1.sol"; -import {IBoltManagerV1} from "../../../src/interfaces/IBoltManagerV1.sol"; +import {BoltSymbioticMiddlewareV2} from "../../../src/contracts/BoltSymbioticMiddlewareV2.sol"; +import {IBoltManagerV2} from "../../../src/interfaces/IBoltManagerV2.sol"; import {IOptInService} from "@symbiotic/interfaces/service/IOptInService.sol"; import {IVault} from "@symbiotic/interfaces/vault/IVault.sol"; contract RegisterSymbioticOperator is Script { struct Config { - BoltSymbioticMiddlewareV1 symbioticMiddleware; + BoltSymbioticMiddlewareV2 symbioticMiddleware; IOptInService symbioticNetworkOptInService; address symbioticNetwork; } @@ -54,7 +54,7 @@ contract RegisterSymbioticOperator is Script { address operatorAddress = vm.envAddress("OPERATOR_ADDRESS"); console.log("Checking operator registration for address", operatorAddress); - IBoltManagerV1 boltManager = _readBoltManager(); + IBoltManagerV2 boltManager = _readBoltManager(); bool isRegistered = boltManager.isOperator(operatorAddress); console.log("Operator is registered:", isRegistered); @@ -68,15 +68,15 @@ contract RegisterSymbioticOperator is Script { return Config({ symbioticNetwork: vm.parseJsonAddress(json, ".symbiotic.network"), - symbioticMiddleware: BoltSymbioticMiddlewareV1(vm.parseJsonAddress(json, ".symbiotic.middleware")), + symbioticMiddleware: BoltSymbioticMiddlewareV2(vm.parseJsonAddress(json, ".symbiotic.middleware")), symbioticNetworkOptInService: IOptInService(vm.parseJsonAddress(json, ".symbiotic.networkOptInService")) }); } - function _readBoltManager() public view returns (IBoltManagerV1) { + function _readBoltManager() public view returns (IBoltManagerV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return IBoltManagerV1(vm.parseJsonAddress(json, ".bolt.manager")); + return IBoltManagerV2(vm.parseJsonAddress(json, ".bolt.manager")); } } diff --git a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol index 52fd1d0b3..cdd69ff2d 100644 --- a/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol +++ b/bolt-contracts/script/holesky/validators/RegisterValidators.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IBoltValidatorsV1} from "../../../src/interfaces/IBoltValidatorsV1.sol"; +import {IBoltValidatorsV2} from "../../../src/interfaces/IBoltValidatorsV2.sol"; import {BLS12381} from "../../../src/lib/bls/BLS12381.sol"; import {Script, console} from "forge-std/Script.sol"; @@ -12,9 +12,12 @@ contract RegisterValidators is Script { using BLS12381 for BLS12381.G1Point; struct RegisterValidatorsConfig { - uint128 maxCommittedGasLimit; + uint32 maxCommittedGasLimit; address authorizedOperator; - BLS12381.G1Point[] pubkeys; + // Note: for Unsafe registration (aka without BLS verification precompile) + // we use compressed pubkey hashes on-chain instead of decompressed points. + // BLS12381.G1Point[] pubkeys; + bytes20[] pubkeys; } function run() public { @@ -23,7 +26,7 @@ contract RegisterValidators is Script { console.log("Registering validators to Bolt"); console.log("Controller address: ", controller); - IBoltValidatorsV1 validators = _readValidators(); + IBoltValidatorsV2 validators = _readValidators(); RegisterValidatorsConfig memory config = _parseConfig(); vm.startBroadcast(controller); @@ -33,43 +36,66 @@ contract RegisterValidators is Script { console.log("Validators registered successfully"); } - function _readValidators() public view returns (IBoltValidatorsV1) { + function _readValidators() public view returns (IBoltValidatorsV2) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/deployments.json"); string memory json = vm.readFile(path); - return IBoltValidatorsV1(vm.parseJsonAddress(json, ".bolt.validators")); + return IBoltValidatorsV2(vm.parseJsonAddress(json, ".bolt.validators")); } - function _parseConfig() public returns (RegisterValidatorsConfig memory config) { + function _parseConfig() public view returns (RegisterValidatorsConfig memory config) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/config/holesky/validators.json"); string memory json = vm.readFile(path); config.authorizedOperator = vm.parseJsonAddress(json, ".authorizedOperator"); - config.maxCommittedGasLimit = uint128(vm.parseJsonUint(json, ".maxCommittedGasLimit")); + config.maxCommittedGasLimit = uint32(vm.parseJsonUint(json, ".maxCommittedGasLimit")); + console.log("Max committed gas limit:", config.maxCommittedGasLimit); string[] memory pubkeysRaw = vm.parseJsonStringArray(json, ".pubkeys"); - BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](pubkeysRaw.length); + // NOTE: for Unsafe registration (aka without BLS verification precompile) + // we use compressed pubkey hashes on-chain instead of decompressed points. + bytes20[] memory pubkeys = new bytes20[](pubkeysRaw.length); for (uint256 i = 0; i < pubkeysRaw.length; i++) { - string memory pubkey = pubkeysRaw[i]; + bytes memory pubkeyBytes = vm.parseBytes(pubkeysRaw[i]); + require(pubkeyBytes.length == 48, "Invalid pubkey length"); + + // compute the pubkey hash: + // 1. create a 64 byte buffer + // 2. copy the pubkey bytes to the rightmost 48 bytes of the buffer + // 3. hash the buffer + // 4. take the 20 leftmost bytes of the hash as the pubkey hash + bytes memory buffer = new bytes(64); + for (uint256 j = 0; j < 48; j++) { + buffer[j + 16] = pubkeyBytes[j]; + } + bytes20 pubkeyHash = bytes20(keccak256(buffer)); + + pubkeys[i] = pubkeyHash; + console.log("Registering pubkey hash:", vm.toString(abi.encodePacked(pubkeyHash))); + } - string[] memory convertCmd = new string[](2); - convertCmd[0] = "./script/pubkey_to_g1_wrapper.sh"; - convertCmd[1] = pubkey; + // BLS12381.G1Point[] memory pubkeys = new BLS12381.G1Point[](pubkeysRaw.length); + // for (uint256 i = 0; i < pubkeysRaw.length; i++) { + // string memory pubkey = pubkeysRaw[i]; - bytes memory output = vm.ffi(convertCmd); - string memory outputStr = string(output); - string[] memory array = vm.split(outputStr, ","); + // string[] memory convertCmd = new string[](2); + // convertCmd[0] = "./script/pubkey_to_g1_wrapper.sh"; + // convertCmd[1] = pubkey; - uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); - uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); + // bytes memory output = vm.ffi(convertCmd); + // string memory outputStr = string(output); + // string[] memory array = vm.split(outputStr, ","); - pubkeys[i] = BLS12381.G1Point(x, y); + // uint256[2] memory x = _bytesToParts(vm.parseBytes(array[0])); + // uint256[2] memory y = _bytesToParts(vm.parseBytes(array[1])); - console.log("Registering pubkey:", vm.toString(abi.encodePacked(pubkeys[i].compress()))); - } + // pubkeys[i] = BLS12381.G1Point(x, y); + + // console.log("Registering pubkey:", vm.toString(abi.encodePacked(pubkeys[i].compress()))); + // } config.pubkeys = pubkeys; } diff --git a/bolt-contracts/src/contracts/BoltValidatorsV2.sol b/bolt-contracts/src/contracts/BoltValidatorsV2.sol index 4a6e601d4..1a533c139 100644 --- a/bolt-contracts/src/contracts/BoltValidatorsV2.sol +++ b/bolt-contracts/src/contracts/BoltValidatorsV2.sol @@ -217,6 +217,10 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg // ========= HELPERS ========= + /// @notice Internal helper to register a single validator + /// @param pubkeyHash BLS public key hash of the validator + /// @param authorizedOperator Address of the authorized operator + /// @param maxCommittedGasLimit Maximum gas limit that the validator can commit for preconfirmations function _registerValidator(bytes20 pubkeyHash, address authorizedOperator, uint32 maxCommittedGasLimit) internal { if (authorizedOperator == address(0)) { revert InvalidAuthorizedOperator(); @@ -224,9 +228,6 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg if (pubkeyHash == bytes20(0)) { revert InvalidPubkey(); } - if (VALIDATORS.contains(pubkeyHash)) { - revert ValidatorAlreadyExists(); - } VALIDATORS.insert( pubkeyHash, @@ -237,6 +238,10 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg emit ValidatorRegistered(pubkeyHash); } + /// @notice Internal helper to register a batch of validators + /// @param pubkeyHashes List of BLS public key hashes of the validators + /// @param authorizedOperator Address of the authorized operator + /// @param maxCommittedGasLimit Maximum gas limit that the validators can commit for preconfirmations function _batchRegisterValidators( bytes20[] memory pubkeyHashes, address authorizedOperator, @@ -256,9 +261,6 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg if (pubkeyHash == bytes20(0)) { revert InvalidPubkey(); } - if (VALIDATORS.contains(pubkeyHash)) { - revert ValidatorAlreadyExists(); - } VALIDATORS.insert(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex); emit ValidatorRegistered(pubkeyHash); @@ -279,7 +281,7 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg }); } - /// @notice Compute the hash of a BLS public key + /// @notice Helper to compute the hash of a BLS public key /// @param pubkey Decompressed BLS public key /// @return Hash of the public key in compressed form function hashPubkey( diff --git a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol index f9cbeb754..fa4fed993 100644 --- a/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol +++ b/bolt-contracts/src/interfaces/IBoltValidatorsV2.sol @@ -13,8 +13,6 @@ interface IBoltValidatorsV2 { error InvalidBLSSignature(); error InvalidAuthorizedOperator(); - error ValidatorAlreadyExists(); - error ValidatorDoesNotExist(); error UnsafeRegistrationNotAllowed(); error UnauthorizedCaller(); error InvalidPubkey(); diff --git a/bolt-contracts/src/lib/ValidatorsLib.sol b/bolt-contracts/src/lib/ValidatorsLib.sol index ac40ecbd4..f5de2474b 100644 --- a/bolt-contracts/src/lib/ValidatorsLib.sol +++ b/bolt-contracts/src/lib/ValidatorsLib.sol @@ -4,7 +4,8 @@ pragma solidity 0.8.25; /// @title ValidatorsLib /// @notice A library for managing a set of validators along with their information library ValidatorsLib { - error ValidatorDoesNotExist(); + error ValidatorAlreadyExists(bytes20 pubkeyHash); + error ValidatorDoesNotExist(bytes20 pubkeyHash); struct _AddressSet { address[] _values; @@ -33,7 +34,7 @@ library ValidatorsLib { function get(ValidatorSet storage self, bytes20 pubkeyHash) internal view returns (_Validator memory) { uint32 index = self._indexes[pubkeyHash]; if (index == 0) { - revert ValidatorDoesNotExist(); + revert ValidatorDoesNotExist(pubkeyHash); } return self._values[index - 1]; @@ -63,6 +64,10 @@ library ValidatorsLib { uint32 controllerIndex, uint32 authorizedOperatorIndex ) internal { + if (self._indexes[pubkeyHash] != 0) { + revert ValidatorAlreadyExists(pubkeyHash); + } + self._values.push(_Validator(pubkeyHash, maxCommittedGasLimit, controllerIndex, authorizedOperatorIndex)); self._indexes[pubkeyHash] = uint32(self._values.length); } @@ -74,7 +79,7 @@ library ValidatorsLib { ) internal { uint32 index = self._indexes[pubkeyHash]; if (index == 0) { - revert ValidatorDoesNotExist(); + revert ValidatorDoesNotExist(pubkeyHash); } self._values[index - 1].maxCommittedGasLimit = maxCommittedGasLimit;