Пример #1
0
def make_decentralized_staker_reservoir(staking_agent: StakingEscrowAgent,
                                        duration_periods: int,
                                        exclude_addresses: Optional[Iterable[ChecksumAddress]] = None,
                                        include_addresses: Optional[Iterable[ChecksumAddress]] = None,
                                        pagination_size: int = None):
    """
    Get a sampler object containing the currently registered stakers.
    """

    # needs to not include both exclude and include addresses
    # so that they aren't included in reservoir, include_address will be re-added to reservoir afterwards
    include_addresses = include_addresses or ()
    without_set = set(include_addresses) | set(exclude_addresses or ())
    try:
        reservoir = staking_agent.get_stakers_reservoir(duration=duration_periods,
                                                        without=without_set,
                                                        pagination_size=pagination_size)
    except StakingEscrowAgent.NotEnoughStakers:
        # TODO: do that in `get_stakers_reservoir()`?
        reservoir = StakersReservoir({})

    # add include addresses
    return MergedReservoir(include_addresses, reservoir)
def test_sampling_distribution(testerchain, token, deploy_contract,
                               token_economics):

    #
    # SETUP
    #

    policy_manager, _ = deploy_contract('PolicyManagerForStakingEscrowMock',
                                        NULL_ADDRESS,
                                        token_economics.seconds_per_period)
    adjudicator, _ = deploy_contract('AdjudicatorForStakingEscrowMock',
                                     token_economics.reward_coefficient)

    staking_escrow_contract, _ = deploy_contract(
        STAKING_ESCROW_CONTRACT_NAME, token.address, policy_manager.address,
        adjudicator.address, NULL_ADDRESS,
        *token_economics.staking_deployment_parameters)
    staking_agent = StakingEscrowAgent(registry=None,
                                       contract=staking_escrow_contract)

    # Travel to the start of the next period to prevent problems with unexpected overflow first period
    testerchain.time_travel(hours=1)

    creator = testerchain.etherbase_account

    # Give Escrow tokens for reward and initialize contract
    tx = token.functions.approve(staking_escrow_contract.address,
                                 10**9).transact({'from': creator})
    testerchain.wait_for_receipt(tx)

    tx = staking_escrow_contract.functions.initialize(10**9, creator).transact(
        {'from': creator})
    testerchain.wait_for_receipt(tx)

    stakers = testerchain.stakers_accounts
    amount = token.functions.balanceOf(creator).call() // len(stakers)

    # Airdrop
    for staker in stakers:
        tx = token.functions.transfer(staker,
                                      amount).transact({'from': creator})
        testerchain.wait_for_receipt(tx)

    all_locked_tokens = len(stakers) * amount
    for staker in stakers:
        balance = token.functions.balanceOf(staker).call()
        tx = token.functions.approve(staking_escrow_contract.address,
                                     balance).transact({'from': staker})
        testerchain.wait_for_receipt(tx)

        staker_power = TransactingPower(account=staker,
                                        signer=Web3Signer(testerchain.client))
        staking_agent.deposit_tokens(amount=balance,
                                     lock_periods=10,
                                     transacting_power=staker_power)
        staking_agent.bond_worker(transacting_power=staker_power,
                                  worker_address=staker)
        staking_agent.commit_to_next_period(transacting_power=staker_power)

    # Wait next period and check all locked tokens
    testerchain.time_travel(hours=1)

    #
    # Test sampling distribution
    #

    ERROR_TOLERANCE = 0.05  # With this tolerance, all sampling ratios should between 5% and 15% (expected is 10%)
    SAMPLES = 1000
    quantity = 3
    counter = Counter()

    sampled, failed = 0, 0
    while sampled < SAMPLES:
        try:
            reservoir = staking_agent.get_stakers_reservoir(duration=1)
            addresses = set(reservoir.draw(quantity))
            addresses.discard(NULL_ADDRESS)
        except staking_agent.NotEnoughStakers:
            failed += 1
            continue
        else:
            sampled += 1
            counter.update(addresses)

    total_times = sum(counter.values())

    expected = amount / all_locked_tokens
    for staker in stakers:
        times = counter[staker]
        sampled_ratio = times / total_times
        abs_error = abs(expected - sampled_ratio)
        assert abs_error < ERROR_TOLERANCE