diff --git a/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol b/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol index a500febf9..de3580aea 100644 --- a/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol +++ b/packages/horizon/contracts/data-service/utilities/ProvisionManager.sol @@ -58,6 +58,13 @@ abstract contract ProvisionManager is Initializable, GraphDirectory, ProvisionMa */ error ProvisionManagerInvalidValue(bytes message, uint256 value, uint256 min, uint256 max); + /** + * @notice Thrown when attempting to set a range where min is greater than max. + * @param min The minimum value. + * @param max The maximum value. + */ + error ProvisionManagerInvalidRange(uint256 min, uint256 max); + /** * @notice Thrown when the caller is not authorized to manage the provision of a service provider. * @param caller The address of the caller. @@ -156,7 +163,7 @@ abstract contract ProvisionManager is Initializable, GraphDirectory, ProvisionMa * * @param _serviceProvider The address of the service provider. */ - function _acceptProvisionParameters(address _serviceProvider) internal virtual { + function _acceptProvisionParameters(address _serviceProvider) internal { _checkProvisionParameters(_serviceProvider, true); _graphStaking().acceptProvisionParameters(_serviceProvider); } @@ -177,6 +184,7 @@ abstract contract ProvisionManager is Initializable, GraphDirectory, ProvisionMa * @param _max The maximum allowed value for the provision tokens. */ function _setProvisionTokensRange(uint256 _min, uint256 _max) internal { + require(_min <= _max, ProvisionManagerInvalidRange(_min, _max)); minimumProvisionTokens = _min; maximumProvisionTokens = _max; emit ProvisionTokensRangeSet(_min, _max); @@ -188,6 +196,7 @@ abstract contract ProvisionManager is Initializable, GraphDirectory, ProvisionMa * @param _max The maximum allowed value for the max verifier cut. */ function _setVerifierCutRange(uint32 _min, uint32 _max) internal { + require(_min <= _max, ProvisionManagerInvalidRange(_min, _max)); minimumVerifierCut = _min; maximumVerifierCut = _max; emit VerifierCutRangeSet(_min, _max); @@ -199,6 +208,7 @@ abstract contract ProvisionManager is Initializable, GraphDirectory, ProvisionMa * @param _max The maximum allowed value for the thawing period. */ function _setThawingPeriodRange(uint64 _min, uint64 _max) internal { + require(_min <= _max, ProvisionManagerInvalidRange(_min, _max)); minimumThawingPeriod = _min; maximumThawingPeriod = _max; emit ThawingPeriodRangeSet(_min, _max); @@ -261,7 +271,7 @@ abstract contract ProvisionManager is Initializable, GraphDirectory, ProvisionMa * @notice Gets the delegation ratio. * @return The delegation ratio */ - function _getDelegationRatio() internal view virtual returns (uint32) { + function _getDelegationRatio() internal view returns (uint32) { return delegationRatio; } diff --git a/packages/horizon/test/data-service/DataService.t.sol b/packages/horizon/test/data-service/DataService.t.sol index 66d12d80b..95e027dd3 100644 --- a/packages/horizon/test/data-service/DataService.t.sol +++ b/packages/horizon/test/data-service/DataService.t.sol @@ -1,30 +1,394 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.26; -import { GraphBaseTest } from "../GraphBase.t.sol"; -import { DataServiceBase } from "./DataServiceBase.sol"; +import { IHorizonStakingMain } from "../../contracts/interfaces/internal/IHorizonStakingMain.sol"; +import { HorizonStakingSharedTest } from "../shared/horizon-staking/HorizonStakingShared.t.sol"; +import { DataServiceBase } from "./implementations/DataServiceBase.sol"; +import { DataServiceOverride } from "./implementations/DataServiceOverride.sol"; +import { ProvisionManager } from "./../../contracts/data-service/utilities/ProvisionManager.sol"; +import { PPMMath } from "./../../contracts/libraries/PPMMath.sol"; -contract DataServiceTest is GraphBaseTest { - function test_WhenTheContractIsDeployedWithAValidController() external { - DataServiceBase dataService = _deployDataService(); +contract DataServiceTest is HorizonStakingSharedTest { + DataServiceBase dataService; + DataServiceOverride dataServiceOverride; - uint32 delegationRatio = dataService.getDelegationRatio(); - assertEq(delegationRatio, type(uint32).min); + function setUp() public override { + super.setUp(); - (uint256 minTokens, uint256 maxTokens) = dataService.getProvisionTokensRange(); - assertEq(minTokens, type(uint256).min); - assertEq(maxTokens, type(uint256).max); + dataService = new DataServiceBase(address(controller)); + dataServiceOverride = new DataServiceOverride(address(controller)); + } + + function test_Constructor_WhenTheContractIsDeployedWithAValidController() external view { + _assert_delegationRatio(type(uint32).min); + _assert_provisionTokens_range(type(uint256).min, type(uint256).max); + _assert_verifierCut_range(type(uint32).min, type(uint32).max); + _assert_thawingPeriod_range(type(uint64).min, type(uint64).max); + } + + // -- Delegation ratio -- + + function test_DelegationRatio_WhenSettingTheDelegationRatio(uint32 delegationRatio) external { + _assert_set_delegationRatio(delegationRatio); + } + + function test_DelegationRatio_WhenGettingTheDelegationRatio(uint32 ratio) external { + dataService.setDelegationRatio(ratio); + _assert_delegationRatio(ratio); + } + + // -- Provision tokens -- + + function test_ProvisionTokens_WhenSettingAValidRange(uint256 min, uint256 max) external { + vm.assume(min <= max); + _assert_set_provisionTokens_range(min, max); + } + + function test_ProvisionTokens_RevertWhen_SettingAnInvalidRange(uint256 min, uint256 max) external { + vm.assume(min > max); + + vm.expectRevert(abi.encodeWithSelector(ProvisionManager.ProvisionManagerInvalidRange.selector, min, max)); + dataService.setProvisionTokensRange(min, max); + } + + function test_ProvisionTokens_WhenGettingTheRange() external { + dataService.setProvisionTokensRange(dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); + _assert_provisionTokens_range(dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); + } + + function test_ProvisionTokens_WhenGettingTheRangeWithAnOverridenGetter() external { + // Overriden getter returns the const values regardless of the set range + dataServiceOverride.setProvisionTokensRange(0, 1); + (uint256 min, uint256 max) = dataServiceOverride.getProvisionTokensRange(); + + assertEq(min, dataServiceOverride.PROVISION_TOKENS_MIN()); + assertEq(max, dataServiceOverride.PROVISION_TOKENS_MAX()); + } + + function test_ProvisionTokens_WhenCheckingAValidProvision(uint256 tokens) external useIndexer { + dataService.setProvisionTokensRange(dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); + tokens = bound(tokens, dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); + + _createProvision(address(dataService), tokens, 0, 0); + dataService.checkProvisionTokens(users.indexer); + } + + function test_ProvisionTokens_WhenCheckingWithAnOverridenChecker(uint256 tokens) external useIndexer { + vm.assume(tokens != 0); + dataServiceOverride.setProvisionTokensRange( + dataService.PROVISION_TOKENS_MIN(), + dataService.PROVISION_TOKENS_MAX() + ); + + // this checker accepts provisions with any amount of tokens + _createProvision(address(dataServiceOverride), tokens, 0, 0); + dataServiceOverride.checkProvisionTokens(users.indexer); + } + + function test_ProvisionTokens_RevertWhen_CheckingAnInvalidProvision(uint256 tokens) external useIndexer { + dataService.setProvisionTokensRange(dataService.PROVISION_TOKENS_MIN(), dataService.PROVISION_TOKENS_MAX()); + tokens = bound(tokens, 1, dataService.PROVISION_TOKENS_MIN() - 1); + + _createProvision(address(dataService), tokens, 0, 0); + vm.expectRevert( + abi.encodeWithSelector( + ProvisionManager.ProvisionManagerInvalidValue.selector, + "tokens", + tokens, + dataService.PROVISION_TOKENS_MIN(), + dataService.PROVISION_TOKENS_MAX() + ) + ); + dataService.checkProvisionTokens(users.indexer); + } + + // -- Verifier cut -- + + function test_VerifierCut_WhenSettingAValidRange(uint32 min, uint32 max) external { + vm.assume(min <= max); + _assert_set_verifierCut_range(min, max); + } + + function test_VerifierCut_RevertWhen_SettingAnInvalidRange(uint32 min, uint32 max) external { + vm.assume(min > max); + + vm.expectRevert(abi.encodeWithSelector(ProvisionManager.ProvisionManagerInvalidRange.selector, min, max)); + dataService.setVerifierCutRange(min, max); + } + + function test_VerifierCut_WhenGettingTheRange() external { + dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + _assert_verifierCut_range(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + } + + function test_VerifierCut_WhenGettingTheRangeWithAnOverridenGetter() external { + // Overriden getter returns the const values regardless of the set range + dataServiceOverride.setVerifierCutRange(0, 1); + (uint32 min, uint32 max) = dataServiceOverride.getVerifierCutRange(); + assertEq(min, dataServiceOverride.VERIFIER_CUT_MIN()); + assertEq(max, dataServiceOverride.VERIFIER_CUT_MAX()); + } + + function test_VerifierCut_WhenCheckingAValidProvision(uint32 verifierCut) external useIndexer { + dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + verifierCut = uint32(bound(verifierCut, dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX())); + + _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); + dataService.checkProvisionParameters(users.indexer, false); + } + + function test_VerifierCut_WhenCheckingWithAnOverridenChecker(uint32 verifierCut) external useIndexer { + verifierCut = uint32(bound(verifierCut, 0, uint32(PPMMath.MAX_PPM))); + dataServiceOverride.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + + // this checker accepts provisions with any verifier cut range + _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); + dataServiceOverride.checkProvisionParameters(users.indexer, false); + } + + function test_VerifierCut_RevertWhen_CheckingAnInvalidProvision(uint32 verifierCut) external useIndexer { + dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + verifierCut = uint32(bound(verifierCut, 0, dataService.VERIFIER_CUT_MIN() - 1)); + + _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), verifierCut, 0); + vm.expectRevert( + abi.encodeWithSelector( + ProvisionManager.ProvisionManagerInvalidValue.selector, + "maxVerifierCut", + verifierCut, + dataService.VERIFIER_CUT_MIN(), + dataService.VERIFIER_CUT_MAX() + ) + ); + dataService.checkProvisionParameters(users.indexer, false); + } + + // -- Thawing period -- - (uint32 minVerifierCut, uint32 maxVerifierCut) = dataService.getVerifierCutRange(); - assertEq(minVerifierCut, type(uint32).min); - assertEq(maxVerifierCut, type(uint32).max); + function test_ThawingPeriod_WhenSettingAValidRange(uint64 min, uint64 max) external { + vm.assume(min <= max); + _assert_set_thawingPeriod_range(min, max); + } + + function test_ThawingPeriod_RevertWhen_SettingAnInvalidRange(uint64 min, uint64 max) external { + vm.assume(min > max); + + vm.expectRevert(abi.encodeWithSelector(ProvisionManager.ProvisionManagerInvalidRange.selector, min, max)); + dataService.setThawingPeriodRange(min, max); + } + + function test_ThawingPeriod_WhenGettingTheRange() external { + dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + _assert_thawingPeriod_range(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + } + + function test_ThawingPeriod_WhenGettingTheRangeWithAnOverridenGetter() external { + // Overriden getter returns the const values regardless of the set range + dataServiceOverride.setThawingPeriodRange(0, 1); + (uint64 min, uint64 max) = dataServiceOverride.getThawingPeriodRange(); + assertEq(min, dataServiceOverride.THAWING_PERIOD_MIN()); + assertEq(max, dataServiceOverride.THAWING_PERIOD_MAX()); + } + + function test_ThawingPeriod_WhenCheckingAValidProvision(uint64 thawingPeriod) external useIndexer { + dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + thawingPeriod = uint32( + bound(thawingPeriod, dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()) + ); + + _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); + dataService.checkProvisionParameters(users.indexer, false); + } + + function test_ThawingPeriod_WhenCheckingWithAnOverridenChecker(uint64 thawingPeriod) external useIndexer { + thawingPeriod = uint32(bound(thawingPeriod, 0, staking.getMaxThawingPeriod())); + dataServiceOverride.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + + // this checker accepts provisions with any verifier cut range + _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); + dataServiceOverride.checkProvisionParameters(users.indexer, false); + } + + function test_ThawingPeriod_RevertWhen_CheckingAnInvalidProvision(uint64 thawingPeriod) external useIndexer { + dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + thawingPeriod = uint32(bound(thawingPeriod, 0, dataService.THAWING_PERIOD_MIN() - 1)); + + _createProvision(address(dataService), dataService.PROVISION_TOKENS_MIN(), 0, thawingPeriod); + vm.expectRevert( + abi.encodeWithSelector( + ProvisionManager.ProvisionManagerInvalidValue.selector, + "thawingPeriod", + thawingPeriod, + dataService.THAWING_PERIOD_MIN(), + dataService.THAWING_PERIOD_MAX() + ) + ); + dataService.checkProvisionParameters(users.indexer, false); + } + + modifier givenProvisionParametersChanged() { + _; + } + + function test_ProvisionParameters_WhenTheNewParametersAreValid( + uint32 maxVerifierCut, + uint64 thawingPeriod + ) external givenProvisionParametersChanged useIndexer { + // bound to valid values + maxVerifierCut = uint32(bound(maxVerifierCut, dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX())); + thawingPeriod = uint64(bound(thawingPeriod, dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX())); + + // set provision parameter ranges + dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + + // stage provision parameter changes + _createProvision( + address(dataService), + dataService.PROVISION_TOKENS_MIN(), + dataService.VERIFIER_CUT_MIN(), + dataService.THAWING_PERIOD_MIN() + ); + staking.setProvisionParameters( + users.indexer, + address(dataService), + maxVerifierCut, + thawingPeriod + ); + + // accept provision parameters + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionParametersSet( + users.indexer, + address(dataService), + maxVerifierCut, + thawingPeriod + ); + dataService.acceptProvisionParameters(users.indexer); + } + + function test_ProvisionParameters_RevertWhen_TheNewThawingPeriodIsInvalid( + uint64 thawingPeriod + ) external givenProvisionParametersChanged useIndexer { + // bound to invalid values + thawingPeriod = uint64(bound(thawingPeriod, 0, dataService.THAWING_PERIOD_MIN() - 1)); + + // set provision parameter ranges + dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + + // stage provision parameter changes + _createProvision( + address(dataService), + dataService.PROVISION_TOKENS_MIN(), + dataService.VERIFIER_CUT_MIN(), + dataService.THAWING_PERIOD_MIN() + ); + staking.setProvisionParameters( + users.indexer, + address(dataService), + dataService.VERIFIER_CUT_MIN(), + thawingPeriod + ); + + // accept provision parameters + vm.expectRevert( + abi.encodeWithSelector( + ProvisionManager.ProvisionManagerInvalidValue.selector, + "thawingPeriod", + thawingPeriod, + dataService.THAWING_PERIOD_MIN(), + dataService.THAWING_PERIOD_MAX() + ) + ); + dataService.acceptProvisionParameters(users.indexer); + } + + function test_ProvisionParameters_RevertWhen_TheNewVerifierCutIsInvalid( + uint32 maxVerifierCut + ) external givenProvisionParametersChanged useIndexer { + // bound to valid values + maxVerifierCut = uint32(bound(maxVerifierCut, dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX())); + + // set provision parameter ranges + dataService.setVerifierCutRange(dataService.VERIFIER_CUT_MIN(), dataService.VERIFIER_CUT_MAX()); + dataService.setThawingPeriodRange(dataService.THAWING_PERIOD_MIN(), dataService.THAWING_PERIOD_MAX()); + + // stage provision parameter changes + _createProvision( + address(dataService), + dataService.PROVISION_TOKENS_MIN(), + dataService.VERIFIER_CUT_MIN(), + dataService.THAWING_PERIOD_MIN() + ); + staking.setProvisionParameters( + users.indexer, + address(dataService), + maxVerifierCut, + dataService.THAWING_PERIOD_MIN() + ); + + // accept provision parameters + vm.expectEmit(); + emit IHorizonStakingMain.ProvisionParametersSet( + users.indexer, + address(dataService), + maxVerifierCut, + dataService.THAWING_PERIOD_MIN() + ); + dataService.acceptProvisionParameters(users.indexer); + } + + // -- Assert functions -- + + function _assert_set_delegationRatio(uint32 ratio) internal { + vm.expectEmit(); + emit ProvisionManager.DelegationRatioSet(ratio); + dataService.setDelegationRatio(ratio); + _assert_delegationRatio(ratio); + } + + function _assert_delegationRatio(uint32 ratio) internal view { + uint32 _delegationRatio = dataService.getDelegationRatio(); + assertEq(_delegationRatio, ratio); + } + + function _assert_set_provisionTokens_range(uint256 min, uint256 max) internal { + vm.expectEmit(); + emit ProvisionManager.ProvisionTokensRangeSet(min, max); + dataService.setProvisionTokensRange(min, max); + _assert_provisionTokens_range(min, max); + } + + function _assert_provisionTokens_range(uint256 min, uint256 max) internal view { + (uint256 _min, uint256 _max) = dataService.getProvisionTokensRange(); + assertEq(_min, min); + assertEq(_max, max); + } + + function _assert_set_verifierCut_range(uint32 min, uint32 max) internal { + vm.expectEmit(); + emit ProvisionManager.VerifierCutRangeSet(min, max); + dataService.setVerifierCutRange(min, max); + _assert_verifierCut_range(min, max); + } + + function _assert_verifierCut_range(uint32 min, uint32 max) internal view { + (uint32 _min, uint32 _max) = dataService.getVerifierCutRange(); + assertEq(_min, min); + assertEq(_max, max); + } - (uint64 minThawingPeriod, uint64 maxThawingPeriod) = dataService.getThawingPeriodRange(); - assertEq(minThawingPeriod, type(uint64).min); - assertEq(maxThawingPeriod, type(uint64).max); + function _assert_set_thawingPeriod_range(uint64 min, uint64 max) internal { + vm.expectEmit(); + emit ProvisionManager.ThawingPeriodRangeSet(min, max); + dataService.setThawingPeriodRange(min, max); + _assert_thawingPeriod_range(min, max); } - function _deployDataService() internal returns (DataServiceBase) { - return new DataServiceBase(address(controller)); + function _assert_thawingPeriod_range(uint64 min, uint64 max) internal view { + (uint64 _min, uint64 _max) = dataService.getThawingPeriodRange(); + assertEq(_min, min); + assertEq(_max, max); } } diff --git a/packages/horizon/test/data-service/DataService.tree b/packages/horizon/test/data-service/DataService.tree index 2a71bce48..cd016608e 100644 --- a/packages/horizon/test/data-service/DataService.tree +++ b/packages/horizon/test/data-service/DataService.tree @@ -1,3 +1,71 @@ -DataService +DataServiceTest::constructor_ └── when the contract is deployed with a valid controller - └── it should set all it's ranges to max \ No newline at end of file + └── it should set all it's ranges to max + +DataServiceTest::delegationRatio_ +├── when setting the delegation ratio +│ ├── it should emit an event +│ └── it should set the range +└── when getting the delegation ratio + └── it should return the correct value + +DataServiceTest::provisionTokens_ +├── when setting a valid range +│ ├── it should emit an event +│ └── it should set the range +├── when setting an invalid range +│ └── it should revert +├── when getting the range +│ └── it should return the correct value +├── when getting the range with an overriden getter +│ └── it should return the correct value +├── when checking a valid provision +│ └── it should not revert +├── when checking with an overriden checker +│ └── it should not revert +└── when checking an invalid provision + └── it should revert + +DataServiceTest::verifierCut_ +├── when setting a valid range +│ ├── it should emit an event +│ └── it should set the range +├── when setting an invalid range +│ └── it should revert +├── when getting the range +│ └── it should return the correct value +├── when getting the range with an overriden getter +│ └── it should return the correct value +├── when checking a valid provision +│ └── it should not revert +├── when checking with an overriden checker +│ └── it should not revert +└── when checking an invalid provision + └── it should revert + +DataServiceTest::thawingPeriod_ +├── when setting a valid range +│ ├── it should emit an event +│ └── it should set the range +├── when setting an invalid range +│ └── it should revert +├── when getting the range +│ └── it should return the correct value +├── when getting the range with an overriden getter +│ └── it should return the correct value +├── when checking a valid provision +│ └── it should not revert +├── when checking with an overriden checker +│ └── it should not revert +└── when checking an invalid provision + └── it should revert + +DataServiceTest::provisionParameters_ +└── given provision parameters changed + ├── when the new parameters are valid + │ ├── it should emit an event + │ └── it should set the new parameters + ├── when the new thawing period is invalid + │ └── it should revert + └── when the new verifier cut is invalid + └── it should revert \ No newline at end of file diff --git a/packages/horizon/test/data-service/DataServiceBase.sol b/packages/horizon/test/data-service/DataServiceBase.sol deleted file mode 100644 index 5103d51c5..000000000 --- a/packages/horizon/test/data-service/DataServiceBase.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity 0.8.26; - -import { DataService } from "../../contracts/data-service/DataService.sol"; -import { IGraphPayments } from "./../../contracts/interfaces/IGraphPayments.sol"; - -contract DataServiceBase is DataService { - constructor(address controller) DataService(controller) initializer { - __DataService_init(); - } - - function register(address serviceProvider, bytes calldata data) external {} - function acceptProvision(address serviceProvider, bytes calldata data) external {} - function startService(address serviceProvider, bytes calldata data) external {} - function stopService(address serviceProvider, bytes calldata data) external {} - function collect(address serviceProvider, IGraphPayments.PaymentTypes feeType, bytes calldata data) external {} - function slash(address serviceProvider, bytes calldata data) external {} -} diff --git a/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol b/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol index 49ddec3d7..dff804a2b 100644 --- a/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol +++ b/packages/horizon/test/data-service/DataServiceUpgradeable.t.sol @@ -2,9 +2,8 @@ pragma solidity 0.8.26; import { GraphBaseTest } from "../GraphBase.t.sol"; -import { DataServiceBaseUpgradeable } from "./DataServiceBaseUpgradeable.sol"; +import { DataServiceBaseUpgradeable } from "./implementations/DataServiceBaseUpgradeable.sol"; import { UnsafeUpgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; -import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; contract DataServiceUpgradeableTest is GraphBaseTest { function test_WhenTheContractIsDeployedWithAValidController() external { @@ -30,7 +29,7 @@ contract DataServiceUpgradeableTest is GraphBaseTest { // Deploy implementation address implementation = address(new DataServiceBaseUpgradeable(address(controller))); - // Deploy proxy (calls initialize) + // Deploy proxy address proxy = UnsafeUpgrades.deployTransparentProxy( implementation, users.governor, diff --git a/packages/horizon/test/data-service/DataServiceUpgradeable.tree b/packages/horizon/test/data-service/DataServiceUpgradeable.tree new file mode 100644 index 000000000..dbb433635 --- /dev/null +++ b/packages/horizon/test/data-service/DataServiceUpgradeable.tree @@ -0,0 +1,3 @@ +DataServiceUpgradeableTest +└── when the contract is deployed with a valid controller + └── it should set all it's ranges to max \ No newline at end of file diff --git a/packages/horizon/test/data-service/implementations/DataServiceBase.sol b/packages/horizon/test/data-service/implementations/DataServiceBase.sol new file mode 100644 index 000000000..1742efc12 --- /dev/null +++ b/packages/horizon/test/data-service/implementations/DataServiceBase.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.26; + +import { DataService } from "../../../contracts/data-service/DataService.sol"; +import { IGraphPayments } from "./../../../contracts/interfaces/IGraphPayments.sol"; + +contract DataServiceBase is DataService { + uint32 public constant DELEGATION_RATIO = 100; + uint256 public constant PROVISION_TOKENS_MIN = 50; + uint256 public constant PROVISION_TOKENS_MAX = 5000; + uint32 public constant VERIFIER_CUT_MIN = 5; + uint32 public constant VERIFIER_CUT_MAX = 100000; + uint64 public constant THAWING_PERIOD_MIN = 15; + uint64 public constant THAWING_PERIOD_MAX = 76; + + constructor(address controller) DataService(controller) initializer { + __DataService_init(); + } + + function register(address serviceProvider, bytes calldata data) external {} + + function acceptProvision(address serviceProvider, bytes calldata data) external {} + + function startService(address serviceProvider, bytes calldata data) external {} + + function stopService(address serviceProvider, bytes calldata data) external {} + + function collect(address serviceProvider, IGraphPayments.PaymentTypes feeType, bytes calldata data) external {} + + function slash(address serviceProvider, bytes calldata data) external {} + + function setDelegationRatio(uint32 ratio) external { + _setDelegationRatio(ratio); + } + + function setProvisionTokensRange(uint256 min, uint256 max) external { + _setProvisionTokensRange(min, max); + } + + function setVerifierCutRange(uint32 min, uint32 max) external { + _setVerifierCutRange(min, max); + } + + function setThawingPeriodRange(uint64 min, uint64 max) external { + _setThawingPeriodRange(min, max); + } + + function checkProvisionTokens(address serviceProvider) external view { + _checkProvisionTokens(serviceProvider); + } + + function checkProvisionParameters(address serviceProvider, bool pending) external view { + _checkProvisionParameters(serviceProvider, pending); + } + + function acceptProvisionParameters(address serviceProvider) external { + _acceptProvisionParameters(serviceProvider); + } +} diff --git a/packages/horizon/test/data-service/DataServiceBaseUpgradeable.sol b/packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol similarity index 83% rename from packages/horizon/test/data-service/DataServiceBaseUpgradeable.sol rename to packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol index 00f2b485b..9913c1b76 100644 --- a/packages/horizon/test/data-service/DataServiceBaseUpgradeable.sol +++ b/packages/horizon/test/data-service/implementations/DataServiceBaseUpgradeable.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.26; -import { DataService } from "../../contracts/data-service/DataService.sol"; -import { IGraphPayments } from "./../../contracts/interfaces/IGraphPayments.sol"; +import { DataService } from "../../../contracts/data-service/DataService.sol"; +import { IGraphPayments } from "./../../../contracts/interfaces/IGraphPayments.sol"; contract DataServiceBaseUpgradeable is DataService { constructor(address controller) DataService(controller) { diff --git a/packages/horizon/test/data-service/implementations/DataServiceOverride.sol b/packages/horizon/test/data-service/implementations/DataServiceOverride.sol new file mode 100644 index 000000000..838be68a5 --- /dev/null +++ b/packages/horizon/test/data-service/implementations/DataServiceOverride.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.26; + +import { DataServiceBase } from "./DataServiceBase.sol"; + +contract DataServiceOverride is DataServiceBase { + constructor(address controller) DataServiceBase(controller) initializer { + __DataService_init(); + } + + function _getProvisionTokensRange() internal pure override returns (uint256, uint256) { + return (PROVISION_TOKENS_MIN, PROVISION_TOKENS_MAX); + } + + function _getVerifierCutRange() internal pure override returns (uint32, uint32) { + return (VERIFIER_CUT_MIN, VERIFIER_CUT_MAX); + } + + function _getThawingPeriodRange() internal pure override returns (uint64, uint64) { + return (THAWING_PERIOD_MIN, THAWING_PERIOD_MAX); + } + + function _checkProvisionTokens(address _serviceProvider) internal pure override {} + function _checkProvisionParameters(address _serviceProvider, bool pending) internal pure override {} +} diff --git a/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol b/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol index 6155bf4c7..a7ec2f614 100644 --- a/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol +++ b/packages/horizon/test/data-service/libraries/ProvisionTracker.t.sol @@ -28,21 +28,10 @@ library ProvisionTrackerWrapper { contract ProvisionTrackerTest is HorizonStakingSharedTest, ProvisionTrackerImplementation { using ProvisionTrackerWrapper for mapping(address => uint256); - modifier useProvision( - uint256 tokens, - uint32, - uint64 - ) override { - vm.assume(tokens > 0); - vm.assume(tokens <= MAX_STAKING_TOKENS); - _createProvision(address(this), tokens, 0, 0); - _; - } - function test_Lock_GivenTheProvisionHasSufficientAvailableTokens( uint256 tokens, uint256 steps - ) external useIndexer useProvision(tokens, 0, 0) { + ) external useIndexer useProvisionDataService(address(this), tokens, 0, 0) { vm.assume(tokens > 0); vm.assume(steps > 0); vm.assume(steps < 100); @@ -63,7 +52,7 @@ contract ProvisionTrackerTest is HorizonStakingSharedTest, ProvisionTrackerImple function test_Lock_RevertGiven_TheProvisionHasInsufficientAvailableTokens( uint256 tokens - ) external useIndexer useProvision(tokens, 0, 0) { + ) external useIndexer useProvisionDataService(address(this), tokens, 0, 0) { uint256 tokensToLock = tokens + 1; vm.expectRevert( abi.encodeWithSelector(ProvisionTracker.ProvisionTrackerInsufficientTokens.selector, tokens, tokensToLock) @@ -74,7 +63,7 @@ contract ProvisionTrackerTest is HorizonStakingSharedTest, ProvisionTrackerImple function test_Release_GivenTheProvisionHasSufficientLockedTokens( uint256 tokens, uint256 steps - ) external useIndexer useProvision(tokens, 0, 0) { + ) external useIndexer useProvisionDataService(address(this), tokens, 0, 0) { vm.assume(tokens > 0); vm.assume(steps > 0); vm.assume(steps < 100); @@ -97,7 +86,7 @@ contract ProvisionTrackerTest is HorizonStakingSharedTest, ProvisionTrackerImple assertEq(provisionTracker[users.indexer], delta); } - function test_Release_RevertGiven_TheProvisionHasInsufficientLockedTokens(uint256 tokens) external useIndexer useProvision(tokens, 0, 0) { + function test_Release_RevertGiven_TheProvisionHasInsufficientLockedTokens(uint256 tokens) external useIndexer useProvisionDataService(address(this), tokens, 0, 0) { // setup provisionTracker.lock(staking, users.indexer, tokens, uint32(0)); diff --git a/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol b/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol index 8ab125dcb..9a30f53cc 100644 --- a/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol +++ b/packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol @@ -7,7 +7,6 @@ import { GraphBaseTest } from "../../GraphBase.t.sol"; import { IGraphPayments } from "../../../contracts/interfaces/IGraphPayments.sol"; abstract contract HorizonStakingSharedTest is GraphBaseTest { - /* * MODIFIERS */ @@ -24,12 +23,22 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { _; } - modifier useProvision(uint256 tokens, uint32 maxVerifierCut, uint64 thawingPeriod) virtual { - vm.assume(tokens <= MAX_STAKING_TOKENS); - vm.assume(tokens > 0); - vm.assume(maxVerifierCut <= MAX_MAX_VERIFIER_CUT); - vm.assume(thawingPeriod <= MAX_THAWING_PERIOD); - _createProvision(subgraphDataServiceAddress, tokens, maxVerifierCut, thawingPeriod); + modifier useProvision( + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) virtual { + _useProvision(subgraphDataServiceAddress, tokens, maxVerifierCut, thawingPeriod); + _; + } + + modifier useProvisionDataService( + address dataService, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) { + _useProvision(dataService, tokens, maxVerifierCut, thawingPeriod); _; } @@ -42,6 +51,19 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { * HELPERS */ + function _useProvision( + address dataService, + uint256 tokens, + uint32 maxVerifierCut, + uint64 thawingPeriod + ) internal { + vm.assume(tokens <= MAX_STAKING_TOKENS); + vm.assume(tokens > 0); + vm.assume(maxVerifierCut <= MAX_MAX_VERIFIER_CUT); + vm.assume(thawingPeriod <= MAX_THAWING_PERIOD); + _createProvision(dataService, tokens, maxVerifierCut, thawingPeriod); + } + function _createProvision( address dataServiceAddress, uint256 tokens, @@ -50,13 +72,7 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest { ) internal { token.approve(address(staking), tokens); staking.stakeTo(users.indexer, tokens); - staking.provision( - users.indexer, - dataServiceAddress, - tokens, - maxVerifierCut, - thawingPeriod - ); + staking.provision(users.indexer, dataServiceAddress, tokens, maxVerifierCut, thawingPeriod); } function _setDelegationFeeCut(IGraphPayments.PaymentTypes paymentType, uint256 cut) internal {