diff --git a/eth-contracts/contracts/ServiceProviderFactory.sol b/eth-contracts/contracts/ServiceProviderFactory.sol index 58979c06e1a..856a83d036f 100644 --- a/eth-contracts/contracts/ServiceProviderFactory.sol +++ b/eth-contracts/contracts/ServiceProviderFactory.sol @@ -189,9 +189,9 @@ contract ServiceProviderFactory is InitializableV2 { ); // Update min and max totals for this service provider - (uint typeMin, uint typeMax) = ServiceTypeManager( + (, uint typeMin, uint typeMax) = ServiceTypeManager( serviceTypeManagerAddress - ).getServiceTypeStakeInfo(_serviceType); + ).getServiceTypeInfo(_serviceType); spDetails[msg.sender].minAccountStake = spDetails[msg.sender].minAccountStake.add(typeMin); spDetails[msg.sender].maxAccountStake = spDetails[msg.sender].maxAccountStake.add(typeMax); @@ -281,9 +281,9 @@ contract ServiceProviderFactory is InitializableV2 { spDetails[msg.sender].numberOfEndpoints -= 1; // Update min and max totals for this service provider - (uint typeMin, uint typeMax) = ServiceTypeManager( + (, uint typeMin, uint typeMax) = ServiceTypeManager( serviceTypeManagerAddress - ).getServiceTypeStakeInfo(_serviceType); + ).getServiceTypeInfo(_serviceType); spDetails[msg.sender].minAccountStake = spDetails[msg.sender].minAccountStake.sub(typeMin); spDetails[msg.sender].maxAccountStake = spDetails[msg.sender].maxAccountStake.sub(typeMax); diff --git a/eth-contracts/contracts/ServiceTypeManager.sol b/eth-contracts/contracts/ServiceTypeManager.sol index 0057428cbd1..37b862ae936 100644 --- a/eth-contracts/contracts/ServiceTypeManager.sol +++ b/eth-contracts/contracts/ServiceTypeManager.sol @@ -21,14 +21,15 @@ contract ServiceTypeManager is InitializableV2 { /// @dev List of valid service types bytes32[] private validServiceTypes; - /// @dev Struct representing service type stake requirements - struct ServiceTypeStakeRequirements { + /// @dev Struct representing service type info + struct ServiceTypeInfo { + bool isValid; uint minStake; uint maxStake; } - /// @dev mapping of service type to registered requirements - mapping(bytes32 => ServiceTypeStakeRequirements) serviceTypeStakeRequirements; + /// @dev mapping of service type info + mapping(bytes32 => ServiceTypeInfo) serviceTypeInfo; event SetServiceVersion(bytes32 _serviceType, bytes32 _serviceVersion); event Test(string msg, bool value); @@ -61,7 +62,6 @@ contract ServiceTypeManager is InitializableV2 { // ========================================= Service Type Logic ========================================= - /// @notice Add a new service type /** * @notice Add a new service type * @param _serviceType - type of service to add @@ -78,9 +78,21 @@ contract ServiceTypeManager is InitializableV2 { require(msg.sender == governanceAddress, "Only callable by Governance contract"); require(!this.serviceTypeIsValid(_serviceType), "Already known service type"); + require( + _serviceTypeMax > _serviceTypeMin, + "Max stake must be non-zero and greater than min stake" + ); + + // Ensure serviceType cannot be re-added if it previously existed and was removed + // stored maxStake > 0 means it was previously added and removed + require( + serviceTypeInfo[_serviceType].maxStake == 0, + "Cannot re-add serviceType after it was removed." + ); validServiceTypes.push(_serviceType); - serviceTypeStakeRequirements[_serviceType] = ServiceTypeStakeRequirements({ + serviceTypeInfo[_serviceType] = ServiceTypeInfo({ + isValid: true, minStake: _serviceTypeMin, maxStake: _serviceTypeMax }); @@ -109,46 +121,24 @@ contract ServiceTypeManager is InitializableV2 { uint lastIndex = validServiceTypes.length - 1; validServiceTypes[serviceIndex] = validServiceTypes[lastIndex]; validServiceTypes.length--; - // Overwrite values - serviceTypeStakeRequirements[_serviceType].minStake = 0; - serviceTypeStakeRequirements[_serviceType].maxStake = 0; - } - - /** - * @notice Update a service type - * @param _serviceType - type of service - * @param _serviceTypeMin - minimum stake for service type - * @param _serviceTypeMax - maximum stake for service type - */ - function updateServiceType( - bytes32 _serviceType, - uint _serviceTypeMin, - uint _serviceTypeMax - ) external - { - _requireIsInitialized(); - require( - msg.sender == governanceAddress, - "Only callable by Governance contract" - ); - - require(this.serviceTypeIsValid(_serviceType), "Invalid service type"); - serviceTypeStakeRequirements[_serviceType].minStake = _serviceTypeMin; - serviceTypeStakeRequirements[_serviceType].maxStake = _serviceTypeMax; + // Mark as invalid + serviceTypeInfo[_serviceType].isValid = false; + // Note - stake bounds are not reset so they can be checked to prevent serviceType from being re-added } /** - * @notice Get min and max stake for a given service type + * @notice Get isValid, min and max stake for a given service type * @param _serviceType - type of service - * @return min and max stake for type + * @return isValid, min and max stake for type */ - function getServiceTypeStakeInfo(bytes32 _serviceType) - external view returns (uint min, uint max) + function getServiceTypeInfo(bytes32 _serviceType) + external view returns (bool isValid, uint minStake, uint maxStake) { return ( - serviceTypeStakeRequirements[_serviceType].minStake, - serviceTypeStakeRequirements[_serviceType].maxStake + serviceTypeInfo[_serviceType].isValid, + serviceTypeInfo[_serviceType].minStake, + serviceTypeInfo[_serviceType].maxStake ); } @@ -167,7 +157,7 @@ contract ServiceTypeManager is InitializableV2 { function serviceTypeIsValid(bytes32 _serviceType) external view returns (bool isValid) { - return serviceTypeStakeRequirements[_serviceType].maxStake > 0; + return serviceTypeInfo[_serviceType].isValid; } // ========================================= Service Version Logic ========================================= @@ -191,7 +181,7 @@ contract ServiceTypeManager is InitializableV2 { "Already registered" ); - // Update array of known types + // Update array of known versions for type serviceTypeVersions[_serviceType].push(_serviceVersion); // Update status for this specific service version diff --git a/eth-contracts/migrations/7_versioning_service_migration.js b/eth-contracts/migrations/7_versioning_service_migration.js index c9306c5756a..66343ecbdb9 100644 --- a/eth-contracts/migrations/7_versioning_service_migration.js +++ b/eth-contracts/migrations/7_versioning_service_migration.js @@ -82,10 +82,9 @@ module.exports = (deployer, network, accounts) => { callDataCN, { from: guardianAddress } ) - const serviceTypeCNStakeInfo = await serviceTypeManager.getServiceTypeStakeInfo.call(serviceTypeCN) - const [cnTypeMinV, cnTypeMaxV] = [serviceTypeCNStakeInfo[0], serviceTypeCNStakeInfo[1]] - assert.ok(_lib.toBN(cnTypeMin).eq(cnTypeMinV), 'Expected same minStake') - assert.ok(_lib.toBN(cnTypeMax).eq(cnTypeMaxV), 'Expected same max Stake') + const serviceTypeCNInfo = await serviceTypeManager.getServiceTypeInfo.call(serviceTypeCN) + assert.ok(_lib.toBN(cnTypeMin).eq(serviceTypeCNInfo.minStake), 'Expected same minStake') + assert.ok(_lib.toBN(cnTypeMax).eq(serviceTypeCNInfo.maxStake), 'Expected same max Stake') const callDataDP = _lib.abiEncode( ['bytes32', 'uint256', 'uint256'], @@ -98,10 +97,9 @@ module.exports = (deployer, network, accounts) => { callDataDP, { from: guardianAddress } ) - const serviceTypeDPStakeInfo = await serviceTypeManager.getServiceTypeStakeInfo.call(serviceTypeDP) - const [dpTypeMinV, dpTypeMaxV] = [serviceTypeDPStakeInfo[0], serviceTypeDPStakeInfo[1]] - assert.ok(_lib.toBN(dpTypeMin).eq(dpTypeMinV), 'Expected same minStake') - assert.ok(_lib.toBN(dpTypeMax).eq(dpTypeMaxV), 'Expected same maxStake') + const serviceTypeDPInfo = await serviceTypeManager.getServiceTypeInfo.call(serviceTypeDP) + assert.ok(_lib.toBN(dpTypeMin).eq(serviceTypeDPInfo.minStake), 'Expected same minStake') + assert.ok(_lib.toBN(dpTypeMax).eq(serviceTypeDPInfo.maxStake), 'Expected same maxStake') // Deploy ServiceProviderFactory logic and proxy contracts + register proxy const serviceProviderFactory0 = await deployer.deploy(ServiceProviderFactory, { from: proxyDeployerAddress }) diff --git a/eth-contracts/test/delegateManager.test.js b/eth-contracts/test/delegateManager.test.js index 31b20ece39b..5717acbeed4 100644 --- a/eth-contracts/test/delegateManager.test.js +++ b/eth-contracts/test/delegateManager.test.js @@ -17,6 +17,8 @@ const delegateManagerKey = web3.utils.utf8ToHex('DelegateManager') const tokenRegKey = web3.utils.utf8ToHex('Token') const testDiscProvType = web3.utils.utf8ToHex('discovery-provider') +const serviceTypeMinStake = _lib.audToWei(5) +const serviceTypeMaxStake = _lib.audToWei(10000000) const testEndpoint = 'https://localhost:5000' const testEndpoint1 = 'https://localhost:5001' const testEndpoint3 = 'https://localhost:5002' @@ -32,7 +34,7 @@ const callValue0 = _lib.toBN(0) contract('DelegateManager', async (accounts) => { let staking, stakingAddress, token, registry, governance, claimsManager0, claimsManagerProxy - let serviceProviderFactory, claimsManager, delegateManager + let serviceProviderFactory, serviceTypeManager, claimsManager, delegateManager // intentionally not using acct0 to make sure no TX accidentally succeeds without specifying sender const [, proxyAdminAddress, proxyDeployerAddress] = accounts @@ -43,6 +45,10 @@ contract('DelegateManager', async (accounts) => { const delegatorAccount1 = accounts[11] const slasherAccount = stakerAccount + /** + * Initialize Registry, Governance, Token, Staking, ServiceTypeManager, ServiceProviderFactory, ClaimsManager, DelegateManager + * Register discprov serviceType + */ beforeEach(async () => { registry = await _lib.deployRegistry(artifacts, proxyAdminAddress, proxyDeployerAddress) @@ -99,10 +105,10 @@ contract('DelegateManager', async (accounts) => { { from: proxyDeployerAddress } ) await registry.addContract(serviceTypeManagerProxyKey, serviceTypeManagerProxy.address, { from: proxyDeployerAddress }) - let serviceTypeManager = await ServiceTypeManager.at(serviceTypeManagerProxy.address) + serviceTypeManager = await ServiceTypeManager.at(serviceTypeManagerProxy.address) // Register discprov serviceType - await _lib.addServiceType(testDiscProvType, _lib.audToWei(5), _lib.audToWei(10000000), governance, guardianAddress, serviceTypeManagerProxyKey) + await _lib.addServiceType(testDiscProvType, serviceTypeMinStake, serviceTypeMaxStake, governance, guardianAddress, serviceTypeManagerProxyKey) // Deploy ServiceProviderFactory let serviceProviderFactory0 = await ServiceProviderFactory.new({ from: proxyDeployerAddress }) @@ -311,6 +317,11 @@ contract('DelegateManager', async (accounts) => { describe('Delegation tests', () => { let regTx + /** + * Transfer tokens to stakers & delegator + * Register service providers + * Update SP deployer cuts + */ beforeEach(async () => { // Transfer 1000 tokens to stakers await token.transfer(stakerAccount, INITIAL_BAL, { from: proxyDeployerAddress }) @@ -467,7 +478,7 @@ contract('DelegateManager', async (accounts) => { await time.advanceBlockTo(undelegateRequestInfo.lockupExpiryBlock) // Undelegate stake - delegateManager.undelegateStake({ from: delegatorAccount1 }) + await delegateManager.undelegateStake({ from: delegatorAccount1 }) // Confirm all state change operations have occurred undelegateRequestInfo = await delegateManager.getPendingUndelegateRequest(delegatorAccount1) @@ -1537,5 +1548,115 @@ contract('DelegateManager', async (accounts) => { ) }) }) + + it('Delegate ops after serviceType removal', async () => { + /** + * Confirm initial state of serviceType and serviceProvider + * Delegate stake to Staker + * Remove serviceType + * Confirm new state of serviceType and serviceProvider + * Confirm delegation to SP still works + * Deregister SP of serviceType + * Undelegate stake from Staker + * Confirm new delegation state + */ + + const minStakeBN = _lib.toBN(serviceTypeMinStake) + const maxStakeBN = _lib.toBN(serviceTypeMaxStake) + + // Confirm initial serviceType info + const stakeInfo0 = await serviceTypeManager.getServiceTypeInfo.call(testDiscProvType) + assert.isTrue(stakeInfo0.isValid, 'Expected isValid == true') + assert.isTrue(stakeInfo0.minStake.eq(minStakeBN), 'Expected same minStake') + assert.isTrue(stakeInfo0.maxStake.eq(maxStakeBN), 'Expected same maxStake') + + // Confirm initial SP details + const spDetails0 = await serviceProviderFactory.getServiceProviderDetails.call(stakerAccount) + assert.isTrue(spDetails0.deployerStake.eq(DEFAULT_AMOUNT), 'Expected deployerStake == default amount') + assert.isTrue(spDetails0.validBounds, 'Expected validBounds == true') + assert.isTrue(spDetails0.numberOfEndpoints.eq(_lib.toBN(1)), 'Expected one endpoint') + assert.isTrue(spDetails0.minAccountStake.eq(minStakeBN), 'Expected minAccountStake == dpTypeMin') + assert.isTrue(spDetails0.maxAccountStake.eq(maxStakeBN), 'Expected maxAccountStake == dpTypeMax') + + // Delegate stake to Staker + const delegationAmount = _lib.audToWeiBN(60) + await token.approve( + stakingAddress, + delegationAmount, + { from: delegatorAccount1 } + ) + await delegateManager.delegateStake( + stakerAccount, + delegationAmount, + { from: delegatorAccount1 } + ) + + // Remove serviceType + await governance.guardianExecuteTransaction( + serviceTypeManagerProxyKey, + callValue0, + 'removeServiceType(bytes32)', + _lib.abiEncode(['bytes32'], [testDiscProvType]), + { from: guardianAddress } + ) + + // Confirm serviceType info is changed after serviceType removal + const stakeInfo1 = await serviceTypeManager.getServiceTypeInfo.call(testDiscProvType) + assert.isFalse(stakeInfo1.isValid, 'Expected isValid == false') + assert.isTrue(stakeInfo1.minStake.eq(minStakeBN), 'Expected same minStake') + assert.isTrue(stakeInfo1.maxStake.eq(maxStakeBN), 'Expected same maxStake') + + // Confirm SP details are unchanged after serviceType removal + const spDetails = await serviceProviderFactory.getServiceProviderDetails.call(stakerAccount) + assert.isTrue(spDetails.deployerStake.eq(DEFAULT_AMOUNT), 'Expected deployerStake == default amount') + assert.isTrue(spDetails.validBounds, 'Expected validBounds == true') + assert.isTrue(spDetails.numberOfEndpoints.eq(_lib.toBN(1)), 'Expected one endpoint') + assert.isTrue(spDetails.minAccountStake.eq(minStakeBN), 'Expected minAccountStake == dpTypeMin') + assert.isTrue(spDetails.maxAccountStake.eq(maxStakeBN), 'Expected maxAccountStake == dpTypeMax') + + // Confirm delegation to SP still works after serviceType removal + await token.approve( + stakingAddress, + delegationAmount, + { from: delegatorAccount1 } + ) + await delegateManager.delegateStake( + stakerAccount, + delegationAmount, + { from: delegatorAccount1 } + ) + const totalDelegationAmount = delegationAmount.add(delegationAmount) + + // Deregister SP + unstake + await _lib.deregisterServiceProvider( + serviceProviderFactory, + testDiscProvType, + testEndpoint, + stakerAccount + ) + const deregisterRequestInfo = await serviceProviderFactory.getPendingDecreaseStakeRequest.call(stakerAccount) + await time.advanceBlockTo(deregisterRequestInfo.lockupExpiryBlock) + await serviceProviderFactory.decreaseStake({ from: stakerAccount }) + + // Undelegate total amount + await delegateManager.requestUndelegateStake( + stakerAccount, + totalDelegationAmount, + { from: delegatorAccount1 } + ) + const undelegateRequestInfo = await delegateManager.getPendingUndelegateRequest(delegatorAccount1) + await time.advanceBlockTo(undelegateRequestInfo.lockupExpiryBlock) + await delegateManager.undelegateStake({ from: delegatorAccount1 }) + + // Confirm delegation state + const totalStakedForSP = await staking.totalStakedFor(stakerAccount) + assert.isTrue(totalStakedForSP.isZero(), 'Expected totalStaked for SP == 0') + const delegators = await delegateManager.getDelegatorsList(stakerAccount) + assert.equal(delegators.length, 0, 'Expect delegators list length == 0') + const delegatedStake = await delegateManager.getTotalDelegatorStake(delegatorAccount1) + assert.isTrue(delegatedStake.isZero(), 'Expected delegatedStake == 0') + const totalLockedDelegationForSP = await delegateManager.getTotalLockedDelegationForServiceProvider(stakerAccount) + assert.isTrue(totalLockedDelegationForSP.isZero(), 'Expected totalLockedDelegationForSP == 0') + }) }) }) diff --git a/eth-contracts/test/governance.test.js b/eth-contracts/test/governance.test.js index dc9d2b14f61..519b4470a86 100644 --- a/eth-contracts/test/governance.test.js +++ b/eth-contracts/test/governance.test.js @@ -1415,16 +1415,16 @@ contract('Governance.sol', async (accounts) => { it('Transfer guardianship', async () => { const newGuardianAddress = accounts[19] - const newSpMinStake = spMinStake + 2 - const newSpMaxStake = spMaxStake + 2 + const serviceVersion1 = web3.utils.utf8ToHex("0.0.1") + const serviceVersion2 = web3.utils.utf8ToHex("0.0.2") // Confirm current guardianAddress is active assert.equal(await governance.getGuardianAddress(), guardianAddress, 'Expected same guardianAddress') await governance.guardianExecuteTransaction( serviceTypeManagerProxyKey, callValue0, - 'updateServiceType(bytes32,uint256,uint256)', - _lib.abiEncode(['bytes32', 'uint256', 'uint256'], [testDiscProvType, newSpMinStake, newSpMaxStake]), + 'setServiceVersion(bytes32,bytes32)', + _lib.abiEncode(['bytes32', 'bytes32'], [testDiscProvType, serviceVersion1]), { from: guardianAddress } ) @@ -1433,8 +1433,8 @@ contract('Governance.sol', async (accounts) => { governance.guardianExecuteTransaction( serviceTypeManagerProxyKey, callValue0, - 'updateServiceType(bytes32,uint256,uint256)', - _lib.abiEncode(['bytes32', 'uint256', 'uint256'], [testDiscProvType, newSpMinStake, newSpMaxStake]), + 'setServiceVersion(bytes32,bytes32)', + _lib.abiEncode(['bytes32', 'bytes32'], [testDiscProvType, serviceVersion2]), { from: newGuardianAddress } ), "Governance::guardianExecuteTransaction: Only guardian." @@ -1455,8 +1455,8 @@ contract('Governance.sol', async (accounts) => { governance.guardianExecuteTransaction( serviceTypeManagerProxyKey, callValue0, - 'updateServiceType(bytes32,uint256,uint256)', - _lib.abiEncode(['bytes32', 'uint256', 'uint256'], [testDiscProvType, newSpMinStake, newSpMaxStake]), + 'setServiceVersion(bytes32,bytes32)', + _lib.abiEncode(['bytes32', 'bytes32'], [testDiscProvType, serviceVersion2]), { from: guardianAddress } ), "Governance::guardianExecuteTransaction: Only guardian." @@ -1466,8 +1466,8 @@ contract('Governance.sol', async (accounts) => { await governance.guardianExecuteTransaction( serviceTypeManagerProxyKey, callValue0, - 'updateServiceType(bytes32,uint256,uint256)', - _lib.abiEncode(['bytes32', 'uint256', 'uint256'], [testDiscProvType, newSpMinStake, newSpMaxStake]), + 'setServiceVersion(bytes32,bytes32)', + _lib.abiEncode(['bytes32', 'bytes32'], [testDiscProvType, serviceVersion2]), { from: newGuardianAddress } ) }) @@ -1676,7 +1676,7 @@ contract('Governance.sol', async (accounts) => { await registryProxy.setAudiusGovernanceAddress(governance.address, { from: proxyAdminAddress }) // Upgrade registry proxy to new logic address - governance.guardianExecuteTransaction( + await governance.guardianExecuteTransaction( registryRegKey, callValue0, 'upgradeTo(address)', diff --git a/eth-contracts/test/serviceProvider.test.js b/eth-contracts/test/serviceProvider.test.js index 29639ec73c8..ae717d993d9 100644 --- a/eth-contracts/test/serviceProvider.test.js +++ b/eth-contracts/test/serviceProvider.test.js @@ -21,6 +21,7 @@ const testCreatorNodeType = web3.utils.utf8ToHex('creator-node') const testInvalidType = web3.utils.utf8ToHex('invalid-type') const testEndpoint = 'https://localhost:5000' const testEndpoint1 = 'https://localhost:5001' +const testEndpoint2 = 'https://localhost:5002' const MIN_STAKE_AMOUNT = 10 const VOTING_PERIOD = 10 @@ -40,6 +41,7 @@ contract('ServiceProvider test', async (accounts) => { const guardianAddress = proxyDeployerAddress const stakerAccount = accounts[11] const stakerAccount2 = accounts[12] + const stakerAccount3 = accounts[13] const callValue = _lib.toBN(0) const cnTypeMin = _lib.audToWei(10) const cnTypeMax = _lib.audToWei(10000000) @@ -97,6 +99,12 @@ contract('ServiceProvider test', async (accounts) => { return false } + /** + * Initialize Registry, Governance, Token, Staking, ServiceTypeManager, ClaimsManager, ServiceProviderFactory + * Deploy MockDelegateManager for processClaim + * add service types creatornode and discprov + * Transfer 1000 tokens to accounts[11] + */ beforeEach(async () => { // Deploy registry registry = await _lib.deployRegistry(artifacts, proxyAdminAddress, proxyDeployerAddress) @@ -133,7 +141,6 @@ contract('ServiceProvider test', async (accounts) => { governance.address ] ) - proxy = await AudiusAdminUpgradeabilityProxy.new( staking0.address, proxyAdminAddress, @@ -141,9 +148,9 @@ contract('ServiceProvider test', async (accounts) => { governance.address, { from: proxyDeployerAddress } ) - staking = await Staking.at(proxy.address) await registry.addContract(stakingProxyKey, proxy.address, { from: proxyDeployerAddress }) + // Deploy + register ServiceTypeManager let serviceTypeInitializeData = _lib.encodeCall( 'initialize', ['address'], [governance.address] @@ -159,7 +166,7 @@ contract('ServiceProvider test', async (accounts) => { serviceTypeManager = await ServiceTypeManager.at(serviceTypeManagerProxy.address) await registry.addContract(serviceTypeManagerProxyKey, serviceTypeManager.address, { from: proxyDeployerAddress }) - // Deploy claimsManagerProxy + // Deploy + register claimsManagerProxy claimsManager0 = await ClaimsManager.new({ from: proxyDeployerAddress }) const claimsInitializeCallData = _lib.encodeCall( 'initialize', @@ -174,8 +181,6 @@ contract('ServiceProvider test', async (accounts) => { { from: proxyDeployerAddress } ) claimsManager = await ClaimsManager.at(claimsManagerProxy.address) - - // Register claimsManagerProxy await registry.addContract(claimsManagerProxyKey, claimsManagerProxy.address, { from: proxyDeployerAddress }) // Deploy mock delegate manager with only function to forward processClaim call @@ -185,16 +190,16 @@ contract('ServiceProvider test', async (accounts) => { /** addServiceTypes creatornode and discprov via Governance */ await _lib.addServiceType(testCreatorNodeType, cnTypeMin, cnTypeMax, governance, guardianAddress, serviceTypeManagerProxyKey) - const serviceTypeCNStakeInfo = await serviceTypeManager.getServiceTypeStakeInfo.call(testCreatorNodeType) - const [cnTypeMinV, cnTypeMaxV] = [serviceTypeCNStakeInfo[0], serviceTypeCNStakeInfo[1]] - assert.equal(cnTypeMin, cnTypeMinV, 'Expected same minStake') - assert.equal(cnTypeMax, cnTypeMaxV, 'Expected same maxStake') + const serviceTypeCNInfo = await serviceTypeManager.getServiceTypeInfo.call(testCreatorNodeType) + assert.isTrue(serviceTypeCNInfo.isValid, 'Expected serviceTypeCN isValid') + assert.isTrue(serviceTypeCNInfo.minStake.eq(_lib.toBN(cnTypeMin)), 'Expected same minStake') + assert.isTrue(serviceTypeCNInfo.maxStake.eq(_lib.toBN(cnTypeMax)), 'Expected same maxStake') await _lib.addServiceType(testDiscProvType, dpTypeMin, dpTypeMax, governance, guardianAddress, serviceTypeManagerProxyKey) - const serviceTypeDPStakeInfo = await serviceTypeManager.getServiceTypeStakeInfo.call(testDiscProvType) - const [dpTypeMinV, dpTypeMaxV] = [serviceTypeDPStakeInfo[0], serviceTypeDPStakeInfo[1]] - assert.equal(dpTypeMin, dpTypeMinV, 'Expected same minStake') - assert.equal(dpTypeMax, dpTypeMaxV, 'Expected same maxStake') + const serviceTypeDPInfo = await serviceTypeManager.getServiceTypeInfo.call(testDiscProvType) + assert.isTrue(serviceTypeDPInfo.isValid, 'Expected serviceTypeDP isValid') + assert.isTrue(serviceTypeDPInfo.minStake.eq(_lib.toBN(dpTypeMin)), 'Expected same minStake') + assert.isTrue(serviceTypeDPInfo.maxStake.eq(_lib.toBN(dpTypeMax)), 'Expected same maxStake') // Deploy + register ServiceProviderFactory let serviceProviderFactory0 = await ServiceProviderFactory.new({ from: proxyDeployerAddress }) @@ -215,6 +220,7 @@ contract('ServiceProvider test', async (accounts) => { // Transfer 1000 tokens to accounts[11] await token.transfer(accounts[11], INITIAL_BAL, { from: proxyDeployerAddress }) + // ---- Configuring addresses await _lib.configureGovernanceStakingAddress( governance, @@ -278,6 +284,9 @@ contract('ServiceProvider test', async (accounts) => { describe('Registration flow', () => { let regTx + /** + * Register serviceProvider of testDiscProvType + confirm initial state + */ beforeEach(async () => { const initialBal = await token.balanceOf(stakerAccount, { from: proxyDeployerAddress }) @@ -318,185 +327,12 @@ contract('ServiceProvider test', async (accounts) => { 'Expected default stake amount' ) - const spTypeInfo = await serviceTypeManager.getServiceTypeStakeInfo(testDiscProvType) - const typeMin = _lib.fromWei(spTypeInfo[0]) - const typeMax = _lib.fromWei(spTypeInfo[1]) - - // Validate stake requirements - // Both current account bounds and single testDiscProvType bounds expected to be equal - assert.equal( - typeMin, - _lib.fromWei(spDetails.minAccountStake), - 'Expect account min to equal sp type 1 min' - ) - assert.equal( - typeMax, - _lib.fromWei(spDetails.maxAccountStake), - 'Expect account max to equal sp type 1 max' - ) + // Validate serviceType info + const serviceTypeDPInfo = await serviceTypeManager.getServiceTypeInfo(testDiscProvType) + assert.isTrue(serviceTypeDPInfo.minStake.eq(spDetails.minAccountStake), 'Expected serviceTypeDP minStake == sp 1 minAccountStake') + assert.isTrue(serviceTypeDPInfo.maxStake.eq(spDetails.maxAccountStake), 'Expected serviceTypeDP maxStake == sp 1 maxAccountStake') }) - const multipleEndpointScenario = async () => { - let increaseAmt = DEFAULT_AMOUNT - let initialBal = await token.balanceOf(stakerAccount) - let initialStake = await getStakeAmountForAccount(stakerAccount) - - // 2nd endpoint for stakerAccount = https://localhost:5001 - // discovery-provider - // Total Stake = 240 AUD - let registerInfo = await _lib.registerServiceProvider( - token, - staking, - serviceProviderFactory, - testDiscProvType, - testEndpoint1, - increaseAmt, - stakerAccount) - let newSPId = registerInfo.spID - - // Confirm change in token balance - let finalBal = await token.balanceOf(stakerAccount) - let finalStake = await getStakeAmountForAccount(stakerAccount) - - assert.equal( - (initialBal - finalBal), - increaseAmt, - 'Expected decrease in final balance') - - assert.equal( - (finalStake - initialStake), - increaseAmt, - 'Expected increase in total stake') - - let newIdFound = await serviceProviderIDRegisteredToAccount( - stakerAccount, - testDiscProvType, - newSPId) - assert.isTrue(newIdFound, 'Expected valid new ID') - - // 3rd endpoint for stakerAccount - // Transfer 1000 tokens to staker for test - await token.transfer(stakerAccount, INITIAL_BAL, { from: proxyDeployerAddress }) - - let stakedAmount = await staking.totalStakedFor(stakerAccount) - let spDetails = await serviceProviderFactory.getServiceProviderDetails(stakerAccount) - let accountMin = _lib.fromWei(spDetails.minAccountStake) - let accountMax = _lib.fromWei(spDetails.maxAccountStake) - - let accountDiff = _lib.fromWei(stakedAmount) - accountMin - - await decreaseRegisteredProviderStake(_lib.audToWeiBN(accountDiff), stakerAccount) - stakedAmount = await staking.totalStakedFor(stakerAccount) - - let testCnodeEndpoint1 = 'https://localhost:4000' - let testCnodeEndpoint2 = 'https://localhost:4001' - - let cnTypeInfo = await serviceTypeManager.getServiceTypeStakeInfo(testCreatorNodeType) - let cnTypeMin = cnTypeInfo[0] - let cnTypeMax = cnTypeInfo[1] - let dpTypeInfo = await serviceTypeManager.getServiceTypeStakeInfo(testDiscProvType) - let dpTypeMin = dpTypeInfo[0] - - // Total Stake = 240 AUD <-- Expect failure - await _lib.assertRevert( - _lib.registerServiceProvider( - token, - staking, - serviceProviderFactory, - testCreatorNodeType, - testCnodeEndpoint1, - 0, - stakerAccount), - 'Minimum stake requirement not met') - - // 3rd endpoint for stakerAccount = https://localhost:4001 - // creator-node - await _lib.registerServiceProvider( - token, - staking, - serviceProviderFactory, - testCreatorNodeType, - testCnodeEndpoint1, - cnTypeMin, - stakerAccount) - - let testDiscProvs = await getServiceProviderIdsFromAddress(stakerAccount, testDiscProvType) - let testCnodes = await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType) - let cnodeMinStake = cnTypeMin * testCnodes.length - let dpMinStake = dpTypeMin * testDiscProvs.length - - stakedAmount = await staking.totalStakedFor(stakerAccount) - assert.equal(stakedAmount, dpMinStake + cnodeMinStake, 'Expect min staked with total endpoints') - - spDetails = await serviceProviderFactory.getServiceProviderDetails(stakerAccount) - let stakedAmountWei = _lib.fromWei(stakedAmount) - accountMin = _lib.fromWei(spDetails.minAccountStake) - accountMax = _lib.fromWei(spDetails.maxAccountStake) - assert.equal(stakedAmountWei, accountMin, 'Expect min staked with total endpoints') - - accountDiff = accountMax - stakedAmountWei - // Generate BNjs value - let transferAmount = web3.utils.toBN(accountDiff) - .add(web3.utils.toBN(_lib.fromWei(cnTypeMax))) - .add(web3.utils.toBN(200)) - - // Transfer greater than max tokens - await token.transfer(stakerAccount, _lib.audToWeiBN(transferAmount), { from: proxyDeployerAddress }) - - // Attempt to register, expect max stake bounds to be exceeded - await _lib.assertRevert( - _lib.registerServiceProvider( - token, - staking, - serviceProviderFactory, - testCreatorNodeType, - testCnodeEndpoint2, - _lib.audToWeiBN(transferAmount), - stakerAccount), - 'Maximum stake' - ) - - let numCnodeEndpoints = await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType) - - // 4th endpoint for service provider - // creator-node - await _lib.registerServiceProvider( - token, - staking, - serviceProviderFactory, - testCreatorNodeType, - testCnodeEndpoint2, - cnTypeMin, - stakerAccount) - - assert.equal( - numCnodeEndpoints.length + 1, - (await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType)).length, - 'Expect increase in number of endpoints') - - stakedAmount = await staking.totalStakedFor(stakerAccount) - numCnodeEndpoints = await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType) - - // Confirm failure to deregister invalid endpoint and service type combination - await _lib.assertRevert( - _lib.deregisterServiceProvider( - serviceProviderFactory, - testCreatorNodeType, - testEndpoint, - stakerAccount - ), - 'Invalid endpoint for service type' - ) - - // Successfully deregister - await _lib.deregisterServiceProvider( - serviceProviderFactory, - testCreatorNodeType, - testCnodeEndpoint2, - stakerAccount) - assert.isTrue(stakedAmount.eq(await staking.totalStakedFor(stakerAccount)), 'Expect no stake change') - } - it('Confirm correct stake for account', async () => { assert.isTrue((await getStakeAmountForAccount(stakerAccount)).eq(DEFAULT_AMOUNT)) }) @@ -517,6 +353,28 @@ contract('ServiceProvider test', async (accounts) => { ) }) + it('Fail to add serviceType with invalid bounds', async () => { + const testOtherType = web3.utils.utf8ToHex('other') + + // Register with zero maxbounds fails + await _lib.assertRevert( + _lib.addServiceType(testOtherType, _lib.audToWei(4), _lib.audToWei(0), governance, guardianAddress, serviceTypeManagerProxyKey) + /* Cannot check revert msg bc call is made via governance */ + ) + + // Register with max stake <= min stake fails + await _lib.assertRevert( + _lib.addServiceType(testOtherType, _lib.audToWei(4), _lib.audToWei(2), governance, guardianAddress, serviceTypeManagerProxyKey) + /* Cannot check revert msg bc call is made via governance */ + ) + + // Register with zero min and maxbounds fails + await _lib.assertRevert( + _lib.addServiceType(testOtherType, _lib.audToWei(0), _lib.audToWei(0), governance, guardianAddress, serviceTypeManagerProxyKey) + /* Cannot check revert msg bc call is made via governance */ + ) + }) + it('Deregister endpoint and confirm transfer of staking balance to owner', async () => { // Confirm staking contract has correct amt assert.isTrue((await getStakeAmountForAccount(stakerAccount)).eq(DEFAULT_AMOUNT)) @@ -566,14 +424,16 @@ contract('ServiceProvider test', async (accounts) => { }) it('Min direct deployer stake violation', async () => { - let minDirectStake = await serviceProviderFactory.getMinDeployerStake() + const minDirectStake = await serviceProviderFactory.getMinDeployerStake() + // Calculate an invalid direct stake amount - let invalidDirectStake = minDirectStake.sub(_lib.toBN(1)) + const invalidDirectStake = minDirectStake.sub(_lib.toBN(1)) await token.transfer(stakerAccount2, invalidDirectStake, { from: proxyDeployerAddress }) - let dpTypeInfo = await serviceTypeManager.getServiceTypeStakeInfo(testDiscProvType) - let dpTypeMin = dpTypeInfo[0] + + const dpTypeInfo = await serviceTypeManager.getServiceTypeInfo(testDiscProvType) + assert.isTrue(invalidDirectStake.gt(dpTypeInfo.minStake), 'Invalid direct stake above dp type min') + // Validate that this value won't violate service type minimum - assert.isTrue(invalidDirectStake.gt(dpTypeMin), 'Invalid direct stake above dp type min') await _lib.assertRevert( _lib.registerServiceProvider( token, @@ -583,7 +443,8 @@ contract('ServiceProvider test', async (accounts) => { testEndpoint1, invalidDirectStake, stakerAccount2), - 'Direct stake restriction violated') + 'Direct stake restriction violated' + ) }) it('Update service provider cut', async () => { @@ -806,7 +667,164 @@ contract('ServiceProvider test', async (accounts) => { }) it('Multiple endpoints w/multiple accounts varying stake', async () => { - await multipleEndpointScenario() + let increaseAmt = DEFAULT_AMOUNT + let initialBal = await token.balanceOf(stakerAccount) + let initialStake = await getStakeAmountForAccount(stakerAccount) + + // 2nd endpoint for stakerAccount = https://localhost:5001 + // discovery-provider + // Total Stake = 240 AUD + let registerInfo = await _lib.registerServiceProvider( + token, + staking, + serviceProviderFactory, + testDiscProvType, + testEndpoint1, + increaseAmt, + stakerAccount) + let newSPId = registerInfo.spID + + // Confirm change in token balance + let finalBal = await token.balanceOf(stakerAccount) + let finalStake = await getStakeAmountForAccount(stakerAccount) + + assert.equal( + (initialBal - finalBal), + increaseAmt, + 'Expected decrease in final balance') + + assert.equal( + (finalStake - initialStake), + increaseAmt, + 'Expected increase in total stake') + + let newIdFound = await serviceProviderIDRegisteredToAccount( + stakerAccount, + testDiscProvType, + newSPId) + assert.isTrue(newIdFound, 'Expected valid new ID') + + // 3rd endpoint for stakerAccount + // Transfer 1000 tokens to staker for test + await token.transfer(stakerAccount, INITIAL_BAL, { from: proxyDeployerAddress }) + + let stakedAmount = await staking.totalStakedFor(stakerAccount) + let spDetails = await serviceProviderFactory.getServiceProviderDetails(stakerAccount) + let accountMin = _lib.fromWei(spDetails.minAccountStake) + let accountMax = _lib.fromWei(spDetails.maxAccountStake) + + let accountDiff = _lib.fromWei(stakedAmount) - accountMin + + await decreaseRegisteredProviderStake(_lib.audToWeiBN(accountDiff), stakerAccount) + stakedAmount = await staking.totalStakedFor(stakerAccount) + + let testCnodeEndpoint1 = 'https://localhost:4000' + let testCnodeEndpoint2 = 'https://localhost:4001' + + let cnTypeInfo = await serviceTypeManager.getServiceTypeInfo(testCreatorNodeType) + let cnTypeMin = cnTypeInfo.minStake + let cnTypeMax = cnTypeInfo.maxStake + let dpTypeInfo = await serviceTypeManager.getServiceTypeInfo(testDiscProvType) + let dpTypeMin = dpTypeInfo.minStake + + // Total Stake = 240 AUD <-- Expect failure + await _lib.assertRevert( + _lib.registerServiceProvider( + token, + staking, + serviceProviderFactory, + testCreatorNodeType, + testCnodeEndpoint1, + 0, + stakerAccount), + 'Minimum stake requirement not met') + + // 3rd endpoint for stakerAccount = https://localhost:4001 + // creator-node + await _lib.registerServiceProvider( + token, + staking, + serviceProviderFactory, + testCreatorNodeType, + testCnodeEndpoint1, + cnTypeMin, + stakerAccount) + + let testDiscProvs = await getServiceProviderIdsFromAddress(stakerAccount, testDiscProvType) + let testCnodes = await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType) + let cnodeMinStake = cnTypeMin * testCnodes.length + let dpMinStake = dpTypeMin * testDiscProvs.length + + stakedAmount = await staking.totalStakedFor(stakerAccount) + assert.equal(stakedAmount, dpMinStake + cnodeMinStake, 'Expect min staked with total endpoints') + + spDetails = await serviceProviderFactory.getServiceProviderDetails(stakerAccount) + let stakedAmountWei = _lib.fromWei(stakedAmount) + accountMin = _lib.fromWei(spDetails.minAccountStake) + accountMax = _lib.fromWei(spDetails.maxAccountStake) + assert.equal(stakedAmountWei, accountMin, 'Expect min staked with total endpoints') + + accountDiff = accountMax - stakedAmountWei + // Generate BNjs value + let transferAmount = web3.utils.toBN(accountDiff) + .add(web3.utils.toBN(_lib.fromWei(cnTypeMax))) + .add(web3.utils.toBN(200)) + + // Transfer greater than max tokens + await token.transfer(stakerAccount, _lib.audToWeiBN(transferAmount), { from: proxyDeployerAddress }) + + // Attempt to register, expect max stake bounds to be exceeded + await _lib.assertRevert( + _lib.registerServiceProvider( + token, + staking, + serviceProviderFactory, + testCreatorNodeType, + testCnodeEndpoint2, + _lib.audToWeiBN(transferAmount), + stakerAccount), + 'Maximum stake' + ) + + let numCnodeEndpoints = await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType) + + // 4th endpoint for service provider + // creator-node + await _lib.registerServiceProvider( + token, + staking, + serviceProviderFactory, + testCreatorNodeType, + testCnodeEndpoint2, + cnTypeMin, + stakerAccount) + + assert.equal( + numCnodeEndpoints.length + 1, + (await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType)).length, + 'Expect increase in number of endpoints') + + stakedAmount = await staking.totalStakedFor(stakerAccount) + numCnodeEndpoints = await getServiceProviderIdsFromAddress(stakerAccount, testCreatorNodeType) + + // Confirm failure to deregister invalid endpoint and service type combination + await _lib.assertRevert( + _lib.deregisterServiceProvider( + serviceProviderFactory, + testCreatorNodeType, + testEndpoint, + stakerAccount + ), + 'Invalid endpoint for service type' + ) + + // Successfully deregister + await _lib.deregisterServiceProvider( + serviceProviderFactory, + testCreatorNodeType, + testCnodeEndpoint2, + stakerAccount) + assert.isTrue(stakedAmount.eq(await staking.totalStakedFor(stakerAccount)), 'Expect no stake change') }) /* @@ -972,7 +990,7 @@ contract('ServiceProvider test', async (accounts) => { const addServiceTypeSignature = 'addServiceType(bytes32,uint256,uint256)' let isValid = await serviceTypeManager.serviceTypeIsValid(testType) - assert.isTrue(!isValid, 'Invalid type expected') + assert.isFalse(isValid, 'Invalid type expected') // Expect failure as service type has not been registered await _lib.assertRevert( @@ -1112,47 +1130,12 @@ contract('ServiceProvider test', async (accounts) => { isValid = await serviceTypeManager.serviceTypeIsValid(testType) assert.isTrue(isValid, 'Expect valid type after registration') - let info = await serviceTypeManager.getServiceTypeStakeInfo(testType) - assert.isTrue(typeMin.eq(info.min), 'Min values not equal') - assert.isTrue(typeMax.eq(info.max), 'Max values not equal') - - const newMinVal = _lib.audToWei(300) - const newMaxVal = _lib.audToWei(40000) - const newMin = _lib.toBN(newMinVal) - const newMax = _lib.toBN(newMaxVal) - - // Expect failure from invalid account - await _lib.assertRevert( - serviceTypeManager.updateServiceType(testType, newMin, newMax, { from: accounts[12] }), - 'Only callable by Governance contract.' - ) + const info = await serviceTypeManager.getServiceTypeInfo(testType) + assert.isTrue(info.isValid, 'Expected testType to be valid') + assert.isTrue(info.minStake.eq(_lib.toBN(typeMin)), 'Min values not equal') + assert.isTrue(info.maxStake.eq(_lib.toBN(typeMax)), 'Max values not equal') - // updateServiceType should fail with unregistered serviceType const unregisteredType = web3.utils.utf8ToHex('invalid-service') - await _lib.assertRevert( - governance.guardianExecuteTransaction( - serviceTypeManagerProxyKey, - callValue, - 'updateServiceType(bytes32,uint256,uint256)', - _lib.abiEncode(['bytes32', 'uint256', 'uint256'], [unregisteredType, newMinVal, newMaxVal]), - { from: guardianAddress } - ), - "Governance::guardianExecuteTransaction: Transaction failed." - ) - - // updateServiceType successfully - await governance.guardianExecuteTransaction( - serviceTypeManagerProxyKey, - callValue, - 'updateServiceType(bytes32,uint256,uint256)', - _lib.abiEncode(['bytes32', 'uint256', 'uint256'], [testType, newMinVal, newMaxVal]), - { from: guardianAddress } - ) - - // Confirm serviceType was updated - info = await serviceTypeManager.getServiceTypeStakeInfo(testType) - assert.isTrue(newMin.eq(info.min), 'Min values not equal') - assert.isTrue(newMax.eq(info.max), 'Max values not equal') // removeServiceType fails when not called from Governance await _lib.assertRevert( @@ -1182,7 +1165,7 @@ contract('ServiceProvider test', async (accounts) => { // Confirm serviceType is no longer valid after removal isValid = await serviceTypeManager.serviceTypeIsValid(testType) - assert.isTrue(!isValid, 'Expect invalid type after deregistration') + assert.isFalse(isValid, 'Expect invalid type after deregistration') // setGovernanceAddress in ServiceTypeManager.sol await governance.guardianExecuteTransaction( @@ -1198,5 +1181,96 @@ contract('ServiceProvider test', async (accounts) => { "Didn't update governance address correctly in ServiceTypeManager" ) }) + + it('Operations after serviceType removal', async () => { + /** + * Confirm initial state of serviceType and serviceProvider + * Remove serviceType + * Confirm new state of serviceType and serviceProvider + * Deregister SP of serviceType + * Confirm new state of serviceType and serviceProvider + * Attempt to register new SP after serviceType removal + * Confirm serviceType cannot be re-added + */ + + const minStakeBN = _lib.toBN(dpTypeMin) + const maxStakeBN = _lib.toBN(dpTypeMax) + + // Confirm initial serviceType info + const stakeInfo0 = await serviceTypeManager.getServiceTypeInfo.call(testDiscProvType) + assert.isTrue(stakeInfo0.isValid, 'Expected isValid == true') + assert.isTrue(stakeInfo0.minStake.eq(minStakeBN), 'Expected same minStake') + assert.isTrue(stakeInfo0.maxStake.eq(maxStakeBN), 'Expected same maxStake') + + // Confirm initial SP details + const spDetails0 = await serviceProviderFactory.getServiceProviderDetails.call(stakerAccount) + assert.isTrue(spDetails0.deployerStake.eq(DEFAULT_AMOUNT), 'Expected deployerStake == default amount') + assert.isTrue(spDetails0.validBounds, 'Expected validBounds == true') + assert.isTrue(spDetails0.numberOfEndpoints.eq(_lib.toBN(1)), 'Expected one endpoint') + assert.isTrue(spDetails0.minAccountStake.eq(minStakeBN), 'Expected minAccountStake == dpTypeMin') + assert.isTrue(spDetails0.maxAccountStake.eq(maxStakeBN), 'Expected maxAccountStake == dpTypeMax') + + // Remove serviceType + await governance.guardianExecuteTransaction( + serviceTypeManagerProxyKey, + callValue, + 'removeServiceType(bytes32)', + _lib.abiEncode(['bytes32'], [testDiscProvType]), + { from: guardianAddress } + ) + + // Confirm serviceType info is changed after serviceType removal + const stakeInfo1 = await serviceTypeManager.getServiceTypeInfo.call(testDiscProvType) + assert.isFalse(stakeInfo1.isValid, 'Expected isValid == false') + assert.isTrue(stakeInfo1.minStake.eq(minStakeBN), 'Expected same minStake') + assert.isTrue(stakeInfo1.maxStake.eq(maxStakeBN), 'Expected same maxStake') + + // Confirm SP details are unchanged after serviceType removal + const spDetails1 = await serviceProviderFactory.getServiceProviderDetails.call(stakerAccount) + assert.isTrue(spDetails1.deployerStake.eq(DEFAULT_AMOUNT), 'Expected deployerStake == default amount') + assert.isTrue(spDetails1.validBounds, 'Expected validBounds == true') + assert.isTrue(spDetails1.numberOfEndpoints.eq(_lib.toBN(1)), 'Expected one endpoint') + assert.isTrue(spDetails1.minAccountStake.eq(minStakeBN), 'Expected minAccountStake == dpTypeMin') + assert.isTrue(spDetails1.maxAccountStake.eq(maxStakeBN), 'Expected maxAccountStake == dpTypeMax') + + // Deregister SP + unstake + await _lib.deregisterServiceProvider( + serviceProviderFactory, + testDiscProvType, + testEndpoint, + stakerAccount + ) + const deregisterRequestInfo = await serviceProviderFactory.getPendingDecreaseStakeRequest.call(stakerAccount) + await time.advanceBlockTo(deregisterRequestInfo.lockupExpiryBlock) + await serviceProviderFactory.decreaseStake({ from: stakerAccount }) + + // Confirm SP details are changed after deregistration + const spDetails2 = await serviceProviderFactory.getServiceProviderDetails.call(stakerAccount) + assert.isTrue(spDetails2.deployerStake.isZero(), 'Expected deployerStake == 0') + assert.isTrue(spDetails2.validBounds, 'Expected validBounds == true') + assert.isTrue(spDetails2.numberOfEndpoints.isZero(), 'Expected numberOfEndpoints == 0') + assert.isTrue(spDetails2.minAccountStake.isZero(), 'Expected minAccountStake == 0') + assert.isTrue(spDetails2.maxAccountStake.isZero(), 'Expected maxAccountStake == 0') + + // Confirm new SP cannot be registered after serviceType removal + await _lib.assertRevert( + _lib.registerServiceProvider( + token, + staking, + serviceProviderFactory, + testDiscProvType, + testEndpoint2, + DEFAULT_AMOUNT, + stakerAccount3 + ), + "Valid service type required" + ) + + // Confirm serviceType cannot be re-added + await _lib.assertRevert( + _lib.addServiceType(testDiscProvType, dpTypeMin, dpTypeMax, governance, guardianAddress, serviceTypeManagerProxyKey) + /* Cannot check revert msg bc call is made via governance */ + ) + }) }) })