Skip to content

Commit

Permalink
Add global validator set, calc deposit data root on-chain, tweak inte…
Browse files Browse the repository at this point in the history
…rfaces
  • Loading branch information
kanewallmann committed Jan 29, 2025
1 parent f08eeb4 commit a4517c3
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 58 deletions.
125 changes: 105 additions & 20 deletions contracts/contract/megapool/RocketMegapoolDelegate.sol
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.18;
pragma abicoder v2;

import "../RocketBase.sol";
import "../../interface/RocketVaultInterface.sol";
import "../../interface/deposit/RocketDepositPoolInterface.sol";
import "../../interface/casper/DepositInterface.sol";
import "../../interface/megapool/RocketMegapoolDelegateInterface.sol";
import "../../interface/node/RocketNodeManagerInterface.sol";
import "../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol";
import "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMegapoolInterface.sol";
import "../../interface/token/RocketTokenRETHInterface.sol";
import {RocketMegapoolProxy} from "./RocketMegapoolProxy.sol";
import "./RocketMegapoolDelegateBase.sol";

import {BeaconStateVerifier} from "../util/BeaconStateVerifier.sol";
import {RocketStorageInterface} from "../../interface/RocketStorageInterface.sol";
import {RocketVaultInterface} from "../../interface/RocketVaultInterface.sol";
import {DepositInterface} from "../../interface/casper/DepositInterface.sol";
import {RocketDAONodeTrustedSettingsMinipoolInterface} from "../../interface/dao/node/settings/RocketDAONodeTrustedSettingsMinipoolInterface.sol";
import {RocketDAOProtocolSettingsMegapoolInterface} from "../../interface/dao/protocol/settings/RocketDAOProtocolSettingsMegapoolInterface.sol";
import {RocketDepositPoolInterface} from "../../interface/deposit/RocketDepositPoolInterface.sol";
import {RocketMegapoolDelegateInterface} from "../../interface/megapool/RocketMegapoolDelegateInterface.sol";
import {RocketNetworkRevenuesInterface} from "../../interface/network/RocketNetworkRevenuesInterface.sol";
import {RocketNodeManagerInterface} from "../../interface/node/RocketNodeManagerInterface.sol";
import {ValidatorProof, BeaconStateVerifierInterface} from "../../interface/util/BeaconStateVerifierInterface.sol";
import {IERC20} from "../../interface/util/IERC20.sol";
import {RocketMegapoolDelegateBase} from "./RocketMegapoolDelegateBase.sol";
import {RocketMegapoolStorageLayout} from "./RocketMegapoolStorageLayout.sol";
pragma abicoder v2;

/// @title RocketMegapool
/// @notice This contract manages multiple validators. It serves as the target of Beacon Chain withdrawal credentials.
contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDelegateInterface {
// Constants
uint256 constant internal prestakeValue = 1 ether;
uint256 constant internal fullDepositValue = 32 ether;
uint256 constant internal milliToWei = 10**15;
uint256 constant internal milliToWei = 10 ** 15;
uint256 constant internal calcBase = 1 ether;

// Events
Expand Down Expand Up @@ -69,10 +68,12 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
// Store prestake data
{
PrestakeData memory prestake;
prestake._depositDataRoot = _depositDataRoot;
prestake._signature = _validatorSignature;
prestakeData[validatorId] = prestake;
}
// Compute and verify supplied deposit data root is correct
bytes32 depositDataRoot = computeDepositDataRoot(_validatorPubkey, _validatorSignature, uint64(prestakeValue / 1 gwei));
require(depositDataRoot == _depositDataRoot, "Invalid deposit data root");
// Increase total bond used for bond requirement calculations
nodeBond += _bondAmount;
// Request full deposit amount from deposit pool
Expand All @@ -81,6 +82,89 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
emit MegapoolValidatorEnqueued(address(this), validatorId, block.timestamp);
}

function computeDepositDataRoot(bytes memory pubkey, bytes memory signature, uint64 amount) public view returns (bytes32 ret) {
bytes32 withdrawalCredentials = getWithdrawalCredentials();
assembly {
let result
let temp := mload(0x40)

// [0x00] = pubkey[0x00:0x20]
// [0x20] = pubkey[0x20:0x30] . bytes16(0)
mstore(0x00, mload(add(pubkey, 0x20)))
mstore(0x20, and(mload(add(pubkey, 0x40)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000))

// temp[0x00] = sha256([0x00:0x40])
result := staticcall(84, 0x02, 0x00, 0x40, temp, 0x20)
if iszero(result) {
revert(0, 0)
}

// temp[0x20] = withdrawal_credentials
mstore(add(temp, 0x20), withdrawalCredentials)

// temp[0x00] = sha256(temp[0x00:0x40])
result := staticcall(84, 0x02, temp, 0x40, temp, 0x20)
if iszero(result) {
revert(0, 0)
}

// temp[0x20] = sha256(signature[0x00:0x40])
result := staticcall(84, 0x02, add(signature, 0x20), 0x40, add(temp, 0x20), 0x20)
if iszero(result) {
revert(0,0)
}

// [0x00] = signature[0x40]
// [0x20] = bytes32(0)
mstore(0x00, mload(add(signature, 0x60)))
mstore(0x20, 0)

// [0x20] = sha256([0x00:0x40])
result := staticcall(84, 0x02, 0x00, 0x40, 0x20, 0x20)
if iszero(result) {
revert(0, 0)
}

// [0x00] = temp[0x20]
mstore(0x00, mload(add(temp, 0x20)))

// [0x20] = sha256([0x00:0x40])
result := staticcall(84, 0x02, 0x00, 0x40, 0x20, 0x20)
if iszero(result) {
revert(0, 0)
}

// [0x00] = to_little_endian(amount) . bytes24(0)
mstore(0x00, 0)
mstore8(0x00, shr(0x00, amount))
mstore8(0x01, shr(0x08, amount))
mstore8(0x02, shr(0x10, amount))
mstore8(0x03, shr(0x18, amount))
mstore8(0x04, shr(0x20, amount))
mstore8(0x05, shr(0x28, amount))
mstore8(0x06, shr(0x30, amount))
mstore8(0x07, shr(0x38, amount))

// [0x20] = sha256([0x00:0x40])
result := staticcall(84, 0x02, 0x00, 0x40, 0x20, 0x20)
if iszero(result) {
revert(0, 0)
}

// [0x00] = temp[0x00]
mstore(0x00, mload(temp))

// [0x00] = sha256([0x00:0x40])
result := staticcall(84, 0x02, 0x00, 0x40, 0x00, 0x20)
if iszero(result) {
revert(0, 0)
}

// Return [0x00:0x20]
ret := mload(0x00)
}
}

/// @notice Removes a validator from the deposit queue
/// @param _validatorId the validator ID
function dequeue(uint32 _validatorId) external onlyMegapoolOwner {
Expand Down Expand Up @@ -116,7 +200,8 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
// Execute prestake operation
PrestakeData memory validatorPrestakeData = prestakeData[_validatorId];
DepositInterface casperDeposit = DepositInterface(getContractAddress("casperDeposit"));
casperDeposit.deposit{value: prestakeValue}(validator.pubKey, abi.encodePacked(getWithdrawalCredentials()), validatorPrestakeData._signature, validatorPrestakeData._depositDataRoot);
bytes32 depositDataRoot = computeDepositDataRoot(validator.pubKey, validatorPrestakeData._signature, uint64(prestakeValue / 1 gwei));
casperDeposit.deposit{value: prestakeValue}(validator.pubKey, abi.encodePacked(getWithdrawalCredentials()), validatorPrestakeData._signature, depositDataRoot);
// Clean up prestake data
delete prestakeData[_validatorId];
// Emit event
Expand Down Expand Up @@ -147,7 +232,7 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
userCapital += uint256(lastRequestedValue - validator.lastRequestedBond) * milliToWei;
nodeCapital += uint256(validator.lastRequestedBond) * milliToWei;
// Update validator status
validator.active = true;
validator.staked = true;
validator.inPrestake = false;
validator.lastAssignmentTime = 0;
validator.lastRequestedBond = 0;
Expand Down Expand Up @@ -181,7 +266,7 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
refundValue += uint256(validator.lastRequestedBond) * milliToWei - prestakeValue;
// Recycle the ETH
RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
rocketDepositPool.recycleDissolvedDeposit{value : validator.lastRequestedValue - validator.lastRequestedBond}();
rocketDepositPool.recycleDissolvedDeposit{value: validator.lastRequestedValue - validator.lastRequestedBond}();
// TODO: Handle recovery of prestakeValue as part of capital distribution process
}

Expand Down Expand Up @@ -297,7 +382,7 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
}

/// @notice Returns the number of validators created for this megapool
function getValidatorCount() override external view returns (uint256) {
function getValidatorCount() override external view returns (uint32) {
return numValidators;
}

Expand Down
43 changes: 43 additions & 0 deletions contracts/contract/megapool/RocketMegapoolManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.18;

import "./RocketMegapoolStorageLayout.sol";
import "./RocketMegapoolStorageLayout.sol";
import {RocketBase} from "../RocketBase.sol";
import {RocketMegapoolInterface} from "../../interface/megapool/RocketMegapoolInterface.sol";
import {RocketStorageInterface} from "../../interface/RocketStorageInterface.sol";
import {RocketMegapoolManagerInterface} from "../../interface/megapool/RocketMegapoolManagerInterface.sol";

/// @notice Manages the global list of validators across all megapools
contract RocketMegapoolManager is RocketBase, RocketMegapoolManagerInterface {

constructor(RocketStorageInterface _rocketStorageAddress) RocketBase(_rocketStorageAddress) {
version = 1;
}

/// @notice Returns the total number validators across all megapools
function getValidatorCount() override external view returns (uint256) {
return getUint(keccak256("megapool.validator.count"));
}

/// @notice Adds a validator record to the global megapool validator set
/// @param _megapoolAddress Address of the megapool which manages this validator
/// @param _validatorId Internal validator ID of the new validator
function addValidator(address _megapoolAddress, uint32 _validatorId) override external onlyLatestContract("rocketMegapoolManager", address(this)) onlyLatestContract("rocketNodeDeposit", msg.sender) {
uint256 index = getUint(keccak256("megapool.validator.count"));
setUint(keccak256("megapool.validator.count"), index + 1);
uint256 encoded = (uint160(_megapoolAddress) << 96) | uint32(_validatorId);
setUint(keccak256(abi.encodePacked("megapool.validator.set", index)), encoded);
}

/// @notice Returns validator info for the given global megapool validator index
/// @param _index The index of the validator to query
function getValidatorInfo(uint256 _index) override external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory) {
uint256 encoded = getUint(keccak256(abi.encodePacked("megapool.validator.set", _index)));
address megapoolAddress = address(uint160(encoded) >> 96);
uint32 validatorId = uint32(encoded);

RocketMegapoolInterface rocketMegapool = RocketMegapoolInterface(megapoolAddress);
return rocketMegapool.getValidatorInfo(validatorId);
}
}
5 changes: 2 additions & 3 deletions contracts/contract/megapool/RocketMegapoolStorageLayout.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ abstract contract RocketMegapoolStorageLayout {
uint32 lastRequestedValue; // Value in milliether last requested
uint32 lastRequestedBond; // Value in milliether of the bond supplied for last request for funds

bool active; // Whether the validator is actively validating on the beacon chain
bool staked; // Whether the validator has staked the minimum required to begin validating (32 ETH)
bool exited; // Whether the validator has exited the beacon chain
bool inQueue; // Whether the validator is currently awaiting funds from the deposit pool
bool inPrestake; // Whether the validator is currently awaiting the stake operation
Expand All @@ -39,7 +39,6 @@ abstract contract RocketMegapoolStorageLayout {
// Extra data temporarily stored for prestake operation
struct PrestakeData {
bytes _signature;
bytes32 _depositDataRoot;
}

//
Expand All @@ -61,7 +60,7 @@ abstract contract RocketMegapoolStorageLayout {
//

address internal nodeAddress; // Megapool owner
uint256 internal numValidators; // Number of individual validators handled by this megapool
uint32 internal numValidators; // Number of individual validators handled by this megapool

uint256 internal assignedValue; // ETH assigned from DP pending prestake/stake
uint256 internal refundValue; // ETH refunded to the owner after a dissolution
Expand Down
3 changes: 3 additions & 0 deletions contracts/contract/node/RocketNodeDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import "../../interface/node/RocketNodeDepositInterface.sol";
import "../../interface/node/RocketNodeManagerInterface.sol";
import "../../interface/node/RocketNodeStakingInterface.sol";
import "../RocketBase.sol";
import {RocketMegapoolManagerInterface} from "../../interface/megapool/RocketMegapoolManagerInterface.sol";

/// @notice Entry point for node operators to perform deposits for the creation of new validators on the network
contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
Expand Down Expand Up @@ -188,11 +189,13 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
// Get or deploy a megapool for the caller
RocketMegapoolFactoryInterface rocketMegapoolFactory = RocketMegapoolFactoryInterface(getContractAddress("rocketMegapoolFactory"));
RocketMegapoolInterface megapool = RocketMegapoolInterface(rocketMegapoolFactory.getOrDeployContract(msg.sender));
RocketMegapoolManagerInterface rocketMegapoolManager = RocketMegapoolManagerInterface(getContractAddress("rocketMegapoolManager"));
// Check bond requirements
checkBondRequirement(megapool, _bondAmount);
checkDebtRequirement(megapool);
// Request a new validator from the megapool
megapool.newValidator(_bondAmount, _useExpressTicket, _validatorPubkey, _validatorSignature, _depositDataRoot);
rocketMegapoolManager.addValidator(address(megapool), megapool.getValidatorCount());
// Send node operator's bond to the deposit pool
RocketDepositPoolInterface rocketDepositPool = RocketDepositPoolInterface(getContractAddress("rocketDepositPool"));
rocketDepositPool.nodeDeposit{value: msg.value}(_bondAmount);
Expand Down
61 changes: 33 additions & 28 deletions contracts/contract/upgrade/RocketUpgradeOneDotFour.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract RocketUpgradeOneDotFour is RocketBase {
address public rocketMegapoolDelegate;
address public rocketMegapoolFactory;
address public rocketMegapoolProxy;
address public rocketMegapoolManager;
address public rocketNodeManager;
address public rocketNodeDeposit;
address public rocketNodeStaking;
Expand All @@ -41,6 +42,7 @@ contract RocketUpgradeOneDotFour is RocketBase {
string public rocketMegapoolDelegateAbi;
string public rocketMegapoolFactoryAbi;
string public rocketMegapoolProxyAbi;
string public rocketMegapoolManagerAbi;
string public rocketNodeManagerAbi;
string public rocketNodeDepositAbi;
string public rocketNodeStakingAbi;
Expand Down Expand Up @@ -81,39 +83,41 @@ contract RocketUpgradeOneDotFour is RocketBase {
rocketMegapoolDelegate = _addresses[0];
rocketMegapoolFactory = _addresses[1];
rocketMegapoolProxy = _addresses[2];
rocketNodeManager = _addresses[3];
rocketNodeDeposit = _addresses[4];
rocketNodeStaking = _addresses[5];
rocketDepositPool = _addresses[6];
linkedListStorage = _addresses[7];
rocketDAOProtocolSettingsNode = _addresses[8];
rocketDAOProtocolSettingsDeposit = _addresses[9];
rocketDAOProtocolSettingsNetwork = _addresses[10];
rocketDAOProtocolSettingsSecurity = _addresses[11];
rocketDAOSecurityProposals = _addresses[12];
rocketNetworkRevenues = _addresses[13];
rocketNetworkSnapshots = _addresses[14];
blockRoots = _addresses[15];
beaconStateVerifier = _addresses[16];
rocketMegapoolManager = _addresses[3];
rocketNodeManager = _addresses[4];
rocketNodeDeposit = _addresses[5];
rocketNodeStaking = _addresses[6];
rocketDepositPool = _addresses[7];
linkedListStorage = _addresses[8];
rocketDAOProtocolSettingsNode = _addresses[9];
rocketDAOProtocolSettingsDeposit = _addresses[10];
rocketDAOProtocolSettingsNetwork = _addresses[11];
rocketDAOProtocolSettingsSecurity = _addresses[12];
rocketDAOSecurityProposals = _addresses[13];
rocketNetworkRevenues = _addresses[14];
rocketNetworkSnapshots = _addresses[15];
blockRoots = _addresses[16];
beaconStateVerifier = _addresses[17];

// Set ABIs
rocketMegapoolDelegateAbi = _abis[0];
rocketMegapoolFactoryAbi = _abis[1];
rocketMegapoolProxyAbi = _abis[2];
rocketNodeManagerAbi = _abis[3];
rocketNodeDepositAbi = _abis[4];
rocketNodeStakingAbi = _abis[5];
rocketDepositPoolAbi = _abis[6];
linkedListStorageAbi = _abis[7];
rocketDAOProtocolSettingsNodeAbi = _abis[8];
rocketDAOProtocolSettingsDepositAbi = _abis[9];
rocketDAOProtocolSettingsNetworkAbi = _abis[10];
rocketDAOProtocolSettingsSecurityAbi = _abis[11];
rocketDAOSecurityProposalsAbi = _abis[12];
rocketNetworkRevenuesAbi = _abis[13];
rocketNetworkSnapshotsAbi = _abis[14];
blockRootsAbi = _abis[15];
beaconStateVerifierAbi = _abis[16];
rocketMegapoolManagerAbi = _abis[3];
rocketNodeManagerAbi = _abis[4];
rocketNodeDepositAbi = _abis[5];
rocketNodeStakingAbi = _abis[6];
rocketDepositPoolAbi = _abis[7];
linkedListStorageAbi = _abis[8];
rocketDAOProtocolSettingsNodeAbi = _abis[9];
rocketDAOProtocolSettingsDepositAbi = _abis[10];
rocketDAOProtocolSettingsNetworkAbi = _abis[11];
rocketDAOProtocolSettingsSecurityAbi = _abis[12];
rocketDAOSecurityProposalsAbi = _abis[13];
rocketNetworkRevenuesAbi = _abis[14];
rocketNetworkSnapshotsAbi = _abis[15];
blockRootsAbi = _abis[16];
beaconStateVerifierAbi = _abis[17];
}

/// @notice Prevents further changes from being applied
Expand All @@ -131,6 +135,7 @@ contract RocketUpgradeOneDotFour is RocketBase {
_addContract("rocketMegapoolDelegate", rocketMegapoolDelegate, rocketMegapoolDelegateAbi);
_addContract("rocketMegapoolFactory", rocketMegapoolFactory, rocketMegapoolFactoryAbi);
_addContract("rocketMegapoolProxy", rocketMegapoolProxy, rocketMegapoolProxyAbi);
_addContract("rocketMegapoolManager", rocketMegapoolManager, rocketMegapoolManagerAbi);
_addContract("linkedListStorage", linkedListStorage, linkedListStorageAbi);
_addContract("rocketNetworkRevenues", rocketNetworkRevenues, rocketNetworkRevenuesAbi);
_addContract("blockRoots", blockRoots, blockRootsAbi);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface RocketMegapoolDelegateInterface is RocketMegapoolDelegateBaseInterface
function dissolveValidator(uint32 _validatorId) external;
function getNodeAddress() external returns (address);

function getValidatorCount() external view returns (uint256);
function getValidatorCount() external view returns (uint32);
function getValidatorInfo(uint32 _validatorId) external view returns (RocketMegapoolStorageLayout.ValidatorInfo memory);
function getAssignedValue() external view returns (uint256);
function getDebt() external view returns (uint256);
Expand Down
Loading

0 comments on commit a4517c3

Please sign in to comment.