mirror of https://github.com/nucypher/nucypher.git
1319 lines
68 KiB
Python
1319 lines
68 KiB
Python
"""
|
|
This file is part of nucypher.
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
nucypher is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
"""
|
|
import re
|
|
|
|
import pytest
|
|
from web3 import Web3
|
|
|
|
from nucypher.blockchain.eth.actors import Staker, StakeHolder
|
|
from nucypher.blockchain.eth.constants import MAX_UINT16, NULL_ADDRESS
|
|
from nucypher.blockchain.eth.token import NU, Stake
|
|
from nucypher.blockchain.eth.utils import datetime_at_period
|
|
from nucypher.cli.actions.select import select_client_account_for_staking
|
|
from nucypher.cli.commands.stake import stake, StakeHolderConfigOptions, StakerOptions, TransactingStakerOptions
|
|
from nucypher.cli.literature import (
|
|
NO_TOKENS_TO_WITHDRAW, COLLECTING_TOKEN_REWARD, CONFIRM_COLLECTING_WITHOUT_MINTING,
|
|
NO_FEE_TO_WITHDRAW, COLLECTING_ETH_FEE, NO_MINTABLE_PERIODS, STILL_LOCKED_TOKENS, CONFIRM_MINTING,
|
|
PROMPT_PROLONG_VALUE, CONFIRM_PROLONG, SUCCESSFUL_STAKE_PROLONG, PERIOD_ADVANCED_WARNING, PROMPT_STAKE_DIVIDE_VALUE,
|
|
PROMPT_STAKE_EXTEND_VALUE, CONFIRM_BROADCAST_STAKE_DIVIDE, SUCCESSFUL_STAKE_DIVIDE, SUCCESSFUL_STAKE_INCREASE,
|
|
PROMPT_STAKE_INCREASE_VALUE, CONFIRM_INCREASING_STAKE, PROMPT_DEPOSIT_OR_LOCK, PROMPT_STAKE_CREATE_VALUE,
|
|
PROMPT_STAKE_CREATE_LOCK_PERIODS, CONFIRM_LARGE_STAKE_VALUE, CONFIRM_LARGE_STAKE_DURATION, CONFIRM_STAGED_STAKE,
|
|
CONFIRM_BROADCAST_CREATE_STAKE, INSUFFICIENT_BALANCE_TO_INCREASE, MAXIMUM_STAKE_REACHED,
|
|
INSUFFICIENT_BALANCE_TO_CREATE, ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE, CONFIRM_MERGE, SUCCESSFUL_STAKES_MERGE
|
|
)
|
|
from nucypher.config.constants import TEMPORARY_DOMAIN
|
|
from nucypher.types import SubStakeInfo, StakerInfo
|
|
from tests.constants import MOCK_PROVIDER_URI, YES, INSECURE_DEVELOPMENT_PASSWORD, NO
|
|
|
|
|
|
@pytest.fixture()
|
|
def surrogate_stakers(mock_testerchain, test_registry, mock_staking_agent):
|
|
address_1 = mock_testerchain.etherbase_account
|
|
address_2 = mock_testerchain.unassigned_accounts[0]
|
|
|
|
mock_staking_agent.get_all_stakes.return_value = []
|
|
|
|
def get_missing_commitments(checksum_address):
|
|
if checksum_address == address_2:
|
|
return 0
|
|
else:
|
|
return 1
|
|
mock_staking_agent.get_missing_commitments.side_effect = get_missing_commitments
|
|
|
|
return address_1, address_2
|
|
|
|
|
|
@pytest.fixture()
|
|
def surrogate_stakes(mock_staking_agent, token_economics, surrogate_stakers):
|
|
value = 2 * token_economics.minimum_allowed_locked + 1
|
|
current_period = 10
|
|
duration = token_economics.minimum_locked_periods + 1
|
|
final_period = current_period + duration
|
|
stakes_1 = [SubStakeInfo(current_period - 1, final_period - 1, value),
|
|
SubStakeInfo(current_period - 1, final_period, value),
|
|
SubStakeInfo(current_period + 1, final_period, value),
|
|
SubStakeInfo(current_period - 2, current_period, value // 2),
|
|
SubStakeInfo(current_period - 2, current_period + 1, value // 2),
|
|
SubStakeInfo(current_period - 2, current_period - 1, value),
|
|
SubStakeInfo(current_period - 2, current_period - 2, value)]
|
|
stakes_2 = [SubStakeInfo(current_period - 2, final_period + 2, value)]
|
|
|
|
mock_staking_agent.get_current_period.return_value = current_period
|
|
|
|
def get_all_stakes(staker_address):
|
|
if staker_address == surrogate_stakers[0]:
|
|
return stakes_1
|
|
elif staker_address == surrogate_stakers[1]:
|
|
return stakes_2
|
|
else:
|
|
return []
|
|
mock_staking_agent.get_all_stakes.side_effect = get_all_stakes
|
|
|
|
def get_substake_info(staker_address, stake_index):
|
|
if staker_address == surrogate_stakers[0]:
|
|
return stakes_1[stake_index]
|
|
elif staker_address == surrogate_stakers[1]:
|
|
return stakes_2[stake_index]
|
|
else:
|
|
return []
|
|
mock_staking_agent.get_substake_info.side_effect = get_substake_info
|
|
|
|
# only for calculating sub-stake status
|
|
def get_staker_info(staker_address):
|
|
if staker_address == surrogate_stakers[0]:
|
|
return StakerInfo(value=0,
|
|
current_committed_period=current_period-1,
|
|
next_committed_period=current_period,
|
|
last_committed_period=0,
|
|
lock_restake_until_period=0,
|
|
completed_work=0,
|
|
worker_start_period=0,
|
|
worker=NULL_ADDRESS,
|
|
flags=bytes(0))
|
|
else:
|
|
return StakerInfo(value=0,
|
|
current_committed_period=0,
|
|
next_committed_period=0,
|
|
last_committed_period=0,
|
|
lock_restake_until_period=0,
|
|
completed_work=0,
|
|
worker_start_period=0,
|
|
worker=NULL_ADDRESS,
|
|
flags=bytes(0))
|
|
mock_staking_agent.get_staker_info.side_effect = get_staker_info
|
|
|
|
return stakes_1, stakes_2
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_stakeholder_configuration(test_emitter, test_registry, mock_testerchain, mock_staking_agent):
|
|
|
|
stakeholder_config_options = StakeHolderConfigOptions(provider_uri=MOCK_PROVIDER_URI,
|
|
poa=None,
|
|
light=None,
|
|
registry_filepath=None,
|
|
network=TEMPORARY_DOMAIN,
|
|
signer_uri=None)
|
|
|
|
mock_staking_agent.get_all_stakes.return_value = [SubStakeInfo(1, 2, 3)]
|
|
force = False
|
|
selected_index = 0
|
|
selected_account = mock_testerchain.client.accounts[selected_index]
|
|
expected_stakeholder = StakeHolder(registry=test_registry,
|
|
domains={TEMPORARY_DOMAIN},
|
|
initial_address=selected_account)
|
|
expected_stakeholder.refresh_stakes()
|
|
|
|
staker_options = StakerOptions(config_options=stakeholder_config_options, staking_address=selected_account)
|
|
transacting_staker_options = TransactingStakerOptions(staker_options=staker_options,
|
|
hw_wallet=None,
|
|
beneficiary_address=None,
|
|
allocation_filepath=None)
|
|
stakeholder_from_configuration = transacting_staker_options.create_character(emitter=test_emitter, config_file=None)
|
|
client_account, staking_address = select_client_account_for_staking(emitter=test_emitter,
|
|
stakeholder=stakeholder_from_configuration,
|
|
staking_address=selected_account,
|
|
individual_allocation=None,
|
|
force=force)
|
|
assert client_account == staking_address == selected_account
|
|
assert stakeholder_from_configuration.stakes == expected_stakeholder.stakes
|
|
assert stakeholder_from_configuration.checksum_address == client_account
|
|
|
|
staker_options = StakerOptions(config_options=stakeholder_config_options, staking_address=None)
|
|
transacting_staker_options = TransactingStakerOptions(staker_options=staker_options,
|
|
hw_wallet=None,
|
|
beneficiary_address=None,
|
|
allocation_filepath=None)
|
|
stakeholder_from_configuration = transacting_staker_options.create_character(emitter=None, config_file=None)
|
|
client_account, staking_address = select_client_account_for_staking(emitter=test_emitter,
|
|
stakeholder=stakeholder_from_configuration,
|
|
staking_address=selected_account,
|
|
individual_allocation=None,
|
|
force=force)
|
|
assert client_account == staking_address == selected_account
|
|
assert stakeholder_from_configuration.stakes == expected_stakeholder.stakes
|
|
assert stakeholder_from_configuration.checksum_address == client_account
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_no_token_reward(click_runner, surrogate_stakers, mock_staking_agent):
|
|
# No tokens at all
|
|
mock_staking_agent.calculate_staking_reward.return_value = 0
|
|
|
|
collection_args = ('collect-reward',
|
|
'--no-policy-fee',
|
|
'--staking-reward',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, collection_args, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert NO_TOKENS_TO_WITHDRAW in result.output
|
|
|
|
mock_staking_agent.calculate_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.collect_staking_reward.assert_not_called()
|
|
mock_staking_agent.assert_no_transactions()
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_collecting_token_reward(click_runner, surrogate_stakers, mock_staking_agent, mocker):
|
|
mock_mintable_periods = mocker.spy(Staker, 'mintable_periods')
|
|
|
|
# Collect some reward
|
|
reward = NU(1, 'NU')
|
|
staked = NU(100, 'NU')
|
|
mock_staking_agent.calculate_staking_reward.return_value = reward.to_nunits()
|
|
mock_staking_agent.non_withdrawable_stake.return_value = staked.to_nunits()
|
|
|
|
collection_args = ('collect-reward',
|
|
'--no-policy-fee',
|
|
'--staking-reward',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, collection_args, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert COLLECTING_TOKEN_REWARD.format(reward_amount=reward) in result.output
|
|
|
|
mock_staking_agent.calculate_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.collect_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.non_withdrawable_stake.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_mintable_periods.assert_not_called()
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.collect_staking_reward])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_collecting_whole_reward_with_warning(click_runner, surrogate_stakers, mock_staking_agent, mocker):
|
|
mock_mintable_periods = mocker.spy(Staker, 'mintable_periods')
|
|
|
|
# Collect last portion of NU with warning about periods to mint
|
|
reward = NU(1, 'NU')
|
|
mock_staking_agent.calculate_staking_reward.return_value = reward.to_nunits()
|
|
mock_staking_agent.non_withdrawable_stake.return_value = 0
|
|
mock_staking_agent.get_current_period.return_value = 10
|
|
mock_staking_agent.get_current_committed_period.return_value = 8
|
|
mock_staking_agent.get_next_committed_period.return_value = 9
|
|
|
|
collection_args = ('collect-reward',
|
|
'--no-policy-fee',
|
|
'--staking-reward',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = '\n'.join((INSECURE_DEVELOPMENT_PASSWORD, YES))
|
|
result = click_runner.invoke(stake, collection_args, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert COLLECTING_TOKEN_REWARD.format(reward_amount=reward) in result.output
|
|
assert CONFIRM_COLLECTING_WITHOUT_MINTING in result.output
|
|
|
|
mock_staking_agent.calculate_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.collect_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.non_withdrawable_stake.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_staking_agent.get_current_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_next_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_mintable_periods.assert_called_once()
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.collect_staking_reward])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_collecting_whole_reward_without_warning(click_runner, surrogate_stakers, mock_staking_agent, mocker):
|
|
mock_mintable_periods = mocker.spy(Staker, 'mintable_periods')
|
|
|
|
# Collect last portion of NU without warning
|
|
reward = NU(1, 'NU')
|
|
mock_staking_agent.calculate_staking_reward.return_value = reward.to_nunits()
|
|
mock_staking_agent.non_withdrawable_stake.return_value = 0
|
|
mock_staking_agent.get_current_committed_period.return_value = 0
|
|
mock_staking_agent.get_next_committed_period.return_value = 0
|
|
|
|
collection_args = ('collect-reward',
|
|
'--no-policy-fee',
|
|
'--staking-reward',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, collection_args, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert COLLECTING_TOKEN_REWARD.format(reward_amount=reward) in result.output
|
|
assert CONFIRM_COLLECTING_WITHOUT_MINTING not in result.output
|
|
|
|
mock_staking_agent.calculate_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.collect_staking_reward.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.non_withdrawable_stake.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_staking_agent.get_current_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_next_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_mintable_periods.assert_called_once()
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.collect_staking_reward])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_no_policy_fee(click_runner, surrogate_stakers, mock_policy_manager_agent):
|
|
mock_policy_manager_agent.get_fee_amount.return_value = 0
|
|
|
|
collection_args = ('collect-reward',
|
|
'--policy-fee',
|
|
'--no-staking-reward',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, collection_args, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert NO_FEE_TO_WITHDRAW in result.output
|
|
|
|
mock_policy_manager_agent.get_fee_amount.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_policy_manager_agent.collect_policy_fee.assert_not_called()
|
|
mock_policy_manager_agent.assert_no_transactions()
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_collecting_fee(click_runner, surrogate_stakers, mock_policy_manager_agent):
|
|
fee_amount_eth = 11
|
|
mock_policy_manager_agent.get_fee_amount.return_value = Web3.toWei(fee_amount_eth, 'ether')
|
|
|
|
collection_args = ('collect-reward',
|
|
'--policy-fee',
|
|
'--no-staking-reward',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, collection_args, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert COLLECTING_ETH_FEE.format(fee_amount=fee_amount_eth) in result.output
|
|
|
|
mock_policy_manager_agent.get_fee_amount.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_policy_manager_agent.collect_policy_fee.assert_called_once()
|
|
mock_policy_manager_agent.assert_only_transactions([mock_policy_manager_agent.collect_policy_fee])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_nothing_to_mint(click_runner, surrogate_stakers, mock_staking_agent, mocker):
|
|
mock_mintable_periods = mocker.spy(Staker, 'mintable_periods')
|
|
mock_staking_agent.get_current_committed_period.return_value = 0
|
|
mock_staking_agent.get_next_committed_period.return_value = 0
|
|
|
|
mint_command = ('mint',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, mint_command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert NO_MINTABLE_PERIODS in result.output
|
|
|
|
mock_staking_agent.non_withdrawable_stake.assert_not_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_staking_agent.get_current_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_next_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_mintable_periods.assert_called_once()
|
|
mock_staking_agent.assert_no_transactions()
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_mint_with_warning(click_runner, surrogate_stakers, mock_staking_agent, mocker):
|
|
mock_mintable_periods = mocker.spy(Staker, 'mintable_periods')
|
|
mock_staking_agent.get_current_period.return_value = 10
|
|
mock_staking_agent.get_current_committed_period.return_value = 9
|
|
mock_staking_agent.get_next_committed_period.return_value = 8
|
|
mock_staking_agent.non_withdrawable_stake.return_value = NU(1, 'NU').to_nunits()
|
|
|
|
mint_command = ('mint',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = '\n'.join((INSECURE_DEVELOPMENT_PASSWORD, YES))
|
|
result = click_runner.invoke(stake, mint_command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert STILL_LOCKED_TOKENS in result.output
|
|
assert CONFIRM_MINTING.format(mintable_periods=2) in result.output
|
|
|
|
mock_staking_agent.non_withdrawable_stake.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_staking_agent.get_current_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_next_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_mintable_periods.assert_called_once()
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.mint])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_mint_without_warning(click_runner, surrogate_stakers, mock_staking_agent, mocker):
|
|
mock_mintable_periods = mocker.spy(Staker, 'mintable_periods')
|
|
mock_staking_agent.get_current_period.return_value = 10
|
|
mock_staking_agent.get_current_committed_period.return_value = 0
|
|
mock_staking_agent.get_next_committed_period.return_value = 8
|
|
mock_staking_agent.non_withdrawable_stake.return_value = 0
|
|
|
|
mint_command = ('mint',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0])
|
|
|
|
user_input = '\n'.join((INSECURE_DEVELOPMENT_PASSWORD, YES))
|
|
result = click_runner.invoke(stake, mint_command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert STILL_LOCKED_TOKENS not in result.output
|
|
assert CONFIRM_MINTING.format(mintable_periods=1) in result.output
|
|
|
|
mock_staking_agent.non_withdrawable_stake.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_staking_agent.get_current_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_staking_agent.get_next_committed_period.assert_called_once_with(staker_address=surrogate_stakers[0])
|
|
mock_mintable_periods.assert_called_once()
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.mint])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_prolong_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index = 1
|
|
lock_periods = 10
|
|
final_period = surrogate_stakes[selected_index][sub_stake_index][1]
|
|
|
|
command = ('prolong',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
str(sub_stake_index),
|
|
str(lock_periods),
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert PROMPT_PROLONG_VALUE.format(minimum=1, maximum=MAX_UINT16 - final_period) in result.output
|
|
assert CONFIRM_PROLONG.format(lock_periods=lock_periods) in result.output
|
|
assert SUCCESSFUL_STAKE_PROLONG in result.output
|
|
assert PERIOD_ADVANCED_WARNING not in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.prolong_stake.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index,
|
|
periods=lock_periods)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.prolong_stake])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_prolong_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index = 1
|
|
lock_periods = 10
|
|
final_period = surrogate_stakes[selected_index][sub_stake_index][1]
|
|
|
|
command = ('prolong',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0],
|
|
'--index', sub_stake_index,
|
|
'--lock-periods', lock_periods,
|
|
'--force')
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert PROMPT_PROLONG_VALUE.format(minimum=1, maximum=MAX_UINT16 - final_period) not in result.output
|
|
assert CONFIRM_PROLONG.format(lock_periods=lock_periods) not in result.output
|
|
assert SUCCESSFUL_STAKE_PROLONG in result.output
|
|
assert PERIOD_ADVANCED_WARNING not in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.prolong_stake.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index,
|
|
periods=lock_periods)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.prolong_stake])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_divide_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index = 1
|
|
lock_periods = 10
|
|
min_allowed_locked = token_economics.minimum_allowed_locked
|
|
target_value = min_allowed_locked
|
|
|
|
mock_staking_agent.get_worker_from_staker.return_value = NULL_ADDRESS
|
|
|
|
command = ('divide',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
str(sub_stake_index),
|
|
str(NU.from_nunits(target_value).to_tokens()),
|
|
str(lock_periods),
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert PROMPT_STAKE_DIVIDE_VALUE.format(minimum=NU.from_nunits(min_allowed_locked),
|
|
maximum=NU.from_nunits(min_allowed_locked + 1)) in result.output
|
|
assert PROMPT_STAKE_EXTEND_VALUE in result.output
|
|
assert CONFIRM_BROADCAST_STAKE_DIVIDE in result.output
|
|
assert SUCCESSFUL_STAKE_DIVIDE in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.divide_stake.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index,
|
|
target_value=target_value,
|
|
periods=lock_periods)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.divide_stake])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_divide_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
sub_stake_index = 1
|
|
lock_periods = 10
|
|
min_allowed_locked = token_economics.minimum_allowed_locked
|
|
target_value = min_allowed_locked
|
|
|
|
mock_staking_agent.get_worker_from_staker.return_value = surrogate_stakers[0]
|
|
|
|
command = ('divide',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0],
|
|
'--index', sub_stake_index,
|
|
'--lock-periods', lock_periods,
|
|
'--value', NU.from_nunits(target_value).to_tokens(),
|
|
'--force')
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
assert PROMPT_STAKE_DIVIDE_VALUE.format(minimum=NU.from_nunits(min_allowed_locked),
|
|
maximum=NU.from_nunits(min_allowed_locked + 1)) not in result.output
|
|
assert PROMPT_STAKE_EXTEND_VALUE not in result.output
|
|
assert CONFIRM_BROADCAST_STAKE_DIVIDE not in result.output
|
|
assert SUCCESSFUL_STAKE_DIVIDE in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.divide_stake.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index,
|
|
target_value=target_value,
|
|
periods=lock_periods)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.divide_stake])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_increase_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_token_agent,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index = 1
|
|
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
|
|
|
mock_token_agent.get_balance.return_value = 0
|
|
|
|
command = ('increase',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
str(sub_stake_index),
|
|
YES,
|
|
str(additional_value.to_tokens()),
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_INCREASE in result.output
|
|
assert MAXIMUM_STAKE_REACHED not in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.maximum_allowed_locked
|
|
balance = token_economics.minimum_allowed_locked * 5
|
|
mock_token_agent.get_balance.return_value = balance
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_INCREASE not in result.output
|
|
assert MAXIMUM_STAKE_REACHED in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.maximum_allowed_locked // 2
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(balance)
|
|
assert PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit) in result.output
|
|
assert CONFIRM_INCREASING_STAKE.format(stake_index=sub_stake_index, value=additional_value) in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.deposit_and_increase.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index,
|
|
amount=additional_value.to_nunits())
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.deposit_and_increase])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index)
|
|
mock_token_agent.increase_allowance.assert_called_once_with(sender_address=surrogate_stakers[0],
|
|
spender_address=mock_staking_agent.contract.address,
|
|
increase=additional_value.to_nunits())
|
|
mock_token_agent.assert_only_transactions([mock_token_agent.increase_allowance])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_increase_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_token_agent,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
sub_stake_index = 1
|
|
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
|
|
|
locked_tokens = token_economics.minimum_allowed_locked * 5
|
|
mock_staking_agent.get_locked_tokens.return_value = locked_tokens
|
|
mock_token_agent.get_balance.return_value = 2 * token_economics.maximum_allowed_locked
|
|
|
|
command = ('increase',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[0],
|
|
'--index', sub_stake_index,
|
|
'--value', additional_value.to_tokens(),
|
|
'--force')
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(token_economics.maximum_allowed_locked - locked_tokens)
|
|
assert PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit) not in result.output
|
|
assert CONFIRM_INCREASING_STAKE.format(stake_index=sub_stake_index, value=additional_value) not in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK not in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.deposit_and_increase.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index,
|
|
amount=additional_value.to_nunits())
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.deposit_and_increase])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[0],
|
|
stake_index=sub_stake_index)
|
|
mock_token_agent.increase_allowance.assert_called_once_with(sender_address=surrogate_stakers[0],
|
|
spender_address=mock_staking_agent.contract.address,
|
|
increase=additional_value.to_nunits())
|
|
mock_token_agent.assert_only_transactions([mock_token_agent.increase_allowance])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_increase_lock_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index = len(surrogate_stakes) - 1
|
|
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
|
|
|
mock_staking_agent.calculate_staking_reward.return_value = 0
|
|
|
|
command = ('increase',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
str(sub_stake_index),
|
|
NO,
|
|
str(additional_value.to_tokens()),
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_INCREASE in result.output
|
|
assert MAXIMUM_STAKE_REACHED not in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.maximum_allowed_locked
|
|
unlocked_tokens = token_economics.maximum_allowed_locked
|
|
mock_staking_agent.calculate_staking_reward.return_value = unlocked_tokens
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_INCREASE not in result.output
|
|
assert MAXIMUM_STAKE_REACHED in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
locked_tokens = token_economics.maximum_allowed_locked // 3
|
|
mock_staking_agent.get_locked_tokens.return_value = locked_tokens
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(token_economics.maximum_allowed_locked - locked_tokens)
|
|
assert PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit) in result.output
|
|
assert CONFIRM_INCREASING_STAKE.format(stake_index=sub_stake_index, value=additional_value) in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.lock_and_increase.assert_called_once_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index=sub_stake_index,
|
|
amount=additional_value.to_nunits())
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.lock_and_increase])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index=sub_stake_index)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_increase_lock_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index = len(surrogate_stakes) - 1
|
|
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.minimum_allowed_locked * 2
|
|
unlocked_tokens = token_economics.minimum_allowed_locked * 5
|
|
mock_staking_agent.calculate_staking_reward.return_value = unlocked_tokens
|
|
|
|
command = ('increase',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[selected_index],
|
|
'--index', sub_stake_index,
|
|
'--value', additional_value.to_tokens(),
|
|
'--only-lock',
|
|
'--force')
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(unlocked_tokens)
|
|
assert PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit) not in result.output
|
|
assert CONFIRM_INCREASING_STAKE.format(stake_index=sub_stake_index, value=additional_value) not in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK not in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.lock_and_increase.assert_called_once_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index=sub_stake_index,
|
|
amount=additional_value.to_nunits())
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.lock_and_increase])
|
|
mock_staking_agent.get_substake_info.assert_called_once_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index=sub_stake_index)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_create_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_token_agent,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
lock_periods = 366
|
|
value = NU.from_nunits(token_economics.minimum_allowed_locked * 11)
|
|
|
|
mock_token_agent.get_balance.return_value = token_economics.minimum_allowed_locked - 1
|
|
|
|
command = ('create',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
YES,
|
|
str(value.to_tokens()),
|
|
str(lock_periods),
|
|
YES,
|
|
YES,
|
|
YES,
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_CREATE in result.output
|
|
assert MAXIMUM_STAKE_REACHED not in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.maximum_allowed_locked
|
|
balance = token_economics.minimum_allowed_locked * 12
|
|
mock_token_agent.get_balance.return_value = balance
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_CREATE not in result.output
|
|
assert MAXIMUM_STAKE_REACHED in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.maximum_allowed_locked // 2
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(balance)
|
|
lower_limit = NU.from_nunits(token_economics.minimum_allowed_locked)
|
|
min_locktime = token_economics.minimum_locked_periods
|
|
max_locktime = MAX_UINT16 - 10 # MAX_UINT16 - current period
|
|
|
|
assert PROMPT_STAKE_CREATE_VALUE.format(lower_limit=lower_limit, upper_limit=upper_limit) in result.output
|
|
assert PROMPT_STAKE_CREATE_LOCK_PERIODS.format(min_locktime=min_locktime, max_locktime=max_locktime) in result.output
|
|
assert CONFIRM_STAGED_STAKE.format(nunits=str(value.to_nunits()),
|
|
tokens=value,
|
|
staker_address=surrogate_stakers[selected_index],
|
|
lock_periods=lock_periods) in result.output
|
|
assert CONFIRM_BROADCAST_CREATE_STAKE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK in result.output
|
|
assert CONFIRM_LARGE_STAKE_VALUE.format(value=value) in result.output
|
|
assert CONFIRM_LARGE_STAKE_DURATION.format(lock_periods=lock_periods) in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_token_agent.approve_and_call.assert_called_once_with(amount=value.to_nunits(),
|
|
target_address=mock_staking_agent.contract_address,
|
|
sender_address=surrogate_stakers[selected_index],
|
|
call_data=Web3.toBytes(lock_periods))
|
|
mock_token_agent.assert_only_transactions([mock_token_agent.approve_and_call])
|
|
mock_staking_agent.assert_no_transactions()
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_create_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_token_agent,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
|
|
lock_periods = token_economics.minimum_locked_periods
|
|
value = NU.from_nunits(token_economics.minimum_allowed_locked * 2)
|
|
|
|
locked_tokens = token_economics.minimum_allowed_locked * 5
|
|
mock_staking_agent.get_locked_tokens.return_value = locked_tokens
|
|
mock_token_agent.get_balance.return_value = token_economics.maximum_allowed_locked
|
|
|
|
command = ('create',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[selected_index],
|
|
'--lock-periods', lock_periods,
|
|
'--value', value.to_tokens(),
|
|
'--force')
|
|
|
|
user_input = '\n'.join((YES, INSECURE_DEVELOPMENT_PASSWORD))
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(token_economics.maximum_allowed_locked - locked_tokens)
|
|
lower_limit = NU.from_nunits(token_economics.minimum_allowed_locked)
|
|
min_locktime = token_economics.minimum_locked_periods
|
|
max_locktime = MAX_UINT16 - 10 # MAX_UINT16 - current period
|
|
assert PROMPT_STAKE_CREATE_VALUE.format(lower_limit=lower_limit, upper_limit=upper_limit) not in result.output
|
|
assert PROMPT_STAKE_CREATE_LOCK_PERIODS.format(min_locktime=min_locktime, max_locktime=max_locktime) not in result.output
|
|
assert CONFIRM_STAGED_STAKE.format(nunits=str(value.to_nunits()),
|
|
tokens=value,
|
|
staker_address=surrogate_stakers[selected_index],
|
|
lock_periods=lock_periods) not in result.output
|
|
assert CONFIRM_BROADCAST_CREATE_STAKE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK not in result.output
|
|
assert CONFIRM_LARGE_STAKE_VALUE.format(value=value) not in result.output
|
|
assert CONFIRM_LARGE_STAKE_DURATION.format(lock_periods=lock_periods) not in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_token_agent.approve_and_call.assert_called_once_with(amount=value.to_nunits(),
|
|
target_address=mock_staking_agent.contract_address,
|
|
sender_address=surrogate_stakers[selected_index],
|
|
call_data=Web3.toBytes(lock_periods))
|
|
mock_token_agent.assert_only_transactions([mock_token_agent.approve_and_call])
|
|
mock_staking_agent.assert_no_transactions()
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_create_lock_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
lock_periods = 366
|
|
value = NU.from_nunits(token_economics.minimum_allowed_locked * 2)
|
|
|
|
mock_staking_agent.calculate_staking_reward.return_value = token_economics.minimum_allowed_locked - 1
|
|
|
|
command = ('create',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
NO,
|
|
str(value.to_tokens()),
|
|
str(lock_periods),
|
|
YES,
|
|
YES,
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_CREATE in result.output
|
|
assert MAXIMUM_STAKE_REACHED not in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.maximum_allowed_locked
|
|
mock_staking_agent.calculate_staking_reward.return_value = token_economics.maximum_allowed_locked
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 1
|
|
assert INSUFFICIENT_BALANCE_TO_CREATE not in result.output
|
|
assert MAXIMUM_STAKE_REACHED in result.output
|
|
assert SUCCESSFUL_STAKE_INCREASE not in result.output
|
|
|
|
locked_tokens = token_economics.maximum_allowed_locked // 3
|
|
mock_staking_agent.get_locked_tokens.return_value = locked_tokens
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(token_economics.maximum_allowed_locked - locked_tokens)
|
|
lower_limit = NU.from_nunits(token_economics.minimum_allowed_locked)
|
|
min_locktime = token_economics.minimum_locked_periods
|
|
max_locktime = MAX_UINT16 - 10 # MAX_UINT16 - current period
|
|
|
|
assert PROMPT_STAKE_CREATE_VALUE.format(lower_limit=lower_limit, upper_limit=upper_limit) in result.output
|
|
assert PROMPT_STAKE_CREATE_LOCK_PERIODS.format(min_locktime=min_locktime, max_locktime=max_locktime) in result.output
|
|
assert CONFIRM_STAGED_STAKE.format(nunits=str(value.to_nunits()),
|
|
tokens=value,
|
|
staker_address=surrogate_stakers[selected_index],
|
|
lock_periods=lock_periods) in result.output
|
|
assert CONFIRM_BROADCAST_CREATE_STAKE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK in result.output
|
|
assert CONFIRM_LARGE_STAKE_VALUE.format(value=value) not in result.output
|
|
assert CONFIRM_LARGE_STAKE_DURATION.format(lock_periods=lock_periods) in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.lock_and_create.assert_called_once_with(amount=value.to_nunits(),
|
|
lock_periods=lock_periods,
|
|
staker_address=surrogate_stakers[selected_index])
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.lock_and_create])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_create_lock_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
|
|
lock_periods = token_economics.minimum_locked_periods
|
|
value = NU.from_nunits(token_economics.minimum_allowed_locked * 11)
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.minimum_allowed_locked * 5
|
|
unlocked_tokens = token_economics.maximum_allowed_locked // 2
|
|
mock_staking_agent.calculate_staking_reward.return_value = unlocked_tokens
|
|
|
|
command = ('create',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[selected_index],
|
|
'--lock-periods', lock_periods,
|
|
'--value', value.to_tokens(),
|
|
'--only-lock',
|
|
'--force')
|
|
|
|
user_input = '\n'.join((YES, YES, INSECURE_DEVELOPMENT_PASSWORD))
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
upper_limit = NU.from_nunits(unlocked_tokens)
|
|
lower_limit = NU.from_nunits(token_economics.minimum_allowed_locked)
|
|
min_locktime = token_economics.minimum_locked_periods
|
|
max_locktime = MAX_UINT16 - 10 # MAX_UINT16 - current period
|
|
|
|
assert PROMPT_STAKE_CREATE_VALUE.format(lower_limit=lower_limit, upper_limit=upper_limit) not in result.output
|
|
assert PROMPT_STAKE_CREATE_LOCK_PERIODS.format(min_locktime=min_locktime, max_locktime=max_locktime) not in result.output
|
|
assert CONFIRM_STAGED_STAKE.format(nunits=str(value.to_nunits()),
|
|
tokens=value,
|
|
staker_address=surrogate_stakers[selected_index],
|
|
lock_periods=lock_periods) not in result.output
|
|
assert CONFIRM_BROADCAST_CREATE_STAKE in result.output
|
|
assert PROMPT_DEPOSIT_OR_LOCK not in result.output
|
|
assert CONFIRM_LARGE_STAKE_VALUE.format(value=value) not in result.output
|
|
assert CONFIRM_LARGE_STAKE_DURATION.format(lock_periods=lock_periods) not in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.lock_and_create.assert_called_once_with(amount=value.to_nunits(),
|
|
lock_periods=lock_periods,
|
|
staker_address=surrogate_stakers[selected_index])
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.lock_and_create])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_merge_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index_1 = 1
|
|
sub_stake_index_2 = 2
|
|
|
|
command = ('merge',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN)
|
|
|
|
user_input = '\n'.join((str(selected_index),
|
|
str(sub_stake_index_1),
|
|
str(sub_stake_index_2),
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
final_period = surrogate_stakes[selected_index][sub_stake_index_1].last_period
|
|
assert ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE.format(final_period=final_period) in result.output
|
|
assert CONFIRM_MERGE.format(stake_index_1=sub_stake_index_1, stake_index_2=sub_stake_index_2) in result.output
|
|
assert SUCCESSFUL_STAKES_MERGE in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.merge_stakes.assert_called_once_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index_1=sub_stake_index_1,
|
|
stake_index_2=sub_stake_index_2)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.merge_stakes])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_merge_partially_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index_1 = 1
|
|
sub_stake_index_2 = 2
|
|
|
|
base_command = ('merge',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[selected_index])
|
|
user_input = '\n'.join((str(sub_stake_index_2),
|
|
YES,
|
|
INSECURE_DEVELOPMENT_PASSWORD))
|
|
|
|
command = base_command + ('--index-1', sub_stake_index_1)
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
final_period = surrogate_stakes[selected_index][sub_stake_index_1].last_period
|
|
assert ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE.format(final_period=final_period) in result.output
|
|
assert CONFIRM_MERGE.format(stake_index_1=sub_stake_index_1, stake_index_2=sub_stake_index_2) in result.output
|
|
assert SUCCESSFUL_STAKES_MERGE in result.output
|
|
|
|
command = base_command + ('--index-2', sub_stake_index_1)
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
final_period = surrogate_stakes[selected_index][sub_stake_index_1].last_period
|
|
assert ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE.format(final_period=final_period) in result.output
|
|
assert CONFIRM_MERGE.format(stake_index_1=sub_stake_index_1, stake_index_2=sub_stake_index_2) in result.output
|
|
assert SUCCESSFUL_STAKES_MERGE in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.merge_stakes.assert_called_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index_1=sub_stake_index_1,
|
|
stake_index_2=sub_stake_index_2)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.merge_stakes])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_merge_non_interactive(click_runner,
|
|
mocker,
|
|
surrogate_stakers,
|
|
surrogate_stakes,
|
|
mock_staking_agent,
|
|
token_economics,
|
|
mock_testerchain):
|
|
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
|
|
|
selected_index = 0
|
|
sub_stake_index_1 = 1
|
|
sub_stake_index_2 = 2
|
|
|
|
mock_staking_agent.get_locked_tokens.return_value = token_economics.minimum_allowed_locked * 2
|
|
unlocked_tokens = token_economics.minimum_allowed_locked * 5
|
|
mock_staking_agent.calculate_staking_reward.return_value = unlocked_tokens
|
|
|
|
command = ('merge',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,
|
|
'--staking-address', surrogate_stakers[selected_index],
|
|
'--index-1', sub_stake_index_1,
|
|
'--index-2', sub_stake_index_2,
|
|
'--force')
|
|
|
|
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
final_period = surrogate_stakes[selected_index][sub_stake_index_1].last_period
|
|
assert ONLY_DISPLAYING_MERGEABLE_STAKES_NOTE.format(final_period=final_period) not in result.output
|
|
assert CONFIRM_MERGE.format(stake_index_1=sub_stake_index_1, stake_index_2=sub_stake_index_2) not in result.output
|
|
assert SUCCESSFUL_STAKES_MERGE in result.output
|
|
|
|
mock_staking_agent.get_all_stakes.assert_called()
|
|
mock_staking_agent.get_current_period.assert_called()
|
|
mock_refresh_stakes.assert_called()
|
|
mock_staking_agent.merge_stakes.assert_called_once_with(staker_address=surrogate_stakers[selected_index],
|
|
stake_index_1=sub_stake_index_1,
|
|
stake_index_2=sub_stake_index_2)
|
|
mock_staking_agent.assert_only_transactions([mock_staking_agent.merge_stakes])
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_stake_list_active(click_runner, surrogate_stakers, surrogate_stakes, token_economics):
|
|
|
|
command = ('list',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,)
|
|
|
|
user_input = None
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
assert re.search("Status\\s+Missing 1 commitments", result.output, re.MULTILINE)
|
|
assert re.search("Status\\s+Committed #1", result.output, re.MULTILINE)
|
|
|
|
statuses = [Stake.Status.DIVISIBLE,
|
|
Stake.Status.DIVISIBLE,
|
|
Stake.Status.DIVISIBLE,
|
|
Stake.Status.LOCKED,
|
|
Stake.Status.EDITABLE,
|
|
Stake.Status.UNLOCKED,
|
|
Stake.Status.INACTIVE]
|
|
|
|
current_period = 10
|
|
for stakes in surrogate_stakes:
|
|
for index, sub_stake in enumerate(stakes):
|
|
value = NU.from_nunits(sub_stake.locked_value)
|
|
remaining = sub_stake.last_period - current_period + 1
|
|
start_datetime = datetime_at_period(period=sub_stake.first_period,
|
|
seconds_per_period=token_economics.seconds_per_period,
|
|
start_of_period=True)
|
|
unlock_datetime = datetime_at_period(period=sub_stake.last_period + 1,
|
|
seconds_per_period=token_economics.seconds_per_period,
|
|
start_of_period=True)
|
|
enactment = start_datetime.local_datetime().strftime("%b %d %Y")
|
|
termination = unlock_datetime.local_datetime().strftime("%b %d %Y")
|
|
search = f"{index}\\s+│\\s+" \
|
|
f"{value}\\s+│\\s+" \
|
|
f"{remaining}\\s+│\\s+" \
|
|
f"{enactment}\\s+│\\s+" \
|
|
f"{termination}\\s+│\\s+" \
|
|
f"{statuses[index].name}"
|
|
# locked sub-stakes
|
|
if index < 5:
|
|
assert re.search(search, result.output, re.MULTILINE)
|
|
# unlocked sub-stakes
|
|
else:
|
|
assert not re.search(search, result.output, re.MULTILINE)
|
|
|
|
|
|
@pytest.mark.usefixtures("test_registry_source_manager", "patch_stakeholder_configuration")
|
|
def test_stake_list_all(click_runner, surrogate_stakers, surrogate_stakes, token_economics):
|
|
|
|
command = ('list',
|
|
'--all',
|
|
'--provider', MOCK_PROVIDER_URI,
|
|
'--network', TEMPORARY_DOMAIN,)
|
|
|
|
user_input = None
|
|
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
|
assert result.exit_code == 0
|
|
|
|
assert re.search("Status\\s+Missing 1 commitments", result.output, re.MULTILINE)
|
|
assert re.search("Status\\s+Committed #1", result.output, re.MULTILINE)
|
|
|
|
statuses = [Stake.Status.DIVISIBLE,
|
|
Stake.Status.DIVISIBLE,
|
|
Stake.Status.DIVISIBLE,
|
|
Stake.Status.LOCKED,
|
|
Stake.Status.EDITABLE,
|
|
Stake.Status.UNLOCKED,
|
|
Stake.Status.INACTIVE]
|
|
|
|
current_period = 10
|
|
for stakes in surrogate_stakes:
|
|
for index, sub_stake in enumerate(stakes):
|
|
value = NU.from_nunits(sub_stake.locked_value)
|
|
remaining = sub_stake.last_period - current_period + 1
|
|
start_datetime = datetime_at_period(period=sub_stake.first_period,
|
|
seconds_per_period=token_economics.seconds_per_period,
|
|
start_of_period=True)
|
|
unlock_datetime = datetime_at_period(period=sub_stake.last_period + 1,
|
|
seconds_per_period=token_economics.seconds_per_period,
|
|
start_of_period=True)
|
|
enactment = start_datetime.local_datetime().strftime("%b %d %Y")
|
|
termination = unlock_datetime.local_datetime().strftime("%b %d %Y")
|
|
assert re.search(f"{index}\\s+│\\s+"
|
|
f"{value}\\s+│\\s+"
|
|
f"{remaining}\\s+│\\s+"
|
|
f"{enactment}\\s+│\\s+"
|
|
f"{termination}\\s+│\\s+"
|
|
f"{statuses[index].name}", result.output, re.MULTILINE)
|