예제 #1
0
class PolicyAuthor(NucypherTokenActor):
    """Alice base class for blockchain operations, mocking up new policies!"""
    def __init__(self,
                 checksum_address: str,
                 policy_agent: PolicyAgent = None,
                 economics: TokenEconomics = None,
                 *args,
                 **kwargs) -> None:
        """
        :param policy_agent: A policy agent with the blockchain attached;
                             If not passed, a default policy agent and blockchain connection will
                             be created from default values.

        """
        super().__init__(checksum_address=checksum_address, *args, **kwargs)

        # From defaults
        if not policy_agent:
            self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
            self.staking_agent = StakingEscrowAgent(blockchain=self.blockchain)
            self.policy_agent = PolicyAgent(blockchain=self.blockchain)

        # Injected
        else:
            self.policy_agent = policy_agent

        self.economics = economics or TokenEconomics()

    def recruit(self, quantity: int, **options) -> List[str]:
        """
        Uses sampling logic to gather stakers from the blockchain and
        caches the resulting node ethereum addresses.

        :param quantity: Number of ursulas to sample from the blockchain.

        """
        staker_addresses = self.staking_agent.sample(quantity=quantity,
                                                     **options)
        return staker_addresses

    def create_policy(self, *args, **kwargs):
        """
        Hence the name, a PolicyAuthor can create
        a BlockchainPolicy with themself as the author.

        :return: Returns a newly authored BlockchainPolicy with n proposed arrangements.

        """
        from nucypher.blockchain.eth.policies import BlockchainPolicy
        blockchain_policy = BlockchainPolicy(alice=self, *args, **kwargs)
        return blockchain_policy
예제 #2
0
def test_sampling_distribution(testerchain, token, deploy_contract):

    #
    # SETUP
    #

    max_allowed_locked_tokens = 5 * 10 ** 8
    _staking_coefficient = 2 * 10 ** 7
    staking_escrow_contract, _ = deploy_contract(
        contract_name=STAKING_ESCROW_CONTRACT_NAME,
        _token=token.address,
        _hoursPerPeriod=1,
        _miningCoefficient=4 * _staking_coefficient,
        _lockedPeriodsCoefficient=4,
        _rewardedPeriods=4,
        _minLockedPeriods=2,
        _minAllowableLockedTokens=100,
        _maxAllowableLockedTokens=max_allowed_locked_tokens,
        _minWorkerPeriods=1,
        _isTestContract=False
    )
    staking_agent = StakingEscrowAgent(registry=None, contract=staking_escrow_contract)

    policy_manager, _ = deploy_contract(
        'PolicyManagerForStakingEscrowMock', token.address, staking_escrow_contract.address
    )
    tx = staking_escrow_contract.functions.setPolicyManager(policy_manager.address).transact()
    testerchain.wait_for_receipt(tx)

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

        staking_agent.deposit_tokens(amount=balance, lock_periods=10, sender_address=staker, staker_address=staker)
        staking_agent.set_worker(staker_address=staker, worker_address=staker)
        staking_agent.confirm_activity(staker)

    # 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:
            addresses = set(staking_agent.sample(quantity=quantity, additional_ursulas=1, duration=1))
            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