Skip to content

Commit 2e600cb

Browse files
committed
Fix node share calculation on bond reduction
1 parent de23830 commit 2e600cb

File tree

12 files changed

+310
-52
lines changed

12 files changed

+310
-52
lines changed

contracts/contract/megapool/RocketMegapoolDelegate.sol

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,12 +736,15 @@ contract RocketMegapoolDelegate is RocketMegapoolDelegateBase, RocketMegapoolDel
736736
uint256 newBondRequirement = rocketNodeDeposit.getBondRequirement(_newValidatorCount);
737737
uint256 effectiveBond = nodeBond + nodeQueuedBond;
738738
_nodeShare = 0;
739-
if (newBondRequirement < nodeBond) {
739+
if (newBondRequirement < effectiveBond) {
740740
_nodeShare = effectiveBond - newBondRequirement;
741741
}
742742
if (_nodeShare > _value) {
743743
_nodeShare = _value;
744744
}
745+
if (_nodeShare > nodeBond) {
746+
_nodeShare = nodeBond;
747+
}
745748
_userShare = _value - _nodeShare;
746749
}
747750

scripts/deploy-upgrade.v1.4.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ const CHAINS = {
4949
],
5050
},
5151
'private': {
52-
genesisBlockTimestamp: 1762861080n,
52+
genesisBlockTimestamp: 1763665800n,
5353
slotsPerHistoricalRoot: 8192n,
5454
beaconRoots: '0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02',
55-
genesisValidatorRoot: '0xec9caf9aad26d20776fbd9e03b61dee7e7bd155a32d1593d43c47df730c40f14',
55+
genesisValidatorRoot: '0x3befd678a85f6b7e056b3520fac8660955a87bb2006d88a49cba99b25752c668',
5656
forkSlots: [
5757
0n, // Altair
5858
0n, // Bellatrix
@@ -111,6 +111,7 @@ async function deployUpgrade(rocketStorageAddress) {
111111

112112
const deployedContracts = {};
113113
const contractPlan = {};
114+
const txs = [];
114115

115116
async function deployNetworkContract(name) {
116117
const plan = contractPlan[name];
@@ -129,6 +130,7 @@ async function deployUpgrade(rocketStorageAddress) {
129130
// Deploy and log result
130131
const instance = await artifact.newImmediate(...constructorArgs);
131132
const rsTx = await instance.deploymentTransaction();
133+
txs.push(rsTx);
132134
const address = instance.target;
133135
console.log(`Deployed to ${address} @ ${rsTx.hash}`);
134136
console.log();
@@ -191,6 +193,12 @@ async function deployUpgrade(rocketStorageAddress) {
191193
// Deploy upgrade
192194
await deployNetworkContract('RocketUpgradeOneDotFour');
193195

196+
// Wait for all deployment txs to complete
197+
console.log()
198+
console.log(`Waiting for all txs to be mined...`)
199+
console.log()
200+
await Promise.all(txs.map(tx => tx.wait()));
201+
194202
// Set
195203
const upgradeContract = deployedContracts['RocketUpgradeOneDotFour'].instance;
196204
const setAddressesA = [

test/_helpers/invariants.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const { RocketNodeManager, RocketMinipoolManager, RocketMinipoolDelegate } = require('../../test/_utils/artifacts');
22
const { assertBN } = require('./bn');
33
const assert = require('assert');
4+
const { getMegapoolForNodeAddress } = require('./megapool');
5+
const { RocketNodeStaking, RocketDepositPool } = require('../_utils/artifacts');
46

57
async function checkInvariants() {
68
const nodeAddresses = await getNodeAddresses();
@@ -9,6 +11,40 @@ async function checkInvariants() {
911
const minipools = await getMinipoolsByNode(nodeAddress);
1012
await checkNodeInvariants(nodeAddress, minipools);
1113
}
14+
15+
await checkMegapoolInvariants()
16+
}
17+
18+
async function checkMegapoolInvariants() {
19+
// Check deposit.pool.node.balance invariant
20+
const nodeAddresses = await getNodeAddresses();
21+
22+
let totalNodeQueuedBond = 0n
23+
for (const nodeAddress of nodeAddresses) {
24+
const megapool = await getMegapoolForNodeAddress(nodeAddress);
25+
26+
// Sum queued bond
27+
if (megapool) {
28+
const nodeQueuedBond = await megapool.getNodeQueuedBond();
29+
totalNodeQueuedBond += nodeQueuedBond;
30+
}
31+
32+
// Check ETH matched and ETH provided match megapool values
33+
if (megapool) {
34+
const rocketNodeStaking = await RocketNodeStaking.deployed();
35+
const ethBorrowed = await rocketNodeStaking.getNodeMegapoolETHBorrowed(nodeAddress);
36+
const ethBonded = await rocketNodeStaking.getNodeMegapoolETHBonded(nodeAddress);
37+
const nodeBond = (await megapool.getNodeBond()) + (await megapool.getNodeQueuedBond());
38+
const userCapital = (await megapool.getUserCapital()) + (await megapool.getUserQueuedCapital());
39+
assertBN.equal(ethBorrowed, userCapital, 'ETH borrowed did not match user capital');
40+
assertBN.equal(ethBonded, nodeBond, 'ETH bonded did not match node bond');
41+
}
42+
}
43+
44+
// Check sum of queued bond equals the node balance
45+
const rocketDepositPool = await RocketDepositPool.deployed()
46+
const nodeBalance = await rocketDepositPool.getNodeBalance();
47+
assertBN.equal(nodeBalance, totalNodeQueuedBond, "Node balance does not match")
1248
}
1349

1450
async function getNodeAddresses() {
@@ -32,7 +68,7 @@ async function getMinipoolDetails(address) {
3268
finalised,
3369
nodeFee,
3470
userDepositBalance,
35-
nodeDepositBalance
71+
nodeDepositBalance,
3672
};
3773
}
3874

@@ -97,4 +133,4 @@ function weightedAverage(nums, weights) {
97133
return sum / weightSum;
98134
}
99135

100-
module.exports = { checkInvariants };
136+
module.exports = { checkInvariants, checkMegapoolInvariants };

test/_helpers/megapool.js

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ export async function nodeDeposit(node, bondAmount = '4'.ether, useExpressTicket
8888

8989
const queueIndex = await rocketDepositPool.getQueueIndex();
9090
const expressQueueRate = await rocketDAOProtocolSettingsDeposit.getExpressQueueRate();
91-
const nextAssignmentIsExpress = queueIndex % (expressQueueRate + 1n) !== 0n;
9291

9392
async function getData() {
9493
let data = await Promise.all([
@@ -142,6 +141,9 @@ export async function nodeDeposit(node, bondAmount = '4'.ether, useExpressTicket
142141

143142
const data1 = await getData();
144143

144+
const expressQueueLengthAfterDeposit = useExpressTicket ? data1.expressQueueLength + 1n : data1.expressQueueLength;
145+
const nextAssignmentIsExpress = (queueIndex % (expressQueueRate + 1n) !== expressQueueRate) && expressQueueLengthAfterDeposit !== 0n;
146+
145147
const assignmentsEnabled = await rocketDAOProtocolSettingsDeposit.getAssignDepositsEnabled();
146148
const depositPoolCapacity = await rocketDepositPool.getBalance();
147149
const amountRequired = '32'.ether - bondAmount;
@@ -155,6 +157,8 @@ export async function nodeDeposit(node, bondAmount = '4'.ether, useExpressTicket
155157
await tx.wait();
156158
}
157159

160+
const megapool = await getMegapoolForNode(node);
161+
158162
const data2 = await getData();
159163

160164
if (!data1.deployed) {
@@ -187,31 +191,47 @@ export async function nodeDeposit(node, bondAmount = '4'.ether, useExpressTicket
187191

188192
const minipoolInQueue = data1.minipoolQueueLength > 0n;
189193

190-
const expectSelfAssignment =
191-
!minipoolInQueue &&
194+
const expectAssignment = !minipoolInQueue &&
192195
assignmentsEnabled &&
193-
depositPoolCapacity >= amountRequired &&
196+
depositPoolCapacity >= amountRequired;
197+
198+
let expectSelfAssignment =
199+
expectAssignment &&
194200
(
195-
nextAssignmentIsExpress && data1.expressQueueLength === 0n ||
196-
!nextAssignmentIsExpress && data1.standardQueueLength === 0n
201+
(useExpressTicket && data1.expressQueueLength === 0n && nextAssignmentIsExpress) ||
202+
(!useExpressTicket && data1.standardQueueLength === 0n)
197203
);
198204

205+
const queueTop = await rocketDepositPool.getQueueTop();
206+
let expectMegapoolAssignment = expectSelfAssignment || (expectAssignment && (queueTop[0] === megapool.target));
207+
199208
if (useExpressTicket) {
200209
assertBN.equal(numExpressTicketsDelta, -1n, 'Did not consume express ticket');
201-
if (!expectSelfAssignment) {
202-
assertBN.equal(expressQueueLengthDelta, 1n, 'Express queue did not grow by 1');
203-
assertBN.equal(standardQueueLengthDelta, 0n, 'Standard queue grew');
210+
if (expectAssignment) {
211+
if (nextAssignmentIsExpress) {
212+
assertBN.equal(expressQueueLengthDelta, 0n, 'Express queue changed');
213+
assertBN.equal(standardQueueLengthDelta, 0n, 'Standard queue changed');
214+
} else {
215+
assertBN.equal(expressQueueLengthDelta, 1n, 'Express queue did not grow by 1');
216+
assertBN.equal(standardQueueLengthDelta, -1n, 'Standard queue did not shrink by 1');
217+
}
204218
}
205219
} else {
206220
assertBN.equal(numExpressTicketsDelta, 0n, 'Express ticket count incorrect');
207-
if (!expectSelfAssignment) {
208-
assertBN.equal(expressQueueLengthDelta, 0n, 'Express queue grew');
209-
assertBN.equal(standardQueueLengthDelta, 1n, 'Standard queue did not grow by 1');
221+
if (expectAssignment) {
222+
if (nextAssignmentIsExpress) {
223+
console.log(data1);
224+
console.log(data2);
225+
assertBN.equal(expressQueueLengthDelta, -1n, 'Express queue did not shrink by 1');
226+
assertBN.equal(standardQueueLengthDelta, 1n, 'Standard queue did not grow by 1');
227+
} else {
228+
assertBN.equal(expressQueueLengthDelta, 0n, 'Express queue changed');
229+
assertBN.equal(standardQueueLengthDelta, 0n, 'Standard queue changed');
230+
}
210231
}
211232
}
212233

213234
// Confirm state of new validator
214-
const megapool = await getMegapoolForNode(node);
215235
const validatorInfo = await getValidatorInfo(megapool, data1.numValidators);
216236

217237
assertBN.equal(nodeBondDelta + nodeQueuedBondDelta, bondAmount, 'Incorrect node capital');
@@ -226,25 +246,29 @@ export async function nodeDeposit(node, bondAmount = '4'.ether, useExpressTicket
226246
assertBN.equal(assignedValueDelta, 0n, 'Incorrect assigned value');
227247
assertBN.equal(userQueuedCapitalDelta, launchValue - bondAmount);
228248
assertBN.equal(nodeQueuedBondDelta, bondAmount);
229-
}
230-
else if (expectSelfAssignment)
231-
{
249+
} else if (expectSelfAssignment) {
232250
assert.equal(validatorInfo.inQueue, false, 'Incorrect validator status');
233251
assert.equal(validatorInfo.inPrestake, true, 'Incorrect validator status');
234252
assertBN.equal(assignedValueDelta, '31'.ether, 'Incorrect assigned value');
235253
assertBN.equal(nodeBalanceDelta, 0n, 'Incorrect node balance value');
236254
// If validator is assigned immediately, then there should be no change in queued capital balances
237255
assertBN.equal(nodeQueuedBondDelta, 0n);
238256
assertBN.equal(userQueuedCapitalDelta, 0n);
239-
}
240-
else
241-
{
257+
} else {
242258
assert.equal(validatorInfo.inQueue, true, 'Incorrect validator status');
243259
assert.equal(validatorInfo.inPrestake, false, 'Incorrect validator status');
244-
assertBN.equal(assignedValueDelta, 0n, 'Incorrect assigned value');
245-
assertBN.equal(nodeBalanceDelta, expectedNodeBalanceChange, 'Incorrect node balance value');
246-
assertBN.equal(userQueuedCapitalDelta, launchValue - bondAmount);
247-
assertBN.equal(nodeQueuedBondDelta, bondAmount);
260+
261+
if (expectMegapoolAssignment) {
262+
assertBN.equal(assignedValueDelta, '31'.ether, 'Incorrect assigned value');
263+
assertBN.equal(nodeBalanceDelta, 0n, 'Incorrect node balance value');
264+
assertBN.equal(userQueuedCapitalDelta, 0n);
265+
assertBN.equal(nodeQueuedBondDelta, 0n);
266+
} else {
267+
assertBN.equal(assignedValueDelta, 0n, 'Incorrect assigned value');
268+
assertBN.equal(nodeBalanceDelta, expectedNodeBalanceChange, 'Incorrect node balance value');
269+
assertBN.equal(userQueuedCapitalDelta, launchValue - bondAmount);
270+
assertBN.equal(nodeQueuedBondDelta, bondAmount);
271+
}
248272
}
249273

250274
assertBN.equal(validatorInfo.lastRequestedValue, '32'.ether / milliToWei, 'Incorrect validator lastRequestedValue');
@@ -468,6 +492,23 @@ export async function getMegapoolForNode(node) {
468492
return new ethers.Contract(megapoolAddress, combinedAbi, node);
469493
}
470494

495+
export async function getMegapoolForNodeAddress(nodeAddress) {
496+
const rocketMegapoolFactory = await RocketMegapoolFactory.deployed();
497+
const megapoolAddress = await rocketMegapoolFactory.getExpectedAddress(nodeAddress);
498+
499+
if (!await rocketMegapoolFactory.getMegapoolDeployed(nodeAddress)) {
500+
return null
501+
}
502+
503+
const delegateAbi = artifacts.require('RocketMegapoolDelegate').abi;
504+
const proxyAbi = artifacts.require('RocketMegapoolProxy').abi;
505+
506+
const combinedAbi = [...delegateAbi, ...proxyAbi].filter(fragment => fragment.type !== 'constructor');
507+
508+
const [signer] = await ethers.getSigners();
509+
return new ethers.Contract(megapoolAddress, combinedAbi, signer);
510+
}
511+
471512
export async function findInQueue(megapoolAddress, validatorId, queueKey, indexOffset = 0n, positionOffset = 0n) {
472513
const maxSliceLength = 100n; // Number of entries to scan per call
473514

0 commit comments

Comments
 (0)