Prolong stake API and CLI entry; Deprecate Felix template and landing page

pull/1565/head
Kieran Prasch 2020-01-14 14:51:29 -08:00
parent c28d5892bb
commit 923b2deb98
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
10 changed files with 148 additions and 20 deletions

View File

@ -795,6 +795,42 @@ class Staker(NucypherTokenActor):
return new_stake
@only_me
def prolong_stake(self,
stake_index: int,
additional_periods: int = None,
expiration: maya.MayaDT = None) -> tuple:
# Calculate duration in periods
if additional_periods and expiration:
raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.")
# Update staking cache element
stakes = self.stakes
# Select stake to divide from local cache
try:
current_stake = stakes[stake_index]
except KeyError:
if len(stakes):
message = f"Cannot prolong stake - No stake exists with index {stake_index}."
else:
message = "Cannot prolong stake - There are no active stakes."
raise Stake.StakingError(message)
# Calculate stake duration in periods
if expiration:
additional_periods = datetime_to_period(datetime=expiration, seconds_per_period=self.economics.seconds_per_period) - current_stake.final_locked_period
if additional_periods <= 0:
raise Stake.StakingError(f"New expiration {expiration} must be at least 1 period from the "
f"current stake's end period ({current_stake.final_locked_period}).")
stake = current_stake.prolong(additional_periods=additional_periods)
# Update staking cache element
self.stakes.refresh()
return stake
def deposit(self, amount: int, lock_periods: int) -> Tuple[str, str]:
"""Public facing method for token locking."""
if self.is_contract:

View File

@ -455,6 +455,15 @@ class Stake:
log.info(f"{staker.checksum_address} Initialized new stake: {amount} tokens for {lock_periods} periods")
return stake
def prolong(self, additional_periods: int):
self.sync()
if self.is_expired:
raise self.StakingError(f'Cannot divide an expired stake. Selected stake expired {self.unlock_datetime}.')
receipt = self.staking_agent.prolong_stake(staker_address=self.staker_address,
stake_index=self.index,
periods=additional_periods)
return receipt
class WorkTracker:

View File

@ -205,11 +205,6 @@ class Felix(Character, NucypherTokenActor):
}
)
@rest_app.route("/", methods=['GET'])
def home():
rendering = render_template(self.TEMPLATE_NAME)
return rendering
@rest_app.route("/register", methods=['POST'])
def register():
"""Handle new recipient registration via POST request."""

View File

@ -1,4 +1,3 @@
import functools
import json
import click

View File

@ -36,7 +36,7 @@ from nucypher.cli.actions import (
confirm_deployment,
establish_deployer_registry
)
from nucypher.cli.common_options import (
from nucypher.cli.options import (
group_options,
option_config_root,
option_etherscan,
@ -44,7 +44,7 @@ from nucypher.cli.common_options import (
option_hw_wallet,
option_poa,
option_provider_uri,
)
)
from nucypher.cli.config import group_general_config
from nucypher.cli.painting import (
paint_staged_deployment,

View File

@ -636,6 +636,71 @@ def divide(general_config, transacting_staker_options, config_file, force, value
painting.paint_stakes(emitter=emitter, stakes=STAKEHOLDER.stakes)
@stake.command()
@group_transacting_staker_options
@option_config_file
@option_force
@option_lock_periods
@click.option('--index', help="The staker-specific stake index to prolong", type=click.INT)
@group_general_config
def prolong(general_config, transacting_staker_options, config_file, force, lock_periods, index):
"""Prolong an existing stake's duration."""
# Setup
emitter = _setup_emitter(general_config)
STAKEHOLDER = transacting_staker_options.create_character(emitter, config_file)
action_period = STAKEHOLDER.staking_agent.get_current_period()
blockchain = transacting_staker_options.get_blockchain()
economics = STAKEHOLDER.economics
# Handle account selection
client_account, staking_address = handle_client_account_for_staking(
emitter=emitter,
stakeholder=STAKEHOLDER,
staking_address=transacting_staker_options.staker_options.staking_address,
individual_allocation=STAKEHOLDER.individual_allocation,
force=force)
# Handle stake update and selection
if transacting_staker_options.staker_options.staking_address and index is not None: # 0 is valid.
STAKEHOLDER.stakes = StakeList(registry=STAKEHOLDER.registry,
checksum_address=transacting_staker_options.staker_options.staking_address)
STAKEHOLDER.stakes.refresh()
current_stake = STAKEHOLDER.stakes[index]
else:
current_stake = select_stake(stakeholder=STAKEHOLDER, emitter=emitter)
#
# Prolong
#
# Interactive
if not lock_periods:
stake_extension_range = click.IntRange(min=1, max=economics.maximum_allowed_locked, clamp=False)
max_extension = economics.maximum_allowed_locked - current_stake.periods_remaining
lock_periods = click.prompt(f"Enter number of periods to extend (1-{max_extension})", type=stake_extension_range)
if not force:
click.confirm(f"Publish stake extension of {lock_periods} period(s) to the blockchain?", abort=True)
password = transacting_staker_options.get_password(blockchain, client_account)
# Non-interactive: Consistency check to prevent the above agreement from going stale.
last_second_current_period = STAKEHOLDER.staking_agent.get_current_period()
if action_period != last_second_current_period:
emitter.echo("Current period advanced before transaction was broadcasted. Please try again.", red='red')
raise click.Abort
# Authenticate and Execute
STAKEHOLDER.assimilate(checksum_address=current_stake.staker_address, password=password)
emitter.echo("Broadcasting Stake Extension...", color='yellow')
receipt = STAKEHOLDER.prolong_stake(stake_index=current_stake.index, additional_periods=lock_periods)
# Report
emitter.echo('Successfully Prolonged Stake', color='green', verbosity=1)
paint_receipt_summary(emitter=emitter, receipt=receipt, chain_name=blockchain.client.chain_name)
painting.paint_stakes(emitter=emitter, stakes=STAKEHOLDER.stakes)
return # Exit
@stake.command('collect-reward')
@group_transacting_staker_options
@option_config_file

View File

@ -232,14 +232,14 @@ def test_prolong_stake(agency, testerchain, test_registry):
staker_account, worker_account, *other = testerchain.unassigned_accounts
stakes = list(staking_agent.get_all_stakes(staker_address=staker_account))
original_termination = stakes[1]
original_termination = stakes[0][1]
receipt = staking_agent.prolong_stake(staker_account=staker_account, stake_index=0, periods=1)
receipt = staking_agent.prolong_stake(staker_address=staker_account, stake_index=0, periods=1)
assert receipt['status'] == 1
# Ensure stake was extended by one period.
stakes = list(staking_agent.get_all_stakes(staker_address=staker_account))
new_termination = stakes[1]
new_termination = stakes[0][1]
assert new_termination == original_termination + 1

View File

@ -113,10 +113,6 @@ def test_run_felix(click_runner,
web_app = felix.make_web_app()
test_client = web_app.test_client()
# Load the landing page
response = test_client.get('/')
assert response.status_code == 200
# Register a new recipient
response = test_client.post('/register', data={'address': testerchain.client.accounts[-1]})
assert response.status_code == 200

View File

@ -18,8 +18,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import random
import re
import pytest
from nucypher.blockchain.eth.agents import (
PolicyManagerAgent,
StakingEscrowAgent,
@ -27,9 +25,8 @@ from nucypher.blockchain.eth.agents import (
NucypherTokenAgent,
ContractAgency
)
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.token import NU
from nucypher.cli.status import status
from nucypher.cli.commands.status import status
from nucypher.utilities.sandbox.constants import TEST_PROVIDER_URI, MOCK_REGISTRY_FILEPATH

View File

@ -173,6 +173,38 @@ def test_staker_divide_stakes(click_runner,
assert str(NU(token_economics.minimum_allowed_locked, 'NuNit').to_tokens()) in result.output
def test_stake_prolong(click_runner,
testerchain,
test_registry,
manual_staker,
manual_worker,
stakeholder_configuration_file_location):
prolong_args = ('stake', 'prolong',
'--config-file', stakeholder_configuration_file_location,
'--index', 0,
'--lock-periods', 1,
'--staking-address', manual_staker,
'--force')
staker = Staker(is_me=True, checksum_address=manual_staker, registry=test_registry)
staker.stakes.refresh()
stake = staker.stakes[0]
old_termination = stake.final_locked_period
user_input = INSECURE_DEVELOPMENT_PASSWORD
result = click_runner.invoke(nucypher_cli,
prolong_args,
input=user_input,
catch_exceptions=False)
assert result.exit_code == 0
# Ensure Integration with Stakes
stake.sync()
new_termination = stake.final_locked_period
assert new_termination == old_termination + 1
def test_stake_set_worker(click_runner,
testerchain,
test_registry,
@ -194,7 +226,6 @@ def test_stake_set_worker(click_runner,
assert result.exit_code == 0
staker = Staker(is_me=True, checksum_address=manual_staker, registry=test_registry)
assert staker.worker_address == manual_worker