From 4c8aeb0067bfe1d8a49a5358edc19208ddbacf90 Mon Sep 17 00:00:00 2001 From: Joe Clapis Date: Fri, 19 Apr 2024 16:11:51 -0400 Subject: [PATCH 1/4] Added withdrawal address check for msg.sender to _claimAndStake --- contracts/contract/rewards/RocketMerkleDistributorMainnet.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol b/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol index 88d2aefb..cf7de6a0 100644 --- a/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol +++ b/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol @@ -83,7 +83,8 @@ contract RocketMerkleDistributorMainnet is RocketBase, RocketRewardsRelayInterfa require(msg.sender == rplWithdrawalAddress, "Can only claim from RPL withdrawal address"); } else { // Otherwise, must be called from node address or withdrawal address - require(msg.sender == _nodeAddress || msg.sender == withdrawalAddress, "Can only claim from node address"); + address senderWithdrawalAddress = rocketStorage.getNodeWithdrawalAddress(msg.sender); + require(msg.sender == _nodeAddress || msg.sender == withdrawalAddress || senderWithdrawalAddress == _nodeAddress, "Can only claim from node address"); } } From ed522c900dfaa40eaa99176ed452d2b00b8e711f Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Sun, 21 Apr 2024 11:19:23 -0300 Subject: [PATCH 2/4] Adjusting tests for the proposed rules. --- test/rewards/rewards-tests.js | 195 +++++++++++++++---------- test/rewards/scenario-claim-rewards.js | 159 ++++++++++---------- 2 files changed, 192 insertions(+), 162 deletions(-) diff --git a/test/rewards/rewards-tests.js b/test/rewards/rewards-tests.js index 3cfbc18e..52e15194 100644 --- a/test/rewards/rewards-tests.js +++ b/test/rewards/rewards-tests.js @@ -6,8 +6,10 @@ import { registerNode, setNodeTrusted, setNodeWithdrawalAddress, + setNodeRPLWithdrawalAddress, nodeStakeRPL, getNodeEffectiveRPLStake, + nodeStakeRPLFor, } from '../_helpers/node' import { RocketDAONodeTrustedSettingsMinipool, @@ -31,7 +33,7 @@ import { createMinipool, stakeMinipool } from '../_helpers/minipool' import { userDeposit } from '../_helpers/deposit' import { setDAONodeTrustedBootstrapSetting } from '../dao/scenario-dao-node-trusted-bootstrap'; import { executeRewards, submitRewards } from './scenario-submit-rewards'; -import { claimRewards } from './scenario-claim-rewards'; +import { claimRewards, claimRewardsUsingClaimerAddress } from './scenario-claim-rewards'; import { claimAndStakeRewards } from './scenario-claim-and-stake-rewards'; import { parseRewardsMap } from '../_utils/merkle-tree'; import { daoNodeTrustedExecute, daoNodeTrustedPropose, daoNodeTrustedVote } from '../dao/scenario-dao-node-trusted'; @@ -58,6 +60,8 @@ export default function() { unregisteredNodeTrusted1, unregisteredNodeTrusted2, node1WithdrawalAddress, + node2WithdrawalAddress, + node2RPLWithdrawalAddress, random ] = accounts; @@ -233,7 +237,7 @@ export default function() { /*** Regular Nodes *************************/ - it(printTitle('node', 'can claim RPL and ETH'), async () => { + it.only(printTitle('node', 'can claim RPL and ETH'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -253,7 +257,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -285,7 +289,7 @@ export default function() { await submitRewards(0, rewards, '0'.ether, '2'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(registeredNode1, [0], [rewards], { + await claimRewards(node1WithdrawalAddress, [0], [rewards], { from: registeredNode1, }); await claimRewards(registeredNode2, [0], [rewards], { @@ -303,12 +307,75 @@ export default function() { await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(registeredNode1, [1], [rewards], { + await claimRewards(node1WithdrawalAddress, [1], [rewards], { from: registeredNode1, }); await claimRewards(registeredNode2, [1], [rewards], { from: registeredNode2, }); + + // Set node 2 withdrawal addresses + await setNodeWithdrawalAddress(registeredNode2, node2WithdrawalAddress, {from: registeredNode2}); + await setNodeRPLWithdrawalAddress(registeredNode2, node2RPLWithdrawalAddress, {from: node2WithdrawalAddress}); + + // Submit rewards snapshot + const rewards2 = [ + { + address: node1WithdrawalAddress, + network: 0, + trustedNodeRPL: '0'.ether, + nodeRPL: '1'.ether, + nodeETH: '0'.ether + }, + { + address: node2WithdrawalAddress, + network: 0, + trustedNodeRPL: '0'.ether, + nodeRPL: '0'.ether, + nodeETH: '1'.ether + }, + { + address: node2RPLWithdrawalAddress, + network: 0, + trustedNodeRPL: '0'.ether, + nodeRPL: '2'.ether, + nodeETH: '0'.ether + }, + { + address: registeredNodeTrusted1, + network: 0, + trustedNodeRPL: '1'.ether, + nodeRPL: '2'.ether, + nodeETH: '0'.ether + }, + ] + + // Do a third claim interval, after the changes + await submitRewards(2, rewards2, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); + await submitRewards(2, rewards2, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); + + // Claim RPL + await claimRewards(node1WithdrawalAddress, [2], [rewards2], { + from: node1WithdrawalAddress, + }); + // Can't claim ETH rewards from RPL withdrawal address + await shouldRevert(claimRewards(node2WithdrawalAddress, [2], [rewards2], { + from: node2RPLWithdrawalAddress, + })); + // Can't claim RPL rewards from ETH withdrawal address + await shouldRevert(claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { + from: node2WithdrawalAddress, + })) + // Can't claim RPL rewards from node address + await shouldRevert(claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { + from: registeredNode2, + })) + await claimRewards(node2WithdrawalAddress, [2], [rewards2], { + from: node2WithdrawalAddress, + }); + await claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { + from: node2RPLWithdrawalAddress, + }); }); @@ -329,7 +396,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -340,7 +407,7 @@ export default function() { await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(registeredNode1, [0], [rewards], { + await claimRewards(node1WithdrawalAddress, [0], [rewards], { from: node1WithdrawalAddress, }); }); @@ -359,7 +426,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -372,7 +439,7 @@ export default function() { await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); let treeData = parseRewardsMap(rewards); - let proof = treeData.proof.claims[web3.utils.toChecksumAddress(registeredNode1)]; + let proof = treeData.proof.claims[web3.utils.toChecksumAddress(node1WithdrawalAddress)]; let amountsRPL = [proof.amountRPL]; let amountsETH = [proof.amountETH]; let proofs = [proof.proof]; @@ -397,7 +464,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -414,25 +481,25 @@ export default function() { await submitRewards(2, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(registeredNode1, [0, 1], [rewards, rewards], { - from: registeredNode1, + await claimRewards(node1WithdrawalAddress, [0, 1], [rewards, rewards], { + from: node1WithdrawalAddress, }); - await shouldRevert(claimRewards(registeredNode1, [0], [rewards], { - from: registeredNode1, + await shouldRevert(claimRewards(node1WithdrawalAddress, [0], [rewards], { + from: node1WithdrawalAddress, }), 'Was able to claim again', 'Already claimed'); - await shouldRevert(claimRewards(registeredNode1, [1], [rewards], { - from: registeredNode1, + await shouldRevert(claimRewards(node1WithdrawalAddress, [1], [rewards], { + from: node1WithdrawalAddress, }), 'Was able to claim again', 'Already claimed'); - await shouldRevert(claimRewards(registeredNode1, [0, 1], [rewards, rewards], { - from: registeredNode1, + await shouldRevert(claimRewards(node1WithdrawalAddress, [0, 1], [rewards, rewards], { + from: node1WithdrawalAddress, }), 'Was able to claim again', 'Already claimed'); - await shouldRevert(claimRewards(registeredNode1, [0, 2], [rewards, rewards], { - from: registeredNode1, + await shouldRevert(claimRewards(node1WithdrawalAddress, [0, 2], [rewards, rewards], { + from: node1WithdrawalAddress, }), 'Was able to claim again', 'Already claimed'); }); - it(printTitle('node', 'can claim mulitiple periods in a single tx'), async () => { + it.only(printTitle('node', 'can claim mulitiple periods in a single tx'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -445,7 +512,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -469,11 +536,11 @@ export default function() { await submitRewards(2, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(registeredNode1, [0], [rewards], { - from: registeredNode1, + await claimRewards(node1WithdrawalAddress, [0], [rewards], { + from: node1WithdrawalAddress, }); - await claimRewards(registeredNode1, [1, 2], [rewards, rewards], { - from: registeredNode1, + await claimRewards(node1WithdrawalAddress, [1, 2], [rewards, rewards], { + from: node1WithdrawalAddress, }); await claimRewards(registeredNode2, [0, 1, 2], [rewards, rewards, rewards], { from: registeredNode2, @@ -481,7 +548,7 @@ export default function() { }); - it(printTitle('node', 'can claim RPL and stake'), async () => { + it.only(printTitle('node', 'can only claim and stake if no withdrawal address set'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -494,7 +561,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -512,9 +579,9 @@ export default function() { await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimAndStakeRewards(registeredNode1, [0], [rewards], '1'.ether, { - from: registeredNode1, - }); + await shouldRevert(claimAndStakeRewards(node1WithdrawalAddress, [0], [rewards], '1'.ether, { + from: node1WithdrawalAddress, + })); await claimAndStakeRewards(registeredNode2, [0], [rewards], '2'.ether, { from: registeredNode2, }); @@ -524,16 +591,16 @@ export default function() { await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimAndStakeRewards(registeredNode1, [1], [rewards], '0.5'.ether, { - from: registeredNode1, - }); + await shouldRevert(claimAndStakeRewards(node1WithdrawalAddress, [1], [rewards], '0.5'.ether, { + from: node1WithdrawalAddress, + })); await claimAndStakeRewards(registeredNode2, [1], [rewards], '1'.ether, { from: registeredNode2, }); }); - it(printTitle('node', 'can not stake amount greater than claim'), async () => { + it.only(printTitle('node', 'can not stake amount greater than claim'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -546,7 +613,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -557,48 +624,16 @@ export default function() { await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await shouldRevert(claimAndStakeRewards(registeredNode1, [0], [rewards], '2'.ether, { + await shouldRevert(claimAndStakeRewards(node1WithdrawalAddress, [0], [rewards], '2'.ether, { from: registeredNode1, }), 'Was able to stake amount greater than reward', 'Invalid stake amount'); }); - it(printTitle('node', 'can claim RPL and stake multiple snapshots'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: registeredNode1, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - } - ] - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimAndStakeRewards(registeredNode1, [0, 1], [rewards, rewards], '2'.ether, { - from: registeredNode1, - }); - }); - - /*** Random *************************/ - it(printTitle('random', 'can execute reward period if consensus is reached'), async () => { + it.only(printTitle('random', 'can execute reward period if consensus is reached'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -615,7 +650,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -633,7 +668,7 @@ export default function() { await executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}); }); - it(printTitle('random', 'cant execute reward period twice'), async () => { + it.only(printTitle('random', 'cant execute reward period twice'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -650,7 +685,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -669,7 +704,7 @@ export default function() { await shouldRevert(executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}), 'Already executed'); }); - it(printTitle('random', 'can submit past consensus'), async () => { + it.only(printTitle('random', 'can submit past consensus'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -685,7 +720,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -707,7 +742,7 @@ export default function() { /*** Misc *************************/ - it(printTitle('misc', 'claim bitmap is correct'), async () => { + it.only(printTitle('misc', 'claim bitmap is correct'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -720,7 +755,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: registeredNode1, + address: node1WithdrawalAddress, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -737,7 +772,7 @@ export default function() { // Some arbitrary intervals to claim let claimIntervals = [ 0, 4, 6, 9 ]; - await claimRewards(registeredNode1, claimIntervals, Array(claimIntervals.length).fill(rewards), { + await claimRewards(node1WithdrawalAddress, claimIntervals, Array(claimIntervals.length).fill(rewards), { from: registeredNode1, }); @@ -745,7 +780,7 @@ export default function() { const rocketStorage = await RocketStorage.deployed(); const key = web3.utils.soliditySha3( {type: 'string', value: 'rewards.interval.claimed'}, - {type: 'address', value: registeredNode1}, + {type: 'address', value: node1WithdrawalAddress}, {type: 'uint256', value: 0} ) const bitmap = (await rocketStorage.getUint.call(key)).toNumber(); @@ -759,7 +794,7 @@ export default function() { // Confirm second claim fails for each interval for (let i = 0; i < claimIntervals.length; i++) { - await shouldRevert(claimRewards(registeredNode1, [claimIntervals[i]], [rewards], { + await shouldRevert(claimRewards(node1WithdrawalAddress, [claimIntervals[i]], [rewards], { from: registeredNode1, }), 'Was able to claim again', 'Already claimed'); } diff --git a/test/rewards/scenario-claim-rewards.js b/test/rewards/scenario-claim-rewards.js index f588be41..b2e69ab1 100644 --- a/test/rewards/scenario-claim-rewards.js +++ b/test/rewards/scenario-claim-rewards.js @@ -1,86 +1,81 @@ import { - RocketMerkleDistributorMainnet, - RocketNodeManager, - RocketRewardsPool, - RocketStorage, RocketTokenRPL -} from '../_utils/artifacts'; -import { parseRewardsMap } from '../_utils/merkle-tree'; -import { assertBN } from '../_helpers/bn'; - - -// Submit network prices -export async function claimRewards(nodeAddress, indices, rewards, txOptions) { - - // Load contracts - const [ - rocketRewardsPool, - rocketNodeManager, - rocketMerkleDistributorMainnet, - rocketStorage, - rocketTokenRPL, - ] = await Promise.all([ - RocketRewardsPool.deployed(), - RocketNodeManager.deployed(), - RocketMerkleDistributorMainnet.deployed(), - RocketStorage.deployed(), - RocketTokenRPL.deployed(), - ]); - - // Get node withdrawal address - let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress.call(nodeAddress); - - // Get balances - function getBalances() { - return Promise.all([ - rocketRewardsPool.getClaimIntervalTimeStart(), - rocketTokenRPL.balanceOf.call(nodeWithdrawalAddress), - web3.eth.getBalance(nodeWithdrawalAddress) - ]).then( - ([claimIntervalTimeStart, nodeRpl, nodeEth]) => - ({claimIntervalTimeStart, nodeRpl, nodeEth: web3.utils.toBN(nodeEth)}) - ); + RocketMerkleDistributorMainnet, + RocketNodeManager, + RocketRewardsPool, + RocketStorage, + RocketTokenRPL, +} from "../_utils/artifacts"; +import { parseRewardsMap } from "../_utils/merkle-tree"; +import { assertBN } from "../_helpers/bn"; + +export async function claimRewards(claimerAddress, indices, rewards, txOptions) { + // Load contracts + const [rocketRewardsPool, rocketMerkleDistributorMainnet, rocketStorage, rocketTokenRPL] = await Promise.all([ + RocketRewardsPool.deployed(), + RocketMerkleDistributorMainnet.deployed(), + RocketStorage.deployed(), + RocketTokenRPL.deployed(), + ]); + + // Get balances + function getBalances() { + return Promise.all([ + rocketRewardsPool.getClaimIntervalTimeStart(), + rocketTokenRPL.balanceOf.call(claimerAddress), + web3.eth.getBalance(claimerAddress), + ]).then(([claimIntervalTimeStart, nodeRpl, nodeEth]) => ({ + claimIntervalTimeStart, + nodeRpl, + nodeEth: web3.utils.toBN(nodeEth), + })); + } + + let [balances1] = await Promise.all([getBalances()]); + + // Construct claim arguments + let amountsRPL = []; + let amountsETH = []; + let proofs = []; + let totalAmountRPL = "0".BN; + let totalAmountETH = "0".BN; + + for (let i = 0; i < indices.length; i++) { + let treeData = parseRewardsMap(rewards[i]); + + let proof = treeData.proof.claims[web3.utils.toChecksumAddress(claimerAddress)]; + + if (!proof) { + throw new Error("No proof in merkle tree for " + claimerAddress); } - let [balances1] = await Promise.all([ - getBalances(), - ]); - - // Construct claim arguments - let claimer = nodeAddress; - let amountsRPL = []; - let amountsETH = []; - let proofs = []; - let totalAmountRPL = '0'.BN; - let totalAmountETH = '0'.BN; - - for (let i = 0; i < indices.length; i++) { - let treeData = parseRewardsMap(rewards[i]); - - let proof = treeData.proof.claims[web3.utils.toChecksumAddress(claimer)]; - - if (!proof) { - throw new Error('No proof in merkle tree for ' + claimer) - } - - amountsRPL.push(proof.amountRPL); - amountsETH.push(proof.amountETH); - proofs.push(proof.proof); - - totalAmountRPL = totalAmountRPL.add(proof.amountRPL.BN); - totalAmountETH = totalAmountETH.add(proof.amountETH.BN); - } - - const tx = await rocketMerkleDistributorMainnet.claim(nodeAddress, indices, amountsRPL, amountsETH, proofs, txOptions); - let gasUsed = '0'.BN; - - if(nodeWithdrawalAddress.toLowerCase() === txOptions.from.toLowerCase()) { - gasUsed = tx.receipt.gasUsed.BN.mul(tx.receipt.effectiveGasPrice.BN); - } - - let [balances2] = await Promise.all([ - getBalances(), - ]); - - assertBN.equal(balances2.nodeRpl.sub(balances1.nodeRpl), totalAmountRPL, 'Incorrect updated node RPL balance'); - assertBN.equal(balances2.nodeEth.sub(balances1.nodeEth).add(gasUsed), totalAmountETH, 'Incorrect updated node ETH balance'); + amountsRPL.push(proof.amountRPL); + amountsETH.push(proof.amountETH); + proofs.push(proof.proof); + + totalAmountRPL = totalAmountRPL.add(proof.amountRPL.BN); + totalAmountETH = totalAmountETH.add(proof.amountETH.BN); + } + + const tx = await rocketMerkleDistributorMainnet.claim( + claimerAddress, + indices, + amountsRPL, + amountsETH, + proofs, + txOptions + ); + let gasUsed = "0".BN; + + if (claimerAddress.toLowerCase() === txOptions.from.toLowerCase()) { + gasUsed = tx.receipt.gasUsed.BN.mul(tx.receipt.effectiveGasPrice.BN); + } + + let [balances2] = await Promise.all([getBalances()]); + + assertBN.equal(balances2.nodeRpl.sub(balances1.nodeRpl), totalAmountRPL, "Incorrect updated node RPL balance"); + assertBN.equal( + balances2.nodeEth.sub(balances1.nodeEth).add(gasUsed), + totalAmountETH, + "Incorrect updated node ETH balance" + ); } From 73a02437b3f3aabec27258d9448a2389ef524933 Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Sun, 21 Apr 2024 15:46:38 -0300 Subject: [PATCH 3/4] Adjusting tests to separate RPL and ETH rewards when RPLWithdrawal is set. --- test/rewards/rewards-tests.js | 26 ++-- test/rewards/scenario-claim-rewards.js | 159 +++++++++++++------------ 2 files changed, 95 insertions(+), 90 deletions(-) diff --git a/test/rewards/rewards-tests.js b/test/rewards/rewards-tests.js index 52e15194..642c5654 100644 --- a/test/rewards/rewards-tests.js +++ b/test/rewards/rewards-tests.js @@ -237,7 +237,7 @@ export default function() { /*** Regular Nodes *************************/ - it.only(printTitle('node', 'can claim RPL and ETH'), async () => { + it(printTitle('node', 'can claim RPL and ETH'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -257,7 +257,7 @@ export default function() { // Submit rewards snapshot const rewards = [ { - address: node1WithdrawalAddress, + address: registeredNode1, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -289,7 +289,7 @@ export default function() { await submitRewards(0, rewards, '0'.ether, '2'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(node1WithdrawalAddress, [0], [rewards], { + await claimRewards(registeredNode1, [0], [rewards], { from: registeredNode1, }); await claimRewards(registeredNode2, [0], [rewards], { @@ -307,7 +307,7 @@ export default function() { await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(node1WithdrawalAddress, [1], [rewards], { + await claimRewards(registeredNode1, [1], [rewards], { from: registeredNode1, }); await claimRewards(registeredNode2, [1], [rewards], { @@ -321,7 +321,7 @@ export default function() { // Submit rewards snapshot const rewards2 = [ { - address: node1WithdrawalAddress, + address: registeredNode1, network: 0, trustedNodeRPL: '0'.ether, nodeRPL: '1'.ether, @@ -355,7 +355,7 @@ export default function() { await submitRewards(2, rewards2, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); // Claim RPL - await claimRewards(node1WithdrawalAddress, [2], [rewards2], { + await claimRewards(registeredNode1, [2], [rewards2], { from: node1WithdrawalAddress, }); // Can't claim ETH rewards from RPL withdrawal address @@ -499,7 +499,7 @@ export default function() { }); - it.only(printTitle('node', 'can claim mulitiple periods in a single tx'), async () => { + it(printTitle('node', 'can claim mulitiple periods in a single tx'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -548,7 +548,7 @@ export default function() { }); - it.only(printTitle('node', 'can only claim and stake if no withdrawal address set'), async () => { + it(printTitle('node', 'can only claim and stake if no withdrawal address set'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -600,7 +600,7 @@ export default function() { }); - it.only(printTitle('node', 'can not stake amount greater than claim'), async () => { + it(printTitle('node', 'can not stake amount greater than claim'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -633,7 +633,7 @@ export default function() { /*** Random *************************/ - it.only(printTitle('random', 'can execute reward period if consensus is reached'), async () => { + it(printTitle('random', 'can execute reward period if consensus is reached'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -668,7 +668,7 @@ export default function() { await executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}); }); - it.only(printTitle('random', 'cant execute reward period twice'), async () => { + it(printTitle('random', 'cant execute reward period twice'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -704,7 +704,7 @@ export default function() { await shouldRevert(executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}), 'Already executed'); }); - it.only(printTitle('random', 'can submit past consensus'), async () => { + it(printTitle('random', 'can submit past consensus'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); @@ -742,7 +742,7 @@ export default function() { /*** Misc *************************/ - it.only(printTitle('misc', 'claim bitmap is correct'), async () => { + it(printTitle('misc', 'claim bitmap is correct'), async () => { // Initialize RPL inflation & claims contract let rplInflationStartTime = await rplInflationSetup(); await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); diff --git a/test/rewards/scenario-claim-rewards.js b/test/rewards/scenario-claim-rewards.js index b2e69ab1..f588be41 100644 --- a/test/rewards/scenario-claim-rewards.js +++ b/test/rewards/scenario-claim-rewards.js @@ -1,81 +1,86 @@ import { - RocketMerkleDistributorMainnet, - RocketNodeManager, - RocketRewardsPool, - RocketStorage, - RocketTokenRPL, -} from "../_utils/artifacts"; -import { parseRewardsMap } from "../_utils/merkle-tree"; -import { assertBN } from "../_helpers/bn"; - -export async function claimRewards(claimerAddress, indices, rewards, txOptions) { - // Load contracts - const [rocketRewardsPool, rocketMerkleDistributorMainnet, rocketStorage, rocketTokenRPL] = await Promise.all([ - RocketRewardsPool.deployed(), - RocketMerkleDistributorMainnet.deployed(), - RocketStorage.deployed(), - RocketTokenRPL.deployed(), - ]); - - // Get balances - function getBalances() { - return Promise.all([ - rocketRewardsPool.getClaimIntervalTimeStart(), - rocketTokenRPL.balanceOf.call(claimerAddress), - web3.eth.getBalance(claimerAddress), - ]).then(([claimIntervalTimeStart, nodeRpl, nodeEth]) => ({ - claimIntervalTimeStart, - nodeRpl, - nodeEth: web3.utils.toBN(nodeEth), - })); - } - - let [balances1] = await Promise.all([getBalances()]); - - // Construct claim arguments - let amountsRPL = []; - let amountsETH = []; - let proofs = []; - let totalAmountRPL = "0".BN; - let totalAmountETH = "0".BN; - - for (let i = 0; i < indices.length; i++) { - let treeData = parseRewardsMap(rewards[i]); - - let proof = treeData.proof.claims[web3.utils.toChecksumAddress(claimerAddress)]; - - if (!proof) { - throw new Error("No proof in merkle tree for " + claimerAddress); + RocketMerkleDistributorMainnet, + RocketNodeManager, + RocketRewardsPool, + RocketStorage, RocketTokenRPL +} from '../_utils/artifacts'; +import { parseRewardsMap } from '../_utils/merkle-tree'; +import { assertBN } from '../_helpers/bn'; + + +// Submit network prices +export async function claimRewards(nodeAddress, indices, rewards, txOptions) { + + // Load contracts + const [ + rocketRewardsPool, + rocketNodeManager, + rocketMerkleDistributorMainnet, + rocketStorage, + rocketTokenRPL, + ] = await Promise.all([ + RocketRewardsPool.deployed(), + RocketNodeManager.deployed(), + RocketMerkleDistributorMainnet.deployed(), + RocketStorage.deployed(), + RocketTokenRPL.deployed(), + ]); + + // Get node withdrawal address + let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress.call(nodeAddress); + + // Get balances + function getBalances() { + return Promise.all([ + rocketRewardsPool.getClaimIntervalTimeStart(), + rocketTokenRPL.balanceOf.call(nodeWithdrawalAddress), + web3.eth.getBalance(nodeWithdrawalAddress) + ]).then( + ([claimIntervalTimeStart, nodeRpl, nodeEth]) => + ({claimIntervalTimeStart, nodeRpl, nodeEth: web3.utils.toBN(nodeEth)}) + ); } - amountsRPL.push(proof.amountRPL); - amountsETH.push(proof.amountETH); - proofs.push(proof.proof); - - totalAmountRPL = totalAmountRPL.add(proof.amountRPL.BN); - totalAmountETH = totalAmountETH.add(proof.amountETH.BN); - } - - const tx = await rocketMerkleDistributorMainnet.claim( - claimerAddress, - indices, - amountsRPL, - amountsETH, - proofs, - txOptions - ); - let gasUsed = "0".BN; - - if (claimerAddress.toLowerCase() === txOptions.from.toLowerCase()) { - gasUsed = tx.receipt.gasUsed.BN.mul(tx.receipt.effectiveGasPrice.BN); - } - - let [balances2] = await Promise.all([getBalances()]); - - assertBN.equal(balances2.nodeRpl.sub(balances1.nodeRpl), totalAmountRPL, "Incorrect updated node RPL balance"); - assertBN.equal( - balances2.nodeEth.sub(balances1.nodeEth).add(gasUsed), - totalAmountETH, - "Incorrect updated node ETH balance" - ); + let [balances1] = await Promise.all([ + getBalances(), + ]); + + // Construct claim arguments + let claimer = nodeAddress; + let amountsRPL = []; + let amountsETH = []; + let proofs = []; + let totalAmountRPL = '0'.BN; + let totalAmountETH = '0'.BN; + + for (let i = 0; i < indices.length; i++) { + let treeData = parseRewardsMap(rewards[i]); + + let proof = treeData.proof.claims[web3.utils.toChecksumAddress(claimer)]; + + if (!proof) { + throw new Error('No proof in merkle tree for ' + claimer) + } + + amountsRPL.push(proof.amountRPL); + amountsETH.push(proof.amountETH); + proofs.push(proof.proof); + + totalAmountRPL = totalAmountRPL.add(proof.amountRPL.BN); + totalAmountETH = totalAmountETH.add(proof.amountETH.BN); + } + + const tx = await rocketMerkleDistributorMainnet.claim(nodeAddress, indices, amountsRPL, amountsETH, proofs, txOptions); + let gasUsed = '0'.BN; + + if(nodeWithdrawalAddress.toLowerCase() === txOptions.from.toLowerCase()) { + gasUsed = tx.receipt.gasUsed.BN.mul(tx.receipt.effectiveGasPrice.BN); + } + + let [balances2] = await Promise.all([ + getBalances(), + ]); + + assertBN.equal(balances2.nodeRpl.sub(balances1.nodeRpl), totalAmountRPL, 'Incorrect updated node RPL balance'); + assertBN.equal(balances2.nodeEth.sub(balances1.nodeEth).add(gasUsed), totalAmountETH, 'Incorrect updated node ETH balance'); } From 2e71f26ff1e0cd8bcb14bab1e9d1abacbb9f61ac Mon Sep 17 00:00:00 2001 From: Fornax <23104993+fornax2@users.noreply.github.com> Date: Tue, 23 Apr 2024 21:00:24 -0300 Subject: [PATCH 4/4] Tests changing the rewards tree splitting RPL/ETH awardees --- .../RocketMerkleDistributorMainnet.sol | 3 +- test/rewards/rewards-tests.js | 1650 +++++++++-------- test/rewards/scenario-claim-rewards.js | 8 +- 3 files changed, 879 insertions(+), 782 deletions(-) diff --git a/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol b/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol index cf7de6a0..88d2aefb 100644 --- a/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol +++ b/contracts/contract/rewards/RocketMerkleDistributorMainnet.sol @@ -83,8 +83,7 @@ contract RocketMerkleDistributorMainnet is RocketBase, RocketRewardsRelayInterfa require(msg.sender == rplWithdrawalAddress, "Can only claim from RPL withdrawal address"); } else { // Otherwise, must be called from node address or withdrawal address - address senderWithdrawalAddress = rocketStorage.getNodeWithdrawalAddress(msg.sender); - require(msg.sender == _nodeAddress || msg.sender == withdrawalAddress || senderWithdrawalAddress == _nodeAddress, "Can only claim from node address"); + require(msg.sender == _nodeAddress || msg.sender == withdrawalAddress, "Can only claim from node address"); } } diff --git a/test/rewards/rewards-tests.js b/test/rewards/rewards-tests.js index 642c5654..9f704245 100644 --- a/test/rewards/rewards-tests.js +++ b/test/rewards/rewards-tests.js @@ -1,803 +1,899 @@ -import { getCurrentTime, increaseTime } from '../_utils/evm' -import { printTitle } from '../_utils/formatting'; -import { shouldRevert } from '../_utils/testing'; -import { submitPrices } from '../_helpers/network'; +import { getCurrentTime, increaseTime } from "../_utils/evm"; +import { printTitle } from "../_utils/formatting"; +import { shouldRevert } from "../_utils/testing"; +import { submitPrices } from "../_helpers/network"; import { - registerNode, - setNodeTrusted, - setNodeWithdrawalAddress, - setNodeRPLWithdrawalAddress, - nodeStakeRPL, - getNodeEffectiveRPLStake, - nodeStakeRPLFor, -} from '../_helpers/node' + registerNode, + setNodeTrusted, + setNodeWithdrawalAddress, + setNodeRPLWithdrawalAddress, + nodeStakeRPL, + getNodeEffectiveRPLStake, + nodeStakeRPLFor, +} from "../_helpers/node"; import { - RocketDAONodeTrustedSettingsMinipool, - RocketDAOProtocolSettingsNode, - RocketMerkleDistributorMainnet, - RocketSmoothingPool, - RocketStorage, -} from '../_utils/artifacts'; + RocketDAONodeTrustedSettingsMinipool, + RocketDAOProtocolSettingsNode, + RocketMerkleDistributorMainnet, + RocketSmoothingPool, + RocketStorage, +} from "../_utils/artifacts"; import { - setDAONetworkBootstrapRewardsClaimers, - setDAOProtocolBootstrapSetting, - setRewardsClaimIntervalTime, - setRPLInflationStartTime, -} from '../dao/scenario-dao-protocol-bootstrap'; -import { mintRPL } from '../_helpers/tokens'; -import { setRPLInflationIntervalRate } from '../dao/scenario-dao-protocol-bootstrap'; + setDAONetworkBootstrapRewardsClaimers, + setDAOProtocolBootstrapSetting, + setRewardsClaimIntervalTime, + setRPLInflationStartTime, +} from "../dao/scenario-dao-protocol-bootstrap"; +import { mintRPL } from "../_helpers/tokens"; +import { setRPLInflationIntervalRate } from "../dao/scenario-dao-protocol-bootstrap"; // Contracts -import { RocketRewardsPool } from '../_utils/artifacts'; -import { createMinipool, stakeMinipool } from '../_helpers/minipool' -import { userDeposit } from '../_helpers/deposit' -import { setDAONodeTrustedBootstrapSetting } from '../dao/scenario-dao-node-trusted-bootstrap'; -import { executeRewards, submitRewards } from './scenario-submit-rewards'; -import { claimRewards, claimRewardsUsingClaimerAddress } from './scenario-claim-rewards'; -import { claimAndStakeRewards } from './scenario-claim-and-stake-rewards'; -import { parseRewardsMap } from '../_utils/merkle-tree'; -import { daoNodeTrustedExecute, daoNodeTrustedPropose, daoNodeTrustedVote } from '../dao/scenario-dao-node-trusted'; -import { getDAOProposalStartTime } from '../dao/scenario-dao-proposal'; -import { assertBN } from '../_helpers/bn'; -import { upgradeOneDotThree } from '../_utils/upgrade'; - - -export default function() { - contract('RocketRewardsPool', async (accounts) => { - - // One day in seconds - const ONE_DAY = 24 * 60 * 60; - - - // Accounts - const [ - owner, - userOne, - registeredNode1, - registeredNode2, - registeredNodeTrusted1, - registeredNodeTrusted2, - unregisteredNodeTrusted1, - unregisteredNodeTrusted2, - node1WithdrawalAddress, - node2WithdrawalAddress, - node2RPLWithdrawalAddress, - random - ] = accounts; - - - // The testing config - const claimIntervalTime = ONE_DAY * 28; - let scrubPeriod = (60 * 60 * 24); // 24 hours - - // Set some RPL inflation scenes - let rplInflationSetup = async function() { - // Current time - let currentTime = await getCurrentTime(web3); - // Starting block for when inflation will begin - let timeStart = currentTime + ONE_DAY; - // Yearly inflation target - let yearlyInflationTarget = 0.05; - - // Set the daily inflation start time - await setRPLInflationStartTime(timeStart, { from: owner }); - // Set the daily inflation rate - await setRPLInflationIntervalRate(yearlyInflationTarget, { from: owner }); - - // claimIntervalTime must be greater than rewardIntervalTime for tests to properly function - assert.isAbove(claimIntervalTime, ONE_DAY, 'Tests will not function correctly unless claimIntervalTime is greater than inflation period (1 day)') - - // Return the starting time for inflation when it will be available - return timeStart + ONE_DAY; - } - - // Set a rewards claiming contract - let rewardsContractSetup = async function(_trustedNodePerc, _protocolPerc, _nodePerc, _claimAmountPerc) { - // Set the amount this contract can claim - await setDAONetworkBootstrapRewardsClaimers(_trustedNodePerc, _protocolPerc, _nodePerc, { from: owner }); - // Set the claim interval blocks - await setRewardsClaimIntervalTime(claimIntervalTime, { from: owner }); - } - - async function kickTrustedNode(nodeAddress, voters) { - // Encode the calldata for the proposal - let proposalCalldata = web3.eth.abi.encodeFunctionCall( - {name: 'proposalKick', type: 'function', inputs: [{type: 'address', name: '_nodeAddress'}, {type: 'uint256', name: '_rplFine'}]}, - [nodeAddress, '0'] - ); - // Add the proposal - let proposalID = await daoNodeTrustedPropose(`Kick ${nodeAddress}`, proposalCalldata, { - from: registeredNodeTrusted1 - }); - // Current time - let timeCurrent = await getCurrentTime(web3); - // Now increase time until the proposal is 'active' and can be voted on - await increaseTime(web3, (await getDAOProposalStartTime(proposalID)-timeCurrent)+2); - // Now lets vote - for (const voter of voters) { - await daoNodeTrustedVote(proposalID, true, { from: voter }); - } - // Proposal has passed, lets execute it now - await daoNodeTrustedExecute(proposalID, { from: registeredNode1 }); - } - - - // Setup - before(async () => { - // Upgrade to Houston - await upgradeOneDotThree(); - - let slotTimestamp = '1600000000'; - - // Set settings - await setDAONodeTrustedBootstrapSetting(RocketDAONodeTrustedSettingsMinipool, 'minipool.scrub.period', scrubPeriod, {from: owner}); - - // Register nodes - await registerNode({from: registeredNode1}); - await registerNode({from: registeredNode2}); - await registerNode({from: registeredNodeTrusted1}); - await registerNode({from: registeredNodeTrusted2}); - await registerNode({from: unregisteredNodeTrusted1}); - await registerNode({from: unregisteredNodeTrusted2}); - - // Set node 1 withdrawal address - await setNodeWithdrawalAddress(registeredNode1, node1WithdrawalAddress, {from: registeredNode1}); - - // Set nodes as trusted - await setNodeTrusted(registeredNodeTrusted1, 'saas_1', 'node@home.com', owner); - await setNodeTrusted(registeredNodeTrusted2, 'saas_2', 'node@home.com', owner); - - // Set max per-minipool stake to 100% and RPL price to 1 ether - const block = await web3.eth.getBlockNumber(); - await setDAOProtocolBootstrapSetting(RocketDAOProtocolSettingsNode, 'node.per.minipool.stake.maximum', '1'.ether, {from: owner}); - await submitPrices(block, slotTimestamp, '1'.ether, {from: registeredNodeTrusted1}); - await submitPrices(block, slotTimestamp, '1'.ether, {from: registeredNodeTrusted2}); - - // Mint and stake RPL - await mintRPL(owner, registeredNode1, '32'.ether); - await mintRPL(owner, registeredNode2, '32'.ether); - await nodeStakeRPL('32'.ether, {from: registeredNode1}); - await nodeStakeRPL('32'.ether, {from: registeredNode2}); - - // User deposits - await userDeposit({from: userOne, value: '48'.ether}); - - // Create minipools - let minipool1 = await createMinipool({from: registeredNode1, value: '16'.ether}); - let minipool2 = await createMinipool({from: registeredNode2, value: '16'.ether}); - let minipool3 = await createMinipool({from: registeredNode2, value: '16'.ether}); - - // Wait required scrub period - await increaseTime(web3, scrubPeriod + 1); - - // Stake minipools - await stakeMinipool(minipool1, {from: registeredNode1}); - await stakeMinipool(minipool2, {from: registeredNode2}); - await stakeMinipool(minipool3, {from: registeredNode2}); - - // Check node effective stakes - let node1EffectiveStake = await getNodeEffectiveRPLStake(registeredNode1); - let node2EffectiveStake = await getNodeEffectiveRPLStake(registeredNode2); - assertBN.equal(node1EffectiveStake, '16'.ether, 'Incorrect node 1 effective stake'); - assertBN.equal(node2EffectiveStake, '32'.ether, 'Incorrect node 2 effective stake'); - }); - - - /*** Setting Claimers *************************/ - - - it(printTitle('userOne', 'fails to set interval blocks for rewards claim period'), async () => { - // Set the rewards claims interval in seconds - await shouldRevert(setRewardsClaimIntervalTime(100, { - from: userOne, - }), 'Non owner set interval blocks for rewards claim period'); - }); - - - it(printTitle('guardian', 'succeeds setting interval blocks for rewards claim period'), async () => { - // Set the rewards claims interval in blocks - await setRewardsClaimIntervalTime(100, { - from: owner, - }); - }); - - - it(printTitle('userOne', 'fails to set contract claimer percentage for rewards'), async () => { - // Set the amount this contract can claim - await shouldRevert(setDAONetworkBootstrapRewardsClaimers('0.1'.ether, '0.5'.ether, '0.4'.ether, { - from: userOne, - }), 'Non owner set contract claimer percentage for rewards'); - }); - - - it(printTitle('guardian', 'set contract claimer percentage for rewards, then update it'), async () => { - // Set the amount this contract can claim - await setDAONetworkBootstrapRewardsClaimers('0.1'.ether, '0.1'.ether, '0.8'.ether, { - from: owner, - }); - }); - - - it(printTitle('guardian', 'fails to set contract claimer percentages to lower than 100% total'), async () => { - // Set the amount this contract can claim - await shouldRevert(setDAONetworkBootstrapRewardsClaimers('0.1'.ether, '0.1'.ether, '0.1'.ether, { - from: owner, - }), 'Percentages were updated', 'Total does not equal 100%'); - }); - - - it(printTitle('guardian', 'fails to set contract claimer percentages to greater than 100% total'), async () => { - // Set the amount this contract can claim - await shouldRevert(setDAONetworkBootstrapRewardsClaimers('0.4'.ether, '0.4'.ether, '0.4'.ether, { - from: owner, - }), 'Percentages were updated', 'Total does not equal 100%'); - }); - - - /*** Regular Nodes *************************/ - - - it(printTitle('node', 'can claim RPL and ETH'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Send ETH to rewards pool - const rocketSmoothingPool = await RocketSmoothingPool.deployed(); - await web3.eth.sendTransaction({ from: owner, to: rocketSmoothingPool.address, value: '20'.ether}); - - const rocketRewardsPool = await RocketRewardsPool.deployed(); - const pendingRewards = await rocketRewardsPool.getPendingETHRewards.call(); - - // Submit rewards snapshot - const rewards = [ - { - address: registeredNode1, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - { - address: registeredNode2, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '2'.ether, - nodeETH: '1'.ether - }, - { - address: registeredNodeTrusted1, - network: 0, - trustedNodeRPL: '1'.ether, - nodeRPL: '2'.ether, - nodeETH: '0'.ether - }, - { - address: userOne, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1.333'.ether, - nodeETH: '0.3'.ether - }, - ] - await submitRewards(0, rewards, '0'.ether, '2'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '2'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimRewards(registeredNode1, [0], [rewards], { - from: registeredNode1, - }); - await claimRewards(registeredNode2, [0], [rewards], { - from: registeredNode2, - }); - await claimRewards(registeredNodeTrusted1, [0], [rewards], { - from: registeredNodeTrusted1, - }); - await claimRewards(userOne, [0], [rewards], { - from: userOne, - }); - - // Do a second claim interval - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimRewards(registeredNode1, [1], [rewards], { - from: registeredNode1, - }); - await claimRewards(registeredNode2, [1], [rewards], { - from: registeredNode2, - }); - - // Set node 2 withdrawal addresses - await setNodeWithdrawalAddress(registeredNode2, node2WithdrawalAddress, {from: registeredNode2}); - await setNodeRPLWithdrawalAddress(registeredNode2, node2RPLWithdrawalAddress, {from: node2WithdrawalAddress}); - - // Submit rewards snapshot - const rewards2 = [ - { - address: registeredNode1, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - { - address: node2WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '0'.ether, - nodeETH: '1'.ether - }, - { - address: node2RPLWithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '2'.ether, - nodeETH: '0'.ether - }, - { - address: registeredNodeTrusted1, - network: 0, - trustedNodeRPL: '1'.ether, - nodeRPL: '2'.ether, - nodeETH: '0'.ether - }, - ] - - // Do a third claim interval, after the changes - await submitRewards(2, rewards2, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(2, rewards2, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimRewards(registeredNode1, [2], [rewards2], { - from: node1WithdrawalAddress, - }); - // Can't claim ETH rewards from RPL withdrawal address - await shouldRevert(claimRewards(node2WithdrawalAddress, [2], [rewards2], { - from: node2RPLWithdrawalAddress, - })); - // Can't claim RPL rewards from ETH withdrawal address - await shouldRevert(claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { - from: node2WithdrawalAddress, - })) - // Can't claim RPL rewards from node address - await shouldRevert(claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { - from: registeredNode2, - })) - await claimRewards(node2WithdrawalAddress, [2], [rewards2], { - from: node2WithdrawalAddress, - }); - await claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { - from: node2RPLWithdrawalAddress, - }); - }); - - - it(printTitle('node', 'can claim from withdrawal address'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Send ETH to rewards pool - const rocketSmoothingPool = await RocketSmoothingPool.deployed(); - await web3.eth.sendTransaction({ from: owner, to: rocketSmoothingPool.address, value: '20'.ether}); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - } - ] - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimRewards(node1WithdrawalAddress, [0], [rewards], { - from: node1WithdrawalAddress, - }); - }); - - - it(printTitle('node', 'can not claim with invalid proof'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] - - // Create 3 snapshots - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - let treeData = parseRewardsMap(rewards); - let proof = treeData.proof.claims[web3.utils.toChecksumAddress(node1WithdrawalAddress)]; - let amountsRPL = [proof.amountRPL]; - let amountsETH = [proof.amountETH]; - let proofs = [proof.proof]; - - let rocketMerkleDistributorMainnet = await RocketMerkleDistributorMainnet.deployed(); - - // Attempt to claim reward for registeredNode1 with registeredNode2 - await shouldRevert(rocketMerkleDistributorMainnet.claim(registeredNode2, [0], amountsRPL, amountsETH, proofs, {from: registeredNode2}), 'Was able to claim with invalid proof', 'Invalid proof'); - }); - - - it(printTitle('node', 'can not claim same interval twice'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] - - // Create 3 snapshots - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - await submitRewards(2, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(2, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimRewards(node1WithdrawalAddress, [0, 1], [rewards, rewards], { - from: node1WithdrawalAddress, - }); - await shouldRevert(claimRewards(node1WithdrawalAddress, [0], [rewards], { - from: node1WithdrawalAddress, - }), 'Was able to claim again', 'Already claimed'); - await shouldRevert(claimRewards(node1WithdrawalAddress, [1], [rewards], { - from: node1WithdrawalAddress, - }), 'Was able to claim again', 'Already claimed'); - await shouldRevert(claimRewards(node1WithdrawalAddress, [0, 1], [rewards, rewards], { - from: node1WithdrawalAddress, - }), 'Was able to claim again', 'Already claimed'); - await shouldRevert(claimRewards(node1WithdrawalAddress, [0, 2], [rewards, rewards], { - from: node1WithdrawalAddress, - }), 'Was able to claim again', 'Already claimed'); - }); - - - it(printTitle('node', 'can claim mulitiple periods in a single tx'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - { - address: registeredNode2, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '2'.ether, - nodeETH: '0'.ether - } - ] - - // Submit 2 snapshots - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - await submitRewards(2, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(2, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await claimRewards(node1WithdrawalAddress, [0], [rewards], { - from: node1WithdrawalAddress, - }); - await claimRewards(node1WithdrawalAddress, [1, 2], [rewards, rewards], { - from: node1WithdrawalAddress, - }); - await claimRewards(registeredNode2, [0, 1, 2], [rewards, rewards, rewards], { - from: registeredNode2, - }); - }); +import { RocketRewardsPool } from "../_utils/artifacts"; +import { createMinipool, stakeMinipool } from "../_helpers/minipool"; +import { userDeposit } from "../_helpers/deposit"; +import { setDAONodeTrustedBootstrapSetting } from "../dao/scenario-dao-node-trusted-bootstrap"; +import { executeRewards, submitRewards } from "./scenario-submit-rewards"; +import { claimRewards, claimRewardsUsingClaimerAddress } from "./scenario-claim-rewards"; +import { claimAndStakeRewards } from "./scenario-claim-and-stake-rewards"; +import { parseRewardsMap } from "../_utils/merkle-tree"; +import { daoNodeTrustedExecute, daoNodeTrustedPropose, daoNodeTrustedVote } from "../dao/scenario-dao-node-trusted"; +import { getDAOProposalStartTime } from "../dao/scenario-dao-proposal"; +import { assertBN } from "../_helpers/bn"; +import { upgradeOneDotThree } from "../_utils/upgrade"; + +export default function () { + contract.only("RocketRewardsPool", async (accounts) => { + // One day in seconds + const ONE_DAY = 24 * 60 * 60; + + // Accounts + const [ + owner, + userOne, + registeredNode1, + registeredNode2, + registeredNodeTrusted1, + registeredNodeTrusted2, + unregisteredNodeTrusted1, + unregisteredNodeTrusted2, + node1WithdrawalAddress, + node2WithdrawalAddress, + node2RPLWithdrawalAddress, + random, + ] = accounts; + + // The testing config + const claimIntervalTime = ONE_DAY * 28; + let scrubPeriod = 60 * 60 * 24; // 24 hours + + // Set some RPL inflation scenes + let rplInflationSetup = async function () { + // Current time + let currentTime = await getCurrentTime(web3); + // Starting block for when inflation will begin + let timeStart = currentTime + ONE_DAY; + // Yearly inflation target + let yearlyInflationTarget = 0.05; + + // Set the daily inflation start time + await setRPLInflationStartTime(timeStart, { from: owner }); + // Set the daily inflation rate + await setRPLInflationIntervalRate(yearlyInflationTarget, { from: owner }); + + // claimIntervalTime must be greater than rewardIntervalTime for tests to properly function + assert.isAbove( + claimIntervalTime, + ONE_DAY, + "Tests will not function correctly unless claimIntervalTime is greater than inflation period (1 day)" + ); + + // Return the starting time for inflation when it will be available + return timeStart + ONE_DAY; + }; + + // Set a rewards claiming contract + let rewardsContractSetup = async function (_trustedNodePerc, _protocolPerc, _nodePerc, _claimAmountPerc) { + // Set the amount this contract can claim + await setDAONetworkBootstrapRewardsClaimers(_trustedNodePerc, _protocolPerc, _nodePerc, { from: owner }); + // Set the claim interval blocks + await setRewardsClaimIntervalTime(claimIntervalTime, { from: owner }); + }; + + async function kickTrustedNode(nodeAddress, voters) { + // Encode the calldata for the proposal + let proposalCalldata = web3.eth.abi.encodeFunctionCall( + { + name: "proposalKick", + type: "function", + inputs: [ + { type: "address", name: "_nodeAddress" }, + { type: "uint256", name: "_rplFine" }, + ], + }, + [nodeAddress, "0"] + ); + // Add the proposal + let proposalID = await daoNodeTrustedPropose(`Kick ${nodeAddress}`, proposalCalldata, { + from: registeredNodeTrusted1, + }); + // Current time + let timeCurrent = await getCurrentTime(web3); + // Now increase time until the proposal is 'active' and can be voted on + await increaseTime(web3, (await getDAOProposalStartTime(proposalID)) - timeCurrent + 2); + // Now lets vote + for (const voter of voters) { + await daoNodeTrustedVote(proposalID, true, { from: voter }); + } + // Proposal has passed, lets execute it now + await daoNodeTrustedExecute(proposalID, { from: registeredNode1 }); + } + + // Setup + before(async () => { + // Upgrade to Houston + await upgradeOneDotThree(); + + let slotTimestamp = "1600000000"; + + // Set settings + await setDAONodeTrustedBootstrapSetting( + RocketDAONodeTrustedSettingsMinipool, + "minipool.scrub.period", + scrubPeriod, + { from: owner } + ); + + // Register nodes + await registerNode({ from: registeredNode1 }); + await registerNode({ from: registeredNode2 }); + await registerNode({ from: registeredNodeTrusted1 }); + await registerNode({ from: registeredNodeTrusted2 }); + await registerNode({ from: unregisteredNodeTrusted1 }); + await registerNode({ from: unregisteredNodeTrusted2 }); + + // Set node 1 withdrawal address + await setNodeWithdrawalAddress(registeredNode1, node1WithdrawalAddress, { from: registeredNode1 }); + + // Set nodes as trusted + await setNodeTrusted(registeredNodeTrusted1, "saas_1", "node@home.com", owner); + await setNodeTrusted(registeredNodeTrusted2, "saas_2", "node@home.com", owner); + + // Set max per-minipool stake to 100% and RPL price to 1 ether + const block = await web3.eth.getBlockNumber(); + await setDAOProtocolBootstrapSetting( + RocketDAOProtocolSettingsNode, + "node.per.minipool.stake.maximum", + "1".ether, + { from: owner } + ); + await submitPrices(block, slotTimestamp, "1".ether, { from: registeredNodeTrusted1 }); + await submitPrices(block, slotTimestamp, "1".ether, { from: registeredNodeTrusted2 }); + + // Mint and stake RPL + await mintRPL(owner, registeredNode1, "32".ether); + await mintRPL(owner, registeredNode2, "32".ether); + await nodeStakeRPL("32".ether, { from: registeredNode1 }); + await nodeStakeRPL("32".ether, { from: registeredNode2 }); + + // User deposits + await userDeposit({ from: userOne, value: "48".ether }); + + // Create minipools + let minipool1 = await createMinipool({ from: registeredNode1, value: "16".ether }); + let minipool2 = await createMinipool({ from: registeredNode2, value: "16".ether }); + let minipool3 = await createMinipool({ from: registeredNode2, value: "16".ether }); + + // Wait required scrub period + await increaseTime(web3, scrubPeriod + 1); + + // Stake minipools + await stakeMinipool(minipool1, { from: registeredNode1 }); + await stakeMinipool(minipool2, { from: registeredNode2 }); + await stakeMinipool(minipool3, { from: registeredNode2 }); + + // Check node effective stakes + let node1EffectiveStake = await getNodeEffectiveRPLStake(registeredNode1); + let node2EffectiveStake = await getNodeEffectiveRPLStake(registeredNode2); + assertBN.equal(node1EffectiveStake, "16".ether, "Incorrect node 1 effective stake"); + assertBN.equal(node2EffectiveStake, "32".ether, "Incorrect node 2 effective stake"); + }); + /*** Setting Claimers *************************/ - it(printTitle('node', 'can only claim and stake if no withdrawal address set'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - { - address: registeredNode2, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '2'.ether, - nodeETH: '0'.ether - } - ] - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await shouldRevert(claimAndStakeRewards(node1WithdrawalAddress, [0], [rewards], '1'.ether, { - from: node1WithdrawalAddress, - })); - await claimAndStakeRewards(registeredNode2, [0], [rewards], '2'.ether, { - from: registeredNode2, - }); - - // Do a second claim interval - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(1, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await shouldRevert(claimAndStakeRewards(node1WithdrawalAddress, [1], [rewards], '0.5'.ether, { - from: node1WithdrawalAddress, - })); - await claimAndStakeRewards(registeredNode2, [1], [rewards], '1'.ether, { - from: registeredNode2, - }); - }); - + it(printTitle("userOne", "fails to set interval blocks for rewards claim period"), async () => { + // Set the rewards claims interval in seconds + await shouldRevert( + setRewardsClaimIntervalTime(100, { + from: userOne, + }), + "Non owner set interval blocks for rewards claim period" + ); + }); - it(printTitle('node', 'can not stake amount greater than claim'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Claim RPL - await shouldRevert(claimAndStakeRewards(node1WithdrawalAddress, [0], [rewards], '2'.ether, { - from: registeredNode1, - }), 'Was able to stake amount greater than reward', 'Invalid stake amount'); - }); + it(printTitle("guardian", "succeeds setting interval blocks for rewards claim period"), async () => { + // Set the rewards claims interval in blocks + await setRewardsClaimIntervalTime(100, { + from: owner, + }); + }); + it(printTitle("userOne", "fails to set contract claimer percentage for rewards"), async () => { + // Set the amount this contract can claim + await shouldRevert( + setDAONetworkBootstrapRewardsClaimers("0.1".ether, "0.5".ether, "0.4".ether, { + from: userOne, + }), + "Non owner set contract claimer percentage for rewards" + ); + }); - /*** Random *************************/ + it(printTitle("guardian", "set contract claimer percentage for rewards, then update it"), async () => { + // Set the amount this contract can claim + await setDAONetworkBootstrapRewardsClaimers("0.1".ether, "0.1".ether, "0.8".ether, { + from: owner, + }); + }); + it(printTitle("guardian", "fails to set contract claimer percentages to lower than 100% total"), async () => { + // Set the amount this contract can claim + await shouldRevert( + setDAONetworkBootstrapRewardsClaimers("0.1".ether, "0.1".ether, "0.1".ether, { + from: owner, + }), + "Percentages were updated", + "Total does not equal 100%" + ); + }); - it(printTitle('random', 'can execute reward period if consensus is reached'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); + it(printTitle("guardian", "fails to set contract claimer percentages to greater than 100% total"), async () => { + // Set the amount this contract can claim + await shouldRevert( + setDAONetworkBootstrapRewardsClaimers("0.4".ether, "0.4".ether, "0.4".ether, { + from: owner, + }), + "Percentages were updated", + "Total does not equal 100%" + ); + }); - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + /*** Regular Nodes *************************/ + + it(printTitle("node", "can claim RPL and ETH"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Send ETH to rewards pool + const rocketSmoothingPool = await RocketSmoothingPool.deployed(); + await web3.eth.sendTransaction({ from: owner, to: rocketSmoothingPool.address, value: "20".ether }); + + const rocketRewardsPool = await RocketRewardsPool.deployed(); + const pendingRewards = await rocketRewardsPool.getPendingETHRewards.call(); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + { + address: registeredNode2, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "2".ether, + nodeETH: "1".ether, + }, + { + address: registeredNodeTrusted1, + network: 0, + trustedNodeRPL: "1".ether, + nodeRPL: "2".ether, + nodeETH: "0".ether, + }, + { + address: userOne, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1.333".ether, + nodeETH: "0.3".ether, + }, + ]; + await submitRewards(0, rewards, "0".ether, "2".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "2".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimRewards(registeredNode1, [0], [rewards], { + from: registeredNode1, + }); + await claimRewards(registeredNode2, [0], [rewards], { + from: registeredNode2, + }); + await claimRewards(registeredNodeTrusted1, [0], [rewards], { + from: registeredNodeTrusted1, + }); + await claimRewards(userOne, [0], [rewards], { + from: userOne, + }); + + // Do a second claim interval + await submitRewards(1, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(1, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimRewards(registeredNode1, [1], [rewards], { + from: node1WithdrawalAddress, + }); + await claimRewards(registeredNode2, [1], [rewards], { + from: registeredNode2, + }); + + // Set node 2 withdrawal addresses + await setNodeWithdrawalAddress(registeredNode2, node2WithdrawalAddress, { from: registeredNode2 }); + await setNodeRPLWithdrawalAddress(registeredNode2, node2RPLWithdrawalAddress, { from: node2WithdrawalAddress }); + + // Submit rewards snapshot + const rewards2 = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + { + address: node2WithdrawalAddress, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "0".ether, + nodeETH: "1".ether, + }, + { + address: node2RPLWithdrawalAddress, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "2".ether, + nodeETH: "0".ether, + }, + { + address: registeredNodeTrusted1, + network: 0, + trustedNodeRPL: "1".ether, + nodeRPL: "2".ether, + nodeETH: "0".ether, + }, + ]; + + // Do a third claim interval, after the changes + await submitRewards(2, rewards2, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(2, rewards2, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimRewards(registeredNode1, [2], [rewards2], { + from: node1WithdrawalAddress, + }); + // Can't claim ETH rewards from RPL withdrawal address + await shouldRevert( + claimRewards(node2WithdrawalAddress, [2], [rewards2], { + from: node2RPLWithdrawalAddress, + }) + ); + // Can't claim RPL rewards from ETH withdrawal address + await shouldRevert( + claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { + from: node2WithdrawalAddress, + }) + ); + // Can't claim RPL rewards from node address + await shouldRevert( + claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { + from: registeredNode2, + }) + ); + await claimRewards(node2WithdrawalAddress, [2], [rewards2], { + from: node2WithdrawalAddress, + }); + await claimRewards(node2RPLWithdrawalAddress, [2], [rewards2], { + from: node2RPLWithdrawalAddress, + }); + }); - // Add another 2 trusted nodes so consensus becomes 3 votes - await setNodeTrusted(unregisteredNodeTrusted1, 'saas_3', 'node@home.com', owner); - await setNodeTrusted(unregisteredNodeTrusted2, 'saas_4', 'node@home.com', owner); + it(printTitle("node", "can claim from withdrawal address"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Send ETH to rewards pool + const rocketSmoothingPool = await RocketSmoothingPool.deployed(); + await web3.eth.sendTransaction({ from: owner, to: rocketSmoothingPool.address, value: "20".ether }); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimRewards(registeredNode1, [0], [rewards], { + from: node1WithdrawalAddress, + }); + }); - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] + it(printTitle("node", "can not claim with invalid proof"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + + // Create 3 snapshots + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + let treeData = parseRewardsMap(rewards); + let proof = treeData.proof.claims[web3.utils.toChecksumAddress(registeredNode1)]; + let amountsRPL = [proof.amountRPL]; + let amountsETH = [proof.amountETH]; + let proofs = [proof.proof]; + + let rocketMerkleDistributorMainnet = await RocketMerkleDistributorMainnet.deployed(); + + // Attempt to claim reward for registeredNode1 with registeredNode2 + await shouldRevert( + rocketMerkleDistributorMainnet.claim(registeredNode2, [0], amountsRPL, amountsETH, proofs, { + from: registeredNode2, + }), + "Was able to claim with invalid proof", + "Invalid proof" + ); + }); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); + it(printTitle("node", "can not claim same interval twice"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + + // Create 3 snapshots + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + await submitRewards(1, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(1, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + await submitRewards(2, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(2, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimRewards(registeredNode1, [0, 1], [rewards, rewards], { + from: node1WithdrawalAddress, + }); + await shouldRevert( + claimRewards(registeredNode1, [0], [rewards], { + from: node1WithdrawalAddress, + }), + "Was able to claim again", + "Already claimed" + ); + await shouldRevert( + claimRewards(registeredNode1, [1], [rewards], { + from: node1WithdrawalAddress, + }), + "Was able to claim again", + "Already claimed" + ); + await shouldRevert( + claimRewards(registeredNode1, [0, 1], [rewards, rewards], { + from: node1WithdrawalAddress, + }), + "Was able to claim again", + "Already claimed" + ); + await shouldRevert( + claimRewards(registeredNode1, [0, 2], [rewards, rewards], { + from: node1WithdrawalAddress, + }), + "Was able to claim again", + "Already claimed" + ); + }); - // Kick a trusted node so consensus becomes 2 votes again - await kickTrustedNode(unregisteredNodeTrusted1, [registeredNodeTrusted1, registeredNodeTrusted2, unregisteredNodeTrusted1]); + it(printTitle("node", "can claim mulitiple periods in a single tx"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + { + address: registeredNode2, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "2".ether, + nodeETH: "0".ether, + }, + ]; + + // Submit 2 snapshots + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + await submitRewards(1, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(1, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + await submitRewards(2, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(2, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimRewards(registeredNode1, [0], [rewards], { + from: node1WithdrawalAddress, + }); + await claimRewards(registeredNode1, [1, 2], [rewards, rewards], { + from: node1WithdrawalAddress, + }); + await claimRewards(registeredNode2, [0, 1, 2], [rewards, rewards, rewards], { + from: registeredNode2, + }); + }); - // Now we should be able to execute the reward period - await executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}); + it(printTitle("node", "can claim both RPL & ETH if RPL withdrawal is set"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + await setNodeRPLWithdrawalAddress(registeredNode2, node2RPLWithdrawalAddress, { from: registeredNode2 }); + // Send ETH to rewards pool + const rocketSmoothingPool = await RocketSmoothingPool.deployed(); + await web3.eth.sendTransaction({ from: owner, to: rocketSmoothingPool.address, value: "20".ether }); + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode2, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "1".ether, + }, + ]; + + // Submit snapshot + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim both ETH and RPL + await claimRewards(registeredNode2, [0], [rewards], { + from: node2RPLWithdrawalAddress, }); + }); + + it(printTitle("node", "can only claim and stake if no RPL withdrawal address set"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Set node 2 withdrawal addresses + await setNodeWithdrawalAddress(registeredNode2, node2WithdrawalAddress, { from: registeredNode2 }); + await setNodeRPLWithdrawalAddress(registeredNode2, node2RPLWithdrawalAddress, { from: node2WithdrawalAddress }); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + { + address: node2WithdrawalAddress, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "0".ether, + nodeETH: "0".ether, + }, + { + address: node2RPLWithdrawalAddress, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "2".ether, + nodeETH: "0".ether, + }, + { + address: registeredNodeTrusted1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "2".ether, + nodeETH: "0".ether, + }, + ]; + + // Submit rewards + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await claimAndStakeRewards(registeredNode1, [0], [rewards], "1".ether, { + from: node1WithdrawalAddress, + }); + await shouldRevert(claimAndStakeRewards(node2RPLWithdrawalAddress, [0], [rewards], "2".ether, { + from: node2RPLWithdrawalAddress, + })); + }); - it(printTitle('random', 'cant execute reward period twice'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Add another 2 trusted nodes so consensus becomes 3 votes - await setNodeTrusted(unregisteredNodeTrusted1, 'saas_3', 'node@home.com', owner); - await setNodeTrusted(unregisteredNodeTrusted2, 'saas_4', 'node@home.com', owner); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] - - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - - // Kick a trusted node so consensus becomes 2 votes again - await kickTrustedNode(unregisteredNodeTrusted1, [registeredNodeTrusted1, registeredNodeTrusted2, unregisteredNodeTrusted1]); - - // Now we should be able to execute the reward period - await executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}); - await shouldRevert(executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}), 'Already executed'); - }); + it(printTitle("node", "can not stake amount greater than claim"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Claim RPL + await shouldRevert( + claimAndStakeRewards(registeredNode1, [0], [rewards], "2".ether, { + from: registeredNode1, + }), + "Was able to stake amount greater than reward", + "Invalid stake amount" + ); + }); - it(printTitle('random', 'can submit past consensus'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Add another trusted node - await setNodeTrusted(unregisteredNodeTrusted1, 'saas_3', 'node@home.com', owner); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] - - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - // already have consensus, should have executed - await shouldRevert(executeRewards(0, rewards, '0'.ether, '0'.ether, {from: random}), 'Already executed'); - - // should allow another vote past consensus - await submitRewards(0, rewards, '0'.ether, '0'.ether, {from: unregisteredNodeTrusted1}); + /*** Random *************************/ + + it(printTitle("random", "can execute reward period if consensus is reached"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Add another 2 trusted nodes so consensus becomes 3 votes + await setNodeTrusted(unregisteredNodeTrusted1, "saas_3", "node@home.com", owner); + await setNodeTrusted(unregisteredNodeTrusted2, "saas_4", "node@home.com", owner); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Kick a trusted node so consensus becomes 2 votes again + await kickTrustedNode(unregisteredNodeTrusted1, [ + registeredNodeTrusted1, + registeredNodeTrusted2, + unregisteredNodeTrusted1, + ]); + + // Now we should be able to execute the reward period + await executeRewards(0, rewards, "0".ether, "0".ether, { from: random }); + }); - }); + it(printTitle("random", "cant execute reward period twice"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Add another 2 trusted nodes so consensus becomes 3 votes + await setNodeTrusted(unregisteredNodeTrusted1, "saas_3", "node@home.com", owner); + await setNodeTrusted(unregisteredNodeTrusted2, "saas_4", "node@home.com", owner); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + + // Kick a trusted node so consensus becomes 2 votes again + await kickTrustedNode(unregisteredNodeTrusted1, [ + registeredNodeTrusted1, + registeredNodeTrusted2, + unregisteredNodeTrusted1, + ]); + + // Now we should be able to execute the reward period + await executeRewards(0, rewards, "0".ether, "0".ether, { from: random }); + await shouldRevert(executeRewards(0, rewards, "0".ether, "0".ether, { from: random }), "Already executed"); + }); + it(printTitle("random", "can submit past consensus"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Add another trusted node + await setNodeTrusted(unregisteredNodeTrusted1, "saas_3", "node@home.com", owner); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(0, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + // already have consensus, should have executed + await shouldRevert(executeRewards(0, rewards, "0".ether, "0".ether, { from: random }), "Already executed"); + + // should allow another vote past consensus + await submitRewards(0, rewards, "0".ether, "0".ether, { from: unregisteredNodeTrusted1 }); + }); - /*** Misc *************************/ - - - it(printTitle('misc', 'claim bitmap is correct'), async () => { - // Initialize RPL inflation & claims contract - let rplInflationStartTime = await rplInflationSetup(); - await rewardsContractSetup('0.5'.ether, '0'.ether, '0.5'.ether); - - // Move to inflation start plus one claim interval - let currentTime = await getCurrentTime(web3); - assert.isBelow(currentTime, rplInflationStartTime, 'Current block should be below RPL inflation start time'); - await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); - - // Submit rewards snapshot - const rewards = [ - { - address: node1WithdrawalAddress, - network: 0, - trustedNodeRPL: '0'.ether, - nodeRPL: '1'.ether, - nodeETH: '0'.ether - }, - ] - - // Submit 10 reward intervals - for (let i = 0; i < 10; i++) { - await submitRewards(i, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted1}); - await submitRewards(i, rewards, '0'.ether, '0'.ether, {from: registeredNodeTrusted2}); - } - - // Some arbitrary intervals to claim - let claimIntervals = [ 0, 4, 6, 9 ]; - - await claimRewards(node1WithdrawalAddress, claimIntervals, Array(claimIntervals.length).fill(rewards), { - from: registeredNode1, - }); - - // Retrieve the bitmap of claims - const rocketStorage = await RocketStorage.deployed(); - const key = web3.utils.soliditySha3( - {type: 'string', value: 'rewards.interval.claimed'}, - {type: 'address', value: node1WithdrawalAddress}, - {type: 'uint256', value: 0} - ) - const bitmap = (await rocketStorage.getUint.call(key)).toNumber(); - - // Construct the expected bitmap and compare - let expected = 0; - for (let i = 0; i < claimIntervals.length; i++) { - expected |= 1 << claimIntervals[i]; - } - assert.strictEqual(bitmap, expected, 'Incorrect claimed bitmap'); - - // Confirm second claim fails for each interval - for (let i = 0; i < claimIntervals.length; i++) { - await shouldRevert(claimRewards(node1WithdrawalAddress, [claimIntervals[i]], [rewards], { - from: registeredNode1, - }), 'Was able to claim again', 'Already claimed'); - } - }); + /*** Misc *************************/ + + it(printTitle("misc", "claim bitmap is correct"), async () => { + // Initialize RPL inflation & claims contract + let rplInflationStartTime = await rplInflationSetup(); + await rewardsContractSetup("0.5".ether, "0".ether, "0.5".ether); + + // Move to inflation start plus one claim interval + let currentTime = await getCurrentTime(web3); + assert.isBelow(currentTime, rplInflationStartTime, "Current block should be below RPL inflation start time"); + await increaseTime(web3, rplInflationStartTime - currentTime + claimIntervalTime); + + // Submit rewards snapshot + const rewards = [ + { + address: registeredNode1, + network: 0, + trustedNodeRPL: "0".ether, + nodeRPL: "1".ether, + nodeETH: "0".ether, + }, + ]; + + // Submit 10 reward intervals + for (let i = 0; i < 10; i++) { + await submitRewards(i, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted1 }); + await submitRewards(i, rewards, "0".ether, "0".ether, { from: registeredNodeTrusted2 }); + } + + // Some arbitrary intervals to claim + let claimIntervals = [0, 4, 6, 9]; + + await claimRewards(registeredNode1, claimIntervals, Array(claimIntervals.length).fill(rewards), { + from: registeredNode1, + }); + + // Retrieve the bitmap of claims + const rocketStorage = await RocketStorage.deployed(); + const key = web3.utils.soliditySha3( + { type: "string", value: "rewards.interval.claimed" }, + { type: "address", value: registeredNode1 }, + { type: "uint256", value: 0 } + ); + const bitmap = (await rocketStorage.getUint.call(key)).toNumber(); + + // Construct the expected bitmap and compare + let expected = 0; + for (let i = 0; i < claimIntervals.length; i++) { + expected |= 1 << claimIntervals[i]; + } + assert.strictEqual(bitmap, expected, "Incorrect claimed bitmap"); + + // Confirm second claim fails for each interval + for (let i = 0; i < claimIntervals.length; i++) { + await shouldRevert( + claimRewards(registeredNode1, [claimIntervals[i]], [rewards], { + from: registeredNode1, + }), + "Was able to claim again", + "Already claimed" + ); + } }); + }); } diff --git a/test/rewards/scenario-claim-rewards.js b/test/rewards/scenario-claim-rewards.js index f588be41..c2aa96c5 100644 --- a/test/rewards/scenario-claim-rewards.js +++ b/test/rewards/scenario-claim-rewards.js @@ -1,6 +1,6 @@ import { RocketMerkleDistributorMainnet, - RocketNodeManager, + RocketNodeManagerNew, RocketRewardsPool, RocketStorage, RocketTokenRPL } from '../_utils/artifacts'; @@ -20,7 +20,7 @@ export async function claimRewards(nodeAddress, indices, rewards, txOptions) { rocketTokenRPL, ] = await Promise.all([ RocketRewardsPool.deployed(), - RocketNodeManager.deployed(), + RocketNodeManagerNew.deployed(), RocketMerkleDistributorMainnet.deployed(), RocketStorage.deployed(), RocketTokenRPL.deployed(), @@ -28,12 +28,14 @@ export async function claimRewards(nodeAddress, indices, rewards, txOptions) { // Get node withdrawal address let nodeWithdrawalAddress = await rocketNodeManager.getNodeWithdrawalAddress.call(nodeAddress); + // Get node RPL withdrawal address + let nodeRPLWithdrawalAddress = await rocketNodeManager.getNodeRPLWithdrawalAddress.call(nodeAddress); // Get balances function getBalances() { return Promise.all([ rocketRewardsPool.getClaimIntervalTimeStart(), - rocketTokenRPL.balanceOf.call(nodeWithdrawalAddress), + rocketTokenRPL.balanceOf.call(nodeRPLWithdrawalAddress), web3.eth.getBalance(nodeWithdrawalAddress) ]).then( ([claimIntervalTimeStart, nodeRpl, nodeEth]) =>