Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]: CCCP compatibility #560

Draft
wants to merge 1 commit into
base: unstable
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions bolt-contracts/src/contracts/BoltManagerV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeab
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {OperatorMapWithTimeV2} from "../lib/OperatorMapWithTimeV2.sol";
import {EnumerableMapV2} from "../lib/EnumerableMapV2.sol";
Expand Down Expand Up @@ -215,13 +216,18 @@ contract BoltManagerV2 is IBoltManagerV2, OwnableUpgradeable, UUPSUpgradeable {
// ========= OPERATOR FUNCTIONS ====== //

/// @notice Registers an operator with Bolt. Only callable by a supported middleware contract.
function registerOperator(address operatorAddr, string calldata rpc) external onlyMiddleware {
function registerOperator(address operatorAddr, string calldata rpc, address curator) external onlyMiddleware {
if (operators.contains(operatorAddr)) {
revert OperatorAlreadyRegistered();
}

// check curator support interface (ERC165) and prevent invalid curator
if (curator != address(0) && !IERC165(curator).supportsInterface(type(ICredibleCommitmentCurationProvider).interfaceId)) {
revert InvalidCurator();
}

// Create an already enabled operator
EnumerableMapV2.Operator memory operator = EnumerableMapV2.Operator(rpc, msg.sender, Time.timestamp());
EnumerableMapV2.Operator memory operator = EnumerableMapV2.Operator(rpc, msg.sender, Time.timestamp(), curator);

operators.set(operatorAddr, operator);
}
Expand Down
73 changes: 43 additions & 30 deletions bolt-contracts/src/contracts/BoltSymbioticMiddlewareV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";

import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {IBaseDelegator} from "@symbiotic/interfaces/delegator/IBaseDelegator.sol";
import {Subnetwork} from "@symbiotic/contracts/libraries/Subnetwork.sol";
import {IVault} from "@symbiotic/interfaces/vault/IVault.sol";
Expand All @@ -29,7 +30,7 @@ import {IBoltManagerV1} from "../interfaces/IBoltManagerV1.sol";
/// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
/// To validate the storage layout, use the Openzeppelin Foundry Upgrades toolkit.
/// You can also validate manually with forge: forge inspect <contract> storage-layout --pretty
contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUPSUpgradeable {
contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, EIP712, UUPSUpgradeable {
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableMap for EnumerableMap.AddressToUintMap;
using MapWithTimeData for EnumerableMap.AddressToUintMap;
Expand Down Expand Up @@ -140,23 +141,17 @@ contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUP
NAME_HASH = keccak256("SYMBIOTIC");
}

function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

// ========= VIEW FUNCTIONS =========

/// @notice Get the start timestamp of an epoch.
function getEpochStartTs(
uint48 epoch
) public view returns (uint48 timestamp) {
function getEpochStartTs(uint48 epoch) public view returns (uint48 timestamp) {
return START_TIMESTAMP + epoch * parameters.EPOCH_DURATION();
}

/// @notice Get the epoch at a given timestamp.
function getEpochAtTs(
uint48 timestamp
) public view returns (uint48 epoch) {
function getEpochAtTs(uint48 timestamp) public view returns (uint48 epoch) {
return (timestamp - START_TIMESTAMP) / parameters.EPOCH_DURATION();
}

Expand All @@ -174,9 +169,7 @@ contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUP

/// @notice Allow a vault to signal opt-in to Bolt Protocol.
/// @param vault The vault address to signal opt-in for.
function registerVault(
address vault
) public onlyOwner {
function registerVault(address vault) public onlyOwner {
if (vaults.contains(vault)) {
revert AlreadyRegistered();
}
Expand All @@ -193,9 +186,7 @@ contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUP

/// @notice Deregister a vault from working in Bolt Protocol.
/// @param vault The vault address to deregister.
function deregisterVault(
address vault
) public onlyOwner {
function deregisterVault(address vault) public onlyOwner {
if (!vaults.contains(vault)) {
revert NotRegistered();
}
Expand All @@ -205,24 +196,50 @@ contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUP

// ========= SYMBIOTIC MIDDLEWARE LOGIC =========

/// @notice Allow an operator to signal opt-in to Bolt Protocol.
/// msg.sender must be an operator in the Symbiotic network.
function registerOperator(
string calldata rpc
address operator,
string calldata rpc,
address curator,
uint48 deadline,
bytes calldata signature
) public {
if (manager.isOperator(msg.sender)) {
if (deadline < Time.timestamp()) {
revert ExpiredSignature();
}

if (
!SignatureChecker.isValidSignatureNow(
operator,
_hashTypedDataV4(keccak256(abi.encode(msg.sender, operator, rpc, curator, deadline))),
signature
)
) {
revert InvalidSignature();
}

_registerOperator(operator, rpc, curator);
}

/// @notice Allow an operator to signal opt-in to Bolt Protocol.
/// msg.sender must be an operator in the Symbiotic network.
function registerOperator(string calldata rpc, address curator) public {
_registerOperator(msg.sender, rpc, curator);
}

function _registerOperator(address operator, string calldata rpc, address curator) internal {
if (manager.isOperator(operator)) {
revert AlreadyRegistered();
}

if (!IRegistry(OPERATOR_REGISTRY).isEntity(msg.sender)) {
if (!IRegistry(OPERATOR_REGISTRY).isEntity(operator)) {
revert NotOperator();
}

if (!IOptInService(OPERATOR_NET_OPTIN).isOptedIn(msg.sender, BOLT_SYMBIOTIC_NETWORK)) {
if (!IOptInService(OPERATOR_NET_OPTIN).isOptedIn(operator, BOLT_SYMBIOTIC_NETWORK)) {
revert OperatorNotOptedIn();
}

manager.registerOperator(msg.sender, rpc);
manager.registerOperator(operator, rpc, curator);
}

/// @notice Deregister a Symbiotic operator from working in Bolt Protocol.
Expand Down Expand Up @@ -268,9 +285,7 @@ contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUP
/// @notice Check if a vault is currently enabled to work in Bolt Protocol.
/// @param vault The vault address to check the enabled status for.
/// @return True if the vault is enabled, false otherwise.
function isVaultEnabled(
address vault
) public view returns (bool) {
function isVaultEnabled(address vault) public view returns (bool) {
(uint48 enabledTime, uint48 disabledTime) = vaults.getTimes(vault);
return enabledTime != 0 && disabledTime == 0;
}
Expand All @@ -280,9 +295,7 @@ contract BoltSymbioticMiddlewareV2 is IBoltMiddlewareV1, OwnableUpgradeable, UUP
/// @param operator The operator address to get the collaterals and amounts staked for.
/// @return collaterals The collaterals staked by the operator.
/// @dev Assumes that the operator is registered and enabled.
function getOperatorCollaterals(
address operator
) public view returns (address[] memory, uint256[] memory) {
function getOperatorCollaterals(address operator) public view returns (address[] memory, uint256[] memory) {
address[] memory collateralTokens = new address[](vaults.length());
uint256[] memory amounts = new uint256[](vaults.length());

Expand Down
40 changes: 37 additions & 3 deletions bolt-contracts/src/contracts/BoltValidatorsV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeab
import {BLS12381} from "../lib/bls/BLS12381.sol";
import {BLSSignatureVerifier} from "../lib/bls/BLSSignatureVerifier.sol";
import {ValidatorsLib} from "../lib/ValidatorsLib.sol";
import {URCMerkleTree} from "../lib/URCMerkleTree.sol";
import {IBoltValidatorsV2} from "../interfaces/IBoltValidatorsV2.sol";
import {IBoltParametersV1} from "../interfaces/IBoltParametersV1.sol";
import {IBoltManagerV2} from "../interfaces/IBoltManagerV2.sol";
import {ICredibleCommitmentCurationProvider} from "../interfaces/ICredibleCommitmentCurationProvider.sol";

/// @title Bolt Validators
/// @notice This contract is responsible for registering validators and managing their configuration
Expand All @@ -26,6 +29,8 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg
/// @notice Bolt Parameters contract.
IBoltParametersV1 public parameters;

IBoltManagerV2 public manager;

/// @notice Validators (aka Blockspace providers)
/// @dev This struct occupies 6 storage slots.
ValidatorsLib.ValidatorSet internal VALIDATORS;
Expand Down Expand Up @@ -65,6 +70,14 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg
parameters = IBoltParametersV1(_parameters);
}

function setManager(address _manager) external onlyOwner {
if (IBoltManagerV2(_manager).validators() != address(this)) {
revert InvalidManager();
}

manager = IBoltManagerV2(_manager);
}

function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
Expand Down Expand Up @@ -142,6 +155,11 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg
revert InvalidBLSSignature();
}

ICredibleCommitmentCurationProvider curator = ICredibleCommitmentCurationProvider(manager.getOperatorCurator(authorizedOperator));
if (address(curator) != address(0)) {
curator.hookOnlyApprovedRegistrationRoot(regRoot);
}

_registerValidator(hashPubkey(pubkey), authorizedOperator, maxCommittedGasLimit);
}

Expand All @@ -153,7 +171,7 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg
/// @param authorizedOperator The address of the authorized operator
function batchRegisterValidators(
BLS12381.G1Point[] calldata pubkeys,
BLS12381.G2Point calldata signature,
BLS12381.G2Point[] calldata signatures,
uint32 maxCommittedGasLimit,
address authorizedOperator
) public {
Expand All @@ -168,8 +186,11 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg
// try to register the same validators
bytes memory message = abi.encodePacked(block.chainid, msg.sender, expectedValidatorSequenceNumbers);

// Aggregate the pubkeys into a single pubkey to verify the aggregated signature once
BLS12381.G1Point memory aggPubkey = _aggregatePubkeys(pubkeys);
bytes32 registrationRoot = _merkleizeRegistrations(pubkeys, signatures);


// // Aggregate the pubkeys into a single pubkey to verify the aggregated signature once
// BLS12381.G1Point memory aggPubkey = _aggregatePubkeys(pubkeys);

if (!_verifySignature(message, signature, aggPubkey)) {
revert InvalidBLSSignature();
Expand All @@ -183,6 +204,19 @@ contract BoltValidatorsV2 is IBoltValidatorsV2, BLSSignatureVerifier, OwnableUpg
_batchRegisterValidators(pubkeyHashes, authorizedOperator, maxCommittedGasLimit);
}

function _merkleizeRegistrations(BLS12381.G1Point[] calldata pubkeys, BLS12381.G2Point[] calldata signatures,) internal returns (bytes32 registrationRoot) {
// Create leaves array with padding
bytes32[] memory leaves = new bytes32[](pubkeys.length);

// Create leaf nodes by hashing Registration structs
for (uint256 i = 0; i < pubkeys.length; i++) {
leaves[i] = keccak256(abi.encode(pubkeys[i], signatures[i]));
}

registrationRoot = URCMerkleTree.generateTree(leaves);
}


/// @notice Register a batch of Validators and authorize a Collateral Provider and Operator for them
/// @dev This function allows anyone to register a list of Validators.
/// @param pubkeyHashes List of BLS public key hashes for the Validators to be registered
Expand Down
8 changes: 8 additions & 0 deletions bolt-contracts/src/interfaces/IBoltManagerV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IBoltManagerV2 {
error OperatorAlreadyRegistered();
error OperatorNotRegistered();
error UnauthorizedMiddleware();
error InvalidCurator();
// TODO: remove in future upgrade (unused)
error InactiveOperator();

Expand All @@ -25,6 +26,9 @@ interface IBoltManagerV2 {
uint256[] amounts;
}

// returns the address of the validators contract
function validators() external view returns (address);

function registerOperator(address operator, string calldata rpc) external;

function deregisterOperator(
Expand All @@ -43,6 +47,10 @@ interface IBoltManagerV2 {
address operator
) external view returns (bool);

function getOperatorCurator(
address operator
) external view returns (address);

function getProposerStatus(
bytes20 pubkeyHash
) external view returns (ProposerStatus memory status);
Expand Down
1 change: 1 addition & 0 deletions bolt-contracts/src/interfaces/IBoltMiddlewareV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface IBoltMiddlewareV1 {
error OperatorNotOptedIn();
error NotOperator();
error NotAllowed();
error ExpiredSignature();

function NAME_HASH() external view returns (bytes32);

Expand Down
1 change: 1 addition & 0 deletions bolt-contracts/src/interfaces/IBoltValidatorsV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface IBoltValidatorsV2 {
error UnsafeRegistrationNotAllowed();
error UnauthorizedCaller();
error InvalidPubkey();
error InvalidManager();

function getAllValidators() external view returns (ValidatorInfo[] memory);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

interface ICredibleCommitmentCurationProvider {

/// @notice Method check for approval for provided registration root against
/// internal CredibleCommitmentCurationProvider logic.
function isRegistrationRootApproved(bytes32 registrationRoot) external view returns (bool);

/// @notice Hooke used to revert if provided registration root is not approved.
function hookOnlyApprovedRegistrationRoot(bytes32 registrationRoot) external view;
}
2 changes: 2 additions & 0 deletions bolt-contracts/src/lib/EnumerableMapV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ library EnumerableMapV2 {
address middleware;
// Timestamp of registration
uint256 timestamp;
// Optional Curator contract address
address curator;
}

struct OperatorMap {
Expand Down
Loading