示例#1
0
def test_staker_locking_tokens(testerchain, agency, staker, token_economics):
    token_agent, staking_agent, policy_agent = agency

    # Mock Powerup consumption (Ursula-Staker)
    testerchain.transacting_power = BlockchainPower(
        blockchain=testerchain, account=staker.checksum_address)

    assert NU(token_economics.minimum_allowed_locked,
              'NuNit') < staker.token_balance, "Insufficient staker balance"

    staker.initialize_stake(
        amount=NU(token_economics.minimum_allowed_locked,
                  'NuNit'),  # Lock the minimum amount of tokens
        lock_periods=token_economics.minimum_locked_periods)

    # Verify that the escrow is "approved" to receive tokens
    allowance = token_agent.contract.functions.allowance(
        staker.checksum_address, staking_agent.contract_address).call()
    assert 0 == allowance

    # Staking starts after one period
    locked_tokens = staker.locked_tokens()
    assert 0 == locked_tokens

    locked_tokens = staker.locked_tokens(periods=1)
    assert token_economics.minimum_allowed_locked == locked_tokens
示例#2
0
def test_staker_locking_tokens(testerchain, agency, staker, token_economics,
                               test_registry):
    token_agent = ContractAgency.get_agent(NucypherTokenAgent,
                                           registry=test_registry)
    staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
                                             registry=test_registry)

    assert NU(token_economics.minimum_allowed_locked,
              'NuNit') < staker.token_balance, "Insufficient staker balance"

    staker.initialize_stake(
        amount=NU(token_economics.minimum_allowed_locked, 'NuNit'),
        # Lock the minimum amount of tokens
        lock_periods=token_economics.minimum_locked_periods)

    # Verify that the escrow is "approved" to receive tokens
    allowance = token_agent.contract.functions.allowance(
        staker.checksum_address, staking_agent.contract_address).call()
    assert 0 == allowance

    # Staking starts after one period
    locked_tokens = staker.locked_tokens()
    assert 0 == locked_tokens

    locked_tokens = staker.locked_tokens(periods=1)
    assert token_economics.minimum_allowed_locked == locked_tokens
def test_stake_increase(click_runner, stakeholder_configuration_file_location,
                        token_economics, testerchain, agency_local_registry,
                        manual_staker):
    staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
                                             registry=agency_local_registry)
    stakes = list(staking_agent.get_all_stakes(staker_address=manual_staker))
    stakes_length = len(stakes)
    assert stakes_length > 0

    selection = 0
    new_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
    origin_stake = stakes[selection]

    stake_args = ('stake', 'increase', '--config-file',
                  stakeholder_configuration_file_location,
                  '--staking-address', manual_staker, '--value',
                  new_value.to_tokens(), '--index', selection, '--force')

    user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n'
    result = click_runner.invoke(nucypher_cli,
                                 stake_args,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    # Verify the stake is on-chain
    # Test integration with Agency
    stakes = list(staking_agent.get_all_stakes(staker_address=manual_staker))
    assert len(stakes) == stakes_length

    # Test integration with NU
    _start_period, end_period, value = stakes[selection]
    assert NU(int(value), 'NuNit') == origin_stake.locked_value + new_value
    assert end_period == origin_stake.last_period
示例#4
0
def test_staker_locking_tokens(testerchain, agency, staker, token_economics,
                               mock_transacting_power_activation):
    token_agent, staking_agent, policy_agent = agency

    mock_transacting_power_activation(account=staker.checksum_address,
                                      password=INSECURE_DEVELOPMENT_PASSWORD)

    assert NU(token_economics.minimum_allowed_locked,
              'NuNit') < staker.token_balance, "Insufficient staker balance"

    staker.initialize_stake(
        amount=NU(token_economics.minimum_allowed_locked,
                  'NuNit'),  # Lock the minimum amount of tokens
        lock_periods=token_economics.minimum_locked_periods)

    # Verify that the escrow is "approved" to receive tokens
    allowance = token_agent.contract.functions.allowance(
        staker.checksum_address, staking_agent.contract_address).call()
    assert 0 == allowance

    # Staking starts after one period
    locked_tokens = staker.locked_tokens()
    assert 0 == locked_tokens

    locked_tokens = staker.locked_tokens(periods=1)
    assert token_economics.minimum_allowed_locked == locked_tokens
示例#5
0
def test_miner_locking_tokens(testerchain, three_agents, miner,
                              token_economics):

    token_agent, miner_agent, policy_agent = three_agents

    assert NU(token_economics.minimum_allowed_locked,
              'NuNit') < miner.token_balance, "Insufficient miner balance"

    miner.initialize_stake(
        amount=NU(token_economics.minimum_allowed_locked,
                  'NuNit'),  # Lock the minimum amount of tokens
        lock_periods=token_economics.minimum_locked_periods)

    # Verify that the escrow is "approved" to receive tokens
    allowance = token_agent.contract.functions.allowance(
        miner.checksum_address, miner_agent.contract_address).call()
    assert 0 == allowance

    # Staking starts after one period
    locked_tokens = miner_agent.contract.functions.getLockedTokens(
        miner.checksum_address).call()
    assert 0 == locked_tokens

    locked_tokens = miner_agent.contract.functions.getLockedTokens(
        miner.checksum_address, 1).call()
    assert token_economics.minimum_allowed_locked == locked_tokens
示例#6
0
def paint_preallocation_status(emitter, preallocation_agent,
                               token_agent) -> None:
    blockchain = token_agent.blockchain

    staking_address = preallocation_agent.principal_contract.address

    token_balance = NU.from_nunits(token_agent.get_balance(staking_address))
    eth_balance = Web3.fromWei(blockchain.client.get_balance(staking_address),
                               'ether')
    initial_locked_amount = NU.from_nunits(
        preallocation_agent.initial_locked_amount)
    current_locked_amount = NU.from_nunits(preallocation_agent.unvested_tokens)
    available_amount = NU.from_nunits(preallocation_agent.available_balance)
    end_timestamp = preallocation_agent.end_timestamp

    width = 64
    output = f"""
{" Addresses ".center(width, "-")}
Staking contract: ... {staking_address}
Beneficiary: ........ {preallocation_agent.beneficiary}

{" Locked Tokens ".center(width, "-")}
Initial locked amount: {initial_locked_amount}
Current locked amount: {current_locked_amount}
Locked until: ........ {maya.MayaDT(epoch=end_timestamp)}

{" NU and ETH Balance ".center(width, "-")}
NU balance: .......... {token_balance}
    Available: ....... {available_amount} 
ETH balance: ......... {eth_balance} ETH
"""
    emitter.echo(output)
示例#7
0
def test_miner_divides_stake(miner, token_economics):
    stake_value = NU(token_economics.minimum_allowed_locked*5, 'NuNit')
    new_stake_value = NU(token_economics.minimum_allowed_locked*2, 'NuNit')

    stake_index = 0
    miner.initialize_stake(amount=stake_value, lock_periods=int(token_economics.minimum_locked_periods))
    miner.divide_stake(target_value=new_stake_value, stake_index=stake_index+1, additional_periods=2)

    current_period = miner.miner_agent.get_current_period()
    expected_old_stake = (current_period + 1, current_period + 30, stake_value - new_stake_value)
    expected_new_stake = (current_period + 1, current_period + 32, new_stake_value)

    assert 3 == len(miner.stakes), 'A new stake was not added to this miners stakes'
    assert expected_old_stake == miner.stakes[stake_index + 1].to_stake_info(), 'Old stake values are invalid'
    assert expected_new_stake == miner.stakes[stake_index + 2].to_stake_info(), 'New stake values are invalid'

    yet_another_stake_value = NU(token_economics.minimum_allowed_locked, 'NuNit')
    miner.divide_stake(target_value=yet_another_stake_value, stake_index=stake_index + 2, additional_periods=2)

    expected_new_stake = (current_period + 1, current_period + 32, new_stake_value - yet_another_stake_value)
    expected_yet_another_stake = Stake(start_period=current_period + 1,
                                       end_period=current_period + 34,
                                       value=yet_another_stake_value,
                                       miner=miner,
                                       index=3)

    assert 4 == len(miner.stakes), 'A new stake was not added after two stake divisions'
    assert expected_old_stake == miner.stakes[stake_index + 1].to_stake_info(), 'Old stake values are invalid after two stake divisions'
    assert expected_new_stake == miner.stakes[stake_index + 2].to_stake_info(), 'New stake values are invalid after two stake divisions'
    assert expected_yet_another_stake == miner.stakes[stake_index + 3], 'Third stake values are invalid'
示例#8
0
def test_miner_locking_tokens(testerchain, three_agents, miner):
    token_agent, miner_agent, policy_agent = three_agents

    assert NU(MIN_ALLOWED_LOCKED,
              'NuNit') < miner.token_balance, "Insufficient miner balance"

    expiration = maya.now().add(days=MIN_LOCKED_PERIODS)
    miner.initialize_stake(
        amount=NU(MIN_ALLOWED_LOCKED,
                  'NuNit'),  # Lock the minimum amount of tokens
        expiration=expiration)

    # Verify that the escrow is "approved" to receive tokens
    allowance = token_agent.contract.functions.allowance(
        miner.checksum_public_address, miner_agent.contract_address).call()
    assert 0 == allowance

    # Staking starts after one period
    locked_tokens = miner_agent.contract.functions.getLockedTokens(
        miner.checksum_public_address).call()
    assert 0 == locked_tokens

    locked_tokens = miner_agent.contract.functions.getLockedTokens(
        miner.checksum_public_address, 1).call()
    assert MIN_ALLOWED_LOCKED == locked_tokens
示例#9
0
def test_ursula_divide_stakes(click_runner, configuration_file_location,
                              token_economics):

    divide_args = ('ursula', 'stake', '--divide', '--config-file',
                   configuration_file_location, '--poa', '--force', '--index',
                   0, '--value',
                   NU(token_economics.minimum_allowed_locked,
                      'NuNit').to_tokens(), '--duration', 10)

    result = click_runner.invoke(
        nucypher_cli,
        divide_args,
        catch_exceptions=False,
        env=dict(NUCYPHER_KEYRING_PASSWORD=INSECURE_DEVELOPMENT_PASSWORD))
    assert result.exit_code == 0

    stake_args = ('ursula', 'stake', '--config-file',
                  configuration_file_location, '--list', '--poa')

    user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}'
    result = click_runner.invoke(nucypher_cli,
                                 stake_args,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0
    assert str(
        NU(token_economics.minimum_allowed_locked,
           'NuNit').to_tokens()) in result.output
def test_staker_divide_stakes(click_runner,
                              stakeholder_configuration_file_location,
                              token_economics, manual_staker, testerchain,
                              agency_local_registry):

    divide_args = ('stake', 'divide', '--config-file',
                   stakeholder_configuration_file_location, '--force',
                   '--staking-address', manual_staker, '--index', 0, '--value',
                   NU(token_economics.minimum_allowed_locked,
                      'NuNit').to_tokens(), '--lock-periods', 10)

    result = click_runner.invoke(
        nucypher_cli,
        divide_args,
        catch_exceptions=False,
        env=dict(NUCYPHER_KEYRING_PASSWORD=INSECURE_DEVELOPMENT_PASSWORD))
    assert result.exit_code == 0

    stake_args = ('stake', 'list', '--config-file',
                  stakeholder_configuration_file_location)

    user_input = INSECURE_DEVELOPMENT_PASSWORD
    result = click_runner.invoke(nucypher_cli,
                                 stake_args,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0
    assert str(
        NU(token_economics.minimum_allowed_locked,
           'NuNit').to_tokens()) in result.output
示例#11
0
def test_stake(testerchain, token_economics, agency):
    token_agent, staking_agent, _policy_agent = agency

    class FakeUrsula:
        token_agent, staking_agent, _policy_agent = agency

        burner_wallet = Web3().eth.account.create(INSECURE_DEVELOPMENT_PASSWORD)
        checksum_address = burner_wallet.address
        staking_agent = staking_agent
        token_agent = token_agent
        blockchain = testerchain

    ursula = FakeUrsula()
    stake = Stake(checksum_address=ursula.checksum_address,
                  first_locked_period=1,
                  final_locked_period=100,
                  value=NU(100, 'NU'),
                  index=0,
                  staking_agent=staking_agent,
                  economics=token_economics)

    assert stake.value, 'NU' == NU(100, 'NU')

    assert isinstance(stake.time_remaining(), int)      # seconds
    slang_remaining = stake.time_remaining(slang=True)  # words
    assert isinstance(slang_remaining, str)
示例#12
0
def transfer_tokens(general_config, actor_options, target_address, value):
    """Transfer tokens from contract's owner address to another address"""

    emitter = general_config.emitter
    ADMINISTRATOR, deployer_address, _, local_registry = actor_options.create_actor(
        emitter)

    token_agent = ContractAgency.get_agent(
        NucypherTokenAgent,
        registry=local_registry)  # type: NucypherTokenAgent
    tokens = NU.from_nunits(token_agent.get_balance(deployer_address))
    emitter.echo(
        DISPLAY_SENDER_TOKEN_BALANCE_BEFORE_TRANSFER.format(
            token_balance=tokens))
    if not target_address:
        target_address = click.prompt(PROMPT_RECIPIENT_CHECKSUM_ADDRESS,
                                      type=EIP55_CHECKSUM_ADDRESS)
    if not value:
        stake_value_range = click.FloatRange(min=0, clamp=False)
        value = NU.from_tokens(
            click.prompt(PROMPT_TOKEN_VALUE, type=stake_value_range))

    confirmation = CONFIRM_TOKEN_TRANSFER.format(
        value=value,
        deployer_address=deployer_address,
        target_address=target_address)
    click.confirm(confirmation, abort=True)
    receipt = token_agent.transfer(amount=int(value),
                                   sender_address=deployer_address,
                                   target_address=target_address)
    paint_receipt_summary(emitter=emitter, receipt=receipt)
示例#13
0
 def current_stake(self) -> NU:
     """
     The total number of staked tokens, either locked or unlocked in the current period.
     """
     if self.stakes:
         return NU(sum(int(stake.value) for stake in self.stakes), 'NuNit')
     else:
         return NU.ZERO()
示例#14
0
文件: stake.py 项目: gs455/nucypher
def preallocation(click_config,

                  # Stake Options
                  poa, light, registry_filepath, config_file, provider_uri, staking_address, hw_wallet,
                  beneficiary_address, allocation_filepath,

                  # Preallocation subcommands,
                  action,

                  # Other
                  force):
    """
    Claim token rewards collected by a preallocation contract.
    """

    ### Setup ###
    emitter = _setup_emitter(click_config)

    STAKEHOLDER, blockchain = _create_stakeholder(config_file,
                                                  provider_uri,
                                                  poa,
                                                  light,
                                                  registry_filepath,
                                                  staking_address,
                                                  beneficiary_address=beneficiary_address,
                                                  allocation_filepath=allocation_filepath)
    #############
    # Unauthenticated actions: status

    if action == 'status':
        paint_preallocation_status(emitter=emitter,
                                   token_agent=STAKEHOLDER.token_agent,
                                   preallocation_agent=STAKEHOLDER.preallocation_escrow_agent)
        return

    # Authenticated actions: withdraw-tokens

    client_account, staking_address = handle_client_account_for_staking(emitter=emitter,
                                                                        stakeholder=STAKEHOLDER,
                                                                        staking_address=staking_address,
                                                                        individual_allocation=STAKEHOLDER.individual_allocation,
                                                                        force=force)

    password = None
    if not hw_wallet and not blockchain.client.is_local:
        password = get_client_password(checksum_address=client_account)

    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
    if action == 'withdraw':
        token_balance = NU.from_nunits(STAKEHOLDER.token_agent.get_balance(staking_address))
        locked_tokens = NU.from_nunits(STAKEHOLDER.preallocation_escrow_agent.unvested_tokens)
        unlocked_tokens = token_balance - locked_tokens

        emitter.echo(message=f'Collecting {unlocked_tokens} from PreallocationEscrow contract {staking_address}...')
        receipt = STAKEHOLDER.withdraw_preallocation_tokens(unlocked_tokens)
        paint_receipt_summary(receipt=receipt,
                              chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
                              emitter=emitter)
示例#15
0
def paint_stakers(emitter, stakers: List[str], agent) -> None:
    current_period = agent.get_current_period()
    emitter.echo(f"\nCurrent period: {current_period}")
    emitter.echo("\n| Stakers |\n")
    emitter.echo(f"{'Checksum address':42}  Staker information")
    emitter.echo('=' * (42 + 2 + 53))

    for staker in stakers:
        nickname, pairs = nickname_from_seed(staker)
        symbols = f"{pairs[0][1]}  {pairs[1][1]}"
        emitter.echo(f"{staker}  {'Nickname:':10} {nickname} {symbols}")
        tab = " " * len(staker)

        stake = agent.owned_tokens(staker)
        last_confirmed_period = agent.get_last_active_period(staker)
        worker = agent.get_worker_from_staker(staker)
        is_restaking = agent.is_restaking(staker)

        missing_confirmations = current_period - last_confirmed_period
        stake_in_nu = round(NU.from_nunits(stake), 2)
        locked_tokens = round(NU.from_nunits(agent.get_locked_tokens(staker)),
                              2)

        emitter.echo(
            f"{tab}  {'Stake:':10} {stake_in_nu}  (Locked: {locked_tokens})")
        if is_restaking:
            if agent.is_restaking_locked(staker):
                unlock_period = agent.get_restake_unlock_period(staker)
                emitter.echo(
                    f"{tab}  {'Re-staking:':10} Yes  (Locked until period: {unlock_period})"
                )
            else:
                emitter.echo(f"{tab}  {'Re-staking:':10} Yes  (Unlocked)")
        else:
            emitter.echo(f"{tab}  {'Re-staking:':10} No")
        emitter.echo(f"{tab}  {'Activity:':10} ", nl=False)
        if missing_confirmations == -1:
            emitter.echo(f"Next period confirmed (#{last_confirmed_period})",
                         color='green')
        elif missing_confirmations == 0:
            emitter.echo(
                f"Current period confirmed (#{last_confirmed_period}). "
                f"Pending confirmation of next period.",
                color='yellow')
        elif missing_confirmations == current_period:
            emitter.echo(f"Never confirmed activity", color='red')
        else:
            emitter.echo(
                f"Missing {missing_confirmations} confirmations "
                f"(last time for period #{last_confirmed_period})",
                color='red')

        emitter.echo(f"{tab}  {'Worker:':10} ", nl=False)
        if worker == BlockchainInterface.NULL_ADDRESS:
            emitter.echo(f"Worker not set\n", color='red')
        else:
            emitter.echo(f"{worker}\n")
示例#16
0
def test_nucypher_status_stakers(click_runner, agency_local_registry, stakers):

    # Get all stakers info
    stakers_command = ('stakers', '--registry-filepath',
                       str(agency_local_registry.filepath.absolute()),
                       '--provider', TEST_PROVIDER_URI, '--network',
                       TEMPORARY_DOMAIN)

    result = click_runner.invoke(status,
                                 stakers_command,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
                                             registry=agency_local_registry)

    # TODO: Use regex matching instead of this
    assert re.search(f"^Current period: {staking_agent.get_current_period()}",
                     result.output, re.MULTILINE)
    for staker in stakers:
        assert re.search(f"^{staker.checksum_address}", result.output,
                         re.MULTILINE)

    # Get info of only one staker
    some_dude = random.choice(stakers)
    staking_address = some_dude.checksum_address
    stakers_command = ('stakers', '--staking-address', staking_address,
                       '--registry-filepath',
                       str(agency_local_registry.filepath.absolute()),
                       '--provider', TEST_PROVIDER_URI, '--network',
                       TEMPORARY_DOMAIN)

    result = click_runner.invoke(status,
                                 stakers_command,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    owned_tokens = NU.from_nunits(staking_agent.owned_tokens(staking_address))
    current_locked_tokens = NU.from_nunits(
        staking_agent.get_locked_tokens(staking_address))
    next_locked_tokens = NU.from_nunits(
        staking_agent.get_locked_tokens(staking_address, 1))

    assert re.search(f"^Current period: {staking_agent.get_current_period()}",
                     result.output, re.MULTILINE)
    assert re.search(r"Worker:\s+" + some_dude.worker_address, result.output,
                     re.MULTILINE)
    assert re.search(r"Owned:\s+" + str(round(owned_tokens, 2)), result.output,
                     re.MULTILINE)
    assert re.search(
        r"Staked in current period: " + str(round(current_locked_tokens, 2)),
        result.output, re.MULTILINE)
    assert re.search(
        r"Staked in next period: " + str(round(next_locked_tokens, 2)),
        result.output, re.MULTILINE)
    _minimum, default, _maximum = FEE_RATE_RANGE
    assert f"Min fee rate: {default} wei" in result.output
示例#17
0
文件: painting.py 项目: xbee/nucypher
def paint_stakers(emitter, stakers: List[str], staking_agent, policy_agent) -> None:
    current_period = staking_agent.get_current_period()
    emitter.echo(f"\nCurrent period: {current_period}")
    emitter.echo("\n| Stakers |\n")
    emitter.echo(f"{'Checksum address':42}  Staker information")
    emitter.echo('=' * (42 + 2 + 53))

    for staker in stakers:
        nickname, pairs = nickname_from_seed(staker)
        symbols = f"{pairs[0][1]}  {pairs[1][1]}"
        emitter.echo(f"{staker}  {'Nickname:':10} {nickname} {symbols}")
        tab = " " * len(staker)

        owned_tokens = staking_agent.owned_tokens(staker)
        last_confirmed_period = staking_agent.get_last_active_period(staker)
        worker = staking_agent.get_worker_from_staker(staker)
        is_restaking = staking_agent.is_restaking(staker)
        is_winding_down = staking_agent.is_winding_down(staker)

        missing_confirmations = current_period - last_confirmed_period
        owned_in_nu = round(NU.from_nunits(owned_tokens), 2)
        locked_tokens = round(NU.from_nunits(staking_agent.get_locked_tokens(staker)), 2)

        emitter.echo(f"{tab}  {'Owned:':10} {owned_in_nu}  (Staked: {locked_tokens})")
        if is_restaking:
            if staking_agent.is_restaking_locked(staker):
                unlock_period = staking_agent.get_restake_unlock_period(staker)
                emitter.echo(f"{tab}  {'Re-staking:':10} Yes  (Locked until period: {unlock_period})")
            else:
                emitter.echo(f"{tab}  {'Re-staking:':10} Yes  (Unlocked)")
        else:
            emitter.echo(f"{tab}  {'Re-staking:':10} No")
        emitter.echo(f"{tab}  {'Winding down:':10} {'Yes' if is_winding_down else 'No'}")
        emitter.echo(f"{tab}  {'Activity:':10} ", nl=False)
        if missing_confirmations == -1:
            emitter.echo(f"Next period confirmed (#{last_confirmed_period})", color='green')
        elif missing_confirmations == 0:
            emitter.echo(f"Current period confirmed (#{last_confirmed_period}). "
                         f"Pending confirmation of next period.", color='yellow')
        elif missing_confirmations == current_period:
            emitter.echo(f"Never confirmed activity", color='red')
        else:
            emitter.echo(f"Missing {missing_confirmations} confirmations "
                         f"(last time for period #{last_confirmed_period})", color='red')

        emitter.echo(f"{tab}  {'Worker:':10} ", nl=False)
        if worker == NULL_ADDRESS:
            emitter.echo(f"Worker not set", color='red')
        else:
            emitter.echo(f"{worker}")

        fees = prettify_eth_amount(policy_agent.get_reward_amount(staker))
        emitter.echo(f"{tab}  Unclaimed fees: {fees}")

        min_rate = prettify_eth_amount(policy_agent.get_min_reward_rate(staker))
        emitter.echo(f"{tab}  Min reward rate: {min_rate}")
示例#18
0
def test_stake_status(mock_testerchain, token_economics, mock_staking_agent):

    address = mock_testerchain.etherbase_account
    current_period = 3
    staker_info = StakerInfo(current_committed_period=current_period - 1,
                             next_committed_period=current_period,
                             value=0,
                             last_committed_period=0,
                             lock_restake_until_period=False,
                             completed_work=0,
                             worker_start_period=0,
                             worker=NULL_ADDRESS,
                             flags=bytes())

    mock_staking_agent.get_current_period.return_value = current_period
    mock_staking_agent.get_staker_info.return_value = staker_info

    def make_sub_stake(value, first_locked_period, final_locked_period):
        return Stake(checksum_address=address,
                     first_locked_period=first_locked_period,
                     final_locked_period=final_locked_period,
                     value=value,
                     index=0,
                     staking_agent=mock_staking_agent,
                     economics=token_economics,
                     validate_now=False)

    # Prepare unlocked sub-stake
    nu = NU.from_nunits(2 * token_economics.minimum_allowed_locked - 1)
    stake = make_sub_stake(nu, current_period - 2, current_period - 1)
    assert stake.status() == Stake.Status.UNLOCKED

    # Prepare inactive sub-stake
    # Update staker info and create new state
    stake = make_sub_stake(nu, current_period - 2, current_period - 1)

    staker_info = staker_info._replace(current_committed_period=current_period,
                                       next_committed_period=current_period +
                                       1)
    mock_staking_agent.get_staker_info.return_value = staker_info

    assert stake.status() == Stake.Status.INACTIVE

    # Prepare locked sub-stake
    stake = make_sub_stake(nu, current_period - 2, current_period)
    assert stake.status() == Stake.Status.LOCKED

    # Prepare editable sub-stake
    stake = make_sub_stake(nu, current_period - 2, current_period + 1)
    assert stake.status() == Stake.Status.EDITABLE

    # Prepare divisible sub-stake
    nu = NU.from_nunits(2 * token_economics.minimum_allowed_locked)
    stake = make_sub_stake(nu, current_period - 2, current_period + 1)
    assert stake.status() == Stake.Status.DIVISIBLE
示例#19
0
def escrow(general_config: GroupGeneralConfig,
           worklock_options: WorkLockOptions,
           force: bool,
           hw_wallet: bool,
           value: Decimal):
    """Create an ETH escrow, or increase an existing escrow"""
    emitter, registry, blockchain = worklock_options.setup(general_config=general_config)
    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)  # type: WorkLockAgent
    now = maya.now().epoch
    if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_bidding_date:
        emitter.echo(BIDDING_WINDOW_CLOSED, color='red')
        raise click.Abort()

    _bidder_address = worklock_options.get_bidder_address(emitter, registry)
    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    existing_bid_amount = bidder.get_deposited_eth
    if not value:
        if force:
            raise click.MissingParameter("Missing --value.")

        if not existing_bid_amount:  # It's the first bid
            minimum_bid = bidder.worklock_agent.minimum_allowed_bid
            minimum_bid_in_eth = Web3.fromWei(minimum_bid, 'ether')
            prompt = BID_AMOUNT_PROMPT_WITH_MIN_BID.format(minimum_bid_in_eth=minimum_bid_in_eth)
        else:  # There's an existing bid and the bidder is increasing the amount
            emitter.message(EXISTING_BID_AMOUNT_NOTICE.format(eth_amount=Web3.fromWei(existing_bid_amount, 'ether')))
            minimum_bid_in_eth = Web3.fromWei(1, 'ether')
            prompt = BID_INCREASE_AMOUNT_PROMPT
        value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth))

    value = int(Web3.toWei(Decimal(value), 'ether'))

    if not force:
        if not existing_bid_amount:
            paint_bidding_notice(emitter=emitter, bidder=bidder)
            click.confirm(f"Place WorkLock escrow of {prettify_eth_amount(value)}?", abort=True)
        else:
            click.confirm(f"Increase current escrow ({prettify_eth_amount(existing_bid_amount)}) "
                          f"by {prettify_eth_amount(value)}?", abort=True)

    receipt = bidder.place_bid(value=value)
    emitter.message("Publishing WorkLock Escrow...")

    maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked)
    available_claim = NU.from_nunits(bidder.available_claim)
    message = f'\nCurrent escrow: {prettify_eth_amount(bidder.get_deposited_eth)} | Allocation: {available_claim}\n'
    if available_claim > maximum:
        message += f"\nThis allocation is currently above the allowed max ({maximum}), " \
                   f"so the escrow may be partially refunded.\n"
    message += f'Note that the available allocation value may fluctuate until the escrow period closes and ' \
               f'allocations are finalized.\n'
    emitter.echo(message, color='yellow')

    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
示例#20
0
def bid(general_config, worklock_options, registry_options, force, hw_wallet, value):
    """Place a bid, or increase an existing bid"""
    emitter = _setup_emitter(general_config)
    registry = registry_options.get_registry(emitter, general_config.debug)

    worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)  # type: WorkLockAgent
    now = maya.now().epoch
    if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_bidding_date:
        raise click.Abort(f"You can't bid, the bidding window is closed.")

    if not worklock_options.bidder_address:
        worklock_options.bidder_address = select_client_account(emitter=emitter,
                                                                provider_uri=registry_options.provider_uri,
                                                                poa=registry_options.poa,
                                                                network=registry_options.network,
                                                                registry=registry,
                                                                show_balances=True)

    bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet)

    if not value:
        if force:
            raise click.MissingParameter("Missing --value.")

        existing_bid_amount = bidder.get_deposited_eth
        if not existing_bid_amount:  # It's the first bid
            minimum_bid = bidder.worklock_agent.minimum_allowed_bid
            minimum_bid_in_eth = Web3.fromWei(minimum_bid, 'ether')
            prompt = f"Enter bid amount in ETH (at least {minimum_bid_in_eth} ETH)"
        else:  # There's an existing bid and the bidder is increasing the amount
            emitter.message(f"You have an existing bid of {Web3.fromWei(existing_bid_amount, 'ether')} ETH")
            minimum_bid_in_eth = Web3.fromWei(1, 'ether')
            prompt = f"Enter the amount in ETH that you want to increase your bid"
        value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth))

    value = int(Web3.toWei(Decimal(value), 'ether'))

    if not force:
        paint_bidding_notice(emitter=emitter, bidder=bidder)
        click.confirm(f"Place WorkLock bid of {prettify_eth_amount(value)}?", abort=True)

    receipt = bidder.place_bid(value=value)
    emitter.message("Publishing WorkLock Bid...")

    maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked)
    available_claim = NU.from_nunits(bidder.available_claim)
    message = f'Current bid: {prettify_eth_amount(bidder.get_deposited_eth)} | Claim: {available_claim}\n'
    if available_claim > maximum:
        message += f"This claim is currently above the allowed max ({maximum}), so the bid may be partially refunded.\n"
    message += f'Note that available claim value may fluctuate until bidding closes and claims are finalized.\n'
    emitter.echo(message, color='yellow')

    paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name)
    return  # Exit
示例#21
0
def paint_staking_rewards(stakeholder, blockchain, emitter, past_periods,
                          staking_address, staking_agent):
    if not past_periods:
        reward_amount = stakeholder.staker.calculate_staking_reward()
        emitter.echo(message=TOKEN_REWARD_CURRENT.format(
            reward_amount=round(reward_amount, TOKEN_DECIMAL_PLACE)))
        return

    economics = stakeholder.staker.economics
    seconds_per_period = economics.seconds_per_period
    current_period = staking_agent.get_current_period()
    from_period = current_period - past_periods
    latest_block = blockchain.client.block_number
    from_block = estimate_block_number_for_period(
        period=from_period,
        seconds_per_period=seconds_per_period,
        latest_block=latest_block)

    argument_filters = {'staker': staking_address}
    event_type = staking_agent.contract.events['Minted']
    entries = event_type.getLogs(fromBlock=from_block,
                                 toBlock='latest',
                                 argument_filters=argument_filters)

    rows = []
    rewards_total = NU(0, 'NU')
    for event_record in entries:
        event_block_number = int(event_record['blockNumber'])
        event_period = event_record['args']['period']
        event_reward = NU(event_record['args']['value'], 'NuNit')
        timestamp = blockchain.client.get_block(event_block_number).timestamp
        event_date = maya.MayaDT(
            epoch=timestamp).local_datetime().strftime("%b %d %Y")
        rows.append([
            event_date,
            event_block_number,
            int(event_period),
            round(event_reward, TOKEN_DECIMAL_PLACE),
        ])
        rewards_total += event_reward

    if not rows:
        emitter.echo(TOKEN_REWARD_NOT_FOUND)
        return

    periods_as_days = economics.days_per_period * past_periods
    emitter.echo(message=TOKEN_REWARD_PAST_HEADER.format(periods=past_periods,
                                                         days=periods_as_days))
    emitter.echo(
        tabulate.tabulate(rows,
                          headers=REWARDS_TABLE_COLUMNS,
                          tablefmt="fancy_grid"))
    emitter.echo(message=TOKEN_REWARD_PAST.format(
        reward_amount=round(rewards_total, TOKEN_DECIMAL_PLACE)))
示例#22
0
    def _learn_about_nodes_contract_info(self):
        agent = self.staking_agent

        block_time = agent.blockchain.client.w3.eth.getBlock('latest').timestamp  # precision in seconds
        current_period = agent.get_current_period()

        nodes_dict = self.known_nodes.abridged_nodes_dict()
        self.log.info(f'Processing {len(nodes_dict)} nodes at '
                      f'{MayaDT(epoch=block_time)} | Period {current_period}')
        data = []
        for staker_address in nodes_dict:
            worker = agent.get_worker_from_staker(staker_address)

            stake = agent.owned_tokens(staker_address)
            staked_nu_tokens = float(NU.from_nunits(stake).to_tokens())
            locked_nu_tokens = float(NU.from_nunits(agent.get_locked_tokens(
                staker_address=staker_address)).to_tokens())

            economics = TokenEconomicsFactory.get_economics(registry=self.registry)
            stakes = StakeList(checksum_address=staker_address, registry=self.registry)
            stakes.refresh()

            # store dates as floats for comparison purposes
            start_date = datetime_at_period(stakes.initial_period,
                                            seconds_per_period=economics.seconds_per_period).datetime().timestamp()
            end_date = datetime_at_period(stakes.terminal_period,
                                          seconds_per_period=economics.seconds_per_period).datetime().timestamp()

            last_confirmed_period = agent.get_last_active_period(staker_address)

            # TODO: do we need to worry about how much information is in memory if number of nodes is
            #  large i.e. should I check for size of data and write within loop if too big
            data.append(self.BLOCKCHAIN_DB_LINE_PROTOCOL.format(
                measurement=self.BLOCKCHAIN_DB_MEASUREMENT,
                staker_address=staker_address,
                worker_address=worker,
                start_date=start_date,
                end_date=end_date,
                stake=staked_nu_tokens,
                locked_stake=locked_nu_tokens,
                current_period=current_period,
                last_confirmed_period=last_confirmed_period,
                timestamp=block_time
            ))

        if not self._blockchain_db_client.write_points(data,
                                                       database=self.BLOCKCHAIN_DB_NAME,
                                                       time_precision='s',
                                                       batch_size=10000,
                                                       protocol='line'):
            # TODO: what do we do here
            self.log.warn(f'Unable to write to database {self.BLOCKCHAIN_DB_NAME} at '
                          f'{MayaDT(epoch=block_time)} | Period {current_period}')
示例#23
0
def preallocation(general_config, transacting_staker_options, config_file,
                  action, force):
    """
    Claim token rewards collected by a preallocation contract.
    """

    ### Setup ###
    emitter = _setup_emitter(general_config)

    STAKEHOLDER = transacting_staker_options.create_character(
        emitter, config_file)
    blockchain = transacting_staker_options.get_blockchain()

    # Unauthenticated actions: status

    if action == 'status':
        painting.paint_preallocation_status(
            emitter=emitter,
            token_agent=STAKEHOLDER.token_agent,
            preallocation_agent=STAKEHOLDER.preallocation_escrow_agent)
        return

    # Authenticated actions: withdraw-tokens

    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)

    password = transacting_staker_options.get_password(blockchain,
                                                       client_account)

    STAKEHOLDER.assimilate(checksum_address=client_account, password=password)
    if action == 'withdraw':
        token_balance = NU.from_nunits(
            STAKEHOLDER.token_agent.get_balance(staking_address))
        locked_tokens = NU.from_nunits(
            STAKEHOLDER.preallocation_escrow_agent.unvested_tokens)
        unlocked_tokens = token_balance - locked_tokens

        emitter.echo(
            message=
            f'Collecting {unlocked_tokens} from PreallocationEscrow contract {staking_address}...'
        )
        receipt = STAKEHOLDER.withdraw_preallocation_tokens(unlocked_tokens)
        paint_receipt_summary(
            receipt=receipt,
            chain_name=STAKEHOLDER.wallet.blockchain.client.chain_name,
            emitter=emitter)
def test_withdraw_from_preallocation(
    click_runner,
    testerchain,
    agency_local_registry,
    stakeholder_configuration_file_location,
    beneficiary,
    preallocation_escrow_agent,
):

    staker_address = preallocation_escrow_agent.principal_contract.address
    token_agent = ContractAgency.get_agent(agent_class=NucypherTokenAgent,
                                           registry=agency_local_registry)
    tokens_in_contract = NU.from_nunits(
        token_agent.get_balance(address=staker_address))
    locked_preallocation = NU.from_nunits(
        preallocation_escrow_agent.unvested_tokens)

    collection_args = (
        'stake',
        'preallocation',
        'status',
        '--config-file',
        stakeholder_configuration_file_location,
        '--allocation-filepath',
        MOCK_INDIVIDUAL_ALLOCATION_FILEPATH,
    )

    result = click_runner.invoke(nucypher_cli,
                                 collection_args,
                                 input=INSECURE_DEVELOPMENT_PASSWORD,
                                 catch_exceptions=True)
    assert result.exit_code == 0
    assert f'NU balance: .......... {tokens_in_contract}' in result.output

    balance_before_collecting = token_agent.get_balance(address=beneficiary)

    collection_args = ('stake', 'preallocation', 'withdraw', '--config-file',
                       stakeholder_configuration_file_location,
                       '--allocation-filepath',
                       MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force')

    result = click_runner.invoke(nucypher_cli,
                                 collection_args,
                                 input=INSECURE_DEVELOPMENT_PASSWORD,
                                 catch_exceptions=True)
    assert result.exit_code == 0
    assert token_agent.get_balance(
        address=staker_address) == locked_preallocation
    withdrawn_amount = tokens_in_contract - locked_preallocation
    balance_after_collecting = token_agent.get_balance(address=beneficiary)
    assert balance_after_collecting == balance_before_collecting + withdrawn_amount
示例#25
0
 def staked_tokens(n, latest_crawler_stats):
     data = self.verify_cached_stats(latest_crawler_stats)
     staked = NU.from_nunits(data['global_locked_tokens'])
     return html.Div([
         html.H4('Staked Tokens'),
         html.H5(f"{staked}", id='staked-tokens-value')
     ])
示例#26
0
def test_worker_auto_commitments(mocker, testerchain, test_registry, staker,
                                 agency, token_economics,
                                 mock_transacting_power_activation,
                                 ursula_decentralized_test_config):

    mock_transacting_power_activation(account=staker.checksum_address,
                                      password=INSECURE_DEVELOPMENT_PASSWORD)

    staker.initialize_stake(
        amount=NU(token_economics.minimum_allowed_locked, 'NuNit'),
        lock_periods=int(token_economics.minimum_locked_periods))

    # Get an unused address and create a new worker
    worker_address = testerchain.unassigned_accounts[-1]

    # Control time
    clock = Clock()
    WorkTracker.CLOCK = clock

    # Bond the Worker and Staker
    staker.bond_worker(worker_address=worker_address)

    commit_spy = mocker.spy(Worker, 'commit_to_next_period')

    # Make the Worker
    ursula = make_decentralized_ursulas(
        ursula_config=ursula_decentralized_test_config,
        stakers_addresses=[staker.checksum_address],
        workers_addresses=[worker_address],
        commit_to_next_period=False,
        registry=test_registry).pop()

    commit_spy.assert_not_called()

    initial_period = staker.staking_agent.get_current_period()

    def start():
        # Start running the worker
        start_pytest_ursula_services(ursula=ursula)
        ursula.work_tracker.start(act_now=True)

    def time_travel(_):
        testerchain.time_travel(periods=1)
        clock.advance(WorkTracker.REFRESH_RATE + 1)

    def verify(_):
        # Verify that periods were committed on-chain automatically
        last_committed_period = staker.staking_agent.get_last_committed_period(
            staker_address=staker.checksum_address)
        current_period = staker.staking_agent.get_current_period()
        assert (last_committed_period - current_period) == 1
        assert commit_spy.call_count == current_period - initial_period + 1

    # Run the callbacks
    d = threads.deferToThread(start)
    d.addCallback(verify)
    for i in range(5):
        d.addCallback(time_travel)
        d.addCallback(verify)
    yield d
示例#27
0
def test_staker_collects_staking_reward(testerchain, test_registry, staker,
                                        blockchain_ursulas, agency,
                                        token_economics,
                                        ursula_decentralized_test_config):
    token_agent = ContractAgency.get_agent(NucypherTokenAgent,
                                           registry=test_registry)

    tpower = TransactingPower(account=testerchain.etherbase_account,
                              signer=Web3Signer(testerchain.client))

    # Give more tokens to staker
    token_airdrop(token_agent=token_agent,
                  transacting_power=tpower,
                  addresses=[staker.checksum_address],
                  amount=DEVELOPMENT_TOKEN_AIRDROP_AMOUNT)

    staker.initialize_stake(
        amount=NU(token_economics.minimum_allowed_locked,
                  'NuNit'),  # Lock the minimum amount of tokens
        lock_periods=int(token_economics.minimum_locked_periods)
    )  # ... for the fewest number of periods

    # Get an unused address for a new worker
    worker_address = testerchain.unassigned_accounts[-1]
    staker.bond_worker(worker_address=worker_address)

    # Create this worker and bond it with the staker
    ursula = make_decentralized_ursulas(
        ursula_config=ursula_decentralized_test_config,
        stakers_addresses=[staker.checksum_address],
        workers_addresses=[worker_address],
        registry=test_registry,
        commit_now=False).pop()

    # ...mint few tokens...
    for _ in range(2):
        ursula.commit_to_next_period()
        testerchain.time_travel(periods=1)

    # Check mintable periods
    assert staker.mintable_periods() == 1
    ursula.commit_to_next_period()

    # ...wait more...
    assert staker.mintable_periods() == 0
    testerchain.time_travel(periods=2)
    assert staker.mintable_periods() == 2

    # Capture the current token balance of the staker
    initial_balance = staker.token_balance
    assert token_agent.get_balance(staker.checksum_address) == initial_balance

    # Profit!
    staked = staker.non_withdrawable_stake()
    owned = staker.owned_tokens()
    staker.collect_staking_reward()
    assert staker.owned_tokens() == staked

    final_balance = staker.token_balance
    assert final_balance == initial_balance + owned - staked
示例#28
0
def paint_bidder_status(emitter, bidder):
    claim = NU.from_nunits(bidder.available_claim)
    if claim > bidder.economics.maximum_allowed_locked:
        claim = f"{claim} (Above the allowed max. The bid will be partially refunded)"

    message = f"""
WorkLock Participant {bidder.checksum_address}
=====================================================
Total Bid ............ {prettify_eth_amount(bidder.get_deposited_eth)}
Tokens Allocated ..... {claim}
Tokens Claimed? ...... {"Yes" if bidder._has_claimed else "No"}"""

    compensation = bidder.available_compensation
    if compensation:
        message += f"""
Unspent Bid Amount ... {prettify_eth_amount(compensation)}"""

    message += f"""\n
Completed Work ....... {bidder.completed_work}
Available Refund ..... {prettify_eth_amount(bidder.available_refund)}

Refunded Work ........ {bidder.refunded_work}
Remaining Work ....... {bidder.remaining_work}
"""

    emitter.echo(message)
    return
示例#29
0
def test_nucypher_status_locked_tokens(click_runner, testerchain,
                                       agency_local_registry, stakers):

    staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
                                             registry=agency_local_registry)
    # All workers make a commitment
    for ursula in testerchain.ursulas_accounts:
        staking_agent.commit_to_next_period(worker_address=ursula)
    testerchain.time_travel(periods=1)

    periods = 2
    status_command = ('locked-tokens', '--registry-filepath',
                      agency_local_registry.filepath, '--provider',
                      TEST_PROVIDER_URI, '--network', TEMPORARY_DOMAIN,
                      '--periods', periods)
    light_parameter = [False, True]
    for light in light_parameter:
        testerchain.is_light = light
        result = click_runner.invoke(status,
                                     status_command,
                                     catch_exceptions=False)
        assert result.exit_code == 0

        current_period = staking_agent.get_current_period()
        all_locked = NU.from_nunits(
            staking_agent.get_global_locked_tokens(at_period=current_period))
        assert re.search(f"Locked Tokens for next {periods} periods",
                         result.output, re.MULTILINE)
        assert re.search(f"Min: {all_locked} - Max: {all_locked}",
                         result.output, re.MULTILINE)
示例#30
0
def test_staker_increases_stake(staker, token_economics):
    stake_index = 0
    origin_stake = staker.stakes[stake_index]
    additional_amount = NU.from_nunits(token_economics.minimum_allowed_locked // 100)

    with pytest.raises(ValueError):
        staker.increase_stake(stake=origin_stake)
    # Can't use amount and entire balance flag together
    with pytest.raises(ValueError):
        staker.increase_stake(stake=origin_stake, amount=additional_amount, entire_balance=True)

    staker.increase_stake(stake=origin_stake, amount=additional_amount)

    stake = staker.stakes[stake_index]
    assert stake.first_locked_period == origin_stake.first_locked_period
    assert stake.final_locked_period == origin_stake.final_locked_period
    assert stake.value == origin_stake.value + additional_amount

    # Provided stake must be part of current stakes
    with pytest.raises(ValueError):
        staker.increase_stake(stake=origin_stake, amount=additional_amount)
    stake.index = len(staker.stakes)
    with pytest.raises(ValueError):
        staker.increase_stake(stake=stake, amount=additional_amount)
    stake.index = stake_index

    # Try to increase again using entire balance
    origin_stake = stake
    balance = staker.token_balance
    staker.increase_stake(stake=stake, entire_balance=True)

    stake = staker.stakes[stake_index]
    assert stake.first_locked_period == origin_stake.first_locked_period
    assert stake.final_locked_period == origin_stake.final_locked_period
    assert stake.value == origin_stake.value + balance