From b79a994a277f51737dd82b8bf8c76af69c377d38 Mon Sep 17 00:00:00 2001 From: iamsahu Date: Wed, 8 Nov 2023 15:24:45 +0100 Subject: [PATCH] feat: Multiple stakers --- contracts/test/invariants/Base.sol | 9 +-- contracts/test/invariants/Invariant_river.sol | 2 +- .../test/invariants/handlers/BaseService.sol | 9 ++- .../invariants/handlers/OperatorService.sol | 21 +++++- .../handlers/OracleDaemonService.sol | 17 ++++- .../invariants/handlers/StakerService.sol | 66 ++++++++++++++----- 6 files changed, 98 insertions(+), 26 deletions(-) diff --git a/contracts/test/invariants/Base.sol b/contracts/test/invariants/Base.sol index fb58fbcf..7d073b59 100644 --- a/contracts/test/invariants/Base.sol +++ b/contracts/test/invariants/Base.sol @@ -71,7 +71,7 @@ contract Base is Test, BytesGenerator { address internal newAdmin; address internal collector; address internal newCollector; - address internal allower; + address public allower; address internal newAllowlist; address public oracleMember; address internal bob; @@ -97,8 +97,8 @@ contract Base is Test, BytesGenerator { function setUp() public virtual { deployProtocol(); deployServices(); - addTargetSelectors(); excludeDeployedContracts(); + addTargetSelectors(); } function loadBlockState() public { @@ -198,8 +198,10 @@ contract Base is Test, BytesGenerator { operatorsRegistry = OperatorsRegistryV1(address(operatorsRegistryProxy)); redeemManager = RedeemManagerV1(address(redeemManagerProxy)); - vm.prank(admin); + vm.startPrank(admin); river.setCoverageFund(address(coverageFund)); + oracle.addMember(oracleMember, 1); + vm.stopPrank(); } function deployServices() internal { @@ -216,7 +218,6 @@ contract Base is Test, BytesGenerator { } function dealETH(address _to, uint256 _amount) public { - console.log("dealing"); vm.deal(_to, _amount); } diff --git a/contracts/test/invariants/Invariant_river.sol b/contracts/test/invariants/Invariant_river.sol index 5b16be0d..8ce9f8f0 100644 --- a/contracts/test/invariants/Invariant_river.sol +++ b/contracts/test/invariants/Invariant_river.sol @@ -15,7 +15,7 @@ contract Invariant_River is Base { function invariant_setBalanceToDeposit() public { uint256 balanceToDepositBefore = river.getBalanceToDeposit(); - stakerService.action_stakeAmount(10 ether); + stakerService.action_stakeAmount(10 ether, 10); uint256 balanceToDepositAfter = river.getBalanceToDeposit(); assertEq(balanceToDepositAfter, balanceToDepositBefore + 10 ether); diff --git a/contracts/test/invariants/handlers/BaseService.sol b/contracts/test/invariants/handlers/BaseService.sol index 670b8ab6..bfb6c4dd 100644 --- a/contracts/test/invariants/handlers/BaseService.sol +++ b/contracts/test/invariants/handlers/BaseService.sol @@ -2,8 +2,9 @@ pragma solidity 0.8.10; -import "forge-std/StdInvariant.sol"; import "forge-std/Test.sol"; +import "forge-std/StdInvariant.sol"; + import {Base} from "../Base.sol"; abstract contract BaseService is Test { @@ -29,6 +30,12 @@ abstract contract BaseService is Test { vm.stopPrank(); } + modifier prankAllower() { + vm.startPrank(base.allower()); + _; + vm.stopPrank(); + } + constructor(Base _base) { base = _base; } diff --git a/contracts/test/invariants/handlers/OperatorService.sol b/contracts/test/invariants/handlers/OperatorService.sol index 502b2a5d..4ce7a757 100644 --- a/contracts/test/invariants/handlers/OperatorService.sol +++ b/contracts/test/invariants/handlers/OperatorService.sol @@ -26,8 +26,6 @@ contract OperatorService is BaseService, BytesGenerator { } function staticOperatorsSetup() internal prankAdmin { - base.oracle().addMember(base.oracleMember(), 1); - operatorOneIndex = base.operatorsRegistry().addOperator(operatorOneName, operatorOne); operatorTwoIndex = base.operatorsRegistry().addOperator(operatorTwoName, operatorTwo); @@ -50,7 +48,24 @@ contract OperatorService is BaseService, BytesGenerator { } // function getTargetSelectors() external view override returns (StdInvariant.FuzzSelector memory selectors) { + // bytes4[] memory selectorsArray = new bytes4[](1); + // selectorsArray[0] = this.action_addOperator.selector; + // selectors.selectors = selectorsArray; + // selectors.addr = address(this); // } - // TODO: Add the dynamic operator management + // function action_addOperator() external prankAdmin recordBlockData { + // string memory operatorName = vm.toString(base.operatorsRegistry().getOperatorCount() + 1); + // uint256 operatorIndex = base.operatorsRegistry().addOperator(operatorName, makeAddr(operatorName)); + + // bytes memory hundredKeys = genBytes((48 + 96) * 100); + // base.operatorsRegistry().addValidators(operatorIndex, 100, hundredKeys); + + // uint256[] memory operatorIndexes = new uint256[](1); + // operatorIndexes[0] = operatorIndex; + // uint32[] memory operatorLimits = new uint32[](1); + // operatorLimits[0] = 100; + + // base.operatorsRegistry().setOperatorLimits(operatorIndexes, operatorLimits, block.number); + // } } diff --git a/contracts/test/invariants/handlers/OracleDaemonService.sol b/contracts/test/invariants/handlers/OracleDaemonService.sol index a7bbda20..26648af4 100644 --- a/contracts/test/invariants/handlers/OracleDaemonService.sol +++ b/contracts/test/invariants/handlers/OracleDaemonService.sol @@ -8,20 +8,33 @@ import {Base} from "../Base.sol"; import {BaseService} from "./BaseService.sol"; import {IOracleManagerV1} from "../../../src/interfaces/components/IOracleManager.1.sol"; + contract OracleDaemonService is BaseService { constructor(Base _base) BaseService(_base) {} function getTargetSelectors() external view override returns (StdInvariant.FuzzSelector memory selectors) { bytes4[] memory selectorsArray = new bytes4[](1); - selectorsArray[0] = this.action_report.selector; + selectorsArray[0] = this.action_dummy_report.selector; selectors.selectors = selectorsArray; selectors.addr = address(this); } - function action_report() external prankOracleMember { + function action_dummy_report() external prankOracleMember recordBlockData { IOracleManagerV1.ConsensusLayerReport memory dummyReport; dummyReport.stoppedValidatorCountPerOperator = new uint32[](1); base.oracle().reportConsensusLayerData(dummyReport); } + + // function action_report() external prankOracleMember() recordBlockData { + // // Do we have a check about whether the report is going after the correct amount of duration + // // Do we call time skip to make the report valid? + // // Calculate epoch + // // Get validators balance + // // Get validators skimmed balance + // // Get validators exited balance + // // Get validators exiting balance + // // Get validators count + // // Get Stopped validator count per operator + // } } diff --git a/contracts/test/invariants/handlers/StakerService.sol b/contracts/test/invariants/handlers/StakerService.sol index f531f523..2d341cd6 100644 --- a/contracts/test/invariants/handlers/StakerService.sol +++ b/contracts/test/invariants/handlers/StakerService.sol @@ -5,35 +5,71 @@ pragma solidity 0.8.10; import "forge-std/Test.sol"; import "forge-std/console.sol"; -import {BaseService} from "./BaseService.sol"; import {Base} from "../Base.sol"; +import {BaseService} from "./BaseService.sol"; + +uint256 constant MAX_STAKERS = 100; contract StakerService is BaseService { - constructor(Base _base) BaseService(_base) {} + address[] internal stakers; + address internal currentStaker; + + modifier useStaker(uint256 index) { + currentStaker = stakers[bound(index, 0, MAX_STAKERS - 1)]; + vm.startPrank(currentStaker); + _; + vm.stopPrank(); + } + + constructor(Base _base) BaseService(_base) { + allowListStakers(); + } + + function allowListStakers() internal prankAllower { + address[] memory stakerServiceArray = new address[](MAX_STAKERS); + uint256[] memory stakerServiceMask = new uint256[](MAX_STAKERS); + + for (uint256 index = 0; index < MAX_STAKERS; index++) { + stakers.push(makeAddr(vm.toString(index))); + stakerServiceArray[index] = stakers[index]; + stakerServiceMask[index] = 5; + } + + base.allowlist().allow(stakerServiceArray, stakerServiceMask); + } function getTargetSelectors() external view override returns (StdInvariant.FuzzSelector memory selectors) { - bytes4[] memory selectorsArray = new bytes4[](2); - selectorsArray[0] = this.action_stakeAll.selector; - selectorsArray[1] = this.action_unstakeAll.selector; + bytes4[] memory selectorsArray = new bytes4[](4); + selectorsArray[0] = this.action_stakeAmount.selector; + selectorsArray[1] = this.action_request_redeem.selector; + selectorsArray[2] = this.action_request_redeem_all.selector; + selectorsArray[3] = this.action_claim_redeem_request.selector; selectors.selectors = selectorsArray; selectors.addr = address(this); } - function action_stakeAll() public recordBlockData { - base.dealETH(address(this), 1 ether); - console.log("Staking all funds"); - base.river().deposit{value: 1 ether}(); + function action_stakeAmount(uint256 amount, uint256 stakerIndex) public recordBlockData useStaker(stakerIndex) { + amount = bound(amount, 1e16, 10000 ether); + console.log("Staking amount of funds"); + base.dealETH(currentStaker, amount); + base.river().deposit{value: amount}(); } - function action_unstakeAll() public recordBlockData { + function action_request_redeem(uint256 amount, uint256 stakerIndex) public recordBlockData useStaker(stakerIndex) { + amount = bound(amount, 1e16, base.river().balanceOf(currentStaker)); + console.log("Unstaking"); + // Keep track of the redeem requests + } + + function action_request_redeem_all(uint256 stakerIndex) public recordBlockData useStaker(stakerIndex) { console.log("Unstaking all funds"); + uint256 balance = base.river().balanceOf(currentStaker); + // Keep track of the redeem requests } - function action_stakeAmount(uint256 amount) public recordBlockData { - amount = bound(amount, 1e16, 10000 ether); - console.log("Staking amount of funds"); - base.dealETH(address(this), amount); - base.river().deposit{value: amount}(); + function action_claim_redeem_request(uint256 stakerIndex) public recordBlockData useStaker(stakerIndex) { + console.log("Claiming redeem request"); + // Do we have a check on whether it is being done after the cooldown period? } }