Skip to content

Commit

Permalink
Work on deposit queue exit
Browse files Browse the repository at this point in the history
  • Loading branch information
kanewallmann committed Jan 21, 2025
1 parent 45a6674 commit 2b4196f
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 88 deletions.
130 changes: 75 additions & 55 deletions contracts/contract/deposit/RocketDepositPool.sol

Large diffs are not rendered by default.

12 changes: 1 addition & 11 deletions contracts/contract/megapool/RocketMegapoolDelegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import "../../interface/token/RocketTokenRETHInterface.sol";
import {RocketMegapoolProxy} from "./RocketMegapoolProxy.sol";
import "./RocketMegapoolDelegateBase.sol";

import "hardhat/console.sol";
import {BeaconStateVerifier} from "../util/BeaconStateVerifier.sol";
import {RocketNetworkRevenuesInterface} from "../../interface/network/RocketNetworkRevenuesInterface.sol";

Expand Down Expand Up @@ -84,7 +83,7 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel

/// @notice Removes a validator from the deposit queue
/// @param _validatorId the validator ID
function dequeue(uint32 _validatorId) external onlyLatestContract("rocketNodeDeposit", msg.sender) {
function dequeue(uint32 _validatorId) external onlyMegapoolOwner {
ValidatorInfo memory validator = validators[_validatorId];
// Validate validator status
require(validator.inQueue, "Validator must be in queue");
Expand All @@ -93,7 +92,6 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
rocketDepositPool.exitQueue(_validatorId, validator.expressUsed);
// Decrease total bond used for bond requirement calculations
nodeBond -= validator.lastRequestedBond;
// TODO: Apply an ETH credit of validator.lastRequestedBond
// Update validator state
validator.inQueue = false;
validator.lastRequestedBond = 0;
Expand Down Expand Up @@ -338,22 +336,14 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel

function _calculateRewards(uint256 _rewards) internal view returns (uint256 nodeRewards, uint256 voterRewards, uint256 rethRewards) {
RocketNetworkRevenuesInterface rocketNetworkRevenues = RocketNetworkRevenuesInterface(getContractAddress("rocketNetworkRevenues"));
console.log("lastDistributionBlock %d", lastDistributionBlock);
(uint256 nodeShare, uint256 voterShare, uint256 rethShare) = rocketNetworkRevenues.calculateSplit(lastDistributionBlock);
console.log("%d %d %d", nodeShare, voterShare, rethShare);
uint256 borrowedPortion = _rewards * userCapital / (nodeCapital + userCapital);
console.log("userCapital %d", userCapital);
console.log("nodeCapital %d", nodeCapital);
console.log("borrowedPortion %d", borrowedPortion);
rethRewards = rethShare * borrowedPortion / calcBase;
voterRewards = voterShare * borrowedPortion / calcBase;
nodeRewards = _rewards - rethRewards - voterRewards;
}

function getPendingRewards() override public view returns (uint256) {
console.log("balance %d", address(this).balance);
console.log("refundValue %d", refundValue);
console.log("assignedValue %d", assignedValue);
return
address(this).balance
- refundValue
Expand Down
6 changes: 3 additions & 3 deletions contracts/contract/node/RocketNodeDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {

/// @notice Deposits ETH for the given node operator
/// @param _nodeAddress The address of the node operator to deposit ETH for
function depositEthFor(address _nodeAddress) override external payable onlyRegisteredMinipool(_nodeAddress) {
function depositEthFor(address _nodeAddress) override external payable onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(_nodeAddress) {
// Send the ETH to vault
uint256 amount = msg.value;
RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
Expand All @@ -117,7 +117,7 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
/// @notice Withdraws ETH from a node operator's balance. Must be called from withdrawal address.
/// @param _nodeAddress Address of the node operator to withdraw from
/// @param _amount Amount of ETH to withdraw
function withdrawEth(address _nodeAddress, uint256 _amount) external onlyRegisteredMinipool(_nodeAddress) {
function withdrawEth(address _nodeAddress, uint256 _amount) external onlyLatestContract("rocketNodeDeposit", address(this)) onlyRegisteredNode(_nodeAddress) {
// Check valid caller
address withdrawalAddress = rocketStorage.getNodeWithdrawalAddress(_nodeAddress);
require(msg.sender == withdrawalAddress, "Only withdrawal address can withdraw ETH");
Expand All @@ -135,7 +135,7 @@ contract RocketNodeDeposit is RocketBase, RocketNodeDepositInterface {
emit DepositFor(_nodeAddress, withdrawalAddress, _amount, block.timestamp);
}

/// @notice Accept a node deposit and create a new minipool under the node. Only accepts calls from registered nodes
/// @notice Accept a node deposit and create a new validator under the node. Only accepts calls from registered nodes
/// @param _bondAmount The amount of capital the node operator wants to put up as his bond
/// @param _useExpressTicket If the express queue should be used
/// @param _validatorPubkey Pubkey of the validator the node operator wishes to migrate
Expand Down
20 changes: 10 additions & 10 deletions contracts/contract/util/LinkedListStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ contract LinkedListStorage is RocketBase, LinkedListStorageInterface {

/// @notice The index of an item in a queue. Returns 0 if the value is not found
/// @param _namespace defines the queue to be used
/// @param _value the deposit queue value
function getIndexOf(bytes32 _namespace, DepositQueueValue memory _value) override external view returns (uint256) {
return getUint(keccak256(abi.encodePacked(_namespace, ".index", _value.receiver, _value.validatorId)));
/// @param _key the deposit queue value
function getIndexOf(bytes32 _namespace, DepositQueueKey memory _key) override external view returns (uint256) {
return getUint(keccak256(abi.encodePacked(_namespace, ".index", _key.receiver, _key.validatorId)));
}

/// @notice Finds an item index in a queue and returns the previous item
Expand Down Expand Up @@ -158,16 +158,16 @@ contract LinkedListStorage is RocketBase, LinkedListStorageInterface {

/// @notice Removes an item from a queue. Requires that the item exists in the queue
/// @param _namespace defines the queue to be used
/// @param _item to be removed from the queue
function removeItem(bytes32 _namespace, DepositQueueValue memory _item) public virtual override onlyLatestContract("linkedListStorage", address(this)) onlyLatestNetworkContract {
_removeItem(_namespace, _item);
/// @param _key to be removed from the queue
function removeItem(bytes32 _namespace, DepositQueueKey memory _key) public virtual override onlyLatestContract("linkedListStorage", address(this)) onlyLatestNetworkContract {
_removeItem(_namespace, _key);
}

/// @notice Internal funciton to remove an item from a queue. Requires that the item exists in the queue
/// @param _namespace defines the queue to be used
/// @param _item to be removed from the queue
function _removeItem(bytes32 _namespace, DepositQueueValue memory _item) internal {
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _item.receiver, _item.validatorId)));
/// @param _key to be removed from the queue
function _removeItem(bytes32 _namespace, DepositQueueKey memory _key) internal {
uint256 index = getUint(keccak256(abi.encodePacked(_namespace, ".index", _key.receiver, _key.validatorId)));
uint256 data = getUint(keccak256(abi.encodePacked(_namespace, ".data")));
require(index > 0, "Item does not exist in queue");

Expand All @@ -194,7 +194,7 @@ contract LinkedListStorage is RocketBase, LinkedListStorageInterface {
data |= prevIndex << endOffset;
}

setUint(keccak256(abi.encodePacked(_namespace, ".index", _item.receiver, _item.validatorId)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".index", _key.receiver, _key.validatorId)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".next", index)), 0);
setUint(keccak256(abi.encodePacked(_namespace, ".prev", index)), 0);

Expand Down
6 changes: 3 additions & 3 deletions contracts/contract/util/LinkedListStorageHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ contract LinkedListStorageHelper is LinkedListStorage {

/// @notice Removes an item from a queue. Requires that the item exists in the queue
/// @param _namespace to be used
/// @param _item to be removed from the queue
function removeItem(bytes32 _namespace, DepositQueueValue memory _item) public virtual override {
return _removeItem(_namespace, _item);
/// @param _key to be removed from the queue
function removeItem(bytes32 _namespace, DepositQueueKey memory _key) public virtual override {
return _removeItem(_namespace, _key);
}

function packItem(DepositQueueValue memory _item) public pure returns (uint256 packed) {
Expand Down
5 changes: 3 additions & 2 deletions contracts/interface/deposit/RocketDepositPoolInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ interface RocketDepositPoolInterface {
function getBalance() external view returns (uint256);
function getNodeBalance() external view returns (uint256);
function getUserBalance() external view returns (int256);
function getNodeCreditBalance(address _nodeAddress) external view returns (uint256);
function getExcessBalance() external view returns (uint256);
function deposit() external payable;
function getMaximumDepositAmount() external view returns (uint256);
function nodeDeposit(uint256 _totalAmount) external payable;
function nodeCreditWithdrawal(uint256 _amount) external;
function recycleDissolvedDeposit() external payable;
function recycleExcessCollateral() external payable;
function recycleLiquidatedStake() external payable;
function assignDeposits() external;
function maybeAssignOneDeposit() external;
function maybeAssignDeposits() external returns (bool);
function withdrawExcessBalance(uint256 _amount) external;
function requestFunds(uint256 _bondAmount, uint256 _validatorIndex, uint256 _amount, bool _useExpressTicket) external payable;
function requestFunds(uint256 _bondAmount, uint256 _validatorIndex, uint256 _amount, bool _useExpressTicket) external;
function exitQueue(uint256 validatorIndex, bool expressQueue) external;
function withdrawCredit(uint256 _amount) external;
function getQueueTop() external view returns (address receiver, bool assignmentPossible);
function assignMegapools(uint256 _count) external;
}
9 changes: 7 additions & 2 deletions contracts/interface/util/LinkedListStorageInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ struct DepositQueueValue {
uint32 requestedValue; // in milliether
}

struct DepositQueueKey {
address receiver; // the address that will receive the requested value
uint32 validatorId; // internal validator id
}

interface LinkedListStorageInterface {
function getLength(bytes32 _namespace) external view returns (uint256);
function getItem(bytes32 _namespace, uint _index) external view returns (DepositQueueValue memory);
function peekItem(bytes32 _namespace) external view returns (DepositQueueValue memory);
function getIndexOf(bytes32 _namespace, DepositQueueValue memory _value) external view returns (uint256);
function getIndexOf(bytes32 _namespace, DepositQueueKey memory _key) external view returns (uint256);
function enqueueItem(bytes32 _namespace, DepositQueueValue memory _value) external;
function dequeueItem(bytes32 _namespace) external returns (DepositQueueValue memory);
function removeItem(bytes32 _namespace, DepositQueueValue memory _value) external;
function removeItem(bytes32 _namespace, DepositQueueKey memory _key) external;
}
16 changes: 14 additions & 2 deletions test/megapool/megapool-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { shouldRevert } from '../_utils/testing';
import {
BeaconStateVerifier,
MegapoolUpgradeHelper,
RocketDAONodeTrustedSettingsMinipool,
RocketDAONodeTrustedSettingsMinipool, RocketDepositPool,
RocketMegapoolDelegate,
RocketMegapoolFactory,
RocketStorage,
Expand All @@ -17,6 +17,7 @@ import assert from 'assert';
import { setDAONodeTrustedBootstrapSetting } from '../dao/scenario-dao-node-trusted-bootstrap';
import { stakeMegapoolValidator } from './scenario-stake';
import { assertBN } from '../_helpers/bn';
import { exitQueue } from './scenario-exit-queue';

const helpers = require('@nomicfoundation/hardhat-network-helpers');
const hre = require('hardhat');
Expand Down Expand Up @@ -84,6 +85,12 @@ export default function() {
await shouldRevert(deployMegapool({ from: node }), 'Redeploy worked');
});

it(printTitle('node', 'can exit the deposit queue'), async () => {
await deployMegapool({ from: node });
await nodeDeposit(false, false, { value: '4'.ether, from: node });
await exitQueue(node, 0);
});

describe('With full deposit pool', () => {
const dissolvePeriod = (60 * 60 * 24); // 24 hours

Expand All @@ -94,6 +101,12 @@ export default function() {
await setDAONodeTrustedBootstrapSetting(RocketDAONodeTrustedSettingsMinipool, 'megapool.dissolve.period', dissolvePeriod, { from: owner });
});

it(printTitle('node', 'cannot exit the deposit queue once assigned'), async () => {
await deployMegapool({ from: node });
await nodeDeposit(false, false, { value: '4'.ether, from: node });
await shouldRevert(exitQueue(node, 0), 'Was able to exit the deposit queue once assigned', 'Validator must be in queue');
});

it(printTitle('node', 'can not create a new validator while debt is present'), async () => {
await deployMegapool({ from: node });
await megapool.connect(owner).setDebt('1'.ether);
Expand Down Expand Up @@ -200,7 +213,6 @@ export default function() {
rETH Share: 1 - 0.875 - 0.04375 = 0.08125 ETH
*/
const rewardSplit = await megapool.calculateRewards();
console.log(rewardSplit);
assertBN.equal(rewardSplit[0], '0.16875'.ether);
assertBN.equal(rewardSplit[1], '0.07875'.ether);
assertBN.equal(rewardSplit[2], '0.7525'.ether);
Expand Down
25 changes: 25 additions & 0 deletions test/megapool/scenario-exit-queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { getMegapoolForNode } from '../_helpers/megapool';
import assert from 'assert';
import { RocketDepositPool } from '../_utils/artifacts';
import { assertBN } from '../_helpers/bn';

const milliToWei = 1000000000000000n;

export async function exitQueue(node, validatorIndex) {
const megapool = await getMegapoolForNode(node);
const rocketDepositPool = await RocketDepositPool.deployed();

const validatorInfoBefore = await megapool.getValidatorInfo(validatorIndex);

// Dequeue the validator
await megapool.dequeue(validatorIndex);

// Check the validator status
const validatorInfoAfter = await megapool.getValidatorInfo(validatorIndex);
assert.equal(validatorInfoAfter.active, false);
assert.equal(validatorInfoAfter.inQueue, false);

// Check an ETH credit was applied
const credit = await rocketDepositPool.getNodeCreditBalance(node.address);
assertBN.equal(credit, validatorInfoBefore.lastRequestedBond * milliToWei);
}

0 comments on commit 2b4196f

Please sign in to comment.