Пример #1
0
def blockchain_ursulas(testerchain, stakers, ursula_decentralized_test_config):
    if MOCK_KNOWN_URSULAS_CACHE:
        # TODO: Is this a safe assumption / test behaviour?
        # raise RuntimeError("Ursulas cache was unclear at fixture loading time.  Did you use one of the ursula maker functions without cleaning up?")
        MOCK_KNOWN_URSULAS_CACHE.clear()

    _ursulas = make_decentralized_ursulas(
        ursula_config=ursula_decentralized_test_config,
        stakers_addresses=testerchain.stakers_accounts,
        workers_addresses=testerchain.ursulas_accounts)
    for u in _ursulas:
        u.synchronous_query_timeout = .01  # We expect to never have to wait for content that is actually on-chain during tests.
    testerchain.time_travel(periods=1)

    # Bootstrap the network
    for ursula_to_teach in _ursulas:
        for ursula_to_learn_about in _ursulas:
            # FIXME #2588: FleetSensor should not own fully-functional Ursulas.
            # It only needs to see whatever public info we can normally get via REST.
            # Also sharing mutable Ursulas like that can lead to unpredictable results.
            ursula_to_teach.remember_node(ursula_to_learn_about)

    _ports_to_remove = [ursula.rest_interface.port for ursula in _ursulas]
    yield _ursulas

    for port in _ports_to_remove:
        del MOCK_KNOWN_URSULAS_CACHE[port]

    for u in _ursulas:
        u.stop()
        u._finalize()

    # Pytest will hold on to this object, need to clear it manually.
    # See https://github.com/pytest-dev/pytest/issues/5642
    _ursulas.clear()
Пример #2
0
def test_staker_manages_winding_down(testerchain, test_registry, staker,
                                     token_economics,
                                     ursula_decentralized_test_config):
    # Get worker
    ursula = make_decentralized_ursulas(
        ursula_config=ursula_decentralized_test_config,
        stakers_addresses=[staker.checksum_address],
        workers_addresses=[staker.worker_address],
        registry=test_registry).pop()

    # Enable winding down
    testerchain.time_travel(periods=1)
    base_duration = token_economics.minimum_locked_periods + 4
    receipt = staker.enable_winding_down()
    assert receipt['status'] == 1
    assert staker.locked_tokens(base_duration) != 0
    assert staker.locked_tokens(base_duration + 1) == 0
    ursula.commit_to_next_period()
    assert staker.locked_tokens(base_duration) != 0
    assert staker.locked_tokens(base_duration + 1) == 0

    # Disable winding down
    testerchain.time_travel(periods=1)
    receipt = staker.disable_winding_down()
    assert receipt['status'] == 1
    assert staker.locked_tokens(base_duration - 1) != 0
    assert staker.locked_tokens(base_duration) == 0
    ursula.commit_to_next_period()
    assert staker.locked_tokens(base_duration - 1) != 0
    assert staker.locked_tokens(base_duration) == 0
Пример #3
0
def test_staker_manages_winding_down(testerchain,
                                     test_registry,
                                     staker,
                                     token_economics,
                                     ursula_decentralized_test_config):
    # Get worker
    ursula = make_decentralized_ursulas(ursula_config=ursula_decentralized_test_config,
                                        stakers_addresses=[staker.checksum_address],
                                        workers_addresses=[staker.worker_address],
                                        registry=test_registry).pop()

    # Unlock
    transacting_power = ursula._crypto_power.power_ups(TransactingPower)
    transacting_power.activate(password=INSECURE_DEVELOPMENT_PASSWORD)

    # Enable winding down
    testerchain.time_travel(periods=1)
    base_duration = token_economics.minimum_locked_periods + 4
    receipt = staker.enable_winding_down()
    assert receipt['status'] == 1
    assert staker.locked_tokens(base_duration) != 0
    assert staker.locked_tokens(base_duration + 1) == 0
    ursula.commit_to_next_period()
    assert staker.locked_tokens(base_duration) != 0
    assert staker.locked_tokens(base_duration + 1) == 0

    # Disable winding down
    testerchain.time_travel(periods=1)
    receipt = staker.disable_winding_down()
    assert receipt['status'] == 1
    assert staker.locked_tokens(base_duration - 1) != 0
    assert staker.locked_tokens(base_duration) == 0
    ursula.commit_to_next_period()
    assert staker.locked_tokens(base_duration - 1) != 0
    assert staker.locked_tokens(base_duration) == 0
Пример #4
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
Пример #5
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
Пример #6
0
def blockchain_ursulas(testerchain, stakers, ursula_decentralized_test_config):
    if MOCK_KNOWN_URSULAS_CACHE:
        raise RuntimeError("Ursulas cache was unclear at fixture loading time.  Did you use one of the ursula maker functions without cleaning up?")
    _ursulas = make_decentralized_ursulas(ursula_config=ursula_decentralized_test_config,
                                          stakers_addresses=testerchain.stakers_accounts,
                                          workers_addresses=testerchain.ursulas_accounts,
                                          commit_to_next_period=True)
    for u in _ursulas:
        u.synchronous_query_timeout = .01  # We expect to never have to wait for content that is actually on-chain during tests.
    testerchain.time_travel(periods=1)

    # Bootstrap the network
    for ursula_to_teach in _ursulas:
        for ursula_to_learn_about in _ursulas:
            ursula_to_teach.remember_node(ursula_to_learn_about)

    _ports_to_remove = [ursula.rest_interface.port for ursula in _ursulas]
    yield _ursulas

    for port in _ports_to_remove:
        del MOCK_KNOWN_URSULAS_CACHE[port]

    for u in _ursulas:
        u.stop()
        u._finalize()

    # Pytest will hold on to this object, need to clear it manually.
    # See https://github.com/pytest-dev/pytest/issues/5642
    _ursulas.clear()
Пример #7
0
def test_staker_collects_staking_reward(testerchain, test_registry, staker,
                                        blockchain_ursulas, agency,
                                        token_economics,
                                        mock_transacting_power_activation,
                                        ursula_decentralized_test_config):
    token_agent, staking_agent, policy_agent = agency

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

    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],
        commit_to_next_period=False,
        registry=test_registry).pop()

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

    # Check mintable periods
    assert staker.mintable_periods() == 1
    ursula.transacting_power.activate(password=INSECURE_DEVELOPMENT_PASSWORD)
    ursula.commit_to_next_period()

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

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

    # 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
Пример #8
0
def test_stakers_bond_to_ursulas(testerchain, test_registry, stakers, ursula_decentralized_test_config):
    ursulas = make_decentralized_ursulas(ursula_config=ursula_decentralized_test_config,
                                         stakers_addresses=testerchain.stakers_accounts,
                                         workers_addresses=testerchain.ursulas_accounts,
                                         commit_now=False)

    assert len(ursulas) == len(stakers)
    for ursula in ursulas:
        ursula.validate_worker(registry=test_registry)
        assert ursula.verified_worker
Пример #9
0
def test_stakers_bond_to_ursulas(testerchain, test_registry, staking_providers,
                                 ursula_decentralized_test_config):
    ursulas = make_decentralized_ursulas(
        ursula_config=ursula_decentralized_test_config,
        staking_provider_addresses=testerchain.stake_providers_accounts,
        operator_addresses=testerchain.ursulas_accounts)

    assert len(ursulas) == len(staking_providers)
    for ursula in ursulas:
        ursula.validate_operator(registry=test_registry)
        assert ursula.verified_operator
Пример #10
0
def blockchain_ursulas(testerchain, stakers, ursula_decentralized_test_config):
    _ursulas = make_decentralized_ursulas(ursula_config=ursula_decentralized_test_config,
                                          stakers_addresses=testerchain.stakers_accounts,
                                          workers_addresses=testerchain.ursulas_accounts,
                                          commit_to_next_period=True)
    for u in _ursulas:
        u.synchronous_query_timeout = .01  # We expect to never have to wait for content that is actually on-chain during tests.
    testerchain.time_travel(periods=1)

    # Bootstrap the network
    for ursula_to_teach in _ursulas:
        for ursula_to_learn_about in _ursulas:
            ursula_to_teach.remember_node(ursula_to_learn_about)

    yield _ursulas
Пример #11
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')
    replacement_spy = mocker.spy(WorkTracker, '_WorkTracker__fire_replacement_commitment')

    # 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()

    initial_period = staker.staking_agent.get_current_period()

    def start():
        print("Starting Worker for auto-commitment simulation")
        start_pytest_ursula_services(ursula=ursula)

    def advance_one_period(_):
        print('Advancing one period')
        testerchain.time_travel(periods=1)
        clock.advance(WorkTracker.INTERVAL_CEIL + 1)

    def pending_commitments(_):
        print('Starting unmined transaction simulation')
        testerchain.client.add_middleware(unmined_receipt_simulator_middleware)

    def advance_one_cycle(_):
        print('Advancing one tracking iteration')
        clock.advance(ursula.work_tracker._tracking_task.interval + 1)

    def advance_until_replacement_indicated(_):
        print("Advancing until replacement is indicated")
        testerchain.time_travel(periods=1)
        clock.advance(WorkTracker.INTERVAL_CEIL + 1)
        mocker.patch.object(WorkTracker, 'max_confirmation_time', return_value=1.0)
        clock.advance(ursula.work_tracker.max_confirmation_time() + 1)

    def verify_unmined_commitment(_):
        print('Verifying worker has unmined commitment transaction')
        assert len(ursula.work_tracker.pending) == 1
        current_period = staker.staking_agent.get_current_period()
        assert commit_spy.call_count == current_period - initial_period + 1

    def verify_replacement_commitment(_):
        print('Verifying worker has replaced commitment transaction')
        assert len(ursula.work_tracker.pending) == 1
        assert replacement_spy.call_count > 0

    def verify_confirmed(_):
        print('Verifying worker made a commitments')
        # 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
        assert replacement_spy.call_count == 0

    # Behavioural Test, like a screenplay made of legos

    # Ursula commits on startup
    d = threads.deferToThread(start)
    d.addCallback(verify_confirmed)

    # Ursula commits for 3 periods with no problem
    for i in range(3):
        d.addCallback(advance_one_period)
        d.addCallback(verify_confirmed)

    # Introduce unmined transactions
    d.addCallback(pending_commitments)

    # Ursula's commitment transaction gets stuck
    for i in range(4):
        d.addCallback(advance_one_cycle)
        d.addCallback(verify_unmined_commitment)

    # Ursula recovers from this situation
    d.addCallback(advance_one_cycle)
    d.addCallback(verify_confirmed)

    # but it happens again, resulting in a replacement transaction
    d.addCallback(advance_until_replacement_indicated)
    d.addCallback(advance_one_cycle)
    d.addCallback(verify_replacement_commitment)

    yield d
Пример #12
0
def test_work_tracker(mocker, testerchain, test_registry, staker, agency,
                      application_economics, ursula_decentralized_test_config):

    staker.initialize_stake(
        amount=NU(application_economics.min_authorization, 'NuNit'),
        lock_periods=int(application_economics.min_operator_seconds))

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

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

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

    commit_spy = mocker.spy(Worker, 'commit_to_next_period')
    replacement_spy = mocker.spy(
        ClassicPREWorkTracker,
        '_ClassicPREWorkTracker__fire_replacement_commitment')

    # Make the Worker
    ursula = make_decentralized_ursulas(
        ursula_config=ursula_decentralized_test_config,
        staking_provider_addresses=[staker.checksum_address],
        operator_addresses=[worker_address],
        registry=test_registry).pop()

    ursula.run(preflight=False,
               discovery=False,
               start_reactor=False,
               worker=True,
               eager=True,
               block_until_ready=False)  # "start" services

    initial_period = staker.staking_agent.get_current_period()

    def start():
        log("Starting Worker for auto-commitment simulation")
        start_pytest_ursula_services(ursula=ursula)

    def advance_one_period(_):
        log('Advancing one period')
        testerchain.time_travel(periods=1)
        clock.advance(ClassicPREWorkTracker.INTERVAL_CEIL + 1)

    def check_pending_commitments(number_of_commitments):
        def _check_pending_commitments(_):
            log(f'Checking we have {number_of_commitments} pending commitments'
                )
            assert number_of_commitments == len(ursula.work_tracker.pending)

        return _check_pending_commitments

    def pending_commitments(_):
        log('Starting unmined transaction simulation')
        testerchain.client.add_middleware(unmined_receipt_simulator_middleware)

    def advance_one_cycle(_):
        log('Advancing one tracking iteration')
        clock.advance(ursula.work_tracker._tracking_task.interval + 1)

    def advance_until_replacement_indicated(_):
        last_committed_period = staker.staking_agent.get_last_committed_period(
            staker_address=staker.checksum_address)
        log("Advancing until replacement is indicated")
        testerchain.time_travel(periods=1)
        clock.advance(ClassicPREWorkTracker.INTERVAL_CEIL + 1)
        mocker.patch.object(ClassicPREWorkTracker,
                            'max_confirmation_time',
                            return_value=1.0)
        mock_last_committed_period = mocker.PropertyMock(
            return_value=last_committed_period)
        mocker.patch.object(Worker,
                            'last_committed_period',
                            new_callable=mock_last_committed_period)
        clock.advance(ursula.work_tracker.max_confirmation_time() + 1)

    def verify_unmined_commitment(_):
        log('Verifying worker has unmined commitment transaction')

        # FIXME: The test doesn't model accurately an unmined TX, but an unconfirmed receipt,
        # so the tracker does not have pending TXs. If we want to model pending TXs we need to actually
        # prevent them from being mined.
        #
        # assert len(ursula.work_tracker.pending) == 1
        current_period = staker.staking_agent.get_current_period()
        assert commit_spy.call_count == current_period - initial_period + 1

    def verify_replacement_commitment(_):
        log('Verifying worker has replaced commitment transaction')
        assert replacement_spy.call_count > 0

    def verify_confirmed(_):
        # 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()

        expected_commitments = current_period - initial_period + 1
        log(f'Verifying worker made {expected_commitments} commitments so far')
        assert (last_committed_period - current_period) == 1
        assert commit_spy.call_count == expected_commitments
        assert replacement_spy.call_count == 0

    # Behavioural Test, like a screenplay made of legos

    # Ursula commits on startup
    d = threads.deferToThread(start)
    d.addCallback(verify_confirmed)
    d.addCallback(advance_one_period)

    d.addCallback(check_pending_commitments(1))
    d.addCallback(advance_one_cycle)
    d.addCallback(check_pending_commitments(0))

    # Ursula commits for 3 periods with no problem
    for i in range(3):
        d.addCallback(advance_one_period)
        d.addCallback(verify_confirmed)
        d.addCallback(check_pending_commitments(1))

    # Introduce unmined transactions
    d.addCallback(advance_one_period)
    d.addCallback(pending_commitments)

    # Ursula's commitment transaction gets stuck
    for i in range(4):
        d.addCallback(advance_one_cycle)
        d.addCallback(verify_unmined_commitment)

    # Ursula recovers from this situation
    d.addCallback(advance_one_cycle)
    d.addCallback(verify_confirmed)
    d.addCallback(advance_one_cycle)
    d.addCallback(check_pending_commitments(0))

    # but it happens again, resulting in a replacement transaction
    d.addCallback(advance_until_replacement_indicated)
    d.addCallback(advance_one_cycle)
    d.addCallback(check_pending_commitments(1))
    d.addCallback(advance_one_cycle)
    d.addCallback(verify_replacement_commitment)

    yield d