Ejemplo n.º 1
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)
Ejemplo n.º 2
0
    def divide_stake(self,
                     stake_index: int,
                     target_value: NU,
                     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.")

        # Select stake to divide from local cache
        try:
            current_stake = self.stakes[stake_index]
        except KeyError:
            if len(self.stakes):
                message = f"Cannot divide stake - No stake exists with index {stake_index}."
            else:
                message = "Cannot divide stake - There are no active stakes."
            raise Stake.StakingError(message)

        # Calculate stake duration in periods
        if expiration:
            additional_periods = datetime_to_period(datetime=expiration) - current_stake.end_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.end_period}).")

        # Do it already!
        modified_stake, new_stake = current_stake.divide(target_value=target_value,
                                                         additional_periods=additional_periods)

        # Update staking cache
        self.__read_stakes()

        return modified_stake, new_stake
Ejemplo n.º 3
0
    def initialize_stake(self,
                         amount: NU,
                         lock_periods: int = None,
                         expiration: maya.MayaDT = None,
                         entire_balance: bool = False) -> Stake:
        """Create a new stake."""

        #
        # Duration
        #

        if lock_periods and expiration:
            raise ValueError(
                "Pass the number of lock periods or an expiration MayaDT; not both."
            )
        if expiration:
            lock_periods = calculate_period_duration(future_time=expiration)

        #
        # Value
        #

        if entire_balance and amount:
            raise ValueError("Specify an amount or entire balance, not both")
        if entire_balance:
            amount = self.token_balance
        if not self.token_balance >= amount:
            raise self.MinerError(
                f"Insufficient token balance ({self.token_agent}) for new stake initialization of {amount}"
            )

        # Ensure the new stake will not exceed the staking limit
        if (self.current_stake +
                amount) > self.economics.maximum_allowed_locked:
            raise Stake.StakingError(
                f"Cannot divide stake - Maximum stake value exceeded with a target value of {amount}."
            )

        #
        # Stake
        #

        # Write to blockchain
        new_stake = Stake.initialize_stake(miner=self,
                                           amount=amount,
                                           lock_periods=lock_periods)
        self.__read_stakes()  # Update local staking cache
        return new_stake
Ejemplo n.º 4
0
def test_select_divisible_stake(test_emitter, token_economics,
                                mock_staking_agent, test_registry,
                                mock_testerchain, mock_stdin, capsys,
                                divisible_stakes,
                                stakeholder_with_divisible_stakes):

    expected_stake = Stake.from_stake_info(
        stake_info=divisible_stakes[0],
        staking_agent=mock_staking_agent,  # stakinator
        index=0,
        checksum_address=stakeholder_with_divisible_stakes.checksum_address,
        economics=token_economics)

    # SUCCESS: Display all divisible-only stakes and make a selection
    mock_stdin.line(str(SELECTION))

    selected_stake = select_stake(
        emitter=test_emitter,
        divisible=True,
        stakeholder=stakeholder_with_divisible_stakes)

    assert isinstance(selected_stake, Stake)
    assert selected_stake == expected_stake

    # Examine the output
    captured = capsys.readouterr()
    assert NO_STAKES_FOUND not in captured.out
    assert ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE in captured.out
    assert_stake_table_painted(output=captured.out)
    assert mock_stdin.empty()
Ejemplo n.º 5
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'
Ejemplo n.º 6
0
def test_stake_equality(token_economics, get_random_checksum_address, mocker):
    address = get_random_checksum_address()
    a_different_address = get_random_checksum_address()

    mock_agent = mocker.Mock(contract_address=a_different_address)

    stake = Stake(checksum_address=address,
                  first_locked_period=1,
                  final_locked_period=2,
                  value=NU(100, 'NU'),
                  index=0,
                  staking_agent=mock_agent,
                  economics=token_economics,
                  validate_now=False)

    assert stake == stake

    duck_stake = mocker.Mock(index=0,
                             value=NU(100, 'NU'),
                             first_locked_period=1,
                             final_locked_period=2,
                             staker_address=address,
                             staking_agent=mock_agent)
    assert stake == duck_stake

    a_different_stake = Stake(checksum_address=address,
                              first_locked_period=0,
                              final_locked_period=2,
                              value=NU(100, 'NU'),
                              index=1,
                              staking_agent=mock_agent,
                              economics=token_economics,
                              validate_now=False)

    assert stake != a_different_stake

    undercover_agent = mocker.Mock(contract_address=address)
    another_different_stake = Stake(checksum_address=a_different_address,
                                    first_locked_period=1,
                                    final_locked_period=2,
                                    value=NU(100, 'NU'),
                                    index=0,
                                    staking_agent=undercover_agent,
                                    economics=token_economics,
                                    validate_now=False)

    assert stake != another_different_stake
Ejemplo n.º 7
0
 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)
Ejemplo n.º 8
0
 def __read_stakes(self) -> None:
     stakes_reader = self.miner_agent.get_all_stakes(miner_address=self.checksum_public_address)
     stakes = dict()
     for index, stake_info in enumerate(stakes_reader):
         stake = Stake.from_stake_info(owner_address=self.checksum_public_address,
                                       stake_info=stake_info,
                                       index=index)
         stakes[index] = stake
     self.__stakes = stakes
Ejemplo n.º 9
0
    def initialize_stake(self,
                         amount: NU,
                         lock_periods: int = None,
                         expiration: maya.MayaDT = None,
                         entire_balance: bool = False) -> Stake:
        """Create a new stake."""

        # Duration
        if lock_periods and expiration:
            raise ValueError(
                "Pass the number of lock periods or an expiration MayaDT; not both."
            )
        if expiration:
            lock_periods = calculate_period_duration(
                future_time=expiration,
                seconds_per_period=self.economics.seconds_per_period)

        # Value
        if entire_balance and amount:
            raise ValueError("Specify an amount or entire balance, not both")
        if entire_balance:
            amount = self.token_balance
        if not self.token_balance >= amount:
            raise self.InsufficientTokens(
                f"Insufficient token balance ({self.token_agent}) "
                f"for new stake initialization of {amount}")

        # Ensure the new stake will not exceed the staking limit
        if (self.current_stake +
                amount) > self.economics.maximum_allowed_locked:
            raise Stake.StakingError(
                f"Cannot initialize stake - "
                f"Maximum stake value exceeded for {self.checksum_address} "
                f"with a target value of {amount}.")

        # Write to blockchain
        new_stake = Stake.initialize_stake(staker=self,
                                           amount=amount,
                                           lock_periods=lock_periods)

        # Update staking cache element
        self.stakes.refresh()

        return new_stake
Ejemplo n.º 10
0
def test_stake_init(click_runner,
                    stakeholder_configuration_file_location,
                    stake_value,
                    mock_registry_filepath,
                    token_economics,
                    testerchain,
                    agency,
                    manual_staker):

    # 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

    # Staker address has not stakes
    staking_agent = Agency.get_agent(StakingEscrowAgent)
    stakes = list(staking_agent.get_all_stakes(staker_address=manual_staker))
    assert not stakes

    stake_args = ('stake', 'init',
                  '--config-file', stakeholder_configuration_file_location,
                  '--registry-filepath', mock_registry_filepath,
                  '--staking-address', manual_staker,
                  '--value', stake_value.to_tokens(),
                  '--duration', token_economics.minimum_locked_periods,
                  '--force')

    # TODO: This test it writing to the default system directory and ignoring updates to the passes filepath
    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

    # Test integration with BaseConfiguration
    with open(stakeholder_configuration_file_location, 'r') as config_file:
        _config_data = json.loads(config_file.read())

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

    # Test integration with NU
    start_period, end_period, value = stakes[0]
    assert NU(int(value), 'NuNit') == stake_value
    assert (end_period - start_period) == token_economics.minimum_locked_periods - 1

    # Test integration with Stake
    stake = Stake.from_stake_info(index=0,
                                  checksum_address=manual_staker,
                                  stake_info=stakes[0])
    assert stake.value == stake_value
    assert stake.duration == token_economics.minimum_locked_periods
Ejemplo n.º 11
0
def test_stake():

    class FakeUrsula:
        burner_wallet = Web3().eth.account.create(INSECURE_DEVELOPMENT_PASSWORD)
        checksum_public_address = burner_wallet.address
        miner_agent = None

    ursula = FakeUrsula()
    stake = Stake(owner_address=ursula.checksum_public_address,
                  start_period=1,
                  end_period=100,
                  value=NU(100, 'NU'),
                  index=0)

    assert len(stake.id) == 16
    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)
Ejemplo n.º 12
0
def test_stake(testerchain, three_agents):
    class FakeUrsula:
        token_agent, miner_agent, _policy_agent = three_agents

        burner_wallet = Web3().eth.account.create(
            INSECURE_DEVELOPMENT_PASSWORD)
        checksum_public_address = burner_wallet.address
        miner_agent = miner_agent
        token_agent = token_agent
        blockchain = testerchain
        economics = TokenEconomics()

    ursula = FakeUrsula()
    stake = Stake(miner=ursula,
                  start_period=1,
                  end_period=100,
                  value=NU(100, 'NU'),
                  index=0)

    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)
def test_stake_init(click_runner, stakeholder_configuration_file_location,
                    stake_value, token_economics, testerchain,
                    agency_local_registry, manual_staker):

    # Staker address has not stakes
    staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
                                             registry=agency_local_registry)
    stakes = list(staking_agent.get_all_stakes(staker_address=manual_staker))
    assert not stakes

    stake_args = ('stake', 'create', '--config-file',
                  stakeholder_configuration_file_location,
                  '--staking-address', manual_staker, '--value',
                  stake_value.to_tokens(), '--lock-periods',
                  token_economics.minimum_locked_periods, '--force')

    # TODO: This test is writing to the default system directory and ignoring updates to the passed filepath
    user_input = f'0\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + YES_ENTER
    result = click_runner.invoke(nucypher_cli,
                                 stake_args,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    # Test integration with BaseConfiguration
    with open(stakeholder_configuration_file_location, 'r') as config_file:
        _config_data = json.loads(config_file.read())

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

    # Test integration with NU
    start_period, end_period, value = stakes[0]
    assert NU(int(value), 'NuNit') == stake_value
    assert (end_period -
            start_period) == token_economics.minimum_locked_periods - 1

    # Test integration with Stake
    stake = Stake.from_stake_info(index=0,
                                  checksum_address=manual_staker,
                                  stake_info=stakes[0],
                                  staking_agent=staking_agent,
                                  economics=token_economics)
    assert stake.value == stake_value
    assert stake.duration == token_economics.minimum_locked_periods
Ejemplo n.º 14
0
def test_miner_divides_stake(miner):
    stake_value = NU(MIN_ALLOWED_LOCKED * 5, 'NuNit')
    new_stake_value = NU(MIN_ALLOWED_LOCKED * 2, 'NuNit')

    stake_index = 0
    miner.initialize_stake(amount=stake_value,
                           lock_periods=int(MIN_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(MIN_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,
        owner_address=miner.checksum_public_address,
        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'
Ejemplo n.º 15
0
    def __read_stakes(self) -> None:
        """Rewrite the local staking cache by reading on-chain stakes"""

        existing_records = len(self.__stakes)

        # Candidate replacement cache values
        onchain_stakes, terminal_period = list(), 0

        # Read from blockchain
        stakes_reader = self.miner_agent.get_all_stakes(
            miner_address=self.checksum_address)

        for onchain_index, stake_info in enumerate(stakes_reader):

            if not stake_info:
                # This stake index is empty on-chain
                onchain_stake = EMPTY_STAKING_SLOT

            else:
                # On-chain stake detected
                onchain_stake = Stake.from_stake_info(miner=self,
                                                      stake_info=stake_info,
                                                      index=onchain_index)

                # Search for the terminal period
                if onchain_stake.end_period > terminal_period:
                    terminal_period = onchain_stake.end_period

            # Store the replacement stake
            onchain_stakes.append(onchain_stake)

        # Commit the new stake and terminal values to the cache
        if not onchain_stakes:
            self.__stakes = NO_STAKES.bool_value(False)
        else:
            self.__terminal_period = terminal_period
            self.__stakes = onchain_stakes

        # Record most recent cache update
        self.__updated = maya.now()
        new_records = existing_records - len(self.__stakes)
        self.log.debug(
            f"Updated local staking cache ({new_records} new records).")
Ejemplo n.º 16
0
def test_select_using_filter_function(
        test_emitter,
        stakeholder,
        mock_staking_agent,
        mock_testerchain,
        mock_stdin,  # used to assert user hasn't been prompted
        capsys,
        current_period,
        token_economics,
        sub_stakes_functions):
    # Setup
    mock_stakes = make_sub_stakes(current_period, token_economics,
                                  sub_stakes_functions)

    mock_staking_agent.get_all_stakes.return_value = mock_stakes
    staker = mock_testerchain.unassigned_accounts[0]
    stakeholder.set_staker(staker)

    selection = len(mock_stakes) - 1
    expected_stake = Stake.from_stake_info(
        stake_info=mock_stakes[selection],
        staking_agent=mock_staking_agent,  # stakinator
        index=selection,
        checksum_address=stakeholder.checksum_address,
        economics=token_economics)

    # SUCCESS: Display all editable-only stakes with specified final period
    mock_stdin.line(str(selection))
    selected_stake = select_stake(emitter=test_emitter,
                                  staker=stakeholder,
                                  stakes_status=Stake.Status.LOCKED,
                                  filter_function=lambda stake: stake.
                                  final_locked_period == current_period)

    assert isinstance(selected_stake, Stake)
    assert selected_stake == expected_stake

    # Examine the output
    captured = capsys.readouterr()
    assert NO_STAKES_FOUND not in captured.out
    assert_stake_table_painted(output=captured.out)
    assert mock_stdin.empty()
Ejemplo n.º 17
0
def test_select_divisible_stake(
        test_emitter,
        stakeholder,
        mock_staking_agent,
        mock_testerchain,
        mock_stdin,  # used to assert user hasn't been prompted
        capsys,
        current_period,
        token_economics,
        sub_stakes_functions):
    # Setup
    mock_stakes = make_sub_stakes(current_period, token_economics,
                                  sub_stakes_functions)

    mock_staking_agent.get_all_stakes.return_value = mock_stakes
    staker = mock_testerchain.unassigned_accounts[0]
    stakeholder.set_staker(staker)

    selection = len(mock_stakes) - 1
    expected_stake = Stake.from_stake_info(
        stake_info=mock_stakes[selection],
        staking_agent=mock_staking_agent,  # stakinator
        index=selection,
        checksum_address=stakeholder.checksum_address,
        economics=token_economics)

    # SUCCESS: Display all divisible-only stakes and make a selection
    mock_stdin.line(str(selection))
    selected_stake = select_stake(emitter=test_emitter,
                                  staker=stakeholder,
                                  stakes_status=Stake.Status.DIVISIBLE)

    assert isinstance(selected_stake, Stake)
    assert selected_stake == expected_stake

    # Examine the output
    captured = capsys.readouterr()
    assert NO_STAKES_FOUND not in captured.out
    assert ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE in captured.out
    assert_stake_table_painted(output=captured.out)
    assert mock_stdin.empty()
Ejemplo n.º 18
0
    def divide_stake(
        self,
        address: str,
        index: int,
        value: NU,
        duration: int,
        password: str = None,
    ):

        staker = self.get_active_staker(address=address)
        if not staker.is_staking:
            raise Stake.StakingError(
                f"{staker.checksum_address} has no published stakes.")

        self.attach_transacting_power(checksum_address=staker.checksum_address,
                                      password=password)
        result = staker.divide_stake(stake_index=index,
                                     additional_periods=duration,
                                     target_value=value)

        # Save results to disk
        self.to_configuration_file(override=True)
        return result
Ejemplo n.º 19
0
def test_select_editable_stake(test_emitter,
                               stakeholder,
                               mock_staking_agent,
                               mock_testerchain,
                               mock_stdin,  # used to assert user hasn't been prompted
                               capsys,
                               current_period,
                               token_economics,
                               sub_stakes_functions):
    mock_stakes = make_sub_stakes(current_period, token_economics, sub_stakes_functions)

    mock_staking_agent.get_all_stakes.return_value = mock_stakes
    staker = mock_testerchain.unassigned_accounts[0]
    stakeholder.assimilate(staker, password=INSECURE_DEVELOPMENT_PASSWORD)

    selection = len(mock_stakes) - 1
    expected_stake = Stake.from_stake_info(stake_info=mock_stakes[selection],
                                           staking_agent=mock_staking_agent,   # stakinator
                                           index=selection,
                                           checksum_address=stakeholder.checksum_address,
                                           economics=token_economics)

    # User's selection
    mock_stdin.line(str(selection))
    selected_stake = select_stake(emitter=test_emitter, staker=stakeholder.staker)

    # Check stake accuracy
    assert isinstance(selected_stake, Stake)
    assert selected_stake == expected_stake

    # Examine the output
    captured = capsys.readouterr()
    assert NO_STAKES_FOUND not in captured.out
    assert ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE not in captured.out
    assert_stake_table_painted(output=captured.out)
    assert mock_stdin.empty()
Ejemplo n.º 20
0
def test_stake_validation(mock_testerchain, token_economics, mock_staking_agent):

    address = mock_testerchain.etherbase_account

    # Validate stake initialization
    with pytest.raises(Stake.StakingError):
        Stake.initialize_stake(staking_agent=mock_staking_agent,
                               checksum_address=address,
                               economics=token_economics,
                               amount=token_economics.minimum_allowed_locked - 1,
                               lock_periods=token_economics.minimum_locked_periods)

    with pytest.raises(Stake.StakingError):
        Stake.initialize_stake(staking_agent=mock_staking_agent,
                               checksum_address=address,
                               economics=token_economics,
                               amount=token_economics.minimum_allowed_locked,
                               lock_periods=token_economics.minimum_locked_periods - 1)

    with pytest.raises(Stake.StakingError):
        Stake.initialize_stake(staking_agent=mock_staking_agent,
                               checksum_address=address,
                               economics=token_economics,
                               amount=token_economics.maximum_allowed_locked + 1,
                               lock_periods=token_economics.minimum_locked_periods)

    mock_staking_agent.get_locked_tokens.return_value = 0
    Stake.initialize_stake(staking_agent=mock_staking_agent,
                           checksum_address=address,
                           economics=token_economics,
                           amount=token_economics.maximum_allowed_locked,
                           lock_periods=token_economics.minimum_locked_periods)

    # Validate divide method
    current_period = 10
    mock_staking_agent.get_current_period.return_value = 10

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

    nu = NU.from_nunits(2 * token_economics.minimum_allowed_locked - 1)
    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period + 1,
                           value=nu)

    with pytest.raises(Stake.StakingError):
        validate_divide(stake=stake, target_value=token_economics.minimum_allowed_locked, additional_periods=1)

    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period,
                           value=nu + 1)
    with pytest.raises(Stake.StakingError):
        validate_divide(stake=stake, target_value=token_economics.minimum_allowed_locked, additional_periods=1)

    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period + 1,
                           value=nu + 1)
    with pytest.raises(Stake.StakingError):
        validate_divide(stake=stake, target_value=token_economics.minimum_allowed_locked - 1, additional_periods=1)
    validate_divide(stake=stake, target_value=token_economics.minimum_allowed_locked, additional_periods=1)

    # Validate prolong method
    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period,
                           value=nu)
    with pytest.raises(Stake.StakingError):
        validate_prolong(stake=stake, additional_periods=1)

    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period + 2,
                           value=nu)
    with pytest.raises(Stake.StakingError):
        validate_prolong(stake=stake, additional_periods=1)
    with pytest.raises(Stake.StakingError):
        validate_prolong(stake=stake, additional_periods=token_economics.minimum_locked_periods - 3)
    validate_prolong(stake=stake, additional_periods=token_economics.minimum_locked_periods - 2)

    # Validate increase method
    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period,
                           value=nu)
    with pytest.raises(Stake.StakingError):
        validate_increase(stake=stake, amount=nu)

    stake = make_sub_stake(first_locked_period=current_period - 2,
                           final_locked_period=current_period + 1,
                           value=nu)
    stake.staking_agent.get_locked_tokens.return_value = nu
    with pytest.raises(Stake.StakingError):
        validate_increase(stake=stake, amount=NU.from_nunits(token_economics.maximum_allowed_locked - int(nu) + 1))
    validate_increase(stake=stake, amount=NU.from_nunits(token_economics.maximum_allowed_locked - int(nu)))

    # Validate merge method
    stake_1 = make_sub_stake(first_locked_period=current_period - 1,
                             final_locked_period=current_period,
                             value=nu,
                             index=0)
    stake_2 = make_sub_stake(first_locked_period=current_period - 1,
                             final_locked_period=current_period,
                             value=nu,
                             index=1)
    with pytest.raises(Stake.StakingError):
        validate_merge(stake_1=stake_1, stake_2=stake_2)

    stake_1 = make_sub_stake(first_locked_period=current_period - 1,
                             final_locked_period=current_period + 1,
                             value=nu,
                             index=2)
    stake_2 = make_sub_stake(first_locked_period=current_period - 1,
                             final_locked_period=current_period + 2,
                             value=nu,
                             index=3)
    with pytest.raises(Stake.StakingError):
        validate_merge(stake_1=stake_1, stake_2=stake_2)
    with pytest.raises(Stake.StakingError):
        validate_merge(stake_1=stake_1, stake_2=stake_1)

    stake_2 = make_sub_stake(first_locked_period=current_period - 3,
                             final_locked_period=current_period + 1,
                             value=nu,
                             index=4)
    validate_merge(stake_1=stake_1, stake_2=stake_2)
Ejemplo n.º 21
0
def test_stake_sync(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

    # Prepare sub-stake
    nu = NU.from_nunits(2 * token_economics.minimum_allowed_locked - 1)
    stake = Stake(checksum_address=address,
                  first_locked_period=current_period - 2,
                  final_locked_period=current_period + 1,
                  value=nu,
                  index=0,
                  staking_agent=mock_staking_agent,
                  economics=token_economics,
                  validate_now=False)
    assert stake.status() == Stake.Status.EDITABLE

    # Update locked value and sync
    sub_stake_info = stake.to_stake_info()
    nunits = 2 * token_economics.minimum_allowed_locked
    sub_stake_info = sub_stake_info._replace(locked_value=nunits)
    mock_staking_agent.get_substake_info.return_value = sub_stake_info

    stake.sync()
    assert stake.status() == Stake.Status.DIVISIBLE
    assert stake.value == NU.from_nunits(nunits)

    # Update current period and sync
    mock_staking_agent.get_current_period.return_value = current_period + 1
    sub_stake_info = sub_stake_info._replace(locked_value=nunits)
    mock_staking_agent.get_substake_info.return_value = sub_stake_info

    stake.sync()
    assert stake.status() == Stake.Status.LOCKED
    assert stake.final_locked_period == current_period + 1

    # Update final period and sync
    sub_stake_info = sub_stake_info._replace(last_period=current_period)
    mock_staking_agent.get_substake_info.return_value = sub_stake_info

    stake.sync()
    assert stake.status() == Stake.Status.UNLOCKED
    assert stake.final_locked_period == current_period

    # Update first period and sync
    sub_stake_info = sub_stake_info._replace(first_period=current_period)
    mock_staking_agent.get_substake_info.return_value = sub_stake_info

    with pytest.raises(Stake.StakingError):
        stake.sync()
Ejemplo n.º 22
0
def test_stake_via_contract(click_runner,
                            custom_filepath,
                            agency_local_registry,
                            mock_allocation_registry,
                            testerchain,
                            stakeholder_configuration_file_location,
                            stake_value,
                            token_economics,
                            agency,
                            beneficiary,
                            preallocation_escrow_agent
                            ):

    #
    # Inital setup and checks: beneficiary and pre-allocation contract
    #

    # First, let's be sure the beneficiary is in the allocation registry...
    assert mock_allocation_registry.is_beneficiary_enrolled(beneficiary)

    # ... and that the pre-allocation contract has enough tokens
    preallocation_contract_address = preallocation_escrow_agent.principal_contract.address
    token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=agency_local_registry)
    assert token_agent.get_balance(preallocation_contract_address) >= token_economics.minimum_allowed_locked

    # Let's not forget to create a stakeholder
    init_args = ('stake', 'init-stakeholder',
                 '--poa',
                 '--config-root', custom_filepath,
                 '--provider', TEST_PROVIDER_URI,
                 '--network', TEMPORARY_DOMAIN,
                 '--registry-filepath', agency_local_registry.filepath)

    result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False)
    assert result.exit_code == 0

    with open(stakeholder_configuration_file_location) as f:
        print(f.read())

    #
    # The good stuff: Using `nucypher stake create --escrow`
    #

    # Staking contract has no stakes yet
    staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry)
    stakes = list(staking_agent.get_all_stakes(staker_address=preallocation_contract_address))
    assert not stakes

    stake_args = ('stake', 'create',
                  '--config-file', stakeholder_configuration_file_location,
                  '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH,
                  '--value', str(stake_value.to_tokens()),
                  '--lock-periods', token_economics.minimum_locked_periods,
                  '--force')

    # TODO: This test is writing to the default system directory and ignoring updates to the passed filepath
    user_input = '0\n' + 'Y\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n'
    result = click_runner.invoke(nucypher_cli, stake_args, input=user_input, catch_exceptions=False)
    assert result.exit_code == 0

    # Test integration with BaseConfiguration
    with open(stakeholder_configuration_file_location, 'r') as config_file:
        _config_data = json.loads(config_file.read())

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

    # Test integration with NU
    start_period, end_period, value = stakes[0]
    assert NU(int(value), 'NuNit') == stake_value
    assert (end_period - start_period) == token_economics.minimum_locked_periods - 1

    # Test integration with Stake
    stake = Stake.from_stake_info(index=0,
                                  checksum_address=preallocation_contract_address,
                                  stake_info=stakes[0],
                                  staking_agent=staking_agent,
                                  economics=token_economics)
    assert stake.value == stake_value
    assert stake.duration == token_economics.minimum_locked_periods
Ejemplo n.º 23
0
def test_staker_divides_stake(staker, 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
    staker.initialize_stake(amount=stake_value, lock_periods=int(token_economics.minimum_locked_periods))
    stake = staker.stakes[stake_index + 1]

    # Can't use additional periods and expiration together
    with pytest.raises(ValueError):
        staker.divide_stake(target_value=new_stake_value, stake=stake, additional_periods=2, expiration=maya.now())

    staker.divide_stake(target_value=new_stake_value, stake=stake, additional_periods=2)

    current_period = staker.staking_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(staker.stakes), 'A new stake was not added to this stakers stakes'
    assert expected_old_stake == staker.stakes[stake_index + 1].to_stake_info(), 'Old stake values are invalid'
    assert expected_new_stake == staker.stakes[stake_index + 2].to_stake_info(), 'New stake values are invalid'

    # Provided stake must be part of current stakes
    new_stake_value = NU.from_nunits(token_economics.minimum_allowed_locked)
    with pytest.raises(ValueError):
        staker.divide_stake(target_value=new_stake_value, stake=stake, additional_periods=2)
    stake = staker.stakes[stake_index + 1]
    stake.index = len(staker.stakes)
    with pytest.raises(ValueError):
        staker.divide_stake(target_value=new_stake_value, stake=stake, additional_periods=2)

    yet_another_stake_value = NU(token_economics.minimum_allowed_locked, 'NuNit')
    stake = staker.stakes[stake_index + 2]

    # New expiration date must extend stake duration
    origin_stake = stake
    new_expiration = datetime_at_period(period=origin_stake.final_locked_period,
                                        seconds_per_period=token_economics.seconds_per_period,
                                        start_of_period=True)
    with pytest.raises(ValueError):
        staker.divide_stake(target_value=yet_another_stake_value, stake=stake, expiration=new_expiration)

    new_expiration = datetime_at_period(period=origin_stake.final_locked_period + 2,
                                        seconds_per_period=token_economics.seconds_per_period,
                                        start_of_period=True)
    staker.divide_stake(target_value=yet_another_stake_value, stake=stake, expiration=new_expiration)

    expected_new_stake = (current_period + 1, current_period + 32, new_stake_value)
    expected_yet_another_stake = Stake(first_locked_period=current_period + 1,
                                       final_locked_period=current_period + 34,
                                       value=yet_another_stake_value,
                                       checksum_address=staker.checksum_address,
                                       index=3,
                                       staking_agent=staker.staking_agent,
                                       economics=token_economics)

    assert 4 == len(staker.stakes), 'A new stake was not added after two stake divisions'
    assert expected_old_stake == staker.stakes[
        stake_index + 1].to_stake_info(), 'Old stake values are invalid after two stake divisions'
    assert expected_new_stake == staker.stakes[
        stake_index + 2].to_stake_info(), 'New stake values are invalid after two stake divisions'
    assert expected_yet_another_stake.value == staker.stakes[stake_index + 3].value, 'Third stake values are invalid'