Restores staking CLI tests post #1029 via Stakeholder API.

pull/1124/head
Kieran Prasch 2019-07-06 09:52:09 -07:00
parent 7c89dd488c
commit 73f606a31d
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
5 changed files with 187 additions and 84 deletions

View File

@ -745,10 +745,6 @@ class StakeHolder(BaseConfiguration):
self.funding_power.activate()
self.__funding_account = funding_account
self.staking_agent = StakingEscrowAgent(blockchain=blockchain)
self.token_agent = NucypherTokenAgent(blockchain=blockchain)
self.economics = TokenEconomics()
self.__accounts = list()
self.__stakers = dict()
self.__transacting_powers = dict()
@ -782,7 +778,8 @@ class StakeHolder(BaseConfiguration):
@classmethod
def from_configuration_file(cls,
filepath: str = None,
blockchain = None,
provider_uri: str = None,
registry_filepath: str = None,
**overrides) -> 'StakeHolder':
filepath = filepath or cls.default_filepath()
@ -790,8 +787,11 @@ class StakeHolder(BaseConfiguration):
# Sub config
blockchain_payload = payload.pop('blockchain')
if not blockchain:
blockchain = BlockchainInterface.from_dict(payload=blockchain_payload)
blockchain = BlockchainInterface.from_dict(payload=blockchain_payload,
provider_uri=provider_uri,
registry_filepath=registry_filepath)
blockchain.connect()
instance = cls(filepath=filepath,
blockchain=blockchain,
**payload, **overrides)
@ -1039,19 +1039,26 @@ class StakeHolder(BaseConfiguration):
def collect_rewards(self,
staker_address: str,
password: str,
withdraw_address: str = None,
staking: bool = True,
policy: bool = True) -> Dict[str, dict]:
if not staking and not policy:
raise ValueError("Either staking or policy must be True in order to collect rewards")
staker = self.get_active_staker(address=staker_address)
try:
staker = self.get_active_staker(address=staker_address)
except self.NoStakes:
staker = Staker(is_me=True, checksum_address=staker_address, blockchain=self.blockchain)
self.attach_transacting_power(checksum_address=staker.checksum_address, password=password)
receipts = dict()
if staking:
receipts['staking_reward'] = staker.collect_staking_reward()
if policy:
receipts['policy_reward'] = staker.collect_policy_reward(collector_address=self.funding_account)
withdraw_address = withdraw_address or self.funding_account
receipts['policy_reward'] = staker.collect_policy_reward(collector_address=withdraw_address)
self.to_configuration_file(override=True)
return receipts

View File

@ -171,10 +171,13 @@ class BlockchainInterface:
return r
@classmethod
def from_dict(cls, payload: dict) -> 'BlockchainInterface':
def from_dict(cls, payload: dict, **overrides) -> 'BlockchainInterface':
# Apply overrides
payload.update({k: v for k, v in overrides.items() if v is not None})
registry = EthereumContractRegistry(registry_filepath=payload['registry_filepath'])
blockchain = cls(provider_uri=payload['provider_uri'],
registry=registry)
blockchain = cls(provider_uri=payload['provider_uri'], registry=registry)
return blockchain
def to_dict(self) -> dict:

View File

@ -281,12 +281,7 @@ class Stake:
rulebook = (
(self.minimum_nu <= self.value,
'Stake amount too low; ({amount}) must be at least {minimum}'
.format(minimum=self.minimum_nu, amount=self.value)),
(self.maximum_nu >= self.value,
'Stake amount too high; ({amount}) must be no more than {maximum}.'
.format(maximum=self.maximum_nu, amount=self.value)),
f'Stake amount too low; ({self.minimum_nu}) must be at least {self.value}'),
)
if raise_on_fail is True:

View File

@ -101,6 +101,8 @@ def stake(click_config,
#
STAKEHOLDER = StakeHolder.from_configuration_file(filepath=config_file,
provider_uri=provider_uri,
registry_filepath=registry_filepath,
offline=offline)
#
# Eager Actions
@ -172,7 +174,10 @@ def stake(click_config,
confirmation_prompt=False)
if not pre_funded:
fund_now = click.confirm("Fund staking account with funding account?", abort=False, default=True)
if force:
fund_now = True
else:
fund_now = click.confirm("Fund staking account with funding account?", abort=False, default=True)
else:
# TODO: Validate the balance of self-manged funders.
fund_now = False
@ -235,7 +240,11 @@ def stake(click_config,
elif action == 'divide':
"""Divide an existing stake by specifying the new target value and end period"""
current_stake = select_stake(stakeholder=STAKEHOLDER)
if staking_address and index is not None:
staker = STAKEHOLDER.get_active_staker(address=staking_address)
current_stake = staker.stakes[index]
else:
current_stake = select_stake(stakeholder=STAKEHOLDER)
#
# Stage Stake
@ -280,6 +289,8 @@ def stake(click_config,
click.confirm(f"Send {STAKEHOLDER.calculate_reward()} to {STAKEHOLDER.funding_account}?")
password = get_password(confirm=False)
STAKEHOLDER.collect_rewards(staker_address=staking_address, password=password)
STAKEHOLDER.collect_rewards(staker_address=staking_address,
withdraw_address=withdraw_address,
password=password)
return # Exit

View File

@ -6,8 +6,9 @@ import datetime
import maya
import pytest
from nucypher.blockchain.eth.actors import Staker
from nucypher.blockchain.eth.agents import StakingEscrowAgent
from nucypher.blockchain.eth.actors import Staker, StakeHolder, Worker
from nucypher.blockchain.eth.agents import StakingEscrowAgent, Agency
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterface
from nucypher.blockchain.eth.token import NU
from nucypher.characters.lawful import Enrico
from nucypher.cli.main import nucypher_cli
@ -20,16 +21,22 @@ from nucypher.utilities.sandbox.constants import (
INSECURE_DEVELOPMENT_PASSWORD,
MOCK_REGISTRY_FILEPATH,
TEMPORARY_DOMAIN,
)
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)
from nucypher.utilities.sandbox.middleware import MockRestMiddleware
@pytest.fixture(scope='module')
def configuration_file_location(custom_filepath):
def worker_configuration_file_location(custom_filepath):
_configuration_file_location = os.path.join(MOCK_CUSTOM_INSTALLATION_PATH, UrsulaConfiguration.generate_filename())
return _configuration_file_location
@pytest.fixture(scope='module')
def staker_configuration_file_location(custom_filepath):
_configuration_file_location = os.path.join(MOCK_CUSTOM_INSTALLATION_PATH, StakeHolder.generate_filename())
return _configuration_file_location
@pytest.fixture(scope="module")
def charlie_blockchain_test_config(blockchain_ursulas, agency):
token_agent, staking_agent, policy_agent = agency
@ -51,7 +58,7 @@ def charlie_blockchain_test_config(blockchain_ursulas, agency):
@pytest.fixture(scope='module')
def mock_registry_filepath(testerchain):
def mock_registry_filepath(testerchain, agency):
registry = testerchain.registry
@ -65,15 +72,48 @@ def mock_registry_filepath(testerchain):
os.remove(MOCK_REGISTRY_FILEPATH)
def test_initialize_system_blockchain_configuration(click_runner,
custom_filepath,
mock_registry_filepath,
staking_participant):
def test_new_stakeholder(click_runner,
custom_filepath,
mock_registry_filepath,
staking_participant,
testerchain):
init_args = ('stake', 'new-stakeholder',
'--poa',
'--funding-address', testerchain.etherbase_account,
'--config-root', custom_filepath,
'--provider-uri', TEST_PROVIDER_URI,
'--registry-filepath', mock_registry_filepath)
result = click_runner.invoke(nucypher_cli,
init_args,
catch_exceptions=False)
assert result.exit_code == 0
# Files and Directories
assert os.path.isdir(custom_filepath), 'Configuration file does not exist'
custom_config_filepath = os.path.join(custom_filepath, StakeHolder.generate_filename())
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
with open(custom_config_filepath, 'r') as config_file:
raw_config_data = config_file.read()
config_data = json.loads(raw_config_data)
assert len(config_data['stakers']) == NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS
assert config_data['blockchain']['provider_uri'] == TEST_PROVIDER_URI
def test_ursula_init(click_runner,
custom_filepath,
mock_registry_filepath,
staking_participant,
testerchain):
init_args = ('ursula', 'init',
'--poa',
'--network', TEMPORARY_DOMAIN,
'--checksum-address', staking_participant.checksum_address,
'--staker-address', testerchain.etherbase_account,
'--worker-address', staking_participant.checksum_address,
'--config-root', custom_filepath,
'--provider-uri', TEST_PROVIDER_URI,
'--registry-filepath', mock_registry_filepath,
@ -99,45 +139,67 @@ def test_initialize_system_blockchain_configuration(click_runner,
raw_config_data = config_file.read()
config_data = json.loads(raw_config_data)
assert config_data['provider_uri'] == TEST_PROVIDER_URI
assert config_data['checksum_address'] == staking_participant.checksum_address
assert config_data['checksum_address'] == testerchain.etherbase_account
assert TEMPORARY_DOMAIN in config_data['domains']
@pytest.mark.skip(reason="Wait for #1056")
def test_init_ursula_stake(click_runner,
configuration_file_location,
funded_blockchain,
stake_value,
token_economics):
def test_stake_init(click_runner,
staker_configuration_file_location,
funded_blockchain,
stake_value,
mock_registry_filepath,
token_economics,
testerchain,
agency):
# Simulate "Reconnection"
cached_blockchain = BlockchainInterface.reconnect()
registry = cached_blockchain.registry
assert registry.filepath == mock_registry_filepath
def from_dict(*args, **kwargs):
return testerchain
BlockchainInterface.from_dict = from_dict
stake_args = ('stake', 'init',
'--config-file', configuration_file_location,
'--value', stake_value.to_tokens(),
'--config-file', staker_configuration_file_location,
'--registry-filepath', mock_registry_filepath,
'--value', stake_value.to_nunits(),
'--duration', token_economics.minimum_locked_periods,
'--force')
result = click_runner.invoke(nucypher_cli, stake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False)
user_input = f'0\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + f'Y\n'
result = click_runner.invoke(nucypher_cli, stake_args, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
with open(configuration_file_location, 'r') as config_file:
with open(staker_configuration_file_location, 'r') as config_file:
config_data = json.loads(config_file.read())
# Verify the stake is on-chain
staking_agent = StakingEscrowAgent()
stakes = list(staking_agent.get_all_stakes(staker_address=config_data['checksum_address']))
staking_agent = Agency.get_agent(StakingEscrowAgent)
stakes = list(staking_agent.get_all_stakes(staker_address=testerchain.etherbase_account))
assert len(stakes) == 1
start_period, end_period, value = stakes[0]
assert NU(int(value), 'NuNit') == stake_value
@pytest.mark.skip(reason="Wait for #1056")
def test_list_ursula_stakes(click_runner,
funded_blockchain,
configuration_file_location,
stake_value):
stake_args = ('stake', 'list',
'--config-file', configuration_file_location,
'--poa')
def test_stake_list(click_runner,
funded_blockchain,
staker_configuration_file_location,
stake_value,
mock_registry_filepath,
testerchain):
# Simulate "Reconnection"
cached_blockchain = BlockchainInterface.reconnect()
registry = cached_blockchain.registry
assert registry.filepath == mock_registry_filepath
def from_dict(*args, **kwargs):
return testerchain
BlockchainInterface.from_dict = from_dict
stake_args = ('stake', 'list', '--config-file', staker_configuration_file_location)
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}'
result = click_runner.invoke(nucypher_cli, stake_args, input=user_input, catch_exceptions=False)
@ -145,12 +207,15 @@ def test_list_ursula_stakes(click_runner,
assert str(stake_value) in result.output
@pytest.mark.skip(reason="Wait for #1056")
def test_ursula_divide_stakes(click_runner, configuration_file_location, token_economics):
def test_ursula_divide_stakes(click_runner,
staker_configuration_file_location,
token_economics,
testerchain):
divide_args = ('stake', 'divide',
'--config-file', configuration_file_location,
'--config-file', staker_configuration_file_location,
'--force',
'--staking-address', testerchain.etherbase_account,
'--index', 0,
'--value', NU(token_economics.minimum_allowed_locked, 'NuNit').to_tokens(),
'--duration', 10)
@ -162,7 +227,7 @@ def test_ursula_divide_stakes(click_runner, configuration_file_location, token_e
assert result.exit_code == 0
stake_args = ('stake', 'list',
'--config-file', configuration_file_location,
'--config-file', staker_configuration_file_location,
'--poa')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}'
@ -171,14 +236,32 @@ def test_ursula_divide_stakes(click_runner, configuration_file_location, token_e
assert str(NU(token_economics.minimum_allowed_locked, 'NuNit').to_tokens()) in result.output
@pytest.mark.skip(reason="Wait for #1056")
def test_run_blockchain_ursula(click_runner,
configuration_file_location,
staking_participant):
# Now start running your Ursula!
init_args = ('ursula', 'run',
'--dry-run',
'--config-file', configuration_file_location)
def test_stake_set_worker(click_runner,
testerchain,
staker_configuration_file_location,
staking_participant):
init_args = ('stake', 'set-worker',
'--config-file', staker_configuration_file_location,
'--staking-address', testerchain.etherbase_account,
'--worker-address', testerchain.etherbase_account, # TODO: Use Manual Worker
'--force')
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}'
result = click_runner.invoke(nucypher_cli,
init_args,
input=user_input,
catch_exceptions=False)
assert result.exit_code == 0
def test_run_blockchain_ursula(click_runner,
worker_configuration_file_location,
staking_participant):
# Now start running your Ursula!
init_args = ('ursula', 'run',
'--dry-run',
'--config-file', worker_configuration_file_location)
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}'
result = click_runner.invoke(nucypher_cli,
@ -188,9 +271,9 @@ def test_run_blockchain_ursula(click_runner,
assert result.exit_code == 0
@pytest.mark.skip(reason="Wait for #1056")
def test_collect_rewards_integration(click_runner,
configuration_file_location,
testerchain,
staker_configuration_file_location,
blockchain_alice,
blockchain_bob,
random_policy_label,
@ -199,27 +282,30 @@ def test_collect_rewards_integration(click_runner,
policy_value,
policy_rate):
blockchain = staking_participant.blockchain
half_stake_time = token_economics.minimum_locked_periods // 2 # Test setup
logger = staking_participant.log # Enter the Teacher's Logger, and
current_period = 1 # State the initial period for incrementing
staker = Staker(is_me=True,
checksum_address=staking_participant.checksum_address,
blockchain=blockchain)
checksum_address=testerchain.etherbase_account,
blockchain=testerchain)
# The staker is staking.
assert staker.stakes
assert staker.is_staking
pre_stake_token_balance = staker.token_balance
worker = Worker(is_me=True,
worker_address=testerchain.etherbase_account,
checksum_address=testerchain.etherbase_account, # TODO: Use another account
blockchain=testerchain)
# Confirm for half the first stake duration
for _ in range(half_stake_time):
current_period += 1
logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<")
blockchain.time_travel(periods=1)
staker.confirm_activity()
testerchain.time_travel(periods=1)
worker.confirm_activity()
# Alice creates a policy and grants Bob access
blockchain_alice.selection_buffer = 1
@ -263,31 +349,32 @@ def test_collect_rewards_integration(click_runner,
assert random_data == cleartexts[0]
# Ursula Staying online and the clock advancing
blockchain.time_travel(periods=1)
staker.confirm_activity()
testerchain.time_travel(periods=1)
worker.confirm_activity()
current_period += 1
# Finish the passage of time for the first Stake
for _ in range(5): # plus the extended periods from stake division
current_period += 1
logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<")
blockchain.time_travel(periods=1)
staker.confirm_activity()
testerchain.time_travel(periods=1)
worker.confirm_activity()
#
# WHERES THE MONEY URSULA?? - Collecting Rewards
#
# The address the client wants Ursula to send rewards to
burner_wallet = blockchain.w3.eth.account.create(INSECURE_DEVELOPMENT_PASSWORD)
burner_wallet = testerchain.w3.eth.account.create(INSECURE_DEVELOPMENT_PASSWORD)
# The rewards wallet is initially empty, because it is freshly created
assert blockchain.client.get_balance(burner_wallet.address) == 0
assert testerchain.client.get_balance(burner_wallet.address) == 0
# Snag a random teacher from the fleet
collection_args = ('--mock-networking',
'ursula', 'collect-reward',
'--config-file', configuration_file_location,
'stake', 'collect-reward',
'--config-file', staker_configuration_file_location,
'--staking-address', testerchain.etherbase_account,
'--withdraw-address', burner_wallet.address,
'--force')
@ -298,7 +385,7 @@ def test_collect_rewards_integration(click_runner,
assert result.exit_code == 0
# Policy Reward
collected_policy_reward = blockchain.client.get_balance(burner_wallet.address)
collected_policy_reward = testerchain.client.get_balance(burner_wallet.address)
expected_collection = policy_rate * 30
assert collected_policy_reward == expected_collection
@ -307,10 +394,10 @@ def test_collect_rewards_integration(click_runner,
for _ in range(9):
current_period += 1
logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<")
blockchain.time_travel(periods=1)
staker.confirm_activity()
testerchain.time_travel(periods=1)
worker.confirm_activity()
# Staking Reward
calculated_reward = staker.staking_agent.calculate_staking_reward(checksum_address=staker.checksum_address)
assert calculated_reward
assert staker.token_balance > pre_stake_token_balance
assert staker.token_balance > pre_stake_token_balance