From 8eafef3f1c5b7204975b911012c0fc8256609252 Mon Sep 17 00:00:00 2001 From: Victoria Zotova Date: Tue, 11 Jul 2023 17:05:58 -0400 Subject: [PATCH] PRECBDApplication: syncing amount with StakeInfo, tests for syncing operator with StakeInfo --- contracts/contracts/PRECBDApplication.sol | 35 ++++++++++++++++--- tests/application/test_authorization.py | 42 +++++++++++++++++++++++ tests/application/test_operator.py | 28 +++++++++++++-- 3 files changed, 97 insertions(+), 8 deletions(-) diff --git a/contracts/contracts/PRECBDApplication.sol b/contracts/contracts/PRECBDApplication.sol index fa0ea291..ef232b97 100644 --- a/contracts/contracts/PRECBDApplication.sol +++ b/contracts/contracts/PRECBDApplication.sol @@ -376,6 +376,7 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { info.authorized = _toAmount; emit AuthorizationIncreased(_stakingProvider, _fromAmount, _toAmount); + _updateAuthorization(_stakingProvider, info); } /** @@ -406,9 +407,9 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { if (info.authorized == 0) { _stakingProviderFromOperator[info.operator] = address(0); info.operator = address(0); - info.operatorConfirmed = false; _releaseOperator(_stakingProvider); } + _updateAuthorization(_stakingProvider, info); } /** @@ -438,6 +439,7 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { info.deauthorizing = _fromAmount - _toAmount; info.endDeauthorization = uint64(block.timestamp + deauthorizationDuration); emit AuthorizationDecreaseRequested(_stakingProvider, _fromAmount, _toAmount); + _updateAuthorization(_stakingProvider, info); } /** @@ -463,9 +465,9 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { if (info.authorized == 0) { _stakingProviderFromOperator[info.operator] = address(0); info.operator = address(0); - info.operatorConfirmed = false; _releaseOperator(_stakingProvider); } + _updateAuthorization(_stakingProvider, info); } /** @@ -490,9 +492,9 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { if (info.authorized == 0) { _stakingProviderFromOperator[info.operator] = address(0); info.operator = address(0); - info.operatorConfirmed = false; _releaseOperator(_stakingProvider); } + _updateAuthorization(_stakingProvider, info); } //-------------------------Main------------------------- @@ -517,6 +519,13 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { return stakingProviderInfo[_stakingProvider].authorized; } + /** + * @notice Get all tokens delegated to the staking provider + */ + function getEligibleAmount(StakingProviderInfo storage _info) internal view returns (uint96) { + return _info.authorized - _info.deauthorizing; + } + /** * @notice Get the value of authorized tokens for active providers as well as providers and their authorized tokens * @param _startIndex Start index for looking in providers array @@ -542,7 +551,7 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { for (uint256 i = _startIndex; i < endIndex; i++) { address stakingProvider = stakingProviders[i]; StakingProviderInfo storage info = stakingProviderInfo[stakingProvider]; - uint256 eligibleAmount = info.authorized - info.deauthorizing; + uint256 eligibleAmount = getEligibleAmount(info); if (eligibleAmount < minAuthorization || !info.operatorConfirmed) { continue; } @@ -629,7 +638,6 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { // Bond new operator (or unbond if _operator == address(0)) info.operator = _operator; info.operatorStartTimestamp = uint64(block.timestamp); - info.operatorConfirmed = false; emit OperatorBonded(_stakingProvider, _operator, previousOperator, block.timestamp); _releaseOperator(_stakingProvider); } @@ -653,13 +661,30 @@ contract PRECBDApplication is IApplication, OwnableUpgradeable { updatableStakeInfo.updateOperator(stakingProvider, msg.sender); } } + + //-------------------------XChain------------------------- + /** + * @notice Resets operator confirmation + */ function _releaseOperator(address _stakingProvider) internal { + stakingProviderInfo[_stakingProvider].operatorConfirmed = false; if (address(updatableStakeInfo) != address(0)) { updatableStakeInfo.updateOperator(_stakingProvider, address(0)); } } + /** + * @notice Send updated authorized amount to xchain contract + */ + function _updateAuthorization(address _stakingProvider, StakingProviderInfo storage _info) internal { + if (address(updatableStakeInfo) != address(0)) { + // TODO send both authorized and eligible amounts in case of slashing from StakeInfo + uint96 eligibleAmount = getEligibleAmount(_info); + updatableStakeInfo.updateAmount(_stakingProvider, eligibleAmount); + } + } + //-------------------------Slashing------------------------- /** * @notice Slash the provider's stake and reward the investigator diff --git a/tests/application/test_authorization.py b/tests/application/test_authorization.py index 4e24084e..db45076e 100644 --- a/tests/application/test_authorization.py +++ b/tests/application/test_authorization.py @@ -26,6 +26,8 @@ MIN_AUTHORIZATION = Web3.to_wei(40_000, "ether") DEAUTHORIZATION_DURATION = 60 * 60 * 24 * 60 # 60 days in seconds +STAKE_INFO_OPERATOR_SLOT = 0 + def test_authorization_increase(accounts, threshold_staking, pre_cbd_application, stake_info): """ @@ -165,6 +167,7 @@ def test_involuntary_authorization_decrease( assert pre_cbd_application.isAuthorized(staking_provider) assert not pre_cbd_application.isOperatorConfirmed(staking_provider) assert not pre_cbd_application.stakingProviderInfo(staking_provider)[OPERATOR_CONFIRMED_SLOT] + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS events = pre_cbd_application.AuthorizationInvoluntaryDecreased.from_receipt(tx) assert len(events) == 1 @@ -194,6 +197,7 @@ def test_involuntary_authorization_decrease( assert pre_cbd_application.isAuthorized(staking_provider) assert not pre_cbd_application.isOperatorConfirmed(staking_provider) assert not pre_cbd_application.stakingProviderInfo(staking_provider)[OPERATOR_CONFIRMED_SLOT] + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS events = pre_cbd_application.AuthorizationInvoluntaryDecreased.from_receipt(tx) assert len(events) == 1 @@ -224,6 +228,7 @@ def test_involuntary_authorization_decrease( assert pre_cbd_application.isOperatorConfirmed(staking_provider) assert pre_cbd_application.getOperatorFromStakingProvider(staking_provider) == staking_provider assert pre_cbd_application.stakingProviderFromOperator(staking_provider) == staking_provider + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider events = pre_cbd_application.AuthorizationInvoluntaryDecreased.from_receipt(tx) assert len(events) == 1 @@ -245,6 +250,7 @@ def test_involuntary_authorization_decrease( assert not pre_cbd_application.stakingProviderInfo(staking_provider)[OPERATOR_CONFIRMED_SLOT] assert pre_cbd_application.getOperatorFromStakingProvider(staking_provider) == ZERO_ADDRESS assert pre_cbd_application.stakingProviderFromOperator(staking_provider) == ZERO_ADDRESS + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS events = pre_cbd_application.AuthorizationInvoluntaryDecreased.from_receipt(tx) assert len(events) == 1 @@ -276,6 +282,14 @@ def test_involuntary_authorization_decrease( assert event["fromAmount"] == value assert event["toAmount"] == authorization + # Decrease everything again without syncing with StakeInfo + pre_cbd_application.setUpdatableStakeInfo(ZERO_ADDRESS, sender=creator) + threshold_staking.involuntaryAuthorizationDecrease( + staking_provider, authorization, 0, sender=creator + ) + assert pre_cbd_application.getOperatorFromStakingProvider(staking_provider) == ZERO_ADDRESS + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider + def test_authorization_decrease_request( accounts, threshold_staking, pre_cbd_application, stake_info, chain @@ -460,6 +474,7 @@ def test_finish_authorization_decrease( threshold_staking.authorizedStake(staking_provider, pre_cbd_application.address) == new_value ) + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider events = pre_cbd_application.AuthorizationDecreaseApproved.from_receipt(tx) assert len(events) == 1 @@ -485,6 +500,7 @@ def test_finish_authorization_decrease( assert not pre_cbd_application.isOperatorConfirmed(staking_provider) assert not pre_cbd_application.stakingProviderInfo(staking_provider)[OPERATOR_CONFIRMED_SLOT] assert threshold_staking.authorizedStake(staking_provider, pre_cbd_application.address) == 0 + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS events = pre_cbd_application.AuthorizationDecreaseApproved.from_receipt(tx) assert len(events) == 1 @@ -493,6 +509,18 @@ def test_finish_authorization_decrease( assert event["fromAmount"] == value assert event["toAmount"] == 0 + # Decrease everything again without syncing with StakeInfo + value = minimum_authorization + threshold_staking.authorizationIncreased(staking_provider, 0, value, sender=creator) + pre_cbd_application.bondOperator(staking_provider, staking_provider, sender=staking_provider) + pre_cbd_application.confirmOperatorAddress(sender=staking_provider) + threshold_staking.authorizationDecreaseRequested(staking_provider, value, 0, sender=creator) + chain.pending_timestamp += deauthorization_duration + pre_cbd_application.setUpdatableStakeInfo(ZERO_ADDRESS, sender=creator) + pre_cbd_application.finishAuthorizationDecrease(staking_provider, sender=creator) + assert pre_cbd_application.getOperatorFromStakingProvider(staking_provider) == ZERO_ADDRESS + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider + def test_resync(accounts, threshold_staking, pre_cbd_application, stake_info): """ @@ -551,6 +579,7 @@ def test_resync(accounts, threshold_staking, pre_cbd_application, stake_info): assert pre_cbd_application.authorizedStake(staking_provider) == new_value assert pre_cbd_application.isAuthorized(staking_provider) assert pre_cbd_application.isOperatorConfirmed(staking_provider) + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider events = pre_cbd_application.AuthorizationReSynchronized.from_receipt(tx) assert len(events) == 1 @@ -579,6 +608,7 @@ def test_resync(accounts, threshold_staking, pre_cbd_application, stake_info): assert pre_cbd_application.authorizedStake(staking_provider) == new_value assert pre_cbd_application.isAuthorized(staking_provider) assert pre_cbd_application.isOperatorConfirmed(staking_provider) + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider events = pre_cbd_application.AuthorizationReSynchronized.from_receipt(tx) assert len(events) == 1 @@ -601,6 +631,7 @@ def test_resync(accounts, threshold_staking, pre_cbd_application, stake_info): assert not pre_cbd_application.isAuthorized(staking_provider) assert not pre_cbd_application.isOperatorConfirmed(staking_provider) assert not pre_cbd_application.stakingProviderInfo(staking_provider)[OPERATOR_CONFIRMED_SLOT] + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS events = pre_cbd_application.AuthorizationReSynchronized.from_receipt(tx) assert len(events) == 1 @@ -608,3 +639,14 @@ def test_resync(accounts, threshold_staking, pre_cbd_application, stake_info): assert event["stakingProvider"] == staking_provider assert event["fromAmount"] == value assert event["toAmount"] == 0 + + # Resync again without syncing with StakeInfo + value = minimum_authorization + threshold_staking.authorizationIncreased(staking_provider, 0, value, sender=creator) + pre_cbd_application.bondOperator(staking_provider, staking_provider, sender=staking_provider) + pre_cbd_application.confirmOperatorAddress(sender=staking_provider) + threshold_staking.setAuthorized(staking_provider, 0, sender=creator) + pre_cbd_application.setUpdatableStakeInfo(ZERO_ADDRESS, sender=creator) + pre_cbd_application.resynchronizeAuthorization(staking_provider, sender=creator) + assert pre_cbd_application.getOperatorFromStakingProvider(staking_provider) == ZERO_ADDRESS + assert stake_info.stakes(staking_provider)[STAKE_INFO_OPERATOR_SLOT] == staking_provider diff --git a/tests/application/test_operator.py b/tests/application/test_operator.py index 599bd92e..61c79c10 100644 --- a/tests/application/test_operator.py +++ b/tests/application/test_operator.py @@ -23,6 +23,8 @@ MIN_AUTHORIZATION = Web3.to_wei(40_000, "ether") MIN_OPERATOR_SECONDS = 24 * 60 * 60 +STAKE_INFO_OPERATOR_SLOT = 0 + def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_info, chain): ( @@ -94,6 +96,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert not pre_cbd_application.isOperatorConfirmed(operator1) assert pre_cbd_application.getStakingProvidersLength() == 1 assert pre_cbd_application.stakingProviders(0) == staking_provider_3 + assert stake_info.stakes(staking_provider_3)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS + assert stake_info.operatorToProvider(operator1) == ZERO_ADDRESS # No active stakingProviders before confirmation all_locked, staking_providers = pre_cbd_application.getActiveStakingProviders(0, 0) @@ -103,6 +107,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i pre_cbd_application.confirmOperatorAddress(sender=operator1) assert pre_cbd_application.stakingProviderInfo(staking_provider_3)[CONFIRMATION_SLOT] assert pre_cbd_application.isOperatorConfirmed(operator1) + assert stake_info.stakes(staking_provider_3)[STAKE_INFO_OPERATOR_SLOT] == operator1 + assert stake_info.operatorToProvider(operator1) == staking_provider_3 events = pre_cbd_application.OperatorBonded.from_receipt(tx) assert len(events) == 1 @@ -152,6 +158,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert not pre_cbd_application.isOperatorConfirmed(operator1) assert pre_cbd_application.getStakingProvidersLength() == 1 assert pre_cbd_application.stakingProviders(0) == staking_provider_3 + assert stake_info.stakes(staking_provider_3)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS + assert stake_info.operatorToProvider(operator1) == ZERO_ADDRESS # Resetting operator removes from active list before next confirmation all_locked, staking_providers = pre_cbd_application.getActiveStakingProviders(0, 0) @@ -177,6 +185,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert not pre_cbd_application.isOperatorConfirmed(operator2) assert pre_cbd_application.getStakingProvidersLength() == 1 assert pre_cbd_application.stakingProviders(0) == staking_provider_3 + assert stake_info.stakes(staking_provider_3)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS + assert stake_info.operatorToProvider(operator2) == ZERO_ADDRESS events = pre_cbd_application.OperatorBonded.from_receipt(tx) assert len(events) == 1 @@ -194,6 +204,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert not pre_cbd_application.isOperatorConfirmed(operator1) assert pre_cbd_application.isOperatorConfirmed(operator2) assert pre_cbd_application.stakingProviderInfo(staking_provider_3)[CONFIRMATION_SLOT] + assert stake_info.stakes(staking_provider_3)[STAKE_INFO_OPERATOR_SLOT] == operator2 + assert stake_info.operatorToProvider(operator2) == staking_provider_3 # Another staking provider can bond a free operator assert pre_cbd_application.authorizedOverall() == min_authorization @@ -206,6 +218,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert pre_cbd_application.getStakingProvidersLength() == 2 assert pre_cbd_application.stakingProviders(1) == staking_provider_4 assert pre_cbd_application.authorizedOverall() == min_authorization + assert stake_info.stakes(staking_provider_4)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS + assert stake_info.operatorToProvider(operator1) == ZERO_ADDRESS events = pre_cbd_application.OperatorBonded.from_receipt(tx) assert len(events) == 1 @@ -226,6 +240,9 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert pre_cbd_application.isOperatorConfirmed(operator1) assert pre_cbd_application.stakingProviderInfo(staking_provider_4)[CONFIRMATION_SLOT] assert pre_cbd_application.authorizedOverall() == 2 * min_authorization + assert stake_info.stakes(staking_provider_4)[STAKE_INFO_OPERATOR_SLOT] == operator1 + assert stake_info.operatorToProvider(operator1) == staking_provider_4 + chain.pending_timestamp += min_operator_seconds tx = pre_cbd_application.bondOperator(staking_provider_4, operator3, sender=staking_provider_4) timestamp = tx.timestamp @@ -238,6 +255,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert pre_cbd_application.getStakingProvidersLength() == 2 assert pre_cbd_application.stakingProviders(1) == staking_provider_4 assert pre_cbd_application.authorizedOverall() == min_authorization + assert stake_info.stakes(staking_provider_4)[STAKE_INFO_OPERATOR_SLOT] == ZERO_ADDRESS + assert stake_info.operatorToProvider(operator1) == ZERO_ADDRESS # Resetting operator removes from active list before next confirmation all_locked, staking_providers = pre_cbd_application.getActiveStakingProviders(1, 0) @@ -290,6 +309,8 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i staking_provider_1, min_authorization, min_authorization - 1, sender=creator ) pre_cbd_application.confirmOperatorAddress(sender=staking_provider_1) + assert stake_info.stakes(staking_provider_1)[STAKE_INFO_OPERATOR_SLOT] == staking_provider_1 + assert stake_info.operatorToProvider(staking_provider_1) == staking_provider_1 # If stake will be less than minimum then provider is not active threshold_staking.authorizationIncreased( @@ -309,6 +330,9 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert all_locked == 0 assert len(staking_providers) == 0 + # Reset xchain contract before next bonding + pre_cbd_application.setUpdatableStakeInfo(ZERO_ADDRESS, sender=creator) + # Unbond and rebond oeprator pre_cbd_application.bondOperator(staking_provider_3, ZERO_ADDRESS, sender=staking_provider_3) pre_cbd_application.bondOperator(staking_provider_3, operator2, sender=staking_provider_3) @@ -321,9 +345,7 @@ def test_bond_operator(accounts, threshold_staking, pre_cbd_application, stake_i assert pre_cbd_application.stakingProviderFromOperator(operator2) == ZERO_ADDRESS -def test_confirm_address( - accounts, threshold_staking, pre_cbd_application, stake_info, chain, project -): +def test_confirm_address(accounts, threshold_staking, pre_cbd_application, chain, project): creator, staking_provider, operator, *everyone_else = accounts[0:] min_authorization = MIN_AUTHORIZATION min_operator_seconds = MIN_OPERATOR_SECONDS