Remove code/tests that still reference the legacy notion of periods.

pull/3034/head
derekpierre 2022-12-06 16:13:32 -05:00
parent e1e84c80ba
commit 889cc58a88
21 changed files with 39 additions and 1889 deletions

View File

@ -1,20 +1,18 @@
import random
from _pydecimal import Decimal
from typing import Callable, Dict, Union
import maya
from constant_sorrow.constants import (
NOT_STAKING,
UNTRACKED_PENDING_TRANSACTION
)
from _pydecimal import Decimal
from constant_sorrow.constants import NOT_STAKING, UNTRACKED_PENDING_TRANSACTION
from eth_utils import currency
from hexbytes.main import HexBytes
from twisted.internet import reactor, task
from web3.exceptions import TransactionNotFound
from nucypher.blockchain.eth.constants import AVERAGE_BLOCK_TIME_IN_SECONDS, NULL_ADDRESS
from nucypher.blockchain.eth.constants import (
AVERAGE_BLOCK_TIME_IN_SECONDS,
NULL_ADDRESS,
)
from nucypher.types import ERC20UNits, NuNits, TuNits
from nucypher.utilities.gas_strategies import EXPECTED_CONFIRMATION_TIME_IN_SECONDS
from nucypher.utilities.logging import Logger
@ -178,7 +176,6 @@ class WorkTrackerBase:
self.__pending = dict() # TODO: Prime with pending worker transactions
self.__requirement = None
self.__start_time = NOT_STAKING
self.__uptime_period = NOT_STAKING
self._abort_on_error = False
self._consecutive_fails = 0

View File

@ -1,77 +1,12 @@
from decimal import Decimal
from typing import Union
import maya
from constant_sorrow.constants import UNKNOWN_DEVELOPMENT_CHAIN_ID
from eth_typing import BlockNumber
from eth_utils import is_address, is_hex, to_checksum_address
from web3 import Web3
from web3.contract import ContractConstructor, ContractFunction
from nucypher.blockchain.eth.clients import PUBLIC_CHAINS
from nucypher.blockchain.eth.constants import AVERAGE_BLOCK_TIME_IN_SECONDS
def epoch_to_period(epoch: int, seconds_per_period: int) -> int:
period = epoch // seconds_per_period
return period
def datetime_to_period(datetime: maya.MayaDT, seconds_per_period: int) -> int:
"""Converts a MayaDT instance to a period number."""
future_period = epoch_to_period(epoch=datetime.epoch, seconds_per_period=seconds_per_period)
return int(future_period)
def period_to_epoch(period: int, seconds_per_period: int) -> int:
epoch = period * seconds_per_period
return epoch
def get_current_period(seconds_per_period: int) -> int:
now = maya.now().epoch
period = epoch_to_period(epoch=now, seconds_per_period=seconds_per_period)
return period
def datetime_at_period(period: int, seconds_per_period: int, start_of_period: bool = False) -> maya.MayaDT:
"""
Returns the datetime object at a given period, future, or past.
If start_of_period, the datetime object represents the first second of said period.
"""
if start_of_period:
datetime_at_start_of_period = maya.MayaDT(epoch=period_to_epoch(period, seconds_per_period))
return datetime_at_start_of_period
else:
now = maya.now()
current_period = datetime_to_period(datetime=now, seconds_per_period=seconds_per_period)
delta_periods = period - current_period
target_datetime = now + maya.timedelta(seconds=seconds_per_period) * delta_periods
return target_datetime
def calculate_period_duration(future_time: maya.MayaDT, seconds_per_period: int, now: maya.MayaDT = None) -> int:
"""Takes a future MayaDT instance and calculates the duration from now, returning in periods"""
if now is None:
now = maya.now()
future_period = datetime_to_period(datetime=future_time, seconds_per_period=seconds_per_period)
current_period = datetime_to_period(datetime=now, seconds_per_period=seconds_per_period)
periods = future_period - current_period
return periods
def estimate_block_number_for_period(period: int, seconds_per_period: int, latest_block: BlockNumber) -> BlockNumber:
"""Logic for getting the approximate block height of the start of the specified period."""
period_start = datetime_at_period(period=period,
seconds_per_period=seconds_per_period,
start_of_period=True)
seconds_from_midnight = int((maya.now() - period_start).total_seconds())
blocks_from_midnight = seconds_from_midnight // AVERAGE_BLOCK_TIME_IN_SECONDS
block_number_for_period = latest_block - blocks_from_midnight
return block_number_for_period
def etherscan_url(item, network: str, is_token=False) -> str:

View File

@ -24,8 +24,6 @@ PRODUCTION_REGISTRY_ADVISORY = "Using latest published registry from {source}"
LOCAL_REGISTRY_ADVISORY = "Configured to registry filepath {registry_filepath}"
PERIOD_ADVANCED_WARNING = "Current period advanced before the action could be completed. Please try again."
#
# Events
#
@ -33,49 +31,6 @@ PERIOD_ADVANCED_WARNING = "Current period advanced before the action could be co
CONFIRM_OVERWRITE_EVENTS_CSV_FILE = "Overwrite existing CSV events file - {csv_file}?"
#
# Bonding
#
PROMPT_OPERATOR_ADDRESS = "Enter operator address"
CONFIRM_PROVIDER_AND_OPERATOR_ADDRESSES_ARE_EQUAL = """
{address}
The operator address provided is the same as the staking provider.
Continue using the same account for operator and staking provider?"""
SUCCESSFUL_OPERATOR_BONDING = "\nOperator {operator_address} successfully bonded to staking provider {staking_provider_address}"
BONDING_DETAILS = "Bonded at {bonded_date}"
BONDING_RELEASE_INFO = "This operator can be replaced or detached after {release_date}"
SUCCESSFUL_UNBOND_OPERATOR = "Successfully unbonded operator {operator_address} from staking provider {staking_provider_address}"
DETACH_DETAILS = "Unbonded at {bonded_date}"
#
# Rewards
#
COLLECTING_TOKEN_REWARD = 'Collecting {reward_amount} from staking rewards...'
COLLECTING_ETH_FEE = 'Collecting {fee_amount} ETH from policy fees...'
NO_TOKENS_TO_WITHDRAW = "No tokens can be withdrawn."
NO_FEE_TO_WITHDRAW = "No policy fee can be withdrawn."
TOKEN_REWARD_CURRENT = 'Available staking rewards: {reward_amount}.'
TOKEN_REWARD_PAST_HEADER = 'Staking rewards in the last {periods} periods ({days} days):'
TOKEN_REWARD_PAST = 'Total staking rewards: {reward_amount}.'
TOKEN_REWARD_NOT_FOUND = "No staking rewards found."
#
# Configuration
#

View File

@ -51,10 +51,6 @@ option_staking_provider = click.option('--staking-provider', help="Staking provi
option_teacher_uri = click.option('--teacher', 'teacher_uri', help="An Ursula URI to start learning from (seednode)", type=click.STRING)
_option_middleware = click.option('-Z', '--mock-networking', help="Use in-memory transport instead of networking", count=True)
# Avoid circular input
option_rate = click.option('--rate', help="Policy rate per period (in wei)", type=WEI) # TODO: Is wei a sane unit here? Perhaps gwei?
#
# Alphabetical
#

View File

@ -1,5 +1,3 @@
import time
from collections import defaultdict, deque
from contextlib import suppress
@ -877,9 +875,9 @@ class Learner:
f'{sprout} {NOT_SIGNED}.')
except sprout.NotStaking:
self.log.warn(f'Verification Failed - '
f'{sprout} has no active stakes in the current period '
f'({self.staking_agent.get_current_period()}')
self.log.warn(
f"Verification Failed - " f"{sprout} has no active stakes "
)
except sprout.InvalidOperatorSignature:
self.log.warn(f'Verification Failed - '

View File

@ -84,8 +84,10 @@ def _resolve_any_context_variables(
def _validate_chain(chain: int) -> None:
if not isinstance(chain, int):
raise ValueError(f'"The chain" field of c a condition must be the '
f'integer of a chain ID (got "{chain}").')
raise ValueError(
f'The "chain" field of a condition must be the '
f'integer chain ID (got "{chain}").'
)
if chain not in _CONDITION_CHAINS:
raise InvalidCondition(
f"chain ID {chain} is not a permitted "

View File

@ -20,8 +20,8 @@ class PaymentMethod(ABC):
rate: int
value: int
commencement: int # epoch
expiration: int # epoch
duration: int # seconds or periods
expiration: int # epoch
duration: int # seconds
shares: int
@abstractmethod

View File

@ -1,19 +1,13 @@
from typing import TypeVar, NewType, NamedTuple, Union
from typing import NamedTuple, NewType, TypeVar, Union
from eth_typing.evm import ChecksumAddress
from web3.types import Wei, TxReceipt
from web3.types import TxReceipt, Wei
ERC20UNits = NewType("ERC20UNits", int)
NuNits = NewType("NuNits", ERC20UNits)
TuNits = NewType("TuNits", ERC20UNits)
Work = NewType("Work", int)
Agent = TypeVar('Agent', bound='EthereumContractAgent')
Period = NewType('Period', int)
PeriodDelta = NewType('PeriodDelta', int)
ContractReturnValue = TypeVar('ContractReturnValue', bound=Union[TxReceipt, Wei, int, str, bool])
@ -37,9 +31,3 @@ class PolicyInfo(NamedTuple):
# reserved_slot_3
# reserved_slot_4
# reserved_slot_5
class ArrangementInfo(NamedTuple):
node: ChecksumAddress
downtime_index: int
last_refunded_period: int

View File

@ -1,62 +0,0 @@
import json
import pytest
import random
from nucypher.config.constants import TEMPORARY_DOMAIN
from nucypher.crypto.powers import TransactingPower
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.blockchain.eth.signers.software import Web3Signer
from tests.constants import NUMBER_OF_ALLOCATIONS_IN_TESTS
# Prevents TesterBlockchain to be picked up by py.test as a test class
from tests.utils.blockchain import TesterBlockchain as _TesterBlockchain
@pytest.mark.skip()
@pytest.mark.usefixtures('testerchain')
def test_rapid_deployment(application_economics, test_registry, temp_dir_path, get_random_checksum_address):
blockchain = _TesterBlockchain(eth_airdrop=False, test_accounts=4)
deployer_address = blockchain.etherbase_account
deployer_power = TransactingPower(signer=Web3Signer(blockchain.client), account=deployer_address)
administrator = ContractAdministrator(transacting_power=deployer_power,
domain=TEMPORARY_DOMAIN,
registry=test_registry)
blockchain.bootstrap_network(registry=test_registry)
all_yall = blockchain.unassigned_accounts
# Start with some hard-coded cases...
allocation_data = [{'checksum_address': all_yall[1],
'amount': application_economics.maximum_allowed_locked,
'lock_periods': application_economics.min_operator_seconds},
{'checksum_address': all_yall[2],
'amount': application_economics.min_authorization,
'lock_periods': application_economics.min_operator_seconds},
{'checksum_address': all_yall[3],
'amount': application_economics.min_authorization * 100,
'lock_periods': application_economics.min_operator_seconds},
]
# Pile on the rest
for _ in range(NUMBER_OF_ALLOCATIONS_IN_TESTS - len(allocation_data)):
checksum_address = get_random_checksum_address()
amount = random.randint(application_economics.min_authorization, application_economics.maximum_allowed_locked)
duration = random.randint(application_economics.min_operator_seconds, application_economics.maximum_rewarded_periods)
random_allocation = {'checksum_address': checksum_address, 'amount': amount, 'lock_periods': duration}
allocation_data.append(random_allocation)
filepath = temp_dir_path / "allocations.json"
with open(filepath, 'w') as f:
json.dump(allocation_data, f)
minimum, default, maximum = 10, 20, 30
administrator.set_fee_rate_range(minimum, default, maximum)

View File

@ -1,94 +0,0 @@
import pytest
from nucypher_core.umbral import SecretKeyFactory, Signer
from nucypher.blockchain.eth.actors import Investigator
from nucypher.blockchain.eth.agents import ContractAgency, NucypherTokenAgent
from nucypher.blockchain.eth.constants import NULL_ADDRESS
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.token import NU
from nucypher.config.constants import TEMPORARY_DOMAIN
from nucypher.crypto.powers import TransactingPower
from nucypher.crypto.signing import SignatureStamp
def mock_ursula(testerchain, account, mocker):
ursula_privkey = SecretKeyFactory.random()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(),
signer=Signer(ursula_privkey))
signed_stamp = testerchain.client.sign_message(account=account,
message=bytes(ursula_stamp))
ursula = mocker.Mock(stamp=ursula_stamp, operator_signature=signed_stamp)
return ursula
@pytest.mark.skip("David, send help!")
def test_investigator_requests_slashing(testerchain,
test_registry,
agency,
#mock_ursula_reencrypts,
application_economics,
mocker):
staker_account = testerchain.stake_provider_account(0)
worker_account = testerchain.ursula_account(0)
##### STAKING ESCROW STUFF #####
token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=test_registry)
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
locked_tokens = application_economics.min_authorization * 5
# The staker receives an initial amount of tokens
tpower = TransactingPower(account=testerchain.etherbase_account, signer=Web3Signer(testerchain.client))
_txhash = token_agent.transfer(amount=locked_tokens,
target_address=staker_account,
transacting_power=tpower)
# Deposit: The staker deposits tokens in the StakingEscrow contract.
staker_tpower = TransactingPower(account=staker_account, signer=Web3Signer(testerchain.client))
staker = Staker(transacting_power=staker_tpower,
domain=TEMPORARY_DOMAIN,
registry=test_registry)
staker.initialize_stake(amount=NU(locked_tokens, 'NuNit'),
lock_periods=application_economics.min_operator_seconds)
assert staker.locked_tokens(periods=1) == locked_tokens
# The staker hasn't bond a worker yet
assert NULL_ADDRESS == staking_agent.get_worker_from_staker(staker_address=staker_account)
_txhash = staking_agent.bond_worker(transacting_power=staker_tpower, worker_address=worker_account)
assert worker_account == staking_agent.get_worker_from_staker(staker_address=staker_account)
assert staker_account == staking_agent.get_staker_from_worker(worker_address=worker_account)
###### END OF STAKING ESCROW STUFF ####
bob_account = testerchain.bob_account
bob_tpower = TransactingPower(account=bob_account, signer=Web3Signer(testerchain.client))
investigator = Investigator(registry=test_registry,
transacting_power=bob_tpower,
domain=TEMPORARY_DOMAIN)
ursula = mock_ursula(testerchain, worker_account, mocker=mocker)
# Let's create a bad cfrag
evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
assert not investigator.was_this_evidence_evaluated(evidence)
bobby_old_balance = investigator.token_balance
investigator.request_evaluation(evidence=evidence)
assert investigator.was_this_evidence_evaluated(evidence)
investigator_reward = investigator.token_balance - bobby_old_balance
assert investigator_reward > 0
assert investigator_reward == application_economics.base_penalty / application_economics.reward_coefficient
assert staker.locked_tokens(periods=1) < locked_tokens

View File

@ -1,19 +1,10 @@
import pytest
import pytest_twisted
from twisted.internet import threads
from twisted.internet.task import Clock
from web3.middleware.simulate_unmined_transaction import (
unmined_receipt_simulator_middleware,
)
from nucypher.blockchain.eth.actors import Operator
from nucypher.blockchain.eth.agents import ContractAgency, PREApplicationAgent
from nucypher.blockchain.eth.constants import NULL_ADDRESS
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.token import NU
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.logging import Logger
from tests.utils.ursula import make_ursulas, start_pytest_ursula_services
@ -26,169 +17,6 @@ def log(message):
print(message)
@pytest.mark.skip()
@pytest_twisted.inlineCallbacks
def test_work_tracker(
mocker,
testerchain,
test_registry,
staker,
agency,
application_economics,
ursula_test_config,
):
staker.initialize_stake(
amount=NU(application_economics.min_authorization, "NuNit"),
lock_periods=int(application_economics.min_operator_seconds),
)
# Get an unused address and create a new worker
worker_address = testerchain.unassigned_accounts[-1]
# Control time
clock = Clock()
ClassicPREWorkTracker.CLOCK = clock
# Bond the Worker and Staker
staker.bond_worker(worker_address=worker_address)
commit_spy = mocker.spy(Worker, "commit_to_next_period")
replacement_spy = mocker.spy(
ClassicPREWorkTracker, "_ClassicPREWorkTracker__fire_replacement_commitment"
)
# Make the Worker
ursula = make_ursulas(
ursula_config=ursula_test_config,
staking_provider_addresses=[staker.checksum_address],
operator_addresses=[worker_address],
registry=test_registry,
).pop()
ursula.run(
preflight=False,
discovery=False,
start_reactor=False,
worker=True,
eager=True,
block_until_ready=False,
) # "start" services
initial_period = staker.staking_agent.get_current_period()
def start():
log("Starting Worker for auto-commitment simulation")
start_pytest_ursula_services(ursula=ursula)
def advance_one_period(_):
log("Advancing one period")
testerchain.time_travel(periods=1)
clock.advance(ClassicPREWorkTracker.INTERVAL_CEIL + 1)
def check_pending_commitments(number_of_commitments):
def _check_pending_commitments(_):
log(f"Checking we have {number_of_commitments} pending commitments")
assert number_of_commitments == len(ursula.work_tracker.pending)
return _check_pending_commitments
def pending_commitments(_):
log("Starting unmined transaction simulation")
testerchain.client.add_middleware(unmined_receipt_simulator_middleware)
def advance_one_cycle(_):
log("Advancing one tracking iteration")
clock.advance(ursula.work_tracker._tracking_task.interval + 1)
def advance_until_replacement_indicated(_):
last_committed_period = staker.staking_agent.get_last_committed_period(
staker_address=staker.checksum_address
)
log("Advancing until replacement is indicated")
testerchain.time_travel(periods=1)
clock.advance(ClassicPREWorkTracker.INTERVAL_CEIL + 1)
mocker.patch.object(
ClassicPREWorkTracker, "max_confirmation_time", return_value=1.0
)
mock_last_committed_period = mocker.PropertyMock(
return_value=last_committed_period
)
mocker.patch.object(
Worker, "last_committed_period", new_callable=mock_last_committed_period
)
clock.advance(ursula.work_tracker.max_confirmation_time() + 1)
def verify_unmined_commitment(_):
log("Verifying worker has unmined commitment transaction")
# FIXME: The test doesn't model accurately an unmined TX, but an unconfirmed receipt,
# so the tracker does not have pending TXs. If we want to model pending TXs we need to actually
# prevent them from being mined.
#
# assert len(ursula.work_tracker.pending) == 1
current_period = staker.staking_agent.get_current_period()
assert commit_spy.call_count == current_period - initial_period + 1
def verify_replacement_commitment(_):
log("Verifying worker has replaced commitment transaction")
assert replacement_spy.call_count > 0
def verify_confirmed(_):
# Verify that periods were committed on-chain automatically
last_committed_period = staker.staking_agent.get_last_committed_period(
staker_address=staker.checksum_address
)
current_period = staker.staking_agent.get_current_period()
expected_commitments = current_period - initial_period + 1
log(f"Verifying worker made {expected_commitments} commitments so far")
assert (last_committed_period - current_period) == 1
assert commit_spy.call_count == expected_commitments
assert replacement_spy.call_count == 0
# Behavioural Test, like a screenplay made of legos
# Ursula commits on startup
d = threads.deferToThread(start)
d.addCallback(verify_confirmed)
d.addCallback(advance_one_period)
d.addCallback(check_pending_commitments(1))
d.addCallback(advance_one_cycle)
d.addCallback(check_pending_commitments(0))
# Ursula commits for 3 periods with no problem
for i in range(3):
d.addCallback(advance_one_period)
d.addCallback(verify_confirmed)
d.addCallback(check_pending_commitments(1))
# Introduce unmined transactions
d.addCallback(advance_one_period)
d.addCallback(pending_commitments)
# Ursula's commitment transaction gets stuck
for i in range(4):
d.addCallback(advance_one_cycle)
d.addCallback(verify_unmined_commitment)
# Ursula recovers from this situation
d.addCallback(advance_one_cycle)
d.addCallback(verify_confirmed)
d.addCallback(advance_one_cycle)
d.addCallback(check_pending_commitments(0))
# but it happens again, resulting in a replacement transaction
d.addCallback(advance_until_replacement_indicated)
d.addCallback(advance_one_cycle)
d.addCallback(check_pending_commitments(1))
d.addCallback(advance_one_cycle)
d.addCallback(verify_replacement_commitment)
yield d
def test_ursula_operator_confirmation(
ursula_test_config,
testerchain,
@ -308,7 +136,7 @@ def test_ursula_operator_confirmation_autopilot(
start_pytest_ursula_services(ursula=ursula)
def verify_confirmed(_):
# Verify that periods were committed on-chain automatically
# Verify that commitment made on-chain automatically
expected_commitments = 1
log(f"Verifying worker made {expected_commitments} commitments so far")
assert commit_spy.call_count == expected_commitments

View File

@ -1,99 +0,0 @@
import pytest
from nucypher_core.umbral import SecretKeyFactory, Signer
from nucypher.blockchain.eth.actors import NucypherTokenActor
from nucypher.blockchain.eth.agents import (
AdjudicatorAgent,
ContractAgency,
NucypherTokenAgent
)
from nucypher.blockchain.eth.constants import NULL_ADDRESS
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.token import NU
from nucypher.config.constants import TEMPORARY_DOMAIN
from nucypher.crypto.powers import TransactingPower
from nucypher.crypto.signing import SignatureStamp
def mock_ursula(testerchain, account, mocker):
ursula_privkey = SecretKeyFactory.random()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(),
signer=Signer(ursula_privkey))
signed_stamp = testerchain.client.sign_message(account=account,
message=bytes(ursula_stamp))
ursula = mocker.Mock(stamp=ursula_stamp, operator_signature=signed_stamp)
return ursula
@pytest.mark.skip("David, send help!")
def test_adjudicator_slashes(agency,
testerchain,
#mock_ursula_reencrypts,
application_economics,
test_registry,
mocker):
staker_account = testerchain.stake_provider_account(0)
worker_account = testerchain.ursula_account(0)
##### STAKING ESCROW STUFF #####
token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=test_registry)
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
locked_tokens = application_economics.min_authorization * 5
# The staker receives an initial amount of tokens
tpower = TransactingPower(account=testerchain.etherbase_account, signer=Web3Signer(testerchain.client))
_txhash = token_agent.transfer(amount=locked_tokens,
target_address=staker_account,
transacting_power=tpower)
# Deposit: The staker deposits tokens in the StakingEscrow contract.
tpower = TransactingPower(account=staker_account, signer=Web3Signer(testerchain.client))
staker = Staker(domain=TEMPORARY_DOMAIN,
registry=test_registry,
transacting_power=tpower)
staker.initialize_stake(amount=NU(locked_tokens, 'NuNit'),
lock_periods=application_economics.min_operator_seconds)
assert staker.locked_tokens(periods=1) == locked_tokens
# The staker hasn't bond a worker yet
assert NULL_ADDRESS == staking_agent.get_worker_from_staker(staker_address=staker_account)
_txhash = staking_agent.bond_worker(transacting_power=tpower, operator_address=worker_account)
assert worker_account == staking_agent.get_worker_from_staker(staker_address=staker_account)
assert staker_account == staking_agent.get_staker_from_worker(operator_address=worker_account)
###### END OF STAKING ESCROW STUFF ####
adjudicator_agent = AdjudicatorAgent(registry=test_registry)
bob_account = testerchain.bob_account
bobby = NucypherTokenActor(checksum_address=bob_account,
domain=TEMPORARY_DOMAIN,
registry=test_registry)
ursula = mock_ursula(testerchain, worker_account, mocker=mocker)
# Let's create a bad cfrag
evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True)
assert not adjudicator_agent.was_this_evidence_evaluated(evidence)
bobby_old_balance = bobby.token_balance
bob_tpower = TransactingPower(account=bob_account, signer=Web3Signer(testerchain.client))
adjudicator_agent.evaluate_cfrag(evidence=evidence, transacting_power=bob_tpower)
assert adjudicator_agent.was_this_evidence_evaluated(evidence)
investigator_reward = bobby.token_balance - bobby_old_balance
assert investigator_reward > 0
assert investigator_reward == application_economics.base_penalty / application_economics.reward_coefficient
assert staker.locked_tokens(periods=1) < locked_tokens

View File

@ -1,13 +1,8 @@
import pytest
from nucypher_core import MetadataResponse, MetadataResponsePayload
from twisted.logger import LogLevel, globalLogPublisher
from nucypher.acumen.perception import FleetSensor
from nucypher.config.constants import TEMPORARY_DOMAIN
from tests.utils.middleware import MockRestMiddleware
from tests.utils.ursula import make_ursula_for_staking_provider
def test_ursula_stamp_verification_tolerance(ursulas, mocker):
@ -74,100 +69,3 @@ def test_ursula_stamp_verification_tolerance(ursulas, mocker):
warning = warnings[1]['log_format']
assert str(teacher) in warning
assert "Failed to verify MetadataResponse from Teacher" in warning # TODO: Cleanup logging templates
@pytest.mark.skip("See Issue #1075") # TODO: Issue #1075
def test_invalid_operators_tolerance(
testerchain,
test_registry,
ursulas,
agency,
idle_staker,
application_economics,
ursula_test_config,
):
#
# Setup
#
lonely_blockchain_learner, blockchain_teacher, unsigned, *the_others = list(ursulas)
_, staking_agent, _ = agency
warnings = []
def warning_trapper(event):
if event['log_level'] == LogLevel.warn:
warnings.append(event)
# We start with an "idle_staker" (i.e., no tokens in StakingEscrow)
assert 0 == staking_agent.owned_tokens(idle_staker.checksum_address)
# Now let's create an active worker for this staker.
# First, stake something (e.g. the bare minimum)
amount = application_economics.min_authorization
periods = application_economics.min_operator_seconds
idle_staker.initialize_stake(amount=amount, lock_periods=periods)
# Stake starts next period (or else signature validation will fail)
testerchain.time_travel(periods=1)
idle_staker.stake_tracker.refresh()
# We create an active worker node for this staker
worker = make_ursula_for_staking_provider(
staking_provider=idle_staker,
operator_address=testerchain.unassigned_accounts[-1],
ursula_config=ursula_test_config,
blockchain=testerchain,
ursulas_to_learn_about=None,
)
# Since we made a commitment, we need to advance one period
testerchain.time_travel(periods=1)
# The worker is valid and can be verified (even with the force option)
worker.verify_node(force=True, network_middleware=MockRestMiddleware())
# In particular, we know that it's bonded to a staker who is really staking.
assert worker._operator_is_bonded(registry=test_registry)
assert worker._staking_provider_is_really_staking(registry=test_registry)
# OK. Now we learn about this worker.
lonely_blockchain_learner.remember_node(worker)
# The worker already committed one period before. Let's commit to the remaining 29.
for i in range(29):
worker.commit_to_next_period()
testerchain.time_travel(periods=1)
# The stake period has ended, and the staker wants her tokens back ("when lambo?").
# She withdraws up to the last penny (well, last nunit, actually).
idle_staker.mint()
testerchain.time_travel(periods=1)
i_want_it_all = staking_agent.owned_tokens(idle_staker.checksum_address)
idle_staker.withdraw(i_want_it_all)
# OK...so...the staker is not staking anymore ...
assert 0 == staking_agent.owned_tokens(idle_staker.checksum_address)
# ... but the worker node still is "verified" (since we're not forcing on-chain verification)
worker.verify_node(network_middleware=MockRestMiddleware())
# If we force, on-chain verification, the worker is of course not verified
with pytest.raises(worker.NotStaking):
worker.verify_node(force=True, network_middleware=MockRestMiddleware())
# Let's learn from this invalid node
lonely_blockchain_learner._current_teacher_node = worker
globalLogPublisher.addObserver(warning_trapper)
lonely_blockchain_learner.learn_from_teacher_node()
# lonely_blockchain_learner.remember_node(worker) # The same problem occurs if we directly try to remember this node
globalLogPublisher.removeObserver(warning_trapper)
# TODO: What should we really check here? (#1075)
assert len(warnings) == 1
warning = warnings[-1]['log_format']
assert str(worker) in warning
assert "no active stakes" in warning # TODO: Cleanup logging templates
assert worker not in lonely_blockchain_learner.known_nodes
# TODO: Write a similar test but for detached worker (#1075)

View File

@ -1,10 +1,8 @@
import pytest
from eth_tester.exceptions import TransactionFailed
from eth_utils import to_checksum_address
from nucypher.blockchain.eth.constants import NULL_ADDRESS
from eth_utils import to_checksum_address
CONFIRMATION_SLOT = 1
@ -157,7 +155,7 @@ def test_bond_operator(testerchain, threshold_staking, pre_application, applicat
assert event_args['stakingProvider'] == staking_provider_3
# Now the operator has been unbonded ...
assert event_args['operator'] == NULL_ADDRESS
# ... with a new starting period.
# ... with a new start time.
assert event_args['startTimestamp'] == timestamp
# The staking provider can bond now a new operator, without waiting additional time.

View File

@ -1,221 +0,0 @@
import contextlib
from pathlib import Path
import pytest
import requests
from constant_sorrow import constants
from web3.exceptions import ValidationError
from nucypher.blockchain.eth.deployers import (
BaseContractDeployer,
NucypherTokenDeployer,
StakingEscrowDeployer
)
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.sol.compile.constants import SOLIDITY_SOURCE_ROOT, TEST_SOLIDITY_SOURCE_ROOT
from nucypher.blockchain.eth.sol.compile.types import SourceBundle
from nucypher.crypto.powers import TransactingPower
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD
from tests.utils.blockchain import free_gas_price_strategy, TesterBlockchain
USER = "nucypher"
REPO = "nucypher"
BRANCH = "main"
GITHUB_SOURCE_LINK = f"https://api.github.com/repos/{USER}/{REPO}/contents/nucypher/blockchain/eth/sol/source?ref={BRANCH}"
BlockchainDeployerInterface.GAS_STRATEGIES = {**BlockchainDeployerInterface.GAS_STRATEGIES,
'free': free_gas_price_strategy}
def download_github_dir(source_link: str, target_folder: Path):
response = requests.get(source_link)
if response.status_code != 200:
error = f"Failed to call api {source_link} with status code {response.status_code}"
raise RuntimeError(error)
for content in response.json():
path = target_folder / content["name"]
if content["type"] == "dir":
path.mkdir()
download_github_dir(content["url"], path)
else:
download_github_file(content["download_url"], path)
def download_github_file(source_link: str, target_folder: Path):
response = requests.get(source_link)
if response.status_code != 200:
error = f"Failed to call api {source_link} with status code {response.status_code}"
raise RuntimeError(error)
raw_data = response.content
with open(target_folder, 'wb') as registry_file:
registry_file.seek(0)
registry_file.write(raw_data)
registry_file.truncate()
# def parameters_v611(blockchain_interface: BlockchainDeployerInterface,
# transacting_power: TransactingPower,
# deployer: BaseContractDeployer):
# policy_manager_mock, _ = blockchain_interface.deploy_contract(
# transacting_power,
# deployer.registry,
# "OldPolicyManagerMock"
# )
# adjudicator_mock, _ = blockchain_interface.deploy_contract(
# transacting_power,
# deployer.registry,
# "OldAdjudicatorMock"
# )
# parameters = {
# "_genesisHoursPerPeriod": 1,
# "_hoursPerPeriod": 1,
# "_issuanceDecayCoefficient": 1,
# "_lockDurationCoefficient1": 1,
# "_lockDurationCoefficient2": 2,
# "_maximumRewardedPeriods": 1,
# "_firstPhaseTotalSupply": 1,
# "_firstPhaseMaxIssuance": 1,
# "_minLockedPeriods": 2,
# "_minAllowableLockedTokens": 0,
# "_maxAllowableLockedTokens": deployer.economics.maximum_allowed_locked,
# "_minWorkerPeriods": 1,
# "_policyManager": policy_manager_mock.address,
# "_adjudicator": adjudicator_mock.address
# }
# return parameters
# Constructor parameters overrides for previous versions if needed
# All versions below the specified version must use these overrides
# 'None' value removes arg from list of constructor parameters
CONSTRUCTOR_OVERRIDES = {
StakingEscrowDeployer.contract_name: {
"v5.7.1": lambda *args: {"_genesisHoursPerPeriod": None},
# "v6.1.1": parameters_v611
}
}
FORCE_SKIP = {
StakingEscrowDeployer.contract_name: ["v5.6.1", "v6.2.1"],
}
def deploy_base_contract(blockchain_interface: BlockchainDeployerInterface,
deployer: BaseContractDeployer,
transacting_power: TransactingPower,
skipt_test: bool):
contract_name = deployer.contract_name
latest_version, _data = blockchain_interface.find_raw_contract_data(contract_name, "latest")
raw_contracts = blockchain_interface._raw_contract_cache
overrides = dict()
if len(raw_contracts[contract_name]) != 1:
try:
overrides_func = CONSTRUCTOR_OVERRIDES[contract_name][latest_version]
overrides = overrides_func(blockchain_interface,
transacting_power,
deployer)
except KeyError:
pass
version = "latest" if skipt_test else "earliest"
try:
deployer.deploy(transacting_power=transacting_power,
contract_version=version,
deployment_mode=constants.FULL, **overrides)
except ValidationError:
pass # Skip errors related to initialization
def skip_test(blockchain_interface: BlockchainDeployerInterface, contract_name: str):
latest_version, _data = blockchain_interface.find_raw_contract_data(contract_name, "latest")
raw_contracts = blockchain_interface._raw_contract_cache
try:
force_skip = latest_version in FORCE_SKIP[contract_name]
except KeyError:
force_skip = False
return force_skip or len(raw_contracts[contract_name]) == 1
def prepare_staker(blockchain_interface: TesterBlockchain,
deployer: StakingEscrowDeployer,
transacting_power: TransactingPower):
worklock_agent = WorkLockAgent(registry=deployer.registry)
value = worklock_agent.minimum_allowed_bid
worklock_agent.bid(value=value, transacting_power=transacting_power)
blockchain_interface.time_travel(hours=100)
worklock_agent.verify_bidding_correctness(transacting_power=transacting_power, gas_limit=1000000)
worklock_agent.claim(transacting_power=transacting_power)
@pytest.mark.skip("fix after deployers and merging threshold-network")
def test_upgradeability(temp_dir_path):
# Prepare remote source for compilation
download_github_dir(GITHUB_SOURCE_LINK, temp_dir_path)
# Prepare the blockchain
TesterBlockchain.SOURCES = [
SourceBundle(base_path=SOLIDITY_SOURCE_ROOT,
other_paths=(TEST_SOLIDITY_SOURCE_ROOT,)),
SourceBundle(base_path=Path(temp_dir_path))
]
eth_provider_uri = 'tester://pyevm/2' # TODO: Testerchain caching Issues
try:
blockchain_interface = TesterBlockchain(gas_strategy='free')
blockchain_interface.eth_provider_uri = eth_provider_uri
blockchain_interface.connect()
origin = blockchain_interface.client.accounts[0]
BlockchainInterfaceFactory.register_interface(interface=blockchain_interface)
transacting_power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD,
signer=Web3Signer(blockchain_interface.client),
account=origin)
economics = make_token_economics(blockchain_interface)
# Check contracts with multiple versions
contract_name = StakingEscrowDeployer.contract_name
skip_staking_escrow_test = skip_test(blockchain_interface, contract_name)
if skip_staking_escrow_test:
return
# Prepare master version of contracts and upgrade to the latest
registry = InMemoryContractRegistry()
token_deployer = NucypherTokenDeployer(registry=registry, economics=economics)
token_deployer.deploy(transacting_power=transacting_power)
staking_escrow_deployer = StakingEscrowDeployer(registry=registry, economics=economics)
staking_escrow_deployer.deploy(deployment_mode=constants.INIT, transacting_power=transacting_power)
if not skip_staking_escrow_test:
economics.worklock_supply = economics.maximum_allowed_locked
worklock_deployer = WorklockDeployer(registry=registry, economics=economics)
worklock_deployer.deploy(transacting_power=transacting_power)
staking_escrow_deployer = StakingEscrowDeployer(registry=registry, economics=economics)
deploy_base_contract(blockchain_interface, staking_escrow_deployer,
transacting_power=transacting_power,
skipt_test=skip_staking_escrow_test)
if not skip_staking_escrow_test:
prepare_staker(blockchain_interface=blockchain_interface,
deployer=staking_escrow_deployer,
transacting_power=transacting_power)
staking_escrow_deployer.upgrade(transacting_power=transacting_power,
contract_version="latest",
confirmations=0)
finally:
# Unregister interface # TODO: Move to method?
with contextlib.suppress(KeyError):
del BlockchainInterfaceFactory._interfaces[eth_provider_uri]

View File

@ -1,161 +0,0 @@
from decimal import Decimal, localcontext
from math import log
import pytest
from nucypher.blockchain.economics import Economics
@pytest.mark.skip("remove me")
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
#
one_year_in_periods = 365 / 7
# 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 = one_year_in_periods
expected_lock_duration_coefficient_2 = 2 * expected_lock_duration_coefficient_1
expected_phase2_coefficient = 150
expected_minting_coefficient = expected_phase2_coefficient * expected_lock_duration_coefficient_2
assert int(expected_lock_duration_coefficient_1 * decay_half_life) == \
round(expected_minting_coefficient * log(2) * multiplier / one_year_in_periods)
#
# 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 = Economics._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 * one_year_in_periods * multiplier == expected_lock_duration_coefficient_1 * (1 - multiplier)
assert int(one_year_in_periods ** 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 at genesis
24 * 7, # Hours in single period
150, # Coefficient which modifies the rate at which the maximum issuance decays (d)
52, # Numerator of the locking duration coefficient (k1)
52 * 2, # Denominator of the locking duration coefficient (k2)
52, # Max periods that will be additionally rewarded (awarded_periods)
2829579800000000000000000000, # Total supply for the first phase
7017566356164383151812537, # Max possible reward for one period for all stakers in the first phase
4, # 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 = Economics()
with localcontext() as ctx:
ctx.prec = Economics._precision
one_year_in_periods = Decimal(one_year_in_periods)
# 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 + one_year_in_periods) / \
(e.issuance_decay_coefficient * e.lock_duration_coefficient_2)
assert int(initial_rate) == int(e.first_phase_max_issuance)
assert int(LOG2 / (e.token_halving * one_year_in_periods) * (e.erc20_total_supply - int(e.first_phase_total_supply))) == \
int(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.pre_application_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 * 52
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 + 1) > 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 9800 periods).
# That's the year 2208, if token is launched in 2020.
# 23rd century schizoid man!
assert abs(expected_total_supply - e.token_supply_at_period(period=9800)) < e.first_phase_max_issuance
assert e.erc20_total_supply == expected_total_supply
# After 1 year:
expected_reward_one_year = 52 * 7017566356164383151812537
assert abs((expected_initial_supply + expected_reward_one_year) - e.token_supply_at_period(period=52)) <= 100
assert abs(expected_reward_one_year - e.cumulative_rewards_at_period(period=52)) <= 100
assert e.erc20_initial_supply + e.cumulative_rewards_at_period(52) == e.token_supply_at_period(period=52)
# Checking that the supply function is monotonic in phase 1
todays_supply = e.token_supply_at_period(period=0)
for t in range(9800):
tomorrows_supply = e.token_supply_at_period(period=t + 1)
assert tomorrows_supply >= todays_supply
todays_supply = tomorrows_supply

View File

@ -1,686 +0,0 @@
#!/usr/bin/env python3
import io
import json
import os
import re
import time
from pathlib import Path
from unittest.mock import Mock
import tabulate
from nucypher_core.umbral import SecretKey, Signer
from twisted.logger import ILogObserver, globalLogPublisher, jsonFileLogObserver
from web3.contract import Contract
from zope.interface import provider
from nucypher.blockchain.economics import Economics
from nucypher.blockchain.eth.agents import (
AdjudicatorAgent,
NucypherTokenAgent,
PolicyManagerAgent,
)
from nucypher.blockchain.eth.constants import NUCYPHER_CONTRACT_NAMES, NULL_ADDRESS, POLICY_ID_LENGTH
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.crypto.powers import TransactingPower
from nucypher.crypto.signing import SignatureStamp
from nucypher.utilities.logging import Logger
from tests.utils.blockchain import TesterBlockchain
ALGORITHM_SHA256 = 1
TOKEN_ECONOMICS = Economics()
MIN_ALLOWED_LOCKED = TOKEN_ECONOMICS.min_authorization
LOCKED_PERIODS = 30
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 = Path(__file__).parent / 'results'
JSON_OUTPUT_FILENAME = '{}.json'.format(LOG_NAME)
_PATTERN = re.compile(r'''
^ # Anchor at the start of a string
(.+) # Any character sequence longer than 1; Captured
\s=\s # Space-Equal-Space
(\d+) # A sequence of digits; Captured
\s\|\s # Space-Slash-Space
(\d+) # A sequence of digits; Captured
$ # Anchor at the end of the string
''', re.VERBOSE)
def __init__(self) -> None:
self.log = Logger(self.__class__.__name__)
self.gas_estimations = dict()
if not self.OUTPUT_DIR.is_dir():
self.OUTPUT_DIR.mkdir()
@provider(ILogObserver)
def __call__(self, event, *args, **kwargs) -> None:
if event.get('log_namespace') == self.LOG_NAME:
message = event.get("log_format")
matches = self._PATTERN.match(message)
if not matches:
self.log.debug("No match for {} with pattern {}".format(message, self._PATTERN))
return
label, estimates, gas_used = matches.groups()
self.paint_line(label, estimates, gas_used)
self.gas_estimations[label] = int(gas_used)
@staticmethod
def paint_line(label: str, estimates: str, gas_used: str) -> None:
print('{label} {estimates:7,} | {gas:7,}'.format(
label=label.ljust(72, '.'), estimates=int(estimates), gas=int(gas_used)))
def to_json_file(self) -> None:
print('Saving JSON Output...')
epoch_time = str(int(time.time()))
timestamped_filename = '{}-{}'.format(epoch_time, self.JSON_OUTPUT_FILENAME)
filepath = self.OUTPUT_DIR / timestamped_filename
with open(filepath, 'w') as file:
file.write(json.dumps(self.gas_estimations, indent=4))
def start_collection(self) -> None:
print("Starting Data Collection...")
json_filepath = self.OUTPUT_DIR / AnalyzeGas.LOG_FILENAME
json_io = io.open(json_filepath, "w")
json_observer = jsonFileLogObserver(json_io)
globalLogPublisher.addObserver(json_observer)
globalLogPublisher.addObserver(self)
def mock_ursula(testerchain, account):
ursula_privkey = SecretKey.random()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(),
signer=Signer(ursula_privkey))
signed_stamp = testerchain.client.sign_message(account=account,
message=bytes(ursula_stamp))
ursula = Mock(stamp=ursula_stamp, operator_signature=signed_stamp)
return ursula
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 = Economics(
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.estimate_gas(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.bondOperator(staker1), {'from': staker1})
transact(staker_functions.bondOperator(staker2), {'from': staker2})
transact(staker_functions.bondOperator(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_ID_LENGTH))
policy_id_2 = os.urandom(int(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.get_block('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_ID_LENGTH))
policy_id_2 = os.urandom(int(POLICY_ID_LENGTH))
policy_id_3 = os.urandom(int(POLICY_ID_LENGTH))
number_of_periods = 100
value = 3 * number_of_periods * rate
current_timestamp = testerchain.w3.eth.get_block('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_ID_LENGTH))
policy_id_2 = os.urandom(int(POLICY_ID_LENGTH))
policy_id_3 = os.urandom(int(POLICY_ID_LENGTH))
number_of_periods = 100
value = number_of_periods * rate
current_timestamp = testerchain.w3.eth.get_block('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.get_block('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_ID_LENGTH))
policy_id_2 = os.urandom(int(POLICY_ID_LENGTH))
current_timestamp = testerchain.w3.eth.get_block('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.bondOperator(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 = Economics(genesis_hours_per_period=Economics._default_hours_per_period,
hours_per_period=2 * Economics._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.pre_application_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.min_operator_seconds
stake_size = token_economics.min_authorization
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.bondOperator(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.pre_application_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! *********")
if __name__ == "__main__":
print("Starting Up...")
analyzer = AnalyzeGas()
estimate_gas(analyzer=analyzer)
analyzer.to_json_file()

View File

@ -1,66 +0,0 @@
from decimal import Decimal, localcontext
import pytest
from nucypher.blockchain.economics import Economics
@pytest.mark.skip('remove me')
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 = Economics(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
def test_economic_parameter_aliases():
e = Economics()
deployment_params = e.pre_application_deployment_parameters
assert isinstance(deployment_params, tuple)
for parameter in deployment_params:
assert isinstance(parameter, int)

View File

@ -1,51 +0,0 @@
import maya
from eth_typing import BlockNumber
from unittest.mock import patch
from nucypher.blockchain.eth.constants import AVERAGE_BLOCK_TIME_IN_SECONDS
from nucypher.blockchain.eth.utils import epoch_to_period, estimate_block_number_for_period, period_to_epoch
SECONDS_PER_PERIOD = 60 * 60 * 24
def test_epoch_to_period():
timestamp = maya.now().epoch
current_period = epoch_to_period(epoch=timestamp, seconds_per_period=SECONDS_PER_PERIOD)
assert current_period == (timestamp // SECONDS_PER_PERIOD)
def test_period_to_epoch():
current_period = 12345678
epoch = period_to_epoch(period=current_period, seconds_per_period=SECONDS_PER_PERIOD)
assert epoch == (current_period * SECONDS_PER_PERIOD)
def test_estimate_block_number_for_period():
timestamp = maya.now().epoch
period = timestamp // SECONDS_PER_PERIOD
three_periods_back = period - 3
ten_periods_back = period - 10
latest_block_number = BlockNumber(12345678)
now = maya.now()
now_epoch = now.epoch
# ensure the same time is used in method and in test
with patch.object(maya, 'now', return_value=maya.MayaDT(epoch=now_epoch)):
block_number_for_three_periods_back = estimate_block_number_for_period(period=three_periods_back,
seconds_per_period=SECONDS_PER_PERIOD,
latest_block=latest_block_number)
block_number_for_ten_periods_back = estimate_block_number_for_period(period=ten_periods_back,
seconds_per_period=SECONDS_PER_PERIOD,
latest_block=latest_block_number)
for past_period, block_number_for_past_period in ((three_periods_back, block_number_for_three_periods_back),
(ten_periods_back, block_number_for_ten_periods_back)):
start_of_past_period = maya.MayaDT(epoch=(past_period * SECONDS_PER_PERIOD))
diff_in_seconds = int((now - start_of_past_period).total_seconds())
diff_in_blocks = diff_in_seconds // AVERAGE_BLOCK_TIME_IN_SECONDS
assert block_number_for_past_period < latest_block_number
assert block_number_for_past_period == (latest_block_number - diff_in_blocks)

View File

@ -1,18 +1,15 @@
from unittest.mock import MagicMock, Mock
import pytest
import pytest_twisted
from twisted.internet import task
from twisted.internet import threads
from twisted.internet import task, threads
from twisted.internet.task import Clock
from twisted.logger import globalLogPublisher, LogLevel
from twisted.logger import LogLevel, globalLogPublisher
from nucypher.blockchain.eth.clients import EthereumClient
from nucypher.blockchain.eth.token import WorkTracker, WorkTrackerBase
from nucypher.utilities.gas_strategies import GasStrategyError
from nucypher.blockchain.eth.token import WorkTrackerBase, WorkTracker
from nucypher.utilities.logging import Logger, GlobalLoggerSettings
from nucypher.utilities.logging import GlobalLoggerSettings, Logger
logger = Logger("test-logging")
@ -41,14 +38,6 @@ class WorkTrackerArbitraryFailureConditions(WorkTrackerBase):
self.workdone += 1
self._consecutive_fails = 0
@property
def staking_agent(self):
class MockStakingAgent:
def get_current_period(self):
return 1
return MockStakingAgent()
def _crash_gracefully(self, failure=None) -> None:
assert 'zomg something went wrong' in failure.getErrorMessage()

View File

@ -1,8 +1,5 @@
import os
from typing import List, Tuple, Union, Optional
from typing import List, Optional, Tuple, Union
import maya
from eth_tester.exceptions import TransactionFailed
@ -12,10 +9,19 @@ from web3 import Web3
from nucypher.blockchain.economics import Economics
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, BaseContractRegistry
from nucypher.blockchain.eth.interfaces import (
BlockchainDeployerInterface,
BlockchainInterfaceFactory,
)
from nucypher.blockchain.eth.registry import (
BaseContractRegistry,
InMemoryContractRegistry,
)
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.sol.compile.constants import TEST_SOLIDITY_SOURCE_ROOT, SOLIDITY_SOURCE_ROOT
from nucypher.blockchain.eth.sol.compile.constants import (
SOLIDITY_SOURCE_ROOT,
TEST_SOLIDITY_SOURCE_ROOT,
)
from nucypher.blockchain.eth.sol.compile.types import SourceBundle
from nucypher.blockchain.eth.token import NU
from nucypher.config.constants import TEMPORARY_DOMAIN
@ -28,7 +34,7 @@ from tests.constants import (
NUMBER_OF_ETH_TEST_ACCOUNTS,
NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS,
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS,
PYEVM_DEV_URI, TEST_GAS_LIMIT
PYEVM_DEV_URI,
)
@ -177,7 +183,7 @@ class TesterBlockchain(BlockchainDeployerInterface):
more_than_one_arg = sum(map(bool, (hours, seconds))) > 1
if more_than_one_arg:
raise ValueError("Specify hours, seconds, or periods, not a combination")
raise ValueError("Specify hours or seconds not a combination")
if hours:
duration = hours * (60*60)