コード例 #1
0
def token_economics(testerchain):

    # Get current blocktime
    blockchain = BlockchainInterfaceFactory.get_interface(
        provider_uri=testerchain.provider_uri)
    now = blockchain.w3.eth.getBlock(block_identifier='latest').timestamp

    # Calculate instant start time
    one_hour_in_seconds = (60 * 60)
    start_date = now
    bidding_start_date = start_date

    # Ends in one hour
    bidding_end_date = start_date + one_hour_in_seconds
    cancellation_end_date = bidding_end_date + one_hour_in_seconds

    economics = StandardTokenEconomics(
        worklock_boosting_refund_rate=200,
        worklock_commitment_duration=60,  # periods
        worklock_supply=10 * BaseEconomics._default_maximum_allowed_locked,
        bidding_start_date=bidding_start_date,
        bidding_end_date=bidding_end_date,
        cancellation_end_date=cancellation_end_date,
        worklock_min_allowed_bid=Web3.toWei(1, "ether"))
    return economics
コード例 #2
0
def test_economic_parameter_aliases():

    e = StandardTokenEconomics()

    assert e.locked_periods_coefficient == 365
    assert int(e.staking_coefficient) == 768812
    assert e.maximum_rewarded_periods == 365

    deployment_params = e.staking_deployment_parameters
    assert isinstance(deployment_params, tuple)
    for parameter in deployment_params:
        assert isinstance(parameter, int)
コード例 #3
0
def test_crawler_init(get_agent, get_economics):
    staking_agent = MagicMock(spec=StakingEscrowAgent)
    contract_agency = MockContractAgency(staking_agent=staking_agent)
    get_agent.side_effect = contract_agency.get_agent

    token_economics = StandardTokenEconomics()
    get_economics.return_value = token_economics

    crawler = create_crawler(dont_set_teacher=True)

    # crawler not yet started
    assert not crawler.is_running
コード例 #4
0
def test_rough_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / d / k2

    d - Coefficient which modifies the rate at which the maximum issuance decays
    k1 - Numerator of the locking duration coefficient
    k2 - Denominator of the locking duration coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / d / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    where allLockedPeriods == min(T, T1)
    """

    e = StandardTokenEconomics(initial_supply=int(1e9),
                               first_phase_supply=1829579800,
                               first_phase_duration=5,
                               decay_half_life=2,
                               reward_saturation=1,
                               small_stake_multiplier=Decimal(0.5))

    assert float(round(e.erc20_total_supply / Decimal(1e9),
                       2)) == 3.89  # As per economics paper

    # Check that we have correct numbers in day 1 of the second phase
    one_year_in_periods = Decimal(365 / 7)
    initial_rate = (e.erc20_total_supply - int(e.first_phase_total_supply)) \
        * (e.lock_duration_coefficient_1 + one_year_in_periods) \
        / (e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
    assert int(initial_rate) == int(e.first_phase_max_issuance)

    initial_rate_small = (e.erc20_total_supply - int(e.first_phase_total_supply))\
        * e.lock_duration_coefficient_1 \
        / (e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
    assert int(initial_rate_small) == int(initial_rate / 2)

    # Sanity check that total and reward supply calculated correctly
    assert int(LOG2 / (e.token_halving * one_year_in_periods) *
               (e.erc20_total_supply -
                int(e.first_phase_total_supply))) == int(initial_rate)
    assert int(e.reward_supply) == int(e.erc20_total_supply -
                                       Decimal(int(1e9)))

    with localcontext(
    ) as ctx:  # TODO: Needs follow up - why the sudden failure (python 3.8.0)?
        ctx.prec = 18  # Perform a high precision calculation
        # Sanity check for lock_duration_coefficient_1 (k1), issuance_decay_coefficient (d) and lock_duration_coefficient_2 (k2)
        expected = e.lock_duration_coefficient_1 * e.token_halving
        result = e.issuance_decay_coefficient * e.lock_duration_coefficient_2 * LOG2 * e.small_stake_multiplier / one_year_in_periods
        assert expected == result
コード例 #5
0
def test_economic_parameter_aliases():

    e = StandardTokenEconomics()

    assert e.lock_duration_coefficient_1 == 365
    assert e.lock_duration_coefficient_2 == 2 * 365
    assert int(e.issuance_decay_coefficient) == 1053
    assert e.maximum_rewarded_periods == 365

    deployment_params = e.staking_deployment_parameters
    assert isinstance(deployment_params, tuple)
    for parameter in deployment_params:
        assert isinstance(parameter, int)
コード例 #6
0
def test_crawler_start_no_influx_db_connection(get_agent, get_economics):
    staking_agent = MagicMock(spec=StakingEscrowAgent, autospec=True)
    contract_agency = MockContractAgency(staking_agent=staking_agent)
    get_agent.side_effect = contract_agency.get_agent

    token_economics = StandardTokenEconomics()
    get_economics.return_value = token_economics

    crawler = create_crawler(dont_set_teacher=True)
    try:
        with pytest.raises(ConnectionError):
            crawler.start()
    finally:
        crawler.stop()
コード例 #7
0
def test_crawler_stop_before_start(new_influx_db, get_agent, get_economics):
    mock_influxdb_client = new_influx_db.return_value

    staking_agent = MagicMock(spec=StakingEscrowAgent)
    contract_agency = MockContractAgency(staking_agent=staking_agent)
    get_agent.side_effect = contract_agency.get_agent

    token_economics = StandardTokenEconomics()
    get_economics.return_value = token_economics

    crawler = create_crawler()

    crawler.stop()

    new_influx_db.assert_not_called()  # db only initialized when crawler is started
    mock_influxdb_client.close.assert_not_called()  # just to be sure
    assert not crawler.is_running
コード例 #8
0
def test_rough_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / d / k2

    d - Coefficient which modifies the rate at which the maximum issuance decays
    k1 - Numerator of the locking duration coefficient
    k2 - Denominator of the locking duration coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / d / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    where allLockedPeriods == min(T, T1)
    """

    e = StandardTokenEconomics(initial_supply=int(1e9),
                               first_phase_supply=1829579800,
                               first_phase_duration=5,
                               decay_half_life=2,
                               reward_saturation=1,
                               small_stake_multiplier=Decimal(0.5))

    assert float(round(e.erc20_total_supply / Decimal(1e9),
                       2)) == 3.89  # As per economics paper

    # Check that we have correct numbers in day 1 of the second phase
    initial_rate = (e.erc20_total_supply - int(e.first_phase_total_supply)) * (e.lock_duration_coefficient_1 + 365) / \
                   (e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
    assert int(initial_rate) == int(e.first_phase_max_issuance)

    initial_rate_small = (e.erc20_total_supply - int(e.first_phase_total_supply)) * e.lock_duration_coefficient_1 / \
                         (e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
    assert int(initial_rate_small) == int(initial_rate / 2)

    # Sanity check that total and reward supply calculated correctly
    assert int(LOG2 / (e.token_halving * 365) *
               (e.erc20_total_supply -
                int(e.first_phase_total_supply))) == int(initial_rate)
    assert int(e.reward_supply) == int(e.erc20_total_supply -
                                       Decimal(int(1e9)))

    # Sanity check for lock_duration_coefficient_1 (k1), issuance_decay_coefficient (d) and lock_duration_coefficient_2 (k2)
    assert e.lock_duration_coefficient_1 * e.token_halving == \
           e.issuance_decay_coefficient * e.lock_duration_coefficient_2 * LOG2 * e.small_stake_multiplier / 365
コード例 #9
0
def token_economics(testerchain):

    # Get current blocktime
    blockchain = BlockchainInterfaceFactory.get_interface()
    now = blockchain.w3.eth.getBlock(block_identifier='latest').timestamp

    # Calculate instant start time
    one_hour_in_seconds = (60 * 60)
    start_date = now
    bidding_start_date = start_date

    # Ends in one hour
    bidding_end_date = start_date + one_hour_in_seconds

    economics = StandardTokenEconomics(
        worklock_boosting_refund_rate=200,
        worklock_commitment_duration=60 * 60,  # seconds
        worklock_supply=NU.from_tokens(1_000_000),
        bidding_start_date=bidding_start_date,
        bidding_end_date=bidding_end_date)
    return economics
コード例 #10
0
ファイル: actors.py プロジェクト: itsencrypted/nucypher
    def __init__(self,
                 registry: BaseContractRegistry,
                 deployer_address: str = None,
                 client_password: str = None,
                 economics: TokenEconomics = None):
        """
        Note: super() is not called here to avoid setting the token agent.
        TODO: Review this logic ^^ "bare mode".
        """
        self.log = Logger("Deployment-Actor")

        self.deployer_address = deployer_address
        self.checksum_address = self.deployer_address
        self.economics = economics or StandardTokenEconomics()

        self.registry = registry
        self.preallocation_escrow_deployers = dict()
        self.deployers = {d.contract_name: d for d in self.deployer_classes}

        self.transacting_power = TransactingPower(password=client_password, account=deployer_address, cache=True)
        self.transacting_power.activate()
コード例 #11
0
    def __init__(self,
                 registry: BaseContractRegistry,
                 economics: TokenEconomics = None,
                 deployer_address: str = None):

        #
        # Validate
        #
        self.blockchain = BlockchainInterfaceFactory.get_interface()
        if not isinstance(self.blockchain, BlockchainDeployerInterface):
            raise ValueError("No deployer interface connection available.")

        #
        # Defaults
        #
        self.registry = registry
        self.deployment_receipts = dict()
        self._contract = CONTRACT_NOT_DEPLOYED
        self.__proxy_contract = NotImplemented
        self.__deployer_address = deployer_address
        self.__ready_to_deploy = False
        self.__economics = economics or StandardTokenEconomics()
コード例 #12
0
ファイル: fixtures.py プロジェクト: piotr-roslaniec/nucypher
def make_token_economics(blockchain):
    # Get current blocktime
    now = blockchain.w3.eth.getBlock('latest').timestamp

    # Calculate instant start time
    one_hour_in_seconds = (60 * 60)
    start_date = now
    bidding_start_date = start_date

    # Ends in one hour
    bidding_end_date = start_date + one_hour_in_seconds
    cancellation_end_date = bidding_end_date + one_hour_in_seconds

    economics = StandardTokenEconomics(
        worklock_boosting_refund_rate=200,
        worklock_commitment_duration=60,  # genesis periods
        worklock_supply=10 * BaseEconomics._default_maximum_allowed_locked,
        bidding_start_date=bidding_start_date,
        bidding_end_date=bidding_end_date,
        cancellation_end_date=cancellation_end_date,
        worklock_min_allowed_bid=Web3.toWei(1, "ether"))
    return economics
コード例 #13
0
def test_rough_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2

    K2 - Staking coefficient
    K1 - Locked periods coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    where allLockedPeriods == min(T, T1)
    """

    e = StandardTokenEconomics(initial_supply=int(1e9),
                               initial_inflation=1,
                               halving_delay=2,
                               reward_saturation=1,
                               small_stake_multiplier=Decimal(0.5))

    assert float(round(e.erc20_total_supply / Decimal(1e9), 2)) == 3.89  # As per economics paper

    # Check that we have correct numbers in day 1
    initial_rate = (e.erc20_total_supply - e.initial_supply) * (e.locked_periods_coefficient + 365) / e.staking_coefficient
    assert int(initial_rate) == int(e.initial_inflation * e.initial_supply / 365)

    initial_rate_small = (e.erc20_total_supply - e.initial_supply) * e.locked_periods_coefficient / e.staking_coefficient
    assert int(initial_rate_small) == int(initial_rate / 2)

    # Sanity check that total and reward supply calculated correctly
    assert int(LOG2 / (e.token_halving * 365) * (e.erc20_total_supply - e.initial_supply)) == int(initial_rate)
    assert int(e.reward_supply) == int(e.erc20_total_supply - Decimal(int(1e9)))

    # Sanity check for locked_periods_coefficient (k1) and staking_coefficient (k2)
    assert e.locked_periods_coefficient * e.token_halving == e.staking_coefficient * LOG2 * e.small_stake_multiplier / 365
コード例 #14
0
    assert confirmation in captured.out

    # Negative case
    mock_stdin.line(NO)

    with pytest.raises(click.Abort):
        confirm_staged_stake(staker_address=staking_address,
                             value=value,
                             lock_periods=lock_periods)

    captured = capsys.readouterr()
    assert confirmation in captured.out
    assert mock_stdin.empty()


STANDARD_ECONOMICS = StandardTokenEconomics()
MIN_ALLOWED_LOCKED = STANDARD_ECONOMICS.minimum_allowed_locked


@pytest.mark.parametrize(
    'value,duration,must_confirm_value,must_confirm_duration', (
        (NU.from_tokens(1), 1, False, False),
        (NU.from_tokens(1), STANDARD_ECONOMICS.minimum_locked_periods + 1,
         False, False),
        (NU.from_tokens(15), STANDARD_ECONOMICS.minimum_locked_periods + 1,
         False, False),
        (((NU.from_nunits(MIN_ALLOWED_LOCKED) * 10) + 1),
         STANDARD_ECONOMICS.minimum_locked_periods + 1, True, False),
        (NU.from_nunits(MIN_ALLOWED_LOCKED) * 10,
         STANDARD_ECONOMICS.maximum_rewarded_periods + 1, False, True),
        (((NU.from_nunits(MIN_ALLOWED_LOCKED) * 10) + 1),
コード例 #15
0
ファイル: estimate_gas.py プロジェクト: p2p-org/nucypher-old
def estimate_gas(analyzer: AnalyzeGas = None) -> None:
    """
    Execute a linear sequence of NyCypher transactions mimicking
    post-deployment usage on a local PyEVM blockchain;
    Record the resulting estimated transaction gas expenditure.

    Note: The function calls below are *order dependant*
    """

    #
    # Setup
    #

    if analyzer is None:
        analyzer = AnalyzeGas()

    log = Logger(AnalyzeGas.LOG_NAME)
    os.environ['GAS_ESTIMATOR_BACKEND_FUNC'] = 'eth.estimators.gas.binary_gas_search_exact'

    # Blockchain
    economics = StandardTokenEconomics(
        base_penalty=MIN_ALLOWED_LOCKED - 1,
        penalty_history_coefficient=0,
        percentage_penalty_coefficient=2,
        reward_coefficient=2
    )
    testerchain, registry = TesterBlockchain.bootstrap_network(economics=economics)
    web3 = testerchain.w3

    # Accounts
    origin, ursula1, ursula2, ursula3, alice1, alice2, *everyone_else = testerchain.client.accounts

    ursula_with_stamp = mock_ursula(testerchain, ursula1)

    # Contracts
    token_agent = NucypherTokenAgent(registry=registry)
    staking_agent = StakingEscrowAgent(registry=registry)
    policy_agent = PolicyManagerAgent(registry=registry)
    adjudicator_agent = AdjudicatorAgent(registry=registry)

    # Contract Callers
    token_functions = token_agent.contract.functions
    staker_functions = staking_agent.contract.functions
    policy_functions = policy_agent.contract.functions
    adjudicator_functions = adjudicator_agent.contract.functions

    analyzer.start_collection()
    print("********* Estimating Gas *********")

    def transact_and_log(label, function, transaction):
        estimates = function.estimateGas(transaction)
        transaction.update(gas=estimates)
        tx = function.transact(transaction)
        receipt = testerchain.wait_for_receipt(tx)
        log.info(f"{label} = {estimates}|{receipt['gasUsed']}")

    def transact(function, transaction):
        transaction.update(gas=1000000)
        tx = function.transact(transaction)
        testerchain.wait_for_receipt(tx)

        #
    # Give Ursula and Alice some coins
    #
    transact_and_log("Transfer tokens", token_functions.transfer(ursula1, MIN_ALLOWED_LOCKED * 10), {'from': origin})
    transact(token_functions.transfer(ursula2, MIN_ALLOWED_LOCKED * 10), {'from': origin})
    transact(token_functions.transfer(ursula3, MIN_ALLOWED_LOCKED * 10), {'from': origin})

    #
    # Ursula and Alice give Escrow rights to transfer
    #
    transact_and_log("Approving transfer",
                     token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * 6),
                     {'from': ursula1})
    transact(token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * 6), {'from': ursula2})
    transact(token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * 6), {'from': ursula3})

    #
    # Ursula and Alice transfer some tokens to the escrow and lock them
    #
    transact_and_log("Initial deposit tokens, 1st",
                     staker_functions.deposit(MIN_ALLOWED_LOCKED * 3, MIN_LOCKED_PERIODS),
                     {'from': ursula1})
    transact_and_log("Initial deposit tokens, 2nd",
                     staker_functions.deposit(MIN_ALLOWED_LOCKED * 3, MIN_LOCKED_PERIODS),
                     {'from': ursula2})
    transact(staker_functions.deposit(MIN_ALLOWED_LOCKED * 3, MIN_LOCKED_PERIODS), {'from': ursula3})

    transact(staker_functions.setWorker(ursula1), {'from': ursula1})
    transact(staker_functions.setWorker(ursula2), {'from': ursula2})
    transact(staker_functions.setWorker(ursula3), {'from': ursula3})
    transact(staker_functions.setReStake(False), {'from': ursula1})
    transact(staker_functions.setReStake(False), {'from': ursula2})
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    transact(staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Wait 1 period and confirm activity
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Confirm activity, 1st", staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity, 2nd", staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Wait 1 period and mint tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Mining (1 stake), 1st", staker_functions.mint(), {'from': ursula1})
    transact_and_log("Mining (1 stake), 2nd", staker_functions.mint(), {'from': ursula2})
    transact_and_log("Confirm activity again, 1st", staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity again, 2nd", staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Confirm again
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Confirm activity + mint, 1st", staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity + mint, 2nd", staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Get locked tokens
    #
    transact_and_log("Getting locked tokens", staker_functions.getLockedTokens(ursula1, 0), {})

    #
    # Wait 1 period and withdraw tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Withdraw", staker_functions.withdraw(1), {'from': ursula1})

    #
    # Confirm activity with re-stake
    #
    transact(staker_functions.setReStake(True), {'from': ursula1})
    transact(staker_functions.setReStake(True), {'from': ursula2})

    transact_and_log("Confirm activity + mint with re-stake, 1st",
                     staker_functions.confirmActivity(),
                     {'from': ursula1})
    transact_and_log("Confirm activity + mint with re-stake, 2nd",
                     staker_functions.confirmActivity(),
                     {'from': ursula2})

    transact(staker_functions.setReStake(False), {'from': ursula1})
    transact(staker_functions.setReStake(False), {'from': ursula2})

    #
    # Wait 1 period
    #
    testerchain.time_travel(periods=1)

    #
    # Create policy
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 10
    rate = 100
    one_period = economics.hours_per_period * 60 * 60
    value = number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log("Creating policy (1 node, 10 periods, pre-confirmed), 1st",
                     policy_functions.createPolicy(policy_id_1, alice1, end_timestamp, [ursula1]),
                     {'from': alice1, 'value': value})
    transact_and_log("Creating policy (1 node, 10 periods, pre-confirmed), 2nd",
                     policy_functions.createPolicy(policy_id_2, alice1, end_timestamp, [ursula1]),
                     {'from': alice1, 'value': value})

    #
    # Wait 2 periods and confirm activity after downtime
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Confirm activity after downtime, 1st", staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity after downtime, 2nd", staker_functions.confirmActivity(), {'from': ursula2})
    transact(staker_functions.confirmActivity(), {'from': ursula3})

    #
    # Ursula and Alice deposit some tokens to the escrow again
    #
    transact_and_log("Deposit tokens after confirming activity",
                     staker_functions.deposit(MIN_ALLOWED_LOCKED * 2, MIN_LOCKED_PERIODS),
                     {'from': ursula1})
    transact(staker_functions.deposit(MIN_ALLOWED_LOCKED * 2, MIN_LOCKED_PERIODS), {'from': ursula2})

    #
    # Revoke policy
    #
    transact_and_log("Revoking policy", policy_functions.revokePolicy(policy_id_1), {'from': alice1})

    #
    # Wait 1 period
    #
    testerchain.time_travel(periods=1)

    #
    # Create policy with multiple pre-confirmed nodes
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_3 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 100
    value = 3 * number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log("Creating policy (3 nodes, 100 periods, pre-confirmed), 1st",
                     policy_functions.createPolicy(policy_id_1, alice1, end_timestamp, [ursula1, ursula2, ursula3]),
                     {'from': alice1, 'value': value})
    transact_and_log("Creating policy (3 nodes, 100 periods, pre-confirmed), 2nd",
                     policy_functions.createPolicy(policy_id_2, alice1, end_timestamp, [ursula1, ursula2, ursula3]),
                     {'from': alice1, 'value': value})
    value = 2 * number_of_periods * rate
    transact_and_log("Creating policy (2 nodes, 100 periods, pre-confirmed), 3rd",
                     policy_functions.createPolicy(policy_id_3, alice1, end_timestamp, [ursula1, ursula2]),
                     {'from': alice1, 'value': value})

    #
    # Wait 1 period and mint tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Mining with updating reward, 1st", staker_functions.mint(), {'from': ursula1})
    transact_and_log("Mining with updating reward, 2nd", staker_functions.mint(), {'from': ursula2})

    #
    # Create policy again without pre-confirmed nodes
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_3 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 100
    value = number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log("Creating policy (1 node, 100 periods), 1st",
                     policy_functions.createPolicy(policy_id_1, alice2, end_timestamp, [ursula2]),
                     {'from': alice1, 'value': value})
    testerchain.time_travel(periods=1)
    current_timestamp = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log("Creating policy (1 node, 100 periods), 2nd",
                     policy_functions.createPolicy(policy_id_2, alice2, end_timestamp, [ursula2]),
                     {'from': alice1, 'value': value})
    transact_and_log("Creating policy (1 node, 100 periods), 3rd",
                     policy_functions.createPolicy(policy_id_3, alice2, end_timestamp, [ursula1]),
                     {'from': alice1, 'value': value})

    #
    # Mine and revoke policy
    #
    testerchain.time_travel(periods=10)
    transact(staker_functions.confirmActivity(), {'from': ursula1})

    testerchain.time_travel(periods=2)
    transact_and_log("Mining after downtime", staker_functions.mint(), {'from': ursula1})

    testerchain.time_travel(periods=10)
    transact_and_log("Revoking policy after downtime, 1st",
                     policy_functions.revokePolicy(policy_id_1),
                     {'from': alice2})
    transact_and_log("Revoking policy after downtime, 2nd",
                     policy_functions.revokePolicy(policy_id_2),
                     {'from': alice2})
    transact_and_log("Revoking policy after downtime, 3rd",
                     policy_functions.revokePolicy(policy_id_3),
                     {'from': alice2})

    for index in range(5):
        transact(staker_functions.confirmActivity(), {'from': ursula1})
        testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': ursula1})

    #
    # Check regular deposit
    #
    transact_and_log("Deposit tokens",
                     staker_functions.deposit(MIN_ALLOWED_LOCKED, MIN_LOCKED_PERIODS),
                     {'from': ursula1})

    #
    # ApproveAndCall
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': ursula1})

    transact_and_log("ApproveAndCall",
                     token_functions.approveAndCall(staking_agent.contract_address,
                                                    MIN_ALLOWED_LOCKED * 2,
                                                    web3.toBytes(MIN_LOCKED_PERIODS)),
                     {'from': ursula1})

    #
    # Locking tokens
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Locking tokens", staker_functions.lock(MIN_ALLOWED_LOCKED, MIN_LOCKED_PERIODS), {'from': ursula1})

    #
    # Divide stake
    #
    transact_and_log("Divide stake", staker_functions.divideStake(1, MIN_ALLOWED_LOCKED, 2), {'from': ursula1})
    transact(staker_functions.divideStake(3, MIN_ALLOWED_LOCKED, 2), {'from': ursula1})

    #
    # Divide almost finished stake
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    testerchain.time_travel(periods=1)
    transact(staker_functions.confirmActivity(), {'from': ursula1})

    #
    # Slashing tests
    #
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    testerchain.time_travel(periods=1)

    #
    # Slashing
    #
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slash just value", adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    deposit = staker_functions.stakerInfo(ursula1).call()[0]
    unlocked = deposit - staker_functions.getLockedTokens(ursula1, 0).call()
    transact(staker_functions.withdraw(unlocked), {'from': ursula1})

    sub_stakes_length = str(staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slashing one sub stake and saving old one (" + sub_stakes_length + " sub stakes), 1st",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    sub_stakes_length = str(staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slashing one sub stake and saving old one (" + sub_stakes_length + " sub stakes), 2nd",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    sub_stakes_length = str(staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slashing one sub stake and saving old one (" + sub_stakes_length + " sub stakes), 3rd",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    sub_stakes_length = str(staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slashing two sub stakes and saving old one (" + sub_stakes_length + " sub stakes)",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    for index in range(18):
        transact(staker_functions.confirmActivity(), {'from': ursula1})
        testerchain.time_travel(periods=1)

    transact(staker_functions.lock(MIN_ALLOWED_LOCKED, MIN_LOCKED_PERIODS), {'from': ursula1})
    deposit = staker_functions.stakerInfo(ursula1).call()[0]
    unlocked = deposit - staker_functions.getLockedTokens(ursula1, 1).call()
    transact(staker_functions.withdraw(unlocked), {'from': ursula1})

    sub_stakes_length = str(staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slashing two sub stakes, shortest and new one (" + sub_stakes_length + " sub stakes)",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    sub_stakes_length = str(staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slashing three sub stakes, two shortest and new one (" + sub_stakes_length + " sub stakes)",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    slashing_args = generate_args_for_slashing(ursula_with_stamp, corrupt_cfrag=False)
    transact_and_log("Evaluating correct CFrag", adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    transact_and_log("Prolong stake", staker_functions.prolongStake(0, 20), {'from': ursula1})

    print("********* All Done! *********")
コード例 #16
0
ファイル: estimate_gas.py プロジェクト: p2p-org/nucypher-old
from nucypher.blockchain.economics import StandardTokenEconomics
from nucypher.blockchain.eth.agents import NucypherTokenAgent, StakingEscrowAgent, PolicyManagerAgent, AdjudicatorAgent
from nucypher.crypto.signing import SignatureStamp
from nucypher.policy.policies import Policy
from nucypher.utilities.sandbox.blockchain import TesterBlockchain
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer

# FIXME: Needed to use a fixture here, but now estimate_gas.py only runs if executed from main directory
sys.path.insert(0, abspath('tests'))
from fixtures import _mock_ursula_reencrypts as mock_ursula_reencrypts


ALGORITHM_SHA256 = 1
TOKEN_ECONOMICS = StandardTokenEconomics()
MIN_ALLOWED_LOCKED = TOKEN_ECONOMICS.minimum_allowed_locked
MIN_LOCKED_PERIODS = TOKEN_ECONOMICS.minimum_locked_periods
MAX_ALLOWED_LOCKED = TOKEN_ECONOMICS.maximum_allowed_locked
MAX_MINTING_PERIODS = TOKEN_ECONOMICS.maximum_rewarded_periods


class AnalyzeGas:
    """
    Callable twisted log observer with built-in record-keeping for gas estimation runs.
    """

    # Logging
    LOG_NAME = 'estimate-gas'
    LOG_FILENAME = '{}.log.json'.format(LOG_NAME)
    OUTPUT_DIR = os.path.join(abspath(dirname(__file__)), 'results')
コード例 #17
0
def token_economics():
    economics = StandardTokenEconomics()
    return economics
コード例 #18
0
def token_economics():
    economics = StandardTokenEconomics(genesis_hours_per_period=24,
                                       hours_per_period=48,
                                       minimum_locked_periods=2)
    return economics
コード例 #19
0
ファイル: blockchain.py プロジェクト: p2p-org/nucypher-old
class TesterBlockchain(BlockchainDeployerInterface):
    """
    Blockchain subclass with additional test utility methods and options.
    """

    _instance = None

    GAS_STRATEGIES = {
        **BlockchainDeployerInterface.GAS_STRATEGIES, 'free':
        free_gas_price_strategy
    }

    _PROVIDER_URI = 'tester://pyevm'
    TEST_CONTRACTS_DIR = os.path.join(BASE_DIR, 'tests', 'blockchain', 'eth',
                                      'contracts', 'contracts')
    _compiler = SolidityCompiler(
        source_dirs=[(SolidityCompiler.default_contract_dir(),
                      {TEST_CONTRACTS_DIR})])
    _test_account_cache = list()

    _default_test_accounts = NUMBER_OF_ETH_TEST_ACCOUNTS

    # Reserved addresses
    _ETHERBASE = 0
    _ALICE = 1
    _BOB = 2
    _FIRST_STAKER = 5
    _stakers_range = range(NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS)
    _FIRST_URSULA = _FIRST_STAKER + NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS
    _ursulas_range = range(NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)

    _default_token_economics = StandardTokenEconomics()

    def __init__(self,
                 test_accounts=None,
                 poa=True,
                 light=False,
                 eth_airdrop=False,
                 free_transactions=False,
                 compiler: SolidityCompiler = None,
                 *args,
                 **kwargs):

        if not test_accounts:
            test_accounts = self._default_test_accounts
        self.free_transactions = free_transactions

        if compiler:
            TesterBlockchain._compiler = compiler

        super().__init__(provider_uri=self._PROVIDER_URI,
                         provider_process=None,
                         poa=poa,
                         light=light,
                         compiler=self._compiler,
                         *args,
                         **kwargs)

        self.log = Logger("test-blockchain")
        self.connect()

        # Generate additional ethereum accounts for testing
        population = test_accounts
        enough_accounts = len(self.client.accounts) >= population
        if not enough_accounts:
            accounts_to_make = population - len(self.client.accounts)
            self.__generate_insecure_unlocked_accounts(
                quantity=accounts_to_make)
            assert test_accounts == len(self.w3.eth.accounts)

        if eth_airdrop is True:  # ETH for everyone!
            self.ether_airdrop(amount=DEVELOPMENT_ETH_AIRDROP_AMOUNT)

    def attach_middleware(self):
        if self.free_transactions:
            self.w3.eth.setGasPriceStrategy(free_gas_price_strategy)

    def __generate_insecure_unlocked_accounts(self,
                                              quantity: int) -> List[str]:

        #
        # Sanity Check - Only PyEVM can be used.
        #

        # Detect provider platform
        client_version = self.w3.clientVersion

        if 'Geth' in client_version:
            raise RuntimeError("WARNING: Geth providers are not implemented.")
        elif "Parity" in client_version:
            raise RuntimeError(
                "WARNING: Parity providers are not implemented.")

        addresses = list()
        for _ in range(quantity):
            address = self.provider.ethereum_tester.add_account(
                '0x' + os.urandom(32).hex())
            addresses.append(address)
            self._test_account_cache.append(address)
            self.log.info('Generated new insecure account {}'.format(address))
        return addresses

    def ether_airdrop(self, amount: int) -> List[str]:
        """Airdrops ether from creator address to all other addresses!"""

        coinbase, *addresses = self.w3.eth.accounts

        tx_hashes = list()
        for address in addresses:
            tx = {'to': address, 'from': coinbase, 'value': amount}
            txhash = self.w3.eth.sendTransaction(tx)

            _receipt = self.wait_for_receipt(txhash)
            tx_hashes.append(txhash)
            eth_amount = Web3().fromWei(amount, 'ether')
            self.log.info("Airdropped {} ETH {} -> {}".format(
                eth_amount, tx['from'], tx['to']))

        return tx_hashes

    def time_travel(self,
                    hours: int = None,
                    seconds: int = None,
                    periods: int = None):
        """
        Wait the specified number of wait_hours by comparing
        block timestamps and mines a single block.
        """

        more_than_one_arg = sum(map(bool, (hours, seconds, periods))) > 1
        if more_than_one_arg:
            raise ValueError(
                "Specify hours, seconds, or periods, not a combination")

        if periods:
            duration = self._default_token_economics.seconds_per_period * periods
            base = self._default_token_economics.seconds_per_period
        elif hours:
            duration = hours * (60 * 60)
            base = 60 * 60
        elif seconds:
            duration = seconds
            base = 1
        else:
            raise ValueError("Specify either hours, seconds, or periods.")

        now = self.w3.eth.getBlock(block_identifier='latest').timestamp
        end_timestamp = ((now + duration) // base) * base

        self.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp)
        self.w3.eth.web3.testing.mine(1)

        delta = maya.timedelta(seconds=end_timestamp - now)
        self.log.info(
            f"Time traveled {delta} "
            f"| period {epoch_to_period(epoch=end_timestamp, seconds_per_period=self._default_token_economics.seconds_per_period)} "
            f"| epoch {end_timestamp}")

    @classmethod
    def bootstrap_network(
        cls,
        economics: BaseEconomics = None
    ) -> Tuple['TesterBlockchain', 'InMemoryContractRegistry']:
        """For use with metric testing scripts"""

        registry = InMemoryContractRegistry()
        testerchain = cls(compiler=SolidityCompiler())
        BlockchainInterfaceFactory.register_interface(testerchain)
        power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD,
                                 account=testerchain.etherbase_account)
        power.activate()
        testerchain.transacting_power = power

        origin = testerchain.client.etherbase
        deployer = ContractAdministrator(deployer_address=origin,
                                         registry=registry,
                                         economics=economics
                                         or cls._default_token_economics,
                                         staking_escrow_test_mode=True)
        secrets = dict()
        for deployer_class in deployer.upgradeable_deployer_classes:
            secrets[
                deployer_class.contract_name] = INSECURE_DEVELOPMENT_PASSWORD
        _receipts = deployer.deploy_network_contracts(secrets=secrets,
                                                      interactive=False)
        return testerchain, registry

    @property
    def etherbase_account(self):
        return self.client.accounts[self._ETHERBASE]

    @property
    def alice_account(self):
        return self.client.accounts[self._ALICE]

    @property
    def bob_account(self):
        return self.client.accounts[self._BOB]

    def ursula_account(self, index):
        if index not in self._ursulas_range:
            raise ValueError(
                f"Ursula index must be lower than {NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS}"
            )
        return self.client.accounts[index + self._FIRST_URSULA]

    def staker_account(self, index):
        if index not in self._stakers_range:
            raise ValueError(
                f"Staker index must be lower than {NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS}"
            )
        return self.client.accounts[index + self._FIRST_STAKER]

    @property
    def ursulas_accounts(self):
        return list(self.ursula_account(i) for i in self._ursulas_range)

    @property
    def stakers_accounts(self):
        return list(self.staker_account(i) for i in self._stakers_range)

    @property
    def unassigned_accounts(self):
        special_accounts = [
            self.etherbase_account, self.alice_account, self.bob_account
        ]
        assigned_accounts = set(self.stakers_accounts + self.ursulas_accounts +
                                special_accounts)
        accounts = set(self.client.accounts)
        return list(accounts.difference(assigned_accounts))

    def wait_for_receipt(self, txhash: bytes, timeout: int = None) -> dict:
        """Wait for a transaction receipt and return it"""
        timeout = timeout or self.TIMEOUT
        result = self.w3.eth.waitForTransactionReceipt(txhash, timeout=timeout)
        if result.status == 0:
            raise TransactionFailed()
        return result
コード例 #20
0
ファイル: estimate_gas.py プロジェクト: chaoticx-org/nucypher
def estimate_gas(analyzer: AnalyzeGas = None) -> None:
    """
    Execute a linear sequence of NyCypher transactions mimicking
    post-deployment usage on a local PyEVM blockchain;
    Record the resulting estimated transaction gas expenditure.

    Note: The function calls below are *order dependant*
    """

    #
    # Setup
    #

    if analyzer is None:
        analyzer = AnalyzeGas()

    log = Logger(AnalyzeGas.LOG_NAME)
    os.environ[
        'GAS_ESTIMATOR_BACKEND_FUNC'] = 'eth.estimators.gas.binary_gas_search_exact'

    # Blockchain
    economics = StandardTokenEconomics(base_penalty=MIN_ALLOWED_LOCKED - 1,
                                       penalty_history_coefficient=0,
                                       percentage_penalty_coefficient=2,
                                       reward_coefficient=2)
    testerchain, registry = TesterBlockchain.bootstrap_network(
        economics=economics)
    web3 = testerchain.w3

    print("\n********* SIZE OF MAIN CONTRACTS *********")
    MAX_SIZE = 24576
    rows = list()
    for contract_name in NUCYPHER_CONTRACT_NAMES:
        compiled_contract = testerchain._raw_contract_cache[contract_name]

        version = list(compiled_contract).pop()
        # FIXME this value includes constructor code size but should not
        bin_runtime = compiled_contract[version]['evm']['bytecode']['object']
        bin_length_in_bytes = len(bin_runtime) // 2
        percentage = int(100 * bin_length_in_bytes / MAX_SIZE)
        bar = ('*' * (percentage // 2)).ljust(50)
        rows.append(
            (contract_name, bin_length_in_bytes, f'{bar} {percentage}%'))

    headers = ('Contract', 'Size (B)',
               f'% of max allowed contract size ({MAX_SIZE} B)')
    print(tabulate.tabulate(rows, headers=headers, tablefmt="simple"),
          end="\n\n")

    # Accounts
    origin, staker1, staker2, staker3, staker4, alice1, alice2, *everyone_else = testerchain.client.accounts

    ursula_with_stamp = mock_ursula(testerchain, staker1)

    # Contracts
    token_agent = NucypherTokenAgent(registry=registry)
    staking_agent = StakingEscrowAgent(registry=registry)
    policy_agent = PolicyManagerAgent(registry=registry)
    adjudicator_agent = AdjudicatorAgent(registry=registry)

    # Contract Callers
    token_functions = token_agent.contract.functions
    staker_functions = staking_agent.contract.functions
    policy_functions = policy_agent.contract.functions
    adjudicator_functions = adjudicator_agent.contract.functions

    analyzer.start_collection()
    print("********* Estimating Gas *********")

    def transact_and_log(label, function, transaction):
        estimates = function.estimateGas(transaction)
        transaction.update(gas=estimates)
        tx = function.transact(transaction)
        receipt = testerchain.wait_for_receipt(tx)
        log.info(f"{label} = {estimates} | {receipt['gasUsed']}")

    def transact(function, transaction):
        transaction.update(gas=1000000)
        tx = function.transact(transaction)
        testerchain.wait_for_receipt(tx)

    # First deposit ever is the most expensive, make it to remove unusual gas spending
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 10), {'from': origin})
    transact(
        staker_functions.deposit(everyone_else[0], MIN_ALLOWED_LOCKED,
                                 LOCKED_PERIODS), {'from': origin})
    testerchain.time_travel(periods=1)

    #
    # Give Ursula and Alice some coins
    #
    transact_and_log(
        "Transfer tokens",
        token_functions.transfer(staker1, MIN_ALLOWED_LOCKED * 10),
        {'from': origin})
    transact(token_functions.transfer(staker2, MIN_ALLOWED_LOCKED * 10),
             {'from': origin})
    transact(token_functions.transfer(staker3, MIN_ALLOWED_LOCKED * 10),
             {'from': origin})

    #
    # Ursula and Alice give Escrow rights to transfer
    #
    transact_and_log(
        "Approving transfer",
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 7), {'from': staker1})
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 6), {'from': staker2})
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 6), {'from': staker3})

    #
    # Ursula and Alice transfer some tokens to the escrow and lock them
    #
    transact_and_log(
        "Initial deposit tokens, first",
        staker_functions.deposit(staker1, MIN_ALLOWED_LOCKED * 3,
                                 LOCKED_PERIODS), {'from': staker1})
    transact_and_log(
        "Initial deposit tokens, other",
        staker_functions.deposit(staker2, MIN_ALLOWED_LOCKED * 3,
                                 LOCKED_PERIODS), {'from': staker2})
    transact(
        staker_functions.deposit(staker3, MIN_ALLOWED_LOCKED * 3,
                                 LOCKED_PERIODS), {'from': staker3})

    transact(staker_functions.bondWorker(staker1), {'from': staker1})
    transact(staker_functions.bondWorker(staker2), {'from': staker2})
    transact(staker_functions.bondWorker(staker3), {'from': staker3})
    transact(staker_functions.setReStake(False), {'from': staker1})
    transact(staker_functions.setReStake(False), {'from': staker2})
    transact(staker_functions.setWindDown(True), {'from': staker1})
    transact(staker_functions.setWindDown(True), {'from': staker2})
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})
    transact(staker_functions.commitToNextPeriod(), {'from': staker2})

    #
    # Wait 1 period and make a commitment
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Make a commitment, first",
                     staker_functions.commitToNextPeriod(), {'from': staker1})
    transact_and_log("Make a commitment, other",
                     staker_functions.commitToNextPeriod(), {'from': staker2})

    #
    # Wait 1 period and mint tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Minting (1 stake), first", staker_functions.mint(),
                     {'from': staker1})
    transact_and_log("Minting (1 stake), other", staker_functions.mint(),
                     {'from': staker2})
    transact_and_log("Make a commitment again, first",
                     staker_functions.commitToNextPeriod(), {'from': staker1})
    transact_and_log("Make a commitment again, other",
                     staker_functions.commitToNextPeriod(), {'from': staker2})
    transact(staker_functions.commitToNextPeriod(), {'from': staker3})

    #
    # Commit again
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Make a commitment + mint, first",
                     staker_functions.commitToNextPeriod(), {'from': staker1})
    transact_and_log("Make a commitment + mint, other",
                     staker_functions.commitToNextPeriod(), {'from': staker2})

    #
    # Create policy
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 10
    rate = 100
    one_period = economics.hours_per_period * 60 * 60
    value = number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (1 node, 10 periods, pre-committed), first",
        policy_functions.createPolicy(policy_id_1, alice1, end_timestamp,
                                      [staker1]), {
                                          'from': alice1,
                                          'value': value
                                      })
    transact_and_log(
        "Creating policy (1 node, 10 periods, pre-committed), other",
        policy_functions.createPolicy(policy_id_2, alice1, end_timestamp,
                                      [staker1]), {
                                          'from': alice1,
                                          'value': value
                                      })

    #
    # Get locked tokens
    #
    transact_and_log("Getting locked tokens",
                     staker_functions.getLockedTokens(staker1, 0), {})

    #
    # Wait 1 period and withdraw tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Withdraw", staker_functions.withdraw(1),
                     {'from': staker1})

    #
    # Make a commitment with re-stake
    #
    transact(staker_functions.setReStake(True), {'from': staker1})
    transact(staker_functions.setReStake(True), {'from': staker2})

    # Used to remove spending for first call in a period for mint and commitToNextPeriod
    transact(staker_functions.commitToNextPeriod(), {'from': staker3})

    transact_and_log("Make a commitment + mint + re-stake",
                     staker_functions.commitToNextPeriod(), {'from': staker2})
    transact_and_log(
        "Make a commitment + mint + re-stake + first fee + first fee rate",
        staker_functions.commitToNextPeriod(), {'from': staker1})

    transact(staker_functions.setReStake(False), {'from': staker1})
    transact(staker_functions.setReStake(False), {'from': staker2})

    #
    # Wait 2 periods and make a commitment after downtime
    #
    testerchain.time_travel(periods=2)
    transact(staker_functions.commitToNextPeriod(), {'from': staker3})
    transact_and_log("Make a commitment after downtime",
                     staker_functions.commitToNextPeriod(), {'from': staker2})
    transact_and_log("Make a commitment after downtime + updating fee",
                     staker_functions.commitToNextPeriod(), {'from': staker1})

    #
    # Ursula and Alice deposit some tokens to the escrow again
    #
    transact_and_log(
        "Deposit tokens after making a commitment",
        staker_functions.deposit(staker1, MIN_ALLOWED_LOCKED * 2,
                                 LOCKED_PERIODS), {'from': staker1})
    transact(
        staker_functions.deposit(staker2, MIN_ALLOWED_LOCKED * 2,
                                 LOCKED_PERIODS), {'from': staker2})

    #
    # Revoke policy
    #
    transact_and_log("Revoking policy",
                     policy_functions.revokePolicy(policy_id_1),
                     {'from': alice1})

    #
    # Wait 1 period
    #
    testerchain.time_travel(periods=1)

    #
    # Create policy with multiple pre-committed nodes
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_3 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 100
    value = 3 * number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (3 nodes, 100 periods, pre-committed), first",
        policy_functions.createPolicy(policy_id_1, alice1, end_timestamp,
                                      [staker1, staker2, staker3]), {
                                          'from': alice1,
                                          'value': value
                                      })
    transact_and_log(
        "Creating policy (3 nodes, 100 periods, pre-committed), other",
        policy_functions.createPolicy(policy_id_2, alice1, end_timestamp,
                                      [staker1, staker2, staker3]), {
                                          'from': alice1,
                                          'value': value
                                      })
    value = 2 * number_of_periods * rate
    transact_and_log(
        "Creating policy (2 nodes, 100 periods, pre-committed), other",
        policy_functions.createPolicy(policy_id_3, alice1, end_timestamp,
                                      [staker1, staker2]), {
                                          'from': alice1,
                                          'value': value
                                      })

    #
    # Wait 1 period and mint tokens
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': staker3})
    transact_and_log("Last minting + updating fee + updating fee rate",
                     staker_functions.mint(), {'from': staker1})
    transact_and_log("Last minting + first fee + first fee rate",
                     staker_functions.mint(), {'from': staker2})

    #
    # Create policy again without pre-committed nodes
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_3 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 100
    value = number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (1 node, 100 periods)",
        policy_functions.createPolicy(policy_id_1, alice2, end_timestamp,
                                      [staker2]), {
                                          'from': alice1,
                                          'value': value
                                      })
    testerchain.time_travel(periods=1)
    current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (1 node, 100 periods), next period",
        policy_functions.createPolicy(policy_id_2, alice2, end_timestamp,
                                      [staker2]), {
                                          'from': alice1,
                                          'value': value
                                      })
    transact_and_log(
        "Creating policy (1 node, 100 periods), another node",
        policy_functions.createPolicy(policy_id_3, alice2, end_timestamp,
                                      [staker1]), {
                                          'from': alice1,
                                          'value': value
                                      })

    #
    # Mint and revoke policy
    #
    testerchain.time_travel(periods=10)
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})
    transact(staker_functions.commitToNextPeriod(), {'from': staker3})

    testerchain.time_travel(periods=2)
    transact(staker_functions.mint(), {'from': staker3})
    transact_and_log("Last minting after downtime + updating fee",
                     staker_functions.mint(), {'from': staker1})

    testerchain.time_travel(periods=10)
    transact_and_log("Revoking policy after downtime, 1st policy",
                     policy_functions.revokePolicy(policy_id_1),
                     {'from': alice2})
    transact_and_log("Revoking policy after downtime, 2nd policy",
                     policy_functions.revokePolicy(policy_id_2),
                     {'from': alice2})
    transact_and_log("Revoking policy after downtime, 3rd policy",
                     policy_functions.revokePolicy(policy_id_3),
                     {'from': alice2})

    transact(staker_functions.commitToNextPeriod(), {'from': staker1})
    transact(staker_functions.commitToNextPeriod(), {'from': staker2})
    transact(staker_functions.commitToNextPeriod(), {'from': staker3})
    testerchain.time_travel(periods=1)
    #
    # Batch granting
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    current_timestamp = testerchain.w3.eth.getBlock('latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    value = 3 * number_of_periods * rate
    transact_and_log(
        "Creating 2 policies (3 nodes, 100 periods, pre-committed)",
        policy_functions.createPolicies([policy_id_1, policy_id_2], alice1,
                                        end_timestamp,
                                        [staker1, staker2, staker3]), {
                                            'from': alice1,
                                            'value': 2 * value
                                        })

    for index in range(4):
        transact(staker_functions.commitToNextPeriod(), {'from': staker1})
        testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': staker1})

    #
    # Check regular deposit
    #
    transact_and_log(
        "Deposit tokens to new sub-stake",
        staker_functions.deposit(staker1, MIN_ALLOWED_LOCKED, LOCKED_PERIODS),
        {'from': staker1})
    transact_and_log(
        "Deposit tokens using existing sub-stake",
        staker_functions.depositAndIncrease(0, MIN_ALLOWED_LOCKED),
        {'from': staker1})

    #
    # ApproveAndCall
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': staker1})

    transact_and_log(
        "ApproveAndCall",
        token_functions.approveAndCall(staking_agent.contract_address,
                                       MIN_ALLOWED_LOCKED * 2,
                                       web3.toBytes(LOCKED_PERIODS)),
        {'from': staker1})

    #
    # Locking tokens
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})
    transact_and_log(
        "Locking tokens and creating new sub-stake",
        staker_functions.lockAndCreate(MIN_ALLOWED_LOCKED, LOCKED_PERIODS),
        {'from': staker1})
    transact_and_log("Locking tokens using existing sub-stake",
                     staker_functions.lockAndIncrease(0, MIN_ALLOWED_LOCKED),
                     {'from': staker1})

    #
    # Divide stake
    #
    transact_and_log("Divide stake",
                     staker_functions.divideStake(1, MIN_ALLOWED_LOCKED, 2),
                     {'from': staker1})
    transact(staker_functions.divideStake(3, MIN_ALLOWED_LOCKED, 2),
             {'from': staker1})

    #
    # Divide almost finished stake
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})
    testerchain.time_travel(periods=1)
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})

    testerchain.time_travel(periods=1)

    for index in range(18):
        transact(staker_functions.commitToNextPeriod(), {'from': staker1})
        testerchain.time_travel(periods=1)

    transact(
        staker_functions.lockAndCreate(MIN_ALLOWED_LOCKED, LOCKED_PERIODS),
        {'from': staker1})
    deposit = staker_functions.stakerInfo(staker1).call()[0]
    unlocked = deposit - staker_functions.getLockedTokens(staker1, 1).call()
    transact(staker_functions.withdraw(unlocked), {'from': staker1})

    transact_and_log("Prolong stake", staker_functions.prolongStake(0, 20),
                     {'from': staker1})
    transact_and_log("Merge sub-stakes", staker_functions.mergeStake(2, 3),
                     {'from': staker1})

    # Large number of sub-stakes
    number_of_sub_stakes = 24
    transact(token_functions.approve(staking_agent.contract_address, 0),
             {'from': origin})
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * number_of_sub_stakes),
        {'from': origin})
    for i in range(number_of_sub_stakes):
        transact(
            staker_functions.deposit(staker4, MIN_ALLOWED_LOCKED,
                                     LOCKED_PERIODS), {'from': origin})
    transact(staker_functions.bondWorker(staker4), {'from': staker4})
    transact(staker_functions.setWindDown(True), {'from': staker4})

    # Used to remove spending for first call in a period for mint and commitToNextPeriod
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})

    transact_and_log(f"Make a commitment ({number_of_sub_stakes} sub-stakes)",
                     staker_functions.commitToNextPeriod(), {'from': staker4})

    testerchain.time_travel(periods=1)
    transact(staker_functions.commitToNextPeriod(), {'from': staker4})
    testerchain.time_travel(periods=1)

    # Used to remove spending for first call in a period for mint and commitToNextPeriod
    transact(staker_functions.commitToNextPeriod(), {'from': staker1})

    transact_and_log(
        f"Make a commitment + mint + re-stake ({number_of_sub_stakes} sub-stakes)",
        staker_functions.commitToNextPeriod(), {'from': staker4})

    print("********* Estimates of migration *********")

    registry = InMemoryContractRegistry()
    deployer_power = TransactingPower(signer=Web3Signer(testerchain.client),
                                      account=testerchain.etherbase_account)

    def deploy_contract(contract_name, *args, **kwargs):
        return testerchain.deploy_contract(deployer_power, registry,
                                           contract_name, *args, **kwargs)

    token_economics = StandardTokenEconomics(
        genesis_hours_per_period=StandardTokenEconomics.
        _default_hours_per_period,
        hours_per_period=2 * StandardTokenEconomics._default_hours_per_period)

    token, _ = deploy_contract(
        'NuCypherToken',
        _totalSupplyOfTokens=token_economics.erc20_total_supply)
    # Deploy Adjudicator mock
    adjudicator, _ = deploy_contract('AdjudicatorForStakingEscrowMock',
                                     token_economics.reward_coefficient)

    # Deploy old StakingEscrow contract
    deploy_args = token_economics.staking_deployment_parameters
    deploy_args = (deploy_args[0], *deploy_args[2:])
    escrow_old_library, _ = deploy_contract(
        'StakingEscrowOld',
        token.address,
        *deploy_args,
        False  # testContract
    )
    escrow_dispatcher, _ = deploy_contract('Dispatcher',
                                           escrow_old_library.address)

    escrow = testerchain.client.get_contract(abi=escrow_old_library.abi,
                                             address=escrow_dispatcher.address,
                                             ContractFactoryClass=Contract)

    # Deploy old PolicyManager contract
    policy_manager_old_library, _ = deploy_contract(
        contract_name='PolicyManagerOld', _escrow=escrow.address)
    policy_manager_dispatcher, _ = deploy_contract(
        'Dispatcher', policy_manager_old_library.address)

    policy_manager = testerchain.client.get_contract(
        abi=policy_manager_old_library.abi,
        address=policy_manager_dispatcher.address,
        ContractFactoryClass=Contract)

    tx = adjudicator.functions.setStakingEscrow(escrow.address).transact()
    testerchain.wait_for_receipt(tx)
    tx = escrow.functions.setPolicyManager(policy_manager.address).transact()
    testerchain.wait_for_receipt(tx)
    tx = escrow.functions.setAdjudicator(adjudicator.address).transact()
    testerchain.wait_for_receipt(tx)

    # Initialize Escrow contract
    tx = token.functions.approve(
        escrow.address, token_economics.erc20_reward_supply).transact()
    testerchain.wait_for_receipt(tx)
    tx = escrow.functions.initialize(token_economics.erc20_reward_supply,
                                     testerchain.etherbase_account).transact()
    testerchain.wait_for_receipt(tx)

    # Prepare stakers
    stakers = (staker1, staker2, staker3, staker4)
    for staker in stakers:
        max_stake_size = token_economics.maximum_allowed_locked
        tx = token.functions.transfer(staker, max_stake_size).transact()
        testerchain.wait_for_receipt(tx)
        tx = token.functions.approve(escrow.address,
                                     max_stake_size).transact({'from': staker})
        testerchain.wait_for_receipt(tx)

    sub_stakes_1 = 2
    duration = token_economics.minimum_locked_periods
    stake_size = token_economics.minimum_allowed_locked
    for staker in (staker1, staker3):
        for i in range(1, sub_stakes_1 + 1):
            tx = escrow.functions.deposit(staker, stake_size, duration *
                                          i).transact({'from': staker})
            testerchain.wait_for_receipt(tx)
    sub_stakes_2 = 24
    for staker in (staker2, staker4):
        for i in range(1, sub_stakes_2 + 1):
            tx = escrow.functions.deposit(staker, stake_size, duration *
                                          i).transact({'from': staker})
            testerchain.wait_for_receipt(tx)

    for staker in stakers:
        tx = escrow.functions.bondWorker(staker).transact({'from': staker})
        testerchain.wait_for_receipt(tx)

    for i in range(duration):
        tx = escrow.functions.commitToNextPeriod().transact({'from': staker1})
        testerchain.wait_for_receipt(tx)
        tx = escrow.functions.commitToNextPeriod().transact({'from': staker3})
        testerchain.wait_for_receipt(tx)
        if i % 2 == 0:
            tx = escrow.functions.commitToNextPeriod().transact(
                {'from': staker2})
            testerchain.wait_for_receipt(tx)
            tx = escrow.functions.commitToNextPeriod().transact(
                {'from': staker4})
            testerchain.wait_for_receipt(tx)
        testerchain.time_travel(
            periods=1, periods_base=token_economics.genesis_seconds_per_period)

    ##########
    # Deploy new version of contracts
    ##########
    deploy_args = token_economics.staking_deployment_parameters
    escrow_library, _ = deploy_contract('StakingEscrow', token.address,
                                        policy_manager.address,
                                        adjudicator.address, NULL_ADDRESS,
                                        *deploy_args)
    escrow = testerchain.client.get_contract(abi=escrow_library.abi,
                                             address=escrow_dispatcher.address,
                                             ContractFactoryClass=Contract)

    policy_manager_library, _ = deploy_contract(
        contract_name='PolicyManager',
        _escrowDispatcher=escrow.address,
        _escrowImplementation=escrow_library.address)

    tx = escrow_dispatcher.functions.upgrade(escrow_library.address).transact()
    testerchain.wait_for_receipt(tx)
    tx = policy_manager_dispatcher.functions.upgrade(
        policy_manager_library.address).transact()
    testerchain.wait_for_receipt(tx)

    for staker in (staker1, staker2):
        downtime_length = escrow.functions.getPastDowntimeLength(staker).call()
        sub_stakes_length = escrow.functions.getSubStakesLength(staker).call()
        transact_and_log(
            f"Migrate with {sub_stakes_length} sub-stakes and {downtime_length} downtimes",
            escrow.functions.migrate(staker), {'from': staker})
        downtime_length = escrow.functions.getPastDowntimeLength(staker).call()
        sub_stakes_length = escrow.functions.getSubStakesLength(staker).call()
        transact_and_log(
            f"Commit after migration with {sub_stakes_length} sub-stakes and {downtime_length} downtimes",
            escrow.functions.commitToNextPeriod(), {'from': staker})

    for staker in (staker3, staker4):
        downtime_length = escrow.functions.getPastDowntimeLength(staker).call()
        sub_stakes_length = escrow.functions.getSubStakesLength(staker).call()
        transact_and_log(
            f"Commit together with migration with {sub_stakes_length} sub-stakes and {downtime_length} downtimes",
            escrow.functions.commitToNextPeriod(), {'from': staker})

    transact_and_log(f"Dummy migrate call", escrow.functions.migrate(staker1),
                     {'from': staker1})

    print("********* All Done! *********")
コード例 #21
0
class TesterBlockchain(BlockchainDeployerInterface):
    """
    Blockchain subclass with additional test utility methods and options.
    """

    __test__ = False  # prohibit pytest from collecting this object as a test

    # Solidity
    SOURCES: List[SourceBundle] = [
        SourceBundle(base_path=SOLIDITY_SOURCE_ROOT,
                     other_paths=(TEST_SOLIDITY_SOURCE_ROOT, ))
    ]

    # Web3
    GAS_STRATEGIES = {
        **BlockchainDeployerInterface.GAS_STRATEGIES, 'free':
        free_gas_price_strategy
    }
    PROVIDER_URI = PYEVM_DEV_URI
    DEFAULT_GAS_STRATEGY = 'free'

    # Reserved addresses
    _ETHERBASE = 0
    _ALICE = 1
    _BOB = 2
    _FIRST_STAKER = 5
    _FIRST_URSULA = _FIRST_STAKER + NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS

    # Internal
    __STAKERS_RANGE = range(NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS)
    __WORKERS_RANGE = range(NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)
    __ACCOUNT_CACHE = list()

    # Defaults
    DEFAULT_ECONOMICS = StandardTokenEconomics()

    def __init__(self,
                 test_accounts: int = NUMBER_OF_ETH_TEST_ACCOUNTS,
                 poa: bool = True,
                 light: bool = False,
                 eth_airdrop: bool = False,
                 free_transactions: bool = False,
                 compile_now: bool = True,
                 *args,
                 **kwargs):

        self.free_transactions = free_transactions

        EXPECTED_CONFIRMATION_TIME_IN_SECONDS[
            'free'] = 5  # Just some upper-limit

        super().__init__(provider_uri=self.PROVIDER_URI,
                         poa=poa,
                         light=light,
                         *args,
                         **kwargs)

        self.log = Logger("test-blockchain")
        self.connect(compile_now=compile_now)

        # Generate additional ethereum accounts for testing
        population = test_accounts
        enough_accounts = len(self.client.accounts) >= population
        if not enough_accounts:
            accounts_to_make = population - len(self.client.accounts)
            self.__generate_insecure_unlocked_accounts(
                quantity=accounts_to_make)
            assert test_accounts == len(self.w3.eth.accounts)

        if eth_airdrop is True:  # ETH for everyone!
            self.ether_airdrop(amount=DEVELOPMENT_ETH_AIRDROP_AMOUNT)

    def attach_middleware(self):
        if self.free_transactions:
            self.w3.eth.setGasPriceStrategy(free_gas_price_strategy)

    def __generate_insecure_unlocked_accounts(self,
                                              quantity: int) -> List[str]:

        #
        # Sanity Check - Only PyEVM can be used.
        #

        # Detect provider platform
        client_version = self.w3.clientVersion

        if 'Geth' in client_version:
            raise RuntimeError("WARNING: Geth providers are not implemented.")
        elif "Parity" in client_version:
            raise RuntimeError(
                "WARNING: Parity providers are not implemented.")

        addresses = list()
        for _ in range(quantity):
            address = self.provider.ethereum_tester.add_account(
                '0x' + os.urandom(32).hex())
            addresses.append(address)
            self.__ACCOUNT_CACHE.append(address)
            self.log.info('Generated new insecure account {}'.format(address))
        return addresses

    def ether_airdrop(self, amount: int) -> List[str]:
        """Airdrops ether from creator address to all other addresses!"""

        coinbase, *addresses = self.w3.eth.accounts

        tx_hashes = list()
        for address in addresses:
            tx = {'to': address, 'from': coinbase, 'value': amount}
            txhash = self.w3.eth.sendTransaction(tx)

            _receipt = self.wait_for_receipt(txhash)
            tx_hashes.append(txhash)
            eth_amount = Web3().fromWei(amount, 'ether')
            self.log.info("Airdropped {} ETH {} -> {}".format(
                eth_amount, tx['from'], tx['to']))

        return tx_hashes

    def time_travel(self,
                    hours: int = None,
                    seconds: int = None,
                    periods: int = None):
        """
        Wait the specified number of wait_hours by comparing
        block timestamps and mines a single block.
        """

        more_than_one_arg = sum(map(bool, (hours, seconds, periods))) > 1
        if more_than_one_arg:
            raise ValueError(
                "Specify hours, seconds, or periods, not a combination")

        if periods:
            duration = self.DEFAULT_ECONOMICS.seconds_per_period * periods
            base = self.DEFAULT_ECONOMICS.seconds_per_period
        elif hours:
            duration = hours * (60 * 60)
            base = 60 * 60
        elif seconds:
            duration = seconds
            base = 1
        else:
            raise ValueError("Specify either hours, seconds, or periods.")

        now = self.w3.eth.getBlock('latest').timestamp
        end_timestamp = ((now + duration) // base) * base

        self.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp)
        self.w3.eth.web3.testing.mine(1)

        delta = maya.timedelta(seconds=end_timestamp - now)
        self.log.info(
            f"Time traveled {delta} "
            f"| period {epoch_to_period(epoch=end_timestamp, seconds_per_period=self.DEFAULT_ECONOMICS.seconds_per_period)} "
            f"| epoch {end_timestamp}")

    @classmethod
    def bootstrap_network(
        cls,
        registry: Optional[BaseContractRegistry] = None,
        economics: BaseEconomics = None
    ) -> Tuple['TesterBlockchain', 'InMemoryContractRegistry']:
        """For use with metric testing scripts"""

        if registry is None:
            registry = InMemoryContractRegistry()
        testerchain = cls()
        if not BlockchainInterfaceFactory.is_interface_initialized(
                provider_uri=testerchain.provider_uri):
            BlockchainInterfaceFactory.register_interface(
                interface=testerchain)

        origin = testerchain.client.etherbase
        admin = ContractAdministrator(deployer_address=origin,
                                      registry=registry,
                                      signer=Web3Signer(testerchain.client),
                                      economics=economics
                                      or cls.DEFAULT_ECONOMICS)

        gas_limit = None  # TODO: Gas management - #842
        for deployer_class in admin.primary_deployer_classes:
            if deployer_class is StakingEscrowDeployer:
                admin.deploy_contract(
                    contract_name=deployer_class.contract_name,
                    gas_limit=gas_limit,
                    deployment_mode=INIT)
            else:
                admin.deploy_contract(
                    contract_name=deployer_class.contract_name,
                    gas_limit=gas_limit)
        admin.deploy_contract(
            contract_name=StakingEscrowDeployer.contract_name,
            gas_limit=gas_limit)
        return testerchain, registry

    @property
    def etherbase_account(self):
        return self.client.accounts[self._ETHERBASE]

    @property
    def alice_account(self):
        return self.client.accounts[self._ALICE]

    @property
    def bob_account(self):
        return self.client.accounts[self._BOB]

    def ursula_account(self, index):
        if index not in self.__WORKERS_RANGE:
            raise ValueError(
                f"Ursula index must be lower than {NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS}"
            )
        return self.client.accounts[index + self._FIRST_URSULA]

    def staker_account(self, index):
        if index not in self.__STAKERS_RANGE:
            raise ValueError(
                f"Staker index must be lower than {NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS}"
            )
        return self.client.accounts[index + self._FIRST_STAKER]

    @property
    def ursulas_accounts(self):
        return list(self.ursula_account(i) for i in self.__WORKERS_RANGE)

    @property
    def stakers_accounts(self):
        return list(self.staker_account(i) for i in self.__STAKERS_RANGE)

    @property
    def unassigned_accounts(self):
        special_accounts = [
            self.etherbase_account, self.alice_account, self.bob_account
        ]
        assigned_accounts = set(self.stakers_accounts + self.ursulas_accounts +
                                special_accounts)
        accounts = set(self.client.accounts)
        return list(accounts.difference(assigned_accounts))

    def wait_for_receipt(self,
                         txhash: Union[bytes, str, HexBytes],
                         timeout: int = None) -> dict:
        """Wait for a transaction receipt and return it"""
        timeout = timeout or self.TIMEOUT
        result = self.client.wait_for_receipt(transaction_hash=txhash,
                                              timeout=timeout)
        if result.status == 0:
            raise TransactionFailed()
        return result

    def get_block_number(self) -> int:
        return self.client.block_number

    def read_storage_slot(self, address, slot):
        # https://github.com/ethereum/web3.py/issues/1490
        address = to_canonical_address(address)
        return self.client.w3.provider.ethereum_tester.backend.chain.get_vm(
        ).state.get_storage(address, slot)
コード例 #22
0
ファイル: estimate_gas.py プロジェクト: xbee/nucypher
def estimate_gas(analyzer: AnalyzeGas = None) -> None:
    """
    Execute a linear sequence of NyCypher transactions mimicking
    post-deployment usage on a local PyEVM blockchain;
    Record the resulting estimated transaction gas expenditure.

    Note: The function calls below are *order dependant*
    """

    #
    # Setup
    #

    if analyzer is None:
        analyzer = AnalyzeGas()

    log = Logger(AnalyzeGas.LOG_NAME)
    os.environ[
        'GAS_ESTIMATOR_BACKEND_FUNC'] = 'eth.estimators.gas.binary_gas_search_exact'

    # Blockchain
    economics = StandardTokenEconomics(base_penalty=MIN_ALLOWED_LOCKED - 1,
                                       penalty_history_coefficient=0,
                                       percentage_penalty_coefficient=2,
                                       reward_coefficient=2)
    testerchain, registry = TesterBlockchain.bootstrap_network(
        economics=economics)
    web3 = testerchain.w3

    print("\n********* SIZE OF MAIN CONTRACTS *********")
    MAX_SIZE = 24576
    rows = list()
    for contract_name in NUCYPHER_CONTRACT_NAMES:
        compiled_contract = testerchain._raw_contract_cache[contract_name]

        version = list(compiled_contract).pop()
        bin_runtime = compiled_contract[version]['bin-runtime']
        bin_length_in_bytes = len(bin_runtime) // 2
        percentage = int(100 * bin_length_in_bytes / MAX_SIZE)
        bar = ('*' * (percentage // 2)).ljust(50)
        rows.append(
            (contract_name, bin_length_in_bytes, f'{bar} {percentage}%'))

    headers = ('Contract', 'Size (B)',
               f'% of max allowed contract size ({MAX_SIZE} B)')
    print(tabulate.tabulate(rows, headers=headers, tablefmt="simple"),
          end="\n\n")

    # Accounts
    origin, ursula1, ursula2, ursula3, alice1, alice2, *everyone_else = testerchain.client.accounts

    ursula_with_stamp = mock_ursula(testerchain, ursula1)

    # Contracts
    token_agent = NucypherTokenAgent(registry=registry)
    staking_agent = StakingEscrowAgent(registry=registry)
    policy_agent = PolicyManagerAgent(registry=registry)
    adjudicator_agent = AdjudicatorAgent(registry=registry)

    # Contract Callers
    token_functions = token_agent.contract.functions
    staker_functions = staking_agent.contract.functions
    policy_functions = policy_agent.contract.functions
    adjudicator_functions = adjudicator_agent.contract.functions

    analyzer.start_collection()
    print("********* Estimating Gas *********")

    def transact_and_log(label, function, transaction):
        estimates = function.estimateGas(transaction)
        transaction.update(gas=estimates)
        tx = function.transact(transaction)
        receipt = testerchain.wait_for_receipt(tx)
        log.info(f"{label} = {estimates} | {receipt['gasUsed']}")

    def transact(function, transaction):
        transaction.update(gas=1000000)
        tx = function.transact(transaction)
        testerchain.wait_for_receipt(tx)

    #
    # Give Ursula and Alice some coins
    #
    transact_and_log(
        "Transfer tokens",
        token_functions.transfer(ursula1, MIN_ALLOWED_LOCKED * 10),
        {'from': origin})
    transact(token_functions.transfer(ursula2, MIN_ALLOWED_LOCKED * 10),
             {'from': origin})
    transact(token_functions.transfer(ursula3, MIN_ALLOWED_LOCKED * 10),
             {'from': origin})

    #
    # Ursula and Alice give Escrow rights to transfer
    #
    transact_and_log(
        "Approving transfer",
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 6), {'from': ursula1})
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 6), {'from': ursula2})
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 6), {'from': ursula3})

    #
    # Batch deposit tokens
    #
    transact(
        token_functions.approve(staking_agent.contract_address,
                                MIN_ALLOWED_LOCKED * 10), {'from': origin})
    transact_and_log(
        "Batch deposit tokens for 5 owners x 2 sub-stakes",
        staker_functions.batchDeposit(everyone_else[0:5], [2] * 5,
                                      [MIN_ALLOWED_LOCKED] * 10,
                                      [MIN_LOCKED_PERIODS] * 10),
        {'from': origin})

    #
    # Ursula and Alice transfer some tokens to the escrow and lock them
    #
    transact_and_log(
        "Initial deposit tokens, first",
        staker_functions.deposit(MIN_ALLOWED_LOCKED * 3, MIN_LOCKED_PERIODS),
        {'from': ursula1})
    transact_and_log(
        "Initial deposit tokens, other",
        staker_functions.deposit(MIN_ALLOWED_LOCKED * 3, MIN_LOCKED_PERIODS),
        {'from': ursula2})
    transact(
        staker_functions.deposit(MIN_ALLOWED_LOCKED * 3, MIN_LOCKED_PERIODS),
        {'from': ursula3})

    transact(staker_functions.setWorker(ursula1), {'from': ursula1})
    transact(staker_functions.setWorker(ursula2), {'from': ursula2})
    transact(staker_functions.setWorker(ursula3), {'from': ursula3})
    transact(staker_functions.setReStake(False), {'from': ursula1})
    transact(staker_functions.setReStake(False), {'from': ursula2})
    transact(staker_functions.setWindDown(True), {'from': ursula1})
    transact(staker_functions.setWindDown(True), {'from': ursula2})
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    transact(staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Wait 1 period and confirm activity
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Confirm activity, first",
                     staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity, other",
                     staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Wait 1 period and mint tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Mining (1 stake), first", staker_functions.mint(),
                     {'from': ursula1})
    transact_and_log("Mining (1 stake), other", staker_functions.mint(),
                     {'from': ursula2})
    transact_and_log("Confirm activity again, first",
                     staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity again, other",
                     staker_functions.confirmActivity(), {'from': ursula2})
    transact(staker_functions.confirmActivity(), {'from': ursula3})

    #
    # Confirm again
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Confirm activity + mint, first",
                     staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log("Confirm activity + mint, other",
                     staker_functions.confirmActivity(), {'from': ursula2})

    #
    # Create policy
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 10
    rate = 100
    one_period = economics.hours_per_period * 60 * 60
    value = number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock(
        block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (1 node, 10 periods, pre-confirmed), first",
        policy_functions.createPolicy(policy_id_1, alice1, end_timestamp,
                                      [ursula1]), {
                                          'from': alice1,
                                          'value': value
                                      })
    transact_and_log(
        "Creating policy (1 node, 10 periods, pre-confirmed), other",
        policy_functions.createPolicy(policy_id_2, alice1, end_timestamp,
                                      [ursula1]), {
                                          'from': alice1,
                                          'value': value
                                      })

    #
    # Get locked tokens
    #
    transact_and_log("Getting locked tokens",
                     staker_functions.getLockedTokens(ursula1, 0), {})

    #
    # Wait 1 period and withdraw tokens
    #
    testerchain.time_travel(periods=1)
    transact_and_log("Withdraw", staker_functions.withdraw(1),
                     {'from': ursula1})

    #
    # Confirm activity with re-stake
    #
    transact(staker_functions.setReStake(True), {'from': ursula1})
    transact(staker_functions.setReStake(True), {'from': ursula2})

    # Used to remove spending for first call in a day for mint and confirmActivity
    transact(staker_functions.confirmActivity(), {'from': ursula3})

    transact_and_log("Confirm activity + mint + re-stake",
                     staker_functions.confirmActivity(), {'from': ursula2})
    transact_and_log(
        "Confirm activity + mint + re-stake + first reward + first reward rate",
        staker_functions.confirmActivity(), {'from': ursula1})

    transact(staker_functions.setReStake(False), {'from': ursula1})
    transact(staker_functions.setReStake(False), {'from': ursula2})

    #
    # Wait 2 periods and confirm activity after downtime
    #
    testerchain.time_travel(periods=2)
    transact(staker_functions.confirmActivity(), {'from': ursula3})
    transact_and_log("Confirm activity after downtime",
                     staker_functions.confirmActivity(), {'from': ursula2})
    transact_and_log("Confirm activity after downtime + updating reward",
                     staker_functions.confirmActivity(), {'from': ursula1})

    #
    # Ursula and Alice deposit some tokens to the escrow again
    #
    transact_and_log(
        "Deposit tokens after confirming activity",
        staker_functions.deposit(MIN_ALLOWED_LOCKED * 2, MIN_LOCKED_PERIODS),
        {'from': ursula1})
    transact(
        staker_functions.deposit(MIN_ALLOWED_LOCKED * 2, MIN_LOCKED_PERIODS),
        {'from': ursula2})

    #
    # Revoke policy
    #
    transact_and_log("Revoking policy",
                     policy_functions.revokePolicy(policy_id_1),
                     {'from': alice1})

    #
    # Wait 1 period
    #
    testerchain.time_travel(periods=1)

    #
    # Create policy with multiple pre-confirmed nodes
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_3 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 100
    value = 3 * number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock(
        block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (3 nodes, 100 periods, pre-confirmed), first",
        policy_functions.createPolicy(policy_id_1, alice1, end_timestamp,
                                      [ursula1, ursula2, ursula3]), {
                                          'from': alice1,
                                          'value': value
                                      })
    transact_and_log(
        "Creating policy (3 nodes, 100 periods, pre-confirmed), other",
        policy_functions.createPolicy(policy_id_2, alice1, end_timestamp,
                                      [ursula1, ursula2, ursula3]), {
                                          'from': alice1,
                                          'value': value
                                      })
    value = 2 * number_of_periods * rate
    transact_and_log(
        "Creating policy (2 nodes, 100 periods, pre-confirmed), other",
        policy_functions.createPolicy(policy_id_3, alice1, end_timestamp,
                                      [ursula1, ursula2]), {
                                          'from': alice1,
                                          'value': value
                                      })

    #
    # Wait 1 period and mint tokens
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': ursula3})
    transact_and_log("Last mining + updating reward + updating reward rate",
                     staker_functions.mint(), {'from': ursula1})
    transact_and_log("Last mining + first reward + first reward rate",
                     staker_functions.mint(), {'from': ursula2})

    #
    # Create policy again without pre-confirmed nodes
    #
    policy_id_1 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_2 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    policy_id_3 = os.urandom(int(Policy.POLICY_ID_LENGTH))
    number_of_periods = 100
    value = number_of_periods * rate
    current_timestamp = testerchain.w3.eth.getBlock(
        block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (1 node, 100 periods)",
        policy_functions.createPolicy(policy_id_1, alice2, end_timestamp,
                                      [ursula2]), {
                                          'from': alice1,
                                          'value': value
                                      })
    testerchain.time_travel(periods=1)
    current_timestamp = testerchain.w3.eth.getBlock(
        block_identifier='latest').timestamp
    end_timestamp = current_timestamp + (number_of_periods - 1) * one_period
    transact_and_log(
        "Creating policy (1 node, 100 periods), next period",
        policy_functions.createPolicy(policy_id_2, alice2, end_timestamp,
                                      [ursula2]), {
                                          'from': alice1,
                                          'value': value
                                      })
    transact_and_log(
        "Creating policy (1 node, 100 periods), another node",
        policy_functions.createPolicy(policy_id_3, alice2, end_timestamp,
                                      [ursula1]), {
                                          'from': alice1,
                                          'value': value
                                      })

    #
    # Mine and revoke policy
    #
    testerchain.time_travel(periods=10)
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    transact(staker_functions.confirmActivity(), {'from': ursula3})

    testerchain.time_travel(periods=2)
    transact(staker_functions.mint(), {'from': ursula3})
    transact_and_log("Last mining after downtime + updating reward",
                     staker_functions.mint(), {'from': ursula1})

    testerchain.time_travel(periods=10)
    transact_and_log("Revoking policy after downtime, 1st policy",
                     policy_functions.revokePolicy(policy_id_1),
                     {'from': alice2})
    transact_and_log("Revoking policy after downtime, 2nd policy",
                     policy_functions.revokePolicy(policy_id_2),
                     {'from': alice2})
    transact_and_log("Revoking policy after downtime, 3rd policy",
                     policy_functions.revokePolicy(policy_id_3),
                     {'from': alice2})

    for index in range(5):
        transact(staker_functions.confirmActivity(), {'from': ursula1})
        testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': ursula1})

    #
    # Check regular deposit
    #
    transact_and_log(
        "Deposit tokens",
        staker_functions.deposit(MIN_ALLOWED_LOCKED, MIN_LOCKED_PERIODS),
        {'from': ursula1})

    #
    # ApproveAndCall
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.mint(), {'from': ursula1})

    transact_and_log(
        "ApproveAndCall",
        token_functions.approveAndCall(staking_agent.contract_address,
                                       MIN_ALLOWED_LOCKED * 2,
                                       web3.toBytes(MIN_LOCKED_PERIODS)),
        {'from': ursula1})

    #
    # Locking tokens
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    transact_and_log(
        "Locking tokens",
        staker_functions.lock(MIN_ALLOWED_LOCKED, MIN_LOCKED_PERIODS),
        {'from': ursula1})

    #
    # Divide stake
    #
    transact_and_log("Divide stake",
                     staker_functions.divideStake(1, MIN_ALLOWED_LOCKED, 2),
                     {'from': ursula1})
    transact(staker_functions.divideStake(3, MIN_ALLOWED_LOCKED, 2),
             {'from': ursula1})

    #
    # Divide almost finished stake
    #
    testerchain.time_travel(periods=1)
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    testerchain.time_travel(periods=1)
    transact(staker_functions.confirmActivity(), {'from': ursula1})

    #
    # Slashing tests
    #
    transact(staker_functions.confirmActivity(), {'from': ursula1})
    testerchain.time_travel(periods=1)

    #
    # Slashing
    #
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log("Slash just value",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    deposit = staker_functions.stakerInfo(ursula1).call()[0]
    unlocked = deposit - staker_functions.getLockedTokens(ursula1, 0).call()
    transact(staker_functions.withdraw(unlocked), {'from': ursula1})

    sub_stakes_length = str(
        staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log(
        "Slashing one sub stake and saving old one (" +
        sub_stakes_length + " sub stakes), 1st",
        adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    sub_stakes_length = str(
        staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log(
        "Slashing one sub stake and saving old one (" +
        sub_stakes_length + " sub stakes), 2nd",
        adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    sub_stakes_length = str(
        staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log(
        "Slashing one sub stake and saving old one (" +
        sub_stakes_length + " sub stakes), 3rd",
        adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    sub_stakes_length = str(
        staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log(
        "Slashing two sub stakes and saving old one (" +
        sub_stakes_length + " sub stakes)",
        adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    for index in range(18):
        transact(staker_functions.confirmActivity(), {'from': ursula1})
        testerchain.time_travel(periods=1)

    transact(staker_functions.lock(MIN_ALLOWED_LOCKED, MIN_LOCKED_PERIODS),
             {'from': ursula1})
    deposit = staker_functions.stakerInfo(ursula1).call()[0]
    unlocked = deposit - staker_functions.getLockedTokens(ursula1, 1).call()
    transact(staker_functions.withdraw(unlocked), {'from': ursula1})

    sub_stakes_length = str(
        staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log(
        "Slashing two sub stakes, shortest and new one (" +
        sub_stakes_length + " sub stakes)",
        adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    sub_stakes_length = str(
        staker_functions.getSubStakesLength(ursula1).call())
    slashing_args = generate_args_for_slashing(ursula_with_stamp)
    transact_and_log(
        "Slashing three sub stakes, two shortest and new one (" +
        sub_stakes_length + " sub stakes)",
        adjudicator_functions.evaluateCFrag(*slashing_args), {'from': alice1})

    slashing_args = generate_args_for_slashing(ursula_with_stamp,
                                               corrupt_cfrag=False)
    transact_and_log("Evaluating correct CFrag",
                     adjudicator_functions.evaluateCFrag(*slashing_args),
                     {'from': alice1})

    transact_and_log("Prolong stake", staker_functions.prolongStake(0, 20),
                     {'from': ursula1})

    print("********* All Done! *********")
コード例 #23
0
def test_crawler_learn_about_nodes(new_influx_db, get_agent, get_economics,
                                   tempfile_path):
    mock_influxdb_client = new_influx_db.return_value
    mock_influxdb_client.write_points.return_value = True

    # TODO: issue with use of `agent.blockchain` causes spec=StakingEscrowAgent not to be specified in MagicMock
    # Get the following - AttributeError: Mock object has no attribute 'blockchain'
    staking_agent = MagicMock(autospec=True)
    contract_agency = MockContractAgency(staking_agent=staking_agent)
    get_agent.side_effect = contract_agency.get_agent

    token_economics = StandardTokenEconomics()
    get_economics.return_value = token_economics

    crawler = create_crawler(node_db_filepath=tempfile_path)
    node_db_client = CrawlerStorageClient(db_filepath=tempfile_path)
    try:
        crawler.start()
        assert crawler.is_running

        for i in range(0, 5):
            random_node = create_random_mock_node(generate_certificate=True)
            crawler.remember_node(node=random_node, record_fleet_state=True)
            known_nodes = node_db_client.get_known_nodes_metadata()
            assert len(known_nodes) > i
            assert random_node.checksum_address in known_nodes

            previous_states = node_db_client.get_previous_states_metadata()
            assert len(previous_states) > i

            # configure staking agent for blockchain calls
            tokens = NU(int(15000 + i * 2500), 'NU').to_nunits()
            current_period = datetime_to_period(
                maya.now(), token_economics.seconds_per_period)
            initial_period = current_period - i
            terminal_period = current_period + (i + 50)
            last_active_period = current_period - i
            staking_agent.get_worker_from_staker.side_effect = \
                lambda staker_address: crawler.node_storage.get(federated_only=False,
                                                                checksum_address=staker_address).worker_address

            configure_mock_staking_agent(staking_agent=staking_agent,
                                         tokens=tokens,
                                         current_period=current_period,
                                         initial_period=initial_period,
                                         terminal_period=terminal_period,
                                         last_active_period=last_active_period)

            # run crawler callable
            crawler._learn_about_nodes()

            # ensure data written to influx table
            mock_influxdb_client.write_points.assert_called_once()

            # expected db row added
            write_points_call_args_list = mock_influxdb_client.write_points.call_args_list
            influx_db_line_protocol_statement = str(
                write_points_call_args_list[0][0])

            expected_arguments = [
                f'staker_address={random_node.checksum_address}',
                f'worker_address="{random_node.worker_address}"',
                f'stake={float(NU.from_nunits(tokens).to_tokens())}',
                f'locked_stake={float(NU.from_nunits(tokens).to_tokens())}',
                f'current_period={current_period}i',
                f'last_confirmed_period={last_active_period}i',
                f'work_orders={len(random_node.work_orders())}i'
            ]
            for arg in expected_arguments:
                assert arg in influx_db_line_protocol_statement, \
                    f"{arg} in {influx_db_line_protocol_statement} for iteration {i}"

            mock_influxdb_client.reset_mock()
    finally:
        crawler.stop()

    mock_influxdb_client.close.assert_called_once()
    assert not crawler.is_running
コード例 #24
0
def test_exact_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2

    K2 - Staking coefficient
    K1 - Locked periods coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    where allLockedPeriods == min(T, T1)
    """

    #
    # Expected Output
    #

    # Supply
    expected_total_supply = 3885390081777926911255691439
    expected_supply_ratio = Decimal('3.885390081777926911255691439')
    expected_initial_supply = 1000000000000000000000000000

    # Reward
    expected_reward_supply = 2885390081777926911255691439
    reward_saturation = 1

    # Staking
    halving = 2
    multiplier = 0.5
    expected_locked_periods_coefficient = 365
    expected_staking_coefficient = 768812

    assert expected_locked_periods_coefficient * halving == round(expected_staking_coefficient * log(2) * multiplier / 365)

    #
    # Sanity
    #

    # Sanity check ratio accuracy
    expected_scaled_ratio = str(expected_supply_ratio).replace('.', '')
    assert str(expected_total_supply) == expected_scaled_ratio

    # Sanity check denomination size
    expected_scale = 28
    assert len(str(expected_total_supply)) == expected_scale
    assert len(str(expected_initial_supply)) == expected_scale
    assert len(str(expected_reward_supply)) == expected_scale

    # Use same precision as economics class
    with localcontext() as ctx:
        ctx.prec = StandardTokenEconomics._precision

        # Sanity check expected testing outputs
        assert Decimal(expected_total_supply) / expected_initial_supply == expected_supply_ratio
        assert expected_reward_supply == expected_total_supply - expected_initial_supply
        assert reward_saturation * 365 * multiplier == expected_locked_periods_coefficient * (1 - multiplier)
        assert int(365 ** 2 * reward_saturation * halving / log(2) / (1-multiplier)) == expected_staking_coefficient

    # After sanity checking, assemble expected test deployment parameters
    expected_deployment_parameters = (24,       # Hours in single period
                                      768812,   # Staking coefficient (k2)
                                      365,      # Locked periods coefficient (k1)
                                      365,      # Max periods that will be additionally rewarded (awarded_periods)
                                      30,       # Min amount of periods during which tokens can be locked
                                      15000000000000000000000,    # min locked NuNits
                                      4000000000000000000000000,  # max locked NuNits
                                      2)        # Min worker periods
    #
    # Token Economics
    #

    # Check creation
    e = StandardTokenEconomics()

    with localcontext() as ctx:
        ctx.prec = StandardTokenEconomics._precision

        # Check that total_supply calculated correctly
        assert Decimal(e.erc20_total_supply) / e.initial_supply == expected_supply_ratio
        assert e.erc20_total_supply == expected_total_supply

        # Check reward rates
        initial_rate = Decimal((e.erc20_total_supply - e.initial_supply) * (e.locked_periods_coefficient + 365) / e.staking_coefficient)
        assert initial_rate == Decimal((e.initial_inflation * e.initial_supply) / 365)

        initial_rate_small = (e.erc20_total_supply - e.initial_supply) * e.locked_periods_coefficient / e.staking_coefficient
        assert Decimal(initial_rate_small) == Decimal(initial_rate / 2)

        # Check reward supply
        assert Decimal(LOG2 / (e.token_halving * 365) * (e.erc20_total_supply - e.initial_supply)) == initial_rate
        assert e.reward_supply == expected_total_supply - expected_initial_supply

        # Check deployment parameters
        assert e.staking_deployment_parameters == expected_deployment_parameters
        assert e.erc20_initial_supply == expected_initial_supply
        assert e.erc20_reward_supply == expected_reward_supply

        # Additional checks on supply
        assert e.token_supply_at_period(period=0) == expected_initial_supply
        assert e.cumulative_rewards_at_period(0) == 0

        # Last NuNit is mined after 184 years (or 67000 periods).
        # That's the year 2203, if token is launched in 2019.
        # 23rd century schizoid man!
        assert expected_total_supply == e.token_supply_at_period(period=67000)

        # After 1 year:
        assert 1_845_111_188_584347879497984668 == e.token_supply_at_period(period=365)
        assert 845_111_188_584347879497984668 == e.cumulative_rewards_at_period(365)
        assert e.erc20_initial_supply + e.cumulative_rewards_at_period(365) == e.token_supply_at_period(period=365)

        # Checking that the supply function is monotonic
        todays_supply = e.token_supply_at_period(period=0)
        for t in range(67000):
            tomorrows_supply = e.token_supply_at_period(period=t + 1)
            assert tomorrows_supply >= todays_supply
            todays_supply = tomorrows_supply
コード例 #25
0
def new_token_economics(token_economics):
    economics = StandardTokenEconomics(
        genesis_hours_per_period=token_economics.hours_per_period,
        hours_per_period=2 * token_economics.hours_per_period)
    return economics
コード例 #26
0
def test_exact_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / d / k2

    d - Coefficient which modifies the rate at which the maximum issuance decays
    k1 - Numerator of the locking duration coefficient
    k2 - Denominator of the locking duration coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / d / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    where allLockedPeriods == min(T, T1)
    """

    #
    # Expected Output
    #

    # Supply
    expected_total_supply = 3885390081748248632541961138
    expected_supply_ratio = Decimal('3.885390081748248632541961138')
    expected_initial_supply = 1000000000000000000000000000
    expected_phase1_supply = 1829579800000000000000000000

    # Reward
    expected_reward_supply = 2885390081748248632541961138
    reward_saturation = 1

    # Staking 2 phase
    decay_half_life = 2
    multiplier = 0.5
    expected_lock_duration_coefficient_1 = 365
    expected_lock_duration_coefficient_2 = 2 * expected_lock_duration_coefficient_1
    expected_phase2_coefficient = 1053
    expected_minting_coefficient = expected_phase2_coefficient * expected_lock_duration_coefficient_2

    assert expected_lock_duration_coefficient_1 * decay_half_life == round(
        expected_minting_coefficient * log(2) * multiplier / 365)

    #
    # Sanity
    #

    # Sanity check ratio accuracy
    expected_scaled_ratio = str(expected_supply_ratio).replace('.', '')
    assert str(expected_total_supply) == expected_scaled_ratio

    # Sanity check denomination size
    expected_scale = 28
    assert len(str(expected_total_supply)) == expected_scale
    assert len(str(expected_initial_supply)) == expected_scale
    assert len(str(expected_reward_supply)) == expected_scale

    # Use same precision as economics class
    with localcontext() as ctx:
        ctx.prec = StandardTokenEconomics._precision

        # Sanity check expected testing outputs
        assert Decimal(expected_total_supply
                       ) / expected_initial_supply == expected_supply_ratio
        assert expected_reward_supply == expected_total_supply - expected_initial_supply
        assert reward_saturation * 365 * multiplier == expected_lock_duration_coefficient_1 * (
            1 - multiplier)
        assert int(365 ** 2 * reward_saturation * decay_half_life / log(2) / (1-multiplier) / expected_lock_duration_coefficient_2) == \
            expected_phase2_coefficient

    # After sanity checking, assemble expected test deployment parameters
    expected_deployment_parameters = (
        24,  # Hours in single period
        1053,  # Coefficient which modifies the rate at which the maximum issuance decays (d)
        365,  # Numerator of the locking duration coefficient (k1)
        730,  # Denominator of the locking duration coefficient (k2)
        365,  # Max periods that will be additionally rewarded (awarded_periods)
        2829579800000000000000000000,  # Total supply for the first phase
        1002509479452054794520547,  # Max possible reward for one period for all stakers in the first phase
        30,  # Min amount of periods during which tokens can be locked
        15000000000000000000000,  # min locked NuNits
        30000000000000000000000000,  # max locked NuNits
        2)  # Min worker periods
    #
    # Token Economics
    #

    # Check creation
    e = StandardTokenEconomics()

    with localcontext() as ctx:
        ctx.prec = StandardTokenEconomics._precision

        # Check that total_supply calculated correctly
        assert Decimal(
            e.erc20_total_supply) / e.initial_supply == expected_supply_ratio
        assert e.erc20_total_supply == expected_total_supply

        # Check reward rates for the second phase
        initial_rate = (e.erc20_total_supply - int(e.first_phase_total_supply)) * (e.lock_duration_coefficient_1 + 365) / \
                       (e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
        assert int(initial_rate) == int(e.first_phase_max_issuance)
        assert Decimal(LOG2 / (e.token_halving * 365) *
                       (e.erc20_total_supply -
                        int(e.first_phase_total_supply))) == initial_rate

        initial_rate_small = (e.erc20_total_supply - int(e.first_phase_total_supply)) * e.lock_duration_coefficient_1 / \
                             (e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
        assert int(initial_rate_small) == int(initial_rate / 2)

        # Check reward supply
        assert e.reward_supply == expected_total_supply - expected_initial_supply

        # Check deployment parameters
        assert e.staking_deployment_parameters == expected_deployment_parameters
        assert e.erc20_initial_supply == expected_initial_supply
        assert e.erc20_reward_supply == expected_reward_supply

        # Additional checks on supply
        assert e.token_supply_at_period(period=0) == expected_initial_supply
        assert e.cumulative_rewards_at_period(0) == 0

        # Check phase 1 doesn't overshoot
        switch_period = 5 * 365
        assert e.first_phase_final_period() == switch_period
        assert e.token_supply_at_period(
            period=switch_period
        ) == expected_phase1_supply + expected_initial_supply
        assert e.token_supply_at_period(
            period=switch_period) < e.token_supply_at_period(
                period=switch_period + 1)

        assert e.rewards_during_period(period=1) == round(
            e.first_phase_max_issuance)
        assert e.rewards_during_period(period=switch_period) == round(
            e.first_phase_max_issuance)
        assert e.rewards_during_period(period=switch_period + 1) < int(
            e.first_phase_max_issuance)

        # Last NuNit is minted after 188 years (or 68500 periods).
        # That's the year 2208, if token is launched in 2020.
        # 23rd century schizoid man!
        assert expected_total_supply == e.token_supply_at_period(period=68500)

        # After 1 year:
        assert 1_365_915_960_000000000000000000 == e.token_supply_at_period(
            period=365)
        assert 365_915_960_000000000000000000 == e.cumulative_rewards_at_period(
            period=365)
        assert e.erc20_initial_supply + e.cumulative_rewards_at_period(
            365) == e.token_supply_at_period(period=365)

        # Checking that the supply function is monotonic in phase 1
        todays_supply = e.token_supply_at_period(period=0)
        for t in range(68500):
            tomorrows_supply = e.token_supply_at_period(period=t + 1)
            assert tomorrows_supply >= todays_supply
            todays_supply = tomorrows_supply