mirror of https://github.com/nucypher/nucypher.git
Merge pull request #270 from KPrasch/sol-client
Blockchain client updates and bugfixespull/273/head
commit
31503e50e6
File diff suppressed because it is too large
Load Diff
|
@ -67,7 +67,7 @@ class Miner(TokenActor):
|
|||
def __update_locked_tokens(self) -> None:
|
||||
"""Query the contract for the amount of locked tokens on this miner's eth address and cache it"""
|
||||
|
||||
self.__locked_tokens = self.miner_agent.read().getLockedTokens(self.address)
|
||||
self.__locked_tokens = self.miner_agent.contract.functions.getLockedTokens(self.address).call()
|
||||
|
||||
@property
|
||||
def is_staking(self):
|
||||
|
@ -86,36 +86,43 @@ class Miner(TokenActor):
|
|||
def _approve_escrow(self, amount: int) -> str:
|
||||
"""Approve the transfer of token from the miner's address to the escrow contract."""
|
||||
|
||||
txhash = self.token_agent.transact({'from': self.address}).approve(self.miner_agent.contract_address, amount)
|
||||
txhash = self.token_agent.contract.functions.approve(self.miner_agent.contract_address, amount).transact({'from': self.address})
|
||||
self.blockchain.wait_for_receipt(txhash)
|
||||
|
||||
self._transactions.append((datetime.utcnow(), txhash))
|
||||
|
||||
return txhash
|
||||
|
||||
def _send_tokens_to_escrow(self, amount, locktime) -> str:
|
||||
def _send_tokens_to_escrow(self, amount, lock_periods) -> str:
|
||||
"""Send tokes to the escrow from the miner's address"""
|
||||
|
||||
deposit_txhash = self.miner_agent.transact({'from': self.address}).deposit(amount, locktime)
|
||||
deposit_txhash = self.miner_agent.contract.functions.deposit(amount, lock_periods).transact({'from': self.address})
|
||||
|
||||
self.blockchain.wait_for_receipt(deposit_txhash)
|
||||
|
||||
self._transactions.append((datetime.utcnow(), deposit_txhash))
|
||||
|
||||
return deposit_txhash
|
||||
|
||||
def deposit(self, amount: int, locktime: int) -> Tuple[str, str]:
|
||||
def deposit(self, amount: int, lock_periods: int) -> Tuple[str, str]:
|
||||
"""Public facing method for token locking."""
|
||||
approve_txhash = self._approve_escrow(amount=amount)
|
||||
deposit_txhash = self._send_tokens_to_escrow(amount=amount, locktime=locktime)
|
||||
deposit_txhash = self._send_tokens_to_escrow(amount=amount, lock_periods=lock_periods)
|
||||
|
||||
return approve_txhash, deposit_txhash
|
||||
|
||||
# TODO add divide_stake method
|
||||
def switch_lock(self):
|
||||
lock_txhash = self.miner_agent.contract.functions.switchLock().transact({'from': self.address})
|
||||
self.blockchain.wait_for_receipt(lock_txhash)
|
||||
|
||||
def _confirm_activity(self) -> str:
|
||||
self._transactions.append((datetime.utcnow(), lock_txhash))
|
||||
return lock_txhash
|
||||
|
||||
def confirm_activity(self) -> str:
|
||||
"""Miner rewarded for every confirmed period"""
|
||||
|
||||
txhash = self.miner_agent.transact({'from': self.address}).confirmActivity()
|
||||
txhash = self.miner_agent.contract.functions.confirmActivity().transact({'from': self.address})
|
||||
self.blockchain.wait_for_receipt(txhash)
|
||||
|
||||
self._transactions.append((datetime.utcnow(), txhash))
|
||||
|
@ -125,18 +132,17 @@ class Miner(TokenActor):
|
|||
def mint(self) -> Tuple[str, str]:
|
||||
"""Computes and transfers tokens to the miner's account"""
|
||||
|
||||
confirm_txhash = self.miner_agent.transact({'from': self.address, 'gas_price': 0}).confirmActivity()
|
||||
mint_txhash = self.miner_agent.transact({'from': self.address, 'gas_price': 0}).mint()
|
||||
mint_txhash = self.miner_agent.contract.functions.mint().transact({'from': self.address})
|
||||
|
||||
self.blockchain.wait_for_receipt(mint_txhash)
|
||||
self._transactions.append((datetime.utcnow(), mint_txhash))
|
||||
|
||||
return confirm_txhash, mint_txhash
|
||||
return mint_txhash
|
||||
|
||||
def collect_policy_reward(self, policy_manager):
|
||||
"""Collect rewarded ETH"""
|
||||
|
||||
policy_reward_txhash = policy_manager.transact({'from': self.address}).withdraw()
|
||||
policy_reward_txhash = policy_manager.contract.functions.withdraw().transact({'from': self.address})
|
||||
self.blockchain.wait_for_receipt(policy_reward_txhash)
|
||||
|
||||
self._transactions.append((datetime.utcnow(), policy_reward_txhash))
|
||||
|
@ -146,18 +152,27 @@ class Miner(TokenActor):
|
|||
def collect_staking_reward(self) -> str:
|
||||
"""Withdraw tokens rewarded for staking."""
|
||||
|
||||
token_amount = self.miner_agent.read().minerInfo(self.address)[self.miner_agent.MinerInfo.VALUE.value]
|
||||
token_amount_bytes = self.miner_agent.contract.functions.minerInfo(self.address).call()[0]
|
||||
token_amount = self.blockchain.provider.w3.toInt(token_amount_bytes)
|
||||
|
||||
# reward_amount = TODO
|
||||
collection_txhash = self.miner_agent.contract.functions.withdraw(token_amount).transact({'from': self.address})
|
||||
|
||||
reward_txhash = self.miner_agent.transact({'from': self.address}).withdraw(token_amount)
|
||||
self.blockchain.wait_for_receipt(collection_txhash)
|
||||
self._transactions.append((datetime.utcnow(), collection_txhash))
|
||||
|
||||
self.blockchain.wait_for_receipt(reward_txhash)
|
||||
self._transactions.append((datetime.utcnow(), reward_txhash))
|
||||
return collection_txhash
|
||||
|
||||
return reward_txhash
|
||||
def __validate_stake(self, amount: int, lock_periods: int) -> bool:
|
||||
|
||||
def stake(self, amount, locktime, entire_balance=False):
|
||||
assert self.miner_agent.validate_stake_amount(amount=amount)
|
||||
assert self.miner_agent.validate_locktime(lock_periods=lock_periods)
|
||||
|
||||
if not self.token_balance() >= amount:
|
||||
raise self.StakingError("Insufficient miner token balance ({balance})".format(balance=self.token_balance()))
|
||||
else:
|
||||
return True
|
||||
|
||||
def stake(self, amount, lock_periods, entire_balance=False):
|
||||
"""
|
||||
High level staking method for Miners.
|
||||
"""
|
||||
|
@ -167,17 +182,14 @@ class Miner(TokenActor):
|
|||
if entire_balance and amount:
|
||||
raise self.StakingError("Specify an amount or entire balance, not both")
|
||||
|
||||
if not locktime >= 0:
|
||||
min_stake_time = self.miner_agent._deployer._min_release_periods
|
||||
raise self.StakingError('Locktime must be at least {}'.format(min_stake_time))
|
||||
|
||||
if entire_balance is True:
|
||||
amount = self.miner_agent.read().minerInfo(self.address)[self.miner_agent.MinerInfo.VALUE.value]
|
||||
else:
|
||||
if not amount > 0:
|
||||
raise self.StakingError('Staking amount must be greater than zero.')
|
||||
amount = self.miner_agent.contract.functions.getMinerInfo(self.miner_agent.MinerInfo.VALUE.value,
|
||||
self.address, 0).call()
|
||||
amount = self.blockchain.provider.w3.toInt(amount)
|
||||
|
||||
approve_txhash, initial_deposit_txhash = self.deposit(amount=amount, locktime=locktime)
|
||||
assert self.__validate_stake(amount=amount, lock_periods=lock_periods)
|
||||
|
||||
approve_txhash, initial_deposit_txhash = self.deposit(amount=amount, lock_periods=lock_periods)
|
||||
self._transactions.append((datetime.utcnow(), initial_deposit_txhash))
|
||||
|
||||
return staking_transactions
|
||||
|
@ -185,7 +197,7 @@ class Miner(TokenActor):
|
|||
def publish_data(self, data) -> str:
|
||||
"""Store new data"""
|
||||
|
||||
txhash = self.miner_agent.transact({'from': self.address}).setMinerId(data)
|
||||
txhash = self.miner_agent.contract.functions.setMinerId(data).transact({'from': self.address})
|
||||
self.blockchain.wait_for_receipt(txhash)
|
||||
|
||||
self._transactions.append((datetime.utcnow(), txhash))
|
||||
|
@ -195,13 +207,14 @@ class Miner(TokenActor):
|
|||
def fetch_data(self) -> tuple:
|
||||
"""Retrieve all asosciated contract data for this miner."""
|
||||
|
||||
count = self.miner_agent.read().getMinerIdsLength(self.address)
|
||||
count_bytes = self.miner_agent.contract.functions.getMinerIdsLength(self.address).call()
|
||||
|
||||
count = self.blockchain.provider.w3.toInt(count_bytes)
|
||||
|
||||
miner_ids = list()
|
||||
for index in range(count):
|
||||
miner_id = self.miner_agent.read().getMinerId(self.address, index)
|
||||
miner_id = self.miner_agent.contract.functions.getMinerId(self.address, index).call()
|
||||
miner_ids.append(miner_id)
|
||||
|
||||
return tuple(miner_ids)
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import random
|
||||
from abc import ABC
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
from typing import Set, Generator, List
|
||||
|
||||
from functools import partial
|
||||
|
||||
from nucypher.blockchain.eth.deployers import MinerEscrowDeployer, NucypherTokenDeployer, PolicyManagerDeployer, \
|
||||
ContractDeployer
|
||||
from nucypher.blockchain.eth import constants
|
||||
from nucypher.blockchain.eth.constants import NucypherTokenConfig, NucypherMinerConfig
|
||||
from web3.contract import Contract
|
||||
|
||||
|
||||
class EthereumContractAgent(ABC):
|
||||
|
@ -17,26 +17,32 @@ class EthereumContractAgent(ABC):
|
|||
_principal_contract_name = NotImplemented
|
||||
__contract_address = NotImplemented
|
||||
|
||||
class ContractNotDeployed(ContractDeployer.ContractDeploymentError):
|
||||
class ContractNotDeployed(Exception):
|
||||
pass
|
||||
|
||||
def __init__(self, blockchain, *args, **kwargs):
|
||||
def __init__(self, blockchain, contract: Contract=None, *args, **kwargs):
|
||||
self.blockchain = blockchain
|
||||
|
||||
address = blockchain.provider.get_contract_address(contract_name=self._principal_contract_name)[-1] # TODO
|
||||
self._contract = blockchain.provider.get_contract(address)
|
||||
if contract is None:
|
||||
address = blockchain.provider.get_contract_address(contract_name=self._principal_contract_name)[-1] # TODO
|
||||
contract = blockchain.provider.get_contract(address)
|
||||
self.__contract = contract
|
||||
|
||||
def __repr__(self):
|
||||
class_name = self.__class__.__name__
|
||||
r = "{}(blockchain={}, contract={})"
|
||||
return r.format(class_name, self.blockchain, self._contract)
|
||||
return r.format(class_name, self.blockchain, self.__contract)
|
||||
|
||||
def __eq__(self, other):
|
||||
return bool(self.contract_address == other.contract_address)
|
||||
|
||||
@property
|
||||
def contract(self):
|
||||
return self.__contract
|
||||
|
||||
@property
|
||||
def contract_address(self):
|
||||
return self._contract.address
|
||||
return self.__contract.address
|
||||
|
||||
@property
|
||||
def contract_name(self) -> str:
|
||||
|
@ -46,47 +52,27 @@ class EthereumContractAgent(ABC):
|
|||
def origin(self) -> str:
|
||||
return self.blockchain.provider.w3.eth.coinbase # TODO: make swappable
|
||||
|
||||
def read(self):
|
||||
"""
|
||||
Returns an object that exposes the contract instance functions.
|
||||
|
||||
This method is intended for use with method chaining,
|
||||
results in zero state changes, and costs zero gas.
|
||||
Useful as a dry-run before sending an actual transaction.
|
||||
|
||||
See more on interacting with contract instances in the Populus docs:
|
||||
http://populus.readthedocs.io/en/latest/dev_cycle.part-07.html#call-an-instance-function
|
||||
"""
|
||||
return self._contract.call()
|
||||
|
||||
def transact(self, payload: dict):
|
||||
"""Packs kwargs into payload dictionary and transmits an eth contract transaction"""
|
||||
return self._contract.transact(payload)
|
||||
|
||||
def get_balance(self, address: str=None) -> int:
|
||||
"""Get the balance of a token address, or of this contract address"""
|
||||
if address is None:
|
||||
address = self.contract_address
|
||||
return self.read().balanceOf(address)
|
||||
return self.contract.functions.balanceOf(address).call()
|
||||
|
||||
|
||||
class NucypherTokenAgent(EthereumContractAgent):
|
||||
|
||||
_deployer = NucypherTokenDeployer
|
||||
_principal_contract_name = NucypherTokenDeployer._contract_name
|
||||
class NucypherTokenAgent(EthereumContractAgent, NucypherTokenConfig):
|
||||
_principal_contract_name = "NuCypherToken"
|
||||
|
||||
|
||||
class MinerAgent(EthereumContractAgent):
|
||||
class MinerAgent(EthereumContractAgent, NucypherMinerConfig):
|
||||
"""
|
||||
Wraps NuCypher's Escrow solidity smart contract, and manages a... PopulusContract?
|
||||
Wraps NuCypher's Escrow solidity smart contract
|
||||
|
||||
In order to become a participant of the network,
|
||||
a miner locks tokens by depositing to the Escrow contract address
|
||||
for a duration measured in periods.
|
||||
"""
|
||||
|
||||
_deployer = MinerEscrowDeployer
|
||||
_principal_contract_name = MinerEscrowDeployer._contract_name
|
||||
_principal_contract_name = "MinersEscrow"
|
||||
|
||||
class NotEnoughUrsulas(Exception):
|
||||
pass
|
||||
|
@ -98,8 +84,8 @@ class MinerAgent(EthereumContractAgent):
|
|||
CONFIRMED_PERIOD_1 = 3
|
||||
CONFIRMED_PERIOD_2 = 4
|
||||
|
||||
def __init__(self, token_agent: NucypherTokenAgent):
|
||||
super().__init__(blockchain=token_agent.blockchain) # TODO: public
|
||||
def __init__(self, token_agent: NucypherTokenAgent, *args, **kwargs):
|
||||
super().__init__(blockchain=token_agent.blockchain, *args, **kwargs)
|
||||
self.token_agent = token_agent
|
||||
self.miners = list() # Tracks per client
|
||||
|
||||
|
@ -117,10 +103,10 @@ class MinerAgent(EthereumContractAgent):
|
|||
Miner addresses will be returned in the order in which they were added to the MinersEscrow's ledger
|
||||
"""
|
||||
|
||||
count = self.read().getMinersLength()
|
||||
|
||||
count = self.contract.functions.getMinersLength().call()
|
||||
for index in range(count):
|
||||
yield self.read().miners(index)
|
||||
addr = self.contract.functions.miners(index).call()
|
||||
yield self.blockchain.provider.w3.toChecksumAddress(addr)
|
||||
|
||||
def sample(self, quantity: int=10, additional_ursulas: float=1.7, attempts: int=5, duration: int=10) -> List[str]:
|
||||
"""
|
||||
|
@ -147,7 +133,7 @@ class MinerAgent(EthereumContractAgent):
|
|||
|
||||
system_random = random.SystemRandom()
|
||||
n_select = round(quantity*additional_ursulas) # Select more Ursulas
|
||||
n_tokens = self.read().getAllLockedTokens()
|
||||
n_tokens = self.contract.functions.getAllLockedTokens().call()
|
||||
|
||||
if not n_tokens > 0:
|
||||
raise self.NotEnoughUrsulas('There are no locked tokens.')
|
||||
|
@ -156,9 +142,9 @@ class MinerAgent(EthereumContractAgent):
|
|||
points = [0] + sorted(system_random.randrange(n_tokens) for _ in range(n_select))
|
||||
deltas = [i-j for i, j in zip(points[1:], points[:-1])]
|
||||
|
||||
addrs, addr, index, shift = set(), self._deployer._null_addr, 0, 0
|
||||
addrs, addr, index, shift = set(), constants.NULL_ADDRESS, 0, 0
|
||||
for delta in deltas:
|
||||
addr, index, shift = self.read().findCumSum(index, delta + shift, duration)
|
||||
addr, index, shift = self.contract.functions.findCumSum(index, delta + shift, duration).call()
|
||||
addrs.add(addr)
|
||||
|
||||
if len(addrs) >= quantity:
|
||||
|
@ -169,18 +155,17 @@ class MinerAgent(EthereumContractAgent):
|
|||
|
||||
class PolicyAgent(EthereumContractAgent):
|
||||
|
||||
_deployer = PolicyManagerDeployer
|
||||
_principal_contract_name = PolicyManagerDeployer._contract_name
|
||||
_principal_contract_name = "PolicyManager"
|
||||
|
||||
def fetch_arrangement_data(self, arrangement_id: bytes) -> list:
|
||||
blockchain_record = self.read().policies(arrangement_id)
|
||||
blockchain_record = self.contract.functions.policies(arrangement_id).call()
|
||||
return blockchain_record
|
||||
|
||||
def revoke_arrangement(self, arrangement_id: bytes, author, gas_price: int):
|
||||
def revoke_arrangement(self, arrangement_id: bytes, author):
|
||||
"""
|
||||
Revoke by arrangement ID; Only the policy author can revoke the policy
|
||||
"""
|
||||
|
||||
txhash = self.transact({'from': author.address, 'gas_price': gas_price}).revokePolicy(arrangement_id)
|
||||
txhash = self.contract.functions.revokePolicy(arrangement_id).transact({'from': author.address})
|
||||
self.blockchain.wait_for_receipt(txhash)
|
||||
return txhash
|
||||
|
|
|
@ -2,6 +2,7 @@ import random
|
|||
from abc import ABC
|
||||
from typing import List
|
||||
|
||||
from nucypher.blockchain.eth.constants import NucypherMinerConfig
|
||||
from nucypher.blockchain.eth.interfaces import ContractProvider
|
||||
|
||||
|
||||
|
@ -71,7 +72,7 @@ class TheBlockchain(ABC):
|
|||
return result
|
||||
|
||||
|
||||
class TesterBlockchain(TheBlockchain):
|
||||
class TesterBlockchain(TheBlockchain, NucypherMinerConfig):
|
||||
"""Transient, in-memory, local, private chain"""
|
||||
|
||||
_network = 'tester'
|
||||
|
@ -84,42 +85,29 @@ class TesterBlockchain(TheBlockchain):
|
|||
result = self.provider.w3.eth.waitForTransactionReceipt(txhash, timeout=timeout)
|
||||
return result
|
||||
|
||||
def time_travel(self, hours=None, seconds=None):
|
||||
"""Wait the specified number of wait_hours by comparing block timestamps."""
|
||||
if hours:
|
||||
duration = hours * 60 * 60
|
||||
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 lock_periods, not a combination")
|
||||
|
||||
if periods:
|
||||
duration = (self._hours_per_period * periods) * (60 * 60)
|
||||
elif hours:
|
||||
duration = hours * (60 * 60)
|
||||
elif seconds:
|
||||
duration = seconds
|
||||
else:
|
||||
raise Exception("Invalid time")
|
||||
raise ValueError("Specify either hours, seconds, or lock_periods.")
|
||||
|
||||
end_timestamp = self.provider.w3.eth.getBlock('latest').timestamp + duration
|
||||
self.provider.w3.eth.web3.testing.timeTravel(end_timestamp)
|
||||
end_timestamp = self.provider.w3.eth.getBlock(block_identifier='latest').timestamp + duration
|
||||
self.provider.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp)
|
||||
self.provider.w3.eth.web3.testing.mine(1)
|
||||
|
||||
def spawn_miners(self, miner_agent, addresses: list, locktime: int, random_amount=False) -> list:
|
||||
|
||||
"""
|
||||
Deposit and lock a random amount of tokens in the miner escrow
|
||||
from each address, "spawning" new Miners.
|
||||
"""
|
||||
from nucypher.blockchain.eth.actors import Miner
|
||||
|
||||
miners = list()
|
||||
for address in addresses:
|
||||
miner = Miner(miner_agent=miner_agent, address=address)
|
||||
miners.append(miner)
|
||||
|
||||
if random_amount is True:
|
||||
min_stake = miner_agent._min_allowed_locked #TODO
|
||||
max_stake = miner_agent._max_allowed_locked
|
||||
amount = random.randint(min_stake, max_stake)
|
||||
else:
|
||||
amount = miner.token_balance() // 2 # stake half
|
||||
miner.stake(amount=amount, locktime=locktime)
|
||||
|
||||
return miners
|
||||
|
||||
def _global_airdrop(self, amount: int) -> List[str]:
|
||||
"""Airdrops from creator address to all other addresses!"""
|
||||
coinbase, *addresses = self.provider.w3.eth.accounts
|
||||
|
@ -128,6 +116,7 @@ class TesterBlockchain(TheBlockchain):
|
|||
for address in addresses:
|
||||
tx = {'to': address, 'from': coinbase, 'value': amount}
|
||||
txhash = self.provider.w3.eth.sendTransaction(tx)
|
||||
_receipt = self.provider.w3.eth.waitForTransactionReceipt(txhash)
|
||||
tx_hashes.append(txhash)
|
||||
return tx_hashes
|
||||
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
class NuCypherTokenConfig:
|
||||
NULL_ADDRESS = '0x' + '0' * 40
|
||||
|
||||
|
||||
class NucypherTokenConfig:
|
||||
|
||||
class TokenConfigError(ValueError):
|
||||
pass
|
||||
|
||||
__subdigits = 18
|
||||
_M = 10 ** __subdigits # Unit designation
|
||||
__initial_supply = int(1e9) * _M # Initial token supply
|
||||
|
@ -10,31 +17,30 @@ class NuCypherTokenConfig:
|
|||
return self.__saturation
|
||||
|
||||
|
||||
class NuCypherMinerConfig:
|
||||
class NucypherMinerConfig:
|
||||
|
||||
class MinerConfigError(ValueError):
|
||||
pass
|
||||
|
||||
_hours_per_period = 24 # Hours in single period
|
||||
_min_locked_periods = 30 # 720 Hours minimum
|
||||
__max_minting_periods = 365 # Maximum number of periods
|
||||
min_locked_periods = 30 # 720 Hours minimum
|
||||
max_minting_periods = 365 # Maximum number of periods
|
||||
|
||||
_min_allowed_locked = 15000 * NuCypherTokenConfig._M
|
||||
_max_allowed_locked = int(4e6) * NuCypherTokenConfig._M
|
||||
min_allowed_locked = 15000 * NucypherTokenConfig._M
|
||||
max_allowed_locked = int(4e6) * NucypherTokenConfig._M
|
||||
|
||||
_null_addr = '0x' + '0' * 40
|
||||
__remaining_supply = NuCypherTokenConfig._remaining_supply
|
||||
__remaining_supply = NucypherTokenConfig._remaining_supply
|
||||
|
||||
__mining_coeff = [ # TODO
|
||||
_hours_per_period,
|
||||
2 * 10 ** 7,
|
||||
__max_minting_periods,
|
||||
__max_minting_periods,
|
||||
_min_locked_periods,
|
||||
_min_allowed_locked,
|
||||
_max_allowed_locked
|
||||
max_minting_periods,
|
||||
max_minting_periods,
|
||||
min_locked_periods,
|
||||
min_allowed_locked,
|
||||
max_allowed_locked
|
||||
]
|
||||
|
||||
@property
|
||||
def null_address(self):
|
||||
return self._null_addr
|
||||
|
||||
@property
|
||||
def mining_coefficient(self):
|
||||
return self.__mining_coeff
|
||||
|
@ -42,3 +48,43 @@ class NuCypherMinerConfig:
|
|||
@property
|
||||
def remaining_supply(self):
|
||||
return self.__remaining_supply
|
||||
|
||||
def __validate(self, rulebook) -> bool:
|
||||
for rule, failure_message in rulebook:
|
||||
if not rule:
|
||||
raise self.MinerConfigError(failure_message)
|
||||
return True
|
||||
|
||||
def validate_stake_amount(self, amount: int, raise_on_fail=True) -> bool:
|
||||
|
||||
rulebook = (
|
||||
|
||||
(amount >= self.min_allowed_locked,
|
||||
'Stake amount too low; ({amount}) must be at least {minimum}'
|
||||
.format(minimum=self.min_allowed_locked, amount=amount)),
|
||||
|
||||
(amount <= self.max_allowed_locked,
|
||||
'Stake amount too high; ({amount}) must be no more than {maximum}.'
|
||||
.format(maximum=self.max_allowed_locked, amount=amount)),
|
||||
)
|
||||
|
||||
if raise_on_fail is True:
|
||||
self.__validate(rulebook=rulebook)
|
||||
return all(rulebook)
|
||||
|
||||
def validate_locktime(self, lock_periods: int, raise_on_fail=True) -> bool:
|
||||
|
||||
rulebook = (
|
||||
|
||||
(lock_periods >= self.min_locked_periods,
|
||||
'Locktime ({locktime}) too short; must be at least {minimum}'
|
||||
.format(minimum=self.min_locked_periods, locktime=lock_periods)),
|
||||
|
||||
(lock_periods <= self.max_minting_periods,
|
||||
'Locktime ({locktime}) too long; must be no more than {maximum}'
|
||||
.format(maximum=self.max_minting_periods, locktime=lock_periods)),
|
||||
)
|
||||
|
||||
if raise_on_fail is True:
|
||||
self.__validate(rulebook=rulebook)
|
||||
return all(rulebook)
|
||||
|
|
|
@ -2,12 +2,14 @@ from typing import Tuple, Dict
|
|||
|
||||
from web3.contract import Contract
|
||||
|
||||
from nucypher.blockchain.eth.constants import NuCypherMinerConfig, NuCypherTokenConfig
|
||||
from nucypher.blockchain.eth.agents import EthereumContractAgent, MinerAgent, NucypherTokenAgent, PolicyAgent
|
||||
from nucypher.blockchain.eth.constants import NucypherTokenConfig, NucypherMinerConfig
|
||||
from .chains import TheBlockchain
|
||||
|
||||
|
||||
class ContractDeployer:
|
||||
|
||||
agency = NotImplemented
|
||||
_contract_name = NotImplemented
|
||||
_arming_word = "I UNDERSTAND"
|
||||
|
||||
|
@ -149,10 +151,15 @@ class ContractDeployer:
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def make_agent(self) -> EthereumContractAgent:
|
||||
agent = self.agency(blockchain=self.blockchain, contract=self._contract)
|
||||
return agent
|
||||
|
||||
class NucypherTokenDeployer(ContractDeployer, NuCypherTokenConfig):
|
||||
|
||||
class NucypherTokenDeployer(ContractDeployer, NucypherTokenConfig):
|
||||
|
||||
_contract_name = 'NuCypherToken'
|
||||
agency = NucypherTokenAgent
|
||||
|
||||
def __init__(self, blockchain):
|
||||
super().__init__(blockchain=blockchain)
|
||||
|
@ -199,11 +206,12 @@ class DispatcherDeployer(ContractDeployer):
|
|||
return txhash
|
||||
|
||||
|
||||
class MinerEscrowDeployer(ContractDeployer, NuCypherMinerConfig):
|
||||
class MinerEscrowDeployer(ContractDeployer, NucypherMinerConfig):
|
||||
"""
|
||||
Deploys the MinerEscrow ethereum contract to the blockchain. Depends on NuCypherTokenAgent
|
||||
Deploys the MinerEscrow ethereum contract to the blockchain. Depends on NucypherTokenAgent
|
||||
"""
|
||||
|
||||
agency = MinerAgent
|
||||
_contract_name = 'MinersEscrow'
|
||||
|
||||
def __init__(self, token_agent):
|
||||
|
@ -257,7 +265,7 @@ class MinerEscrowDeployer(ContractDeployer, NuCypherMinerConfig):
|
|||
the_escrow_contract = wrapped_escrow_contract
|
||||
|
||||
# 3 - Transfer tokens to the miner escrow #
|
||||
reward_txhash = self.token_agent.transact(origin_args).transfer(the_escrow_contract.address, self.remaining_supply)
|
||||
reward_txhash = self.token_agent.contract.functions.transfer(the_escrow_contract.address, self.remaining_supply).transact(origin_args)
|
||||
_reward_receipt = self.blockchain.wait_for_receipt(reward_txhash)
|
||||
|
||||
# 4 - Initialize the Miner Escrow contract
|
||||
|
@ -275,12 +283,17 @@ class MinerEscrowDeployer(ContractDeployer, NuCypherMinerConfig):
|
|||
self.deployment_transactions = deployment_transactions
|
||||
return deployment_transactions
|
||||
|
||||
def make_agent(self) -> EthereumContractAgent:
|
||||
agent = self.agency(token_agent=self.token_agent, contract=self._contract)
|
||||
return agent
|
||||
|
||||
|
||||
class PolicyManagerDeployer(ContractDeployer):
|
||||
"""
|
||||
Depends on MinerAgent and NuCypherTokenAgent
|
||||
Depends on MinerAgent and NucypherTokenAgent
|
||||
"""
|
||||
|
||||
agency = PolicyAgent
|
||||
_contract_name = 'PolicyManager'
|
||||
|
||||
def __init__(self, miner_agent):
|
||||
|
@ -314,8 +327,8 @@ class PolicyManagerDeployer(ContractDeployer):
|
|||
the_policy_manager_contract = wrapped_policy_manager_contract
|
||||
|
||||
# Configure the MinerEscrow by setting the PolicyManager
|
||||
policy_setter_txhash = self.miner_agent.transact({'from': self.token_agent.origin}).\
|
||||
setPolicyManager(the_policy_manager_contract.address)
|
||||
policy_setter_txhash = self.miner_agent.contract.functions. \
|
||||
setPolicyManager(the_policy_manager_contract.address).transact({'from': self.token_agent.origin})
|
||||
|
||||
self.blockchain.wait_for_receipt(policy_setter_txhash)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class BlockchainArrangement:
|
|||
A relationship between Alice and a single Ursula as part of Blockchain Policy
|
||||
"""
|
||||
|
||||
def __init__(self, author: str, miner: str, value: int, periods: int, arrangement_id: bytes=None):
|
||||
def __init__(self, author: str, miner: str, value: int, lock_periods: int, arrangement_id: bytes=None):
|
||||
|
||||
self.id = arrangement_id
|
||||
|
||||
|
@ -17,11 +17,11 @@ class BlockchainArrangement:
|
|||
self.miner = miner
|
||||
|
||||
# Arrangement value, rate, and duration
|
||||
rate = value // periods
|
||||
rate = value // lock_periods
|
||||
self._rate = rate
|
||||
|
||||
self.value = value
|
||||
self.periods = periods # TODO: datetime -> duration in blocks
|
||||
self.lock_periods = lock_periods # TODO: datetime -> duration in blocks
|
||||
|
||||
self.is_published = False
|
||||
|
||||
|
@ -40,7 +40,7 @@ class BlockchainArrangement:
|
|||
|
||||
txhash = self.policy_agent.transact(payload).createPolicy(self.id,
|
||||
self.miner.address,
|
||||
self.periods)
|
||||
self.lock_periods)
|
||||
|
||||
self.policy_agent._blockchain._chain.wait.for_receipt(txhash)
|
||||
|
||||
|
@ -64,16 +64,16 @@ class BlockchainPolicy:
|
|||
def __init__(self):
|
||||
self._arrangements = list()
|
||||
|
||||
def publish_arrangement(self, miner, periods: int, rate: int, arrangement_id: bytes=None) -> 'BlockchainArrangement':
|
||||
def publish_arrangement(self, miner, lock_periods: int, rate: int, arrangement_id: bytes=None) -> 'BlockchainArrangement':
|
||||
"""
|
||||
Create a new arrangement to carry out a blockchain policy for the specified rate and time.
|
||||
"""
|
||||
|
||||
value = rate * periods
|
||||
value = rate * lock_periods
|
||||
arrangement = BlockchainArrangement(author=self,
|
||||
miner=miner,
|
||||
value=value,
|
||||
periods=periods)
|
||||
lock_periods=lock_periods)
|
||||
|
||||
self._arrangements[arrangement.id] = {arrangement_id: arrangement}
|
||||
return arrangement
|
||||
|
@ -87,7 +87,7 @@ class BlockchainPolicy:
|
|||
duration = end_block - start_block
|
||||
|
||||
miner = Miner(address=miner_address, miner_agent=self.policy_agent.miner_agent)
|
||||
arrangement = BlockchainArrangement(author=self, miner=miner, periods=duration)
|
||||
arrangement = BlockchainArrangement(author=self, miner=miner, lock_periods=duration)
|
||||
|
||||
arrangement.is_published = True
|
||||
return arrangement
|
||||
|
|
|
@ -15,7 +15,6 @@ contract MinersEscrowInterface {
|
|||
/**
|
||||
* @notice Contract for version voting
|
||||
**/
|
||||
// TODO there must be a way to cancel upgrade if there are errors
|
||||
contract Government is Upgradeable {
|
||||
using SafeMath for uint256;
|
||||
|
||||
|
@ -27,7 +26,8 @@ contract Government is Upgradeable {
|
|||
event UpgradeCommitted(
|
||||
uint256 indexed votingNumber,
|
||||
VotingType indexed votingType,
|
||||
address indexed newAddress
|
||||
address indexed newAddress,
|
||||
bool successful
|
||||
);
|
||||
|
||||
enum VotingState {
|
||||
|
@ -104,7 +104,17 @@ contract Government is Upgradeable {
|
|||
address _newAddress
|
||||
) public {
|
||||
require(getVotingState() == VotingState.Finished &&
|
||||
MinersEscrowInterface(escrow).getLockedTokens(msg.sender) != 0);
|
||||
MinersEscrowInterface(escrow).getLockedTokens(msg.sender) != 0 &&
|
||||
(_newAddress != 0x0 &&
|
||||
(_votingType == VotingType.UpgradeGovernment ||
|
||||
_votingType == VotingType.UpgradeEscrow ||
|
||||
_votingType == VotingType.UpgradePolicyManager) ||
|
||||
_votingType == VotingType.RollbackGovernment &&
|
||||
previousTarget != 0x0 ||
|
||||
_votingType == VotingType.RollbackEscrow &&
|
||||
escrow.previousTarget() != 0x0 ||
|
||||
_votingType == VotingType.RollbackPolicyManager &&
|
||||
policyManager.previousTarget() != 0x0));
|
||||
votingNumber = votingNumber.add(1);
|
||||
endVotingTimestamp = block.timestamp.add(votingDurationSeconds);
|
||||
upgradeFinished = false;
|
||||
|
@ -136,20 +146,31 @@ contract Government is Upgradeable {
|
|||
function commitUpgrade() public {
|
||||
require(getVotingState() == VotingState.UpgradeWaiting);
|
||||
upgradeFinished = true;
|
||||
bool upgrade = true;
|
||||
address callAddress;
|
||||
if (votingType == VotingType.UpgradeGovernment) {
|
||||
Dispatcher(address(this)).upgrade(newAddress);
|
||||
callAddress = address(this);
|
||||
} else if (votingType == VotingType.UpgradeEscrow) {
|
||||
escrow.upgrade(newAddress);
|
||||
callAddress = address(escrow);
|
||||
} else if (votingType == VotingType.UpgradePolicyManager) {
|
||||
policyManager.upgrade(newAddress);
|
||||
callAddress = address(policyManager);
|
||||
} else if (votingType == VotingType.RollbackGovernment) {
|
||||
Dispatcher(address(this)).rollback();
|
||||
upgrade = false;
|
||||
callAddress = address(this);
|
||||
} else if (votingType == VotingType.RollbackEscrow) {
|
||||
escrow.rollback();
|
||||
upgrade = false;
|
||||
callAddress = address(escrow);
|
||||
} else if (votingType == VotingType.RollbackPolicyManager) {
|
||||
policyManager.rollback();
|
||||
upgrade = false;
|
||||
callAddress = address(policyManager);
|
||||
}
|
||||
emit UpgradeCommitted(votingNumber, votingType, newAddress);
|
||||
bool result;
|
||||
if (upgrade) {
|
||||
result = callAddress.call(bytes4(keccak256("upgrade(address)")), newAddress);
|
||||
} else {
|
||||
result = callAddress.call(bytes4(keccak256("rollback()")));
|
||||
}
|
||||
emit UpgradeCommitted(votingNumber, votingType, newAddress, result);
|
||||
}
|
||||
|
||||
function verifyState(address _testTarget) public onlyOwner {
|
||||
|
|
|
@ -22,7 +22,7 @@ from nucypher.crypto.powers import CryptoPower, SigningPower, EncryptingPower, D
|
|||
from nucypher.crypto.signature import Signature, signature_splitter, SignatureStamp, StrangerStamp
|
||||
from nucypher.network import blockchain_client
|
||||
from nucypher.network.protocols import dht_value_splitter, dht_with_hrac_splitter
|
||||
from nucypher.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer, ProxyRESTServer
|
||||
from nucypher.network.server import NucypherDHTServer, NucypherSeedOnlyDHTServer, ProxyRESTServer
|
||||
|
||||
|
||||
class Character(object):
|
||||
|
@ -317,7 +317,7 @@ class FakePolicyAgent: # TODO: #192
|
|||
|
||||
|
||||
class Alice(Character, PolicyAuthor):
|
||||
_server_class = NuCypherSeedOnlyDHTServer
|
||||
_server_class = NucypherSeedOnlyDHTServer
|
||||
_default_crypto_powerups = [SigningPower, EncryptingPower, DelegatingPower]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -389,7 +389,7 @@ class Alice(Character, PolicyAuthor):
|
|||
|
||||
|
||||
class Bob(Character):
|
||||
_server_class = NuCypherSeedOnlyDHTServer
|
||||
_server_class = NucypherSeedOnlyDHTServer
|
||||
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -590,7 +590,7 @@ class Bob(Character):
|
|||
|
||||
|
||||
class Ursula(Character, ProxyRESTServer):
|
||||
_server_class = NuCypherDHTServer
|
||||
_server_class = NucypherDHTServer
|
||||
_alice_class = Alice
|
||||
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ class NucypherConfig:
|
|||
|
||||
@classmethod
|
||||
def from_json_config(cls, path: str=None):
|
||||
"""TODO: Reads the config file and creates a NucypherConfig instance"""
|
||||
"""TODO: Reads the config file and creates a NuCypherConfig instance"""
|
||||
with open(cls.__default_json_config_filepath, 'r') as config_file:
|
||||
data = json.loads(config_file.read())
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ class NucypherKeyring:
|
|||
pub_signing_key_path: str=None,
|
||||
transacting_key_path: str=None):
|
||||
"""
|
||||
Generates a NucypherKeyring instance with the provided key paths,
|
||||
Generates a NuCypherKeyring instance with the provided key paths,
|
||||
falling back to default keyring paths.
|
||||
"""
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from nucypher.network.capabilities import ServerCapability
|
|||
from umbral.fragments import CapsuleFrag
|
||||
|
||||
|
||||
class NuCypherNode(Node):
|
||||
class NucypherNode(Node):
|
||||
|
||||
def __init__(self, id, ip=None, port=None, capabilities=None, capabilities_as_strings=[], *args, **kwargs):
|
||||
self.id = id
|
||||
|
|
|
@ -7,18 +7,18 @@ from nucypher.crypto.api import keccak_digest
|
|||
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH
|
||||
from nucypher.crypto.signature import Signature
|
||||
from bytestring_splitter import BytestringSplitter
|
||||
from nucypher.network.node import NuCypherNode
|
||||
from nucypher.network.routing import NuCypherRoutingTable
|
||||
from nucypher.network.node import NucypherNode
|
||||
from nucypher.network.routing import NucypherRoutingTable
|
||||
from umbral.keys import UmbralPublicKey
|
||||
|
||||
dht_value_splitter = default_constant_splitter + BytestringSplitter(Signature, (UmbralPublicKey, PUBLIC_KEY_LENGTH))
|
||||
dht_with_hrac_splitter = dht_value_splitter + BytestringSplitter((bytes, KECCAK_DIGEST_LENGTH))
|
||||
|
||||
|
||||
class NuCypherHashProtocol(KademliaProtocol):
|
||||
class NucypherHashProtocol(KademliaProtocol):
|
||||
def __init__(self, sourceNode, storage, ksize, *args, **kwargs):
|
||||
super().__init__(sourceNode, storage, ksize, *args, **kwargs)
|
||||
self.router = NuCypherRoutingTable(self, ksize, sourceNode)
|
||||
self.router = NucypherRoutingTable(self, ksize, sourceNode)
|
||||
self.illegal_keys_seen = []
|
||||
# TODO: This is the wrong way to do this. See #227.
|
||||
self.treasure_maps = {}
|
||||
|
@ -31,13 +31,13 @@ class NuCypherHashProtocol(KademliaProtocol):
|
|||
return True
|
||||
|
||||
def rpc_ping(self, sender, nodeid, node_capabilities=[]):
|
||||
source = NuCypherNode(nodeid, sender[0], sender[1],
|
||||
source = NucypherNode(nodeid, sender[0], sender[1],
|
||||
capabilities_as_strings=node_capabilities)
|
||||
self.welcomeIfNewNode(source)
|
||||
return self.sourceNode.id
|
||||
|
||||
async def callStore(self, nodeToAsk, key, value):
|
||||
# nodeToAsk = NuCypherNode
|
||||
# nodeToAsk = NucypherNode
|
||||
if self.check_node_for_storage(nodeToAsk):
|
||||
address = (nodeToAsk.ip, nodeToAsk.port)
|
||||
# TODO: encrypt `value` with public key of nodeToAsk
|
||||
|
@ -72,7 +72,7 @@ class NuCypherHashProtocol(KademliaProtocol):
|
|||
return True
|
||||
|
||||
def rpc_store(self, sender, nodeid, key, value):
|
||||
source = NuCypherNode(nodeid, sender[0], sender[1])
|
||||
source = NucypherNode(nodeid, sender[0], sender[1])
|
||||
self.welcomeIfNewNode(source)
|
||||
self.log.debug("got a store request from %s" % str(sender))
|
||||
|
||||
|
@ -108,7 +108,7 @@ class NuCypherHashProtocol(KademliaProtocol):
|
|||
return do_store
|
||||
|
||||
|
||||
class NuCypherSeedOnlyProtocol(NuCypherHashProtocol):
|
||||
class NucypherSeedOnlyProtocol(NucypherHashProtocol):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from kademlia.routing import RoutingTable
|
||||
|
||||
|
||||
class NuCypherRoutingTable(RoutingTable):
|
||||
class NucypherRoutingTable(RoutingTable):
|
||||
|
||||
def addContact(self, node, seed_only=False):
|
||||
if seed_only:
|
||||
|
|
|
@ -17,20 +17,20 @@ from nucypher.crypto.kits import UmbralMessageKit
|
|||
from nucypher.crypto.powers import EncryptingPower, SigningPower
|
||||
from nucypher.keystore.threading import ThreadedSession
|
||||
from nucypher.network.capabilities import SeedOnly, ServerCapability
|
||||
from nucypher.network.node import NuCypherNode
|
||||
from nucypher.network.protocols import NuCypherSeedOnlyProtocol, NuCypherHashProtocol, \
|
||||
from nucypher.network.node import NucypherNode
|
||||
from nucypher.network.protocols import NucypherSeedOnlyProtocol, NucypherHashProtocol, \
|
||||
dht_value_splitter, dht_with_hrac_splitter
|
||||
from nucypher.network.storage import SeedOnlyStorage
|
||||
|
||||
|
||||
class NuCypherDHTServer(Server):
|
||||
protocol_class = NuCypherHashProtocol
|
||||
class NucypherDHTServer(Server):
|
||||
protocol_class = NucypherHashProtocol
|
||||
capabilities = ()
|
||||
digests_set = 0
|
||||
|
||||
def __init__(self, ksize=20, alpha=3, id=None, storage=None, *args, **kwargs):
|
||||
super().__init__(ksize=20, alpha=3, id=None, storage=None, *args, **kwargs)
|
||||
self.node = NuCypherNode(id or digest(
|
||||
self.node = NucypherNode(id or digest(
|
||||
random.getrandbits(255))) # TODO: Assume that this can be attacked to get closer to desired kFrags.
|
||||
|
||||
def serialize_capabilities(self):
|
||||
|
@ -41,7 +41,7 @@ class NuCypherDHTServer(Server):
|
|||
Announce node including capabilities
|
||||
"""
|
||||
result = await self.protocol.ping(addr, self.node.id, self.serialize_capabilities())
|
||||
return NuCypherNode(result[1], addr[0], addr[1]) if result[0] else None
|
||||
return NucypherNode(result[1], addr[0], addr[1]) if result[0] else None
|
||||
|
||||
async def set_digest(self, dkey, value):
|
||||
"""
|
||||
|
@ -86,8 +86,8 @@ class NuCypherDHTServer(Server):
|
|||
return await self.set_digest(key, value)
|
||||
|
||||
|
||||
class NuCypherSeedOnlyDHTServer(NuCypherDHTServer):
|
||||
protocol_class = NuCypherSeedOnlyProtocol
|
||||
class NucypherSeedOnlyDHTServer(NucypherDHTServer):
|
||||
protocol_class = NucypherSeedOnlyProtocol
|
||||
capabilities = (SeedOnly(),)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -53,7 +53,7 @@ class Arrangement(BlockchainArrangement):
|
|||
policy_duration = arrangement_delta.days
|
||||
|
||||
super().__init__(author=self.alice, miner=ursula,
|
||||
value=self.deposit, periods=policy_duration,
|
||||
value=self.deposit, lock_periods=policy_duration,
|
||||
arrangement_id=self._make_arrangement_id())
|
||||
|
||||
def __bytes__(self):
|
||||
|
|
|
@ -86,15 +86,15 @@ contract MinersEscrowForPolicyMock {
|
|||
/**
|
||||
* @notice Emulate getDowntimeLength
|
||||
**/
|
||||
function getDowntimeLength(address _miner) public returns (uint256) {
|
||||
function getDowntimeLength(address) public view returns (uint256) {
|
||||
return downtime.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Emulate getDowntime
|
||||
**/
|
||||
function getDowntime(address _miner, uint256 _index)
|
||||
public returns (uint256 startPeriod, uint256 endPeriod)
|
||||
function getDowntime(address, uint256 _index)
|
||||
public view returns (uint256 startPeriod, uint256 endPeriod)
|
||||
{
|
||||
Downtime storage data = downtime[_index];
|
||||
startPeriod = data.startPeriod;
|
||||
|
@ -104,8 +104,8 @@ contract MinersEscrowForPolicyMock {
|
|||
/**
|
||||
* @notice Emulate minerInfo
|
||||
**/
|
||||
function minerInfo(address _miner)
|
||||
public returns (uint256, uint256, uint256, uint256, uint256 result)
|
||||
function minerInfo(address)
|
||||
public view returns (uint256, uint256, uint256, uint256, uint256 result)
|
||||
{
|
||||
result = lastActivePeriod;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@ contract PolicyManagerBad is PolicyManager {
|
|||
constructor(MinersEscrow _escrow) public PolicyManager(_escrow) {
|
||||
}
|
||||
|
||||
function getNodeRewardDelta(address _node, uint256 _period)
|
||||
public view returns (int256)
|
||||
function getNodeRewardDelta(address, uint256) public view returns (int256)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -29,4 +29,8 @@ contract GovernmentV2Mock is Government {
|
|||
super.verifyState(_testTarget);
|
||||
require(uint256(delegateGet(_testTarget, "valueToCheck()")) == valueToCheck);
|
||||
}
|
||||
|
||||
function setPreviousTarget(address _target) public {
|
||||
previousTarget = _target;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,4 +43,8 @@ contract MinersEscrowV1Mock is Upgradeable {
|
|||
lockedTokens[node] = escrow.lockedTokens(node);
|
||||
}
|
||||
}
|
||||
|
||||
function setPreviousTarget(address _target) public {
|
||||
previousTarget = _target;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ import "proxy/Upgradeable.sol";
|
|||
**/
|
||||
contract PolicyManagerV1Mock is Upgradeable {
|
||||
|
||||
constructor() public {}
|
||||
function verifyState(address) public {}
|
||||
function finishUpgrade(address) public {}
|
||||
|
||||
function setPreviousTarget(address _target) public {
|
||||
previousTarget = _target;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
pragma solidity ^0.4.23;
|
||||
|
||||
|
||||
import "proxy/Upgradeable.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @notice Contract for testing government contract
|
||||
**/
|
||||
contract UpgradeableBad is Upgradeable {
|
||||
|
||||
function verifyState(address) public {
|
||||
revert();
|
||||
}
|
||||
|
||||
function finishUpgrade(address) public {}
|
||||
|
||||
}
|
|
@ -19,7 +19,6 @@ ROLLBACK_POLICY_MANAGER = 5
|
|||
|
||||
@pytest.fixture()
|
||||
def escrow(web3, chain):
|
||||
creator = web3.eth.accounts[0]
|
||||
node1 = web3.eth.accounts[1]
|
||||
node2 = web3.eth.accounts[2]
|
||||
node3 = web3.eth.accounts[3]
|
||||
|
@ -32,28 +31,28 @@ def escrow(web3, chain):
|
|||
escrow_dispatcher, _ = chain.provider.deploy_contract(
|
||||
'Dispatcher', escrow_library.address
|
||||
)
|
||||
escrow = web3.eth.contract(
|
||||
contract = web3.eth.contract(
|
||||
abi=escrow_library.abi,
|
||||
address=escrow_dispatcher.address,
|
||||
ContractFactoryClass=Contract)
|
||||
return escrow
|
||||
return contract
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def policy_manager(web3, chain):
|
||||
creator = web3.eth.accounts[0]
|
||||
# Creator deploys the escrow
|
||||
policy_manager, _ = chain.provider.deploy_contract('PolicyManagerV1Mock')
|
||||
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', policy_manager.address)
|
||||
return dispatcher
|
||||
contract, _ = chain.provider.deploy_contract('PolicyManagerV1Mock')
|
||||
dispatcher, _ = chain.provider.deploy_contract('Dispatcher', contract.address)
|
||||
contract = web3.eth.contract(
|
||||
abi=contract.abi,
|
||||
address=dispatcher.address,
|
||||
ContractFactoryClass=Contract)
|
||||
return contract
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_voting(web3, chain, escrow, policy_manager):
|
||||
creator = web3.eth.accounts[0]
|
||||
node1 = web3.eth.accounts[1]
|
||||
node2 = web3.eth.accounts[2]
|
||||
node3 = web3.eth.accounts[3]
|
||||
creator, node1, node2, node3, *everyone_else = web3.eth.accounts
|
||||
|
||||
# Deploy contract
|
||||
government_library, _ = chain.provider.deploy_contract(
|
||||
|
@ -72,13 +71,13 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
upgrade_committed_log = government.events.UpgradeCommitted.createFilter(fromBlock='latest')
|
||||
|
||||
# Transfer ownership
|
||||
tx = government.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
tx = government.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Check that there are no voting before it's creation
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.commitUpgrade().transact({'from': creator})
|
||||
|
@ -116,11 +115,11 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Nodes vote against update
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert 1 == government.functions.votesFor().call()
|
||||
assert 0 == government.functions.votesAgainst().call()
|
||||
tx = government.functions.vote(False).transact({'from': node2})
|
||||
tx = government.functions.vote(False).transact({'from': node2})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert 1 == government.functions.votesFor().call()
|
||||
assert 2 == government.functions.votesAgainst().call()
|
||||
|
@ -128,7 +127,7 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
|
||||
# Can't vote again
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.vote(False).transact({'from': node2})
|
||||
tx = government.functions.vote(False).transact({'from': node2})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Wait until the end of voting
|
||||
|
@ -139,7 +138,7 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
|
||||
# Can't vote after the ending
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.vote(False).transact({'from': node3})
|
||||
tx = government.functions.vote(False).transact({'from': node3})
|
||||
chain.wait_for_receipt(tx)
|
||||
# Can't commit upgrade because nodes votes against upgrade
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
|
@ -178,8 +177,8 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
chain.wait_for_receipt(tx)
|
||||
# Can't create new voting before upgrading
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.transact({'from': creator}).createVoting(
|
||||
UPGRADE_GOVERNMENT, government_library_v2.address)
|
||||
tx = government.functions.createVoting(UPGRADE_GOVERNMENT, government_library_v2.address)\
|
||||
.transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Commit upgrade
|
||||
|
@ -189,8 +188,7 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
assert government_library_v2.address == government_dispatcher.functions.target().call()
|
||||
|
||||
# Create voting for update Government contract again without voting
|
||||
tx = government.transact({'from': node2}).createVoting(
|
||||
UPGRADE_GOVERNMENT, government_library.address)
|
||||
tx = government.functions.createVoting(UPGRADE_GOVERNMENT, government_library.address).transact({'from': node2})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert 3 == government.functions.votingNumber().call()
|
||||
assert ACTIVE_STATE == government.functions.getVotingState().call()
|
||||
|
@ -207,11 +205,11 @@ def test_voting(web3, chain, escrow, policy_manager):
|
|||
|
||||
assert 4 == government.functions.votingNumber().call()
|
||||
assert ACTIVE_STATE == government.functions.getVotingState().call()
|
||||
tx = government.functions.vote(False).transact({'from': node1})
|
||||
tx = government.functions.vote(False).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(False).transact({'from': node2})
|
||||
tx = government.functions.vote(False).transact({'from': node2})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node3})
|
||||
tx = government.functions.vote(True).transact({'from': node3})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert 3 == government.functions.votesFor().call()
|
||||
assert 3 == government.functions.votesAgainst().call()
|
||||
|
@ -266,13 +264,34 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
policy_manager_library_v2 = policy_manager_library_v2.address
|
||||
|
||||
# Transfer ownership
|
||||
tx = government.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
tx = government.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
tx = escrow.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = policy_manager.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
tx = policy_manager.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Can't create upgrade to the zero address
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.createVoting(UPGRADE_GOVERNMENT, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.createVoting(UPGRADE_ESCROW, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.createVoting(UPGRADE_POLICY_MANAGER, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
# Can't rollback if no previous version
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.createVoting(ROLLBACK_GOVERNMENT, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.createVoting(ROLLBACK_ESCROW, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.createVoting(ROLLBACK_POLICY_MANAGER, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Vote and upgrade government contract
|
||||
tx = government.functions.createVoting(UPGRADE_GOVERNMENT, government_library_v2.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
@ -284,23 +303,23 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert UPGRADE_GOVERNMENT == event_args['votingType']
|
||||
assert government_library_v2.address == event_args['newAddress']
|
||||
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert government_library_v2.address == government_dispatcher.functions.target().call()
|
||||
assert government_library_v2.address == government.functions.target().call()
|
||||
|
||||
events = government.events.UpgradeCommitted()
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 1 == len(events)
|
||||
event_args = events[0]['args']
|
||||
assert 1 == event_args['votingNumber']
|
||||
assert UPGRADE_GOVERNMENT == event_args['votingType']
|
||||
assert government_library_v2.address == event_args['newAddress']
|
||||
assert event_args['successful']
|
||||
|
||||
# Vote and rollback government contract
|
||||
tx = government.functions.createVoting(ROLLBACK_GOVERNMENT, NULL_ADDR).transact({'from': node1})
|
||||
tx = government.functions.createVoting(ROLLBACK_GOVERNMENT, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
events = voting_created_log.get_all_entries()
|
||||
|
@ -310,7 +329,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert ROLLBACK_GOVERNMENT == event_args['votingType']
|
||||
assert NULL_ADDR == event_args['newAddress']
|
||||
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
|
@ -323,6 +342,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert 2 == event_args['votingNumber']
|
||||
assert ROLLBACK_GOVERNMENT == event_args['votingType']
|
||||
assert NULL_ADDR == event_args['newAddress']
|
||||
assert event_args['successful']
|
||||
|
||||
# Vote and upgrade escrow contract
|
||||
tx = government.functions.createVoting(UPGRADE_ESCROW, escrow_library_v2).transact({'from': node1})
|
||||
|
@ -335,7 +355,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert UPGRADE_ESCROW == event_args['votingType']
|
||||
assert escrow_library_v2 == event_args['newAddress']
|
||||
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
|
@ -348,9 +368,10 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert 3 == event_args['votingNumber']
|
||||
assert UPGRADE_ESCROW == event_args['votingType']
|
||||
assert escrow_library_v2 == event_args['newAddress']
|
||||
assert event_args['successful']
|
||||
|
||||
# Vote and rollback escrow contract
|
||||
tx = government.functions.createVoting(ROLLBACK_ESCROW, NULL_ADDR).transact({'from': node1})
|
||||
tx = government.functions.createVoting(ROLLBACK_ESCROW, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
events = voting_created_log.get_all_entries()
|
||||
|
@ -360,7 +381,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert ROLLBACK_ESCROW == event_args['votingType']
|
||||
assert NULL_ADDR == event_args['newAddress']
|
||||
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
|
@ -374,6 +395,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert 4 == event_args['votingNumber']
|
||||
assert ROLLBACK_ESCROW == event_args['votingType']
|
||||
assert NULL_ADDR == event_args['newAddress']
|
||||
assert event_args['successful']
|
||||
|
||||
# Vote and upgrade policy manager contract
|
||||
tx = government.functions.createVoting(UPGRADE_POLICY_MANAGER, policy_manager_library_v2).transact({'from': node1})
|
||||
|
@ -386,7 +408,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert UPGRADE_POLICY_MANAGER == event_args['votingType']
|
||||
assert policy_manager_library_v2 == event_args['newAddress']
|
||||
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
|
@ -399,9 +421,10 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert 5 == event_args['votingNumber']
|
||||
assert UPGRADE_POLICY_MANAGER == event_args['votingType']
|
||||
assert policy_manager_library_v2 == event_args['newAddress']
|
||||
assert event_args['successful']
|
||||
|
||||
# Vote and rollback policy manager contract
|
||||
tx = government.functions.createVoting(ROLLBACK_POLICY_MANAGER, NULL_ADDR).transact({'from': node1})
|
||||
tx = government.functions.createVoting(ROLLBACK_POLICY_MANAGER, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
events = voting_created_log.get_all_entries()
|
||||
|
@ -411,7 +434,7 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert ROLLBACK_POLICY_MANAGER == event_args['votingType']
|
||||
assert NULL_ADDR == event_args['newAddress']
|
||||
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
|
@ -424,6 +447,160 @@ def test_upgrade(web3, chain, escrow, policy_manager):
|
|||
assert 6 == event_args['votingNumber']
|
||||
assert ROLLBACK_POLICY_MANAGER == event_args['votingType']
|
||||
assert NULL_ADDR == event_args['newAddress']
|
||||
assert event_args['successful']
|
||||
|
||||
|
||||
def test_cancel_upgrading(web3, chain, escrow, policy_manager):
|
||||
creator = web3.eth.accounts[0]
|
||||
node1 = web3.eth.accounts[1]
|
||||
|
||||
# Deploy contract
|
||||
government_library, _ = chain.provider.deploy_contract(
|
||||
'GovernmentV2Mock', escrow.address, policy_manager.address, 1,
|
||||
)
|
||||
government_dispatcher, _ = chain.provider.deploy_contract(
|
||||
'Dispatcher', government_library.address
|
||||
)
|
||||
government = web3.eth.contract(
|
||||
abi=government_library.abi,
|
||||
address=government_dispatcher.address,
|
||||
ContractFactoryClass=Contract
|
||||
)
|
||||
escrow_library = escrow.functions.target().call()
|
||||
policy_manager_library = policy_manager.functions.target().call()
|
||||
upgradeable_bad, _ = chain.provider.deploy_contract('UpgradeableBad')
|
||||
upgrade_committed_log = government.events.UpgradeCommitted.createFilter(fromBlock=0)
|
||||
tx = government.functions.transferOwnership(government.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Vote to upgrade to the bad government contract
|
||||
tx = government.functions.createVoting(UPGRADE_GOVERNMENT, upgradeable_bad.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
|
||||
# Upgrading failed and canceled
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
assert government_library.address == government_dispatcher.functions.target().call()
|
||||
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 1 == len(events)
|
||||
event_args = events[0]['args']
|
||||
assert 1 == event_args['votingNumber']
|
||||
assert UPGRADE_GOVERNMENT == event_args['votingType']
|
||||
assert upgradeable_bad.address == event_args['newAddress']
|
||||
assert not event_args['successful']
|
||||
|
||||
# Vote to bad rollback
|
||||
tx = government.functions.setPreviousTarget(upgradeable_bad.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.createVoting(ROLLBACK_GOVERNMENT, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
|
||||
# Rollback failed and canceled
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
assert government_library.address == government.functions.target().call()
|
||||
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 2 == len(events)
|
||||
event_args = events[1]['args']
|
||||
assert 2 == event_args['votingNumber']
|
||||
assert ROLLBACK_GOVERNMENT == event_args['votingType']
|
||||
assert not event_args['successful']
|
||||
|
||||
# Vote to upgrade to the bad escrow contract
|
||||
tx = government.functions.createVoting(UPGRADE_ESCROW, upgradeable_bad.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
|
||||
# Upgrading failed and canceled
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
assert escrow_library == escrow.functions.target().call()
|
||||
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 3 == len(events)
|
||||
event_args = events[2]['args']
|
||||
assert 3 == event_args['votingNumber']
|
||||
assert UPGRADE_ESCROW == event_args['votingType']
|
||||
assert upgradeable_bad.address == event_args['newAddress']
|
||||
assert not event_args['successful']
|
||||
|
||||
# Vote to bad rollback
|
||||
tx = escrow.functions.setPreviousTarget(upgradeable_bad.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.createVoting(ROLLBACK_ESCROW, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
|
||||
# Rollback failed and canceled
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
assert escrow_library == escrow.functions.target().call()
|
||||
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 4 == len(events)
|
||||
event_args = events[3]['args']
|
||||
assert 4 == event_args['votingNumber']
|
||||
assert ROLLBACK_ESCROW == event_args['votingType']
|
||||
assert not event_args['successful']
|
||||
|
||||
# Vote to upgrade to the bad policy manager contract
|
||||
tx = government.functions.createVoting(UPGRADE_POLICY_MANAGER, upgradeable_bad.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
|
||||
# Upgrading failed and canceled
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
assert policy_manager_library == policy_manager.functions.target().call()
|
||||
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 5 == len(events)
|
||||
event_args = events[4]['args']
|
||||
assert 5 == event_args['votingNumber']
|
||||
assert UPGRADE_POLICY_MANAGER == event_args['votingType']
|
||||
assert upgradeable_bad.address == event_args['newAddress']
|
||||
assert not event_args['successful']
|
||||
|
||||
# Vote to bad rollback
|
||||
tx = policy_manager.functions.setPreviousTarget(upgradeable_bad.address).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.createVoting(ROLLBACK_POLICY_MANAGER, NULL_ADDR).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
tx = government.functions.vote(True).transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
chain.time_travel(1)
|
||||
|
||||
# Rollback failed and canceled
|
||||
tx = government.functions.commitUpgrade().transact({'from': node1})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert FINISHED_STATE == government.functions.getVotingState().call()
|
||||
assert policy_manager_library == policy_manager.functions.target().call()
|
||||
|
||||
events = upgrade_committed_log.get_all_entries()
|
||||
assert 6 == len(events)
|
||||
event_args = events[5]['args']
|
||||
assert 6 == event_args['votingNumber']
|
||||
assert ROLLBACK_POLICY_MANAGER == event_args['votingType']
|
||||
assert not event_args['successful']
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
|
@ -450,23 +627,23 @@ def test_verifying_state(web3, chain):
|
|||
ContractFactoryClass=Contract)
|
||||
|
||||
# Upgrade to the second version
|
||||
tx = government_dispatcher.functions.upgrade(government_library_v2.address).transact({'from': creator})
|
||||
tx = government_dispatcher.functions.upgrade(government_library_v2.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert government_library_v2.address == government_dispatcher.functions.target().call()
|
||||
assert address2 == government.functions.escrow().call()
|
||||
assert address1 == government.functions.policyManager().call()
|
||||
assert 2 * 60 * 60 == government.functions.votingDurationSeconds().call()
|
||||
tx = government.functions.setValueToCheck(3).transact({'from': creator})
|
||||
tx = government.functions.setValueToCheck(3).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
assert 3 == government.functions.valueToCheck().call()
|
||||
|
||||
# Can't upgrade to the previous version or to the bad version
|
||||
government_library_bad, _ = chain.provider.deploy_contract('GovernmentBad')
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government_dispatcher.functions.upgrade(government_library_v1.address).transact({'from': creator})
|
||||
tx = government_dispatcher.functions.upgrade(government_library_v1.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government_dispatcher.functions.upgrade(government_library_bad.address).transact({'from': creator})
|
||||
tx = government_dispatcher.functions.upgrade(government_library_bad.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# But can rollback
|
||||
|
@ -477,10 +654,10 @@ def test_verifying_state(web3, chain):
|
|||
assert address2 == government.functions.policyManager().call()
|
||||
assert 60 * 60 == government.functions.votingDurationSeconds().call()
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government.functions.setValueToCheck(2).transact({'from': creator})
|
||||
tx = government.functions.setValueToCheck(2).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
||||
# Try to upgrade to the bad version
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = government_dispatcher.functions.upgrade(government_library_bad.address).transact({'from': creator})
|
||||
tx = government_dispatcher.functions.upgrade(government_library_bad.address).transact({'from': creator})
|
||||
chain.wait_for_receipt(tx)
|
||||
|
|
|
@ -53,10 +53,6 @@ def policy_manager(web3, chain, escrow, request):
|
|||
return contract
|
||||
|
||||
|
||||
def wait_time(chain, wait_periods):
|
||||
chain.time_travel(seconds=wait_periods * 60 * MINUTES_IN_PERIOD)
|
||||
|
||||
|
||||
MINUTES_IN_PERIOD = 10
|
||||
policy_id = os.urandom(20)
|
||||
policy_id_2 = os.urandom(20)
|
||||
|
@ -66,6 +62,14 @@ number_of_periods = 10
|
|||
value = rate * number_of_periods
|
||||
|
||||
|
||||
def wait_time(chain, wait_periods):
|
||||
seconds = wait_periods * 60 * MINUTES_IN_PERIOD
|
||||
|
||||
end_timestamp = chain.provider.w3.eth.getBlock(block_identifier='latest').timestamp + seconds
|
||||
chain.provider.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp)
|
||||
chain.provider.w3.eth.web3.testing.mine(1)
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_create_revoke(web3, chain, escrow, policy_manager):
|
||||
creator, client, bad_node, node1, node2, node3, *everyone_else = web3.eth.accounts
|
||||
|
|
|
@ -1,106 +1,70 @@
|
|||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.actors import Miner
|
||||
from nucypher.blockchain.eth.agents import MinerAgent
|
||||
|
||||
|
||||
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
|
||||
def test_miner_locking_tokens(chain, mock_token_deployer, mock_miner_agent):
|
||||
@pytest.fixture(scope='module')
|
||||
def miner(chain, mock_token_agent, mock_miner_agent):
|
||||
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent._M)
|
||||
_origin, ursula, *everybody_else = chain.provider.w3.eth.accounts
|
||||
miner = Miner(miner_agent=mock_miner_agent, address=ursula)
|
||||
return miner
|
||||
|
||||
miner = Miner(miner_agent=mock_miner_agent, address=chain.provider.w3.eth.accounts[1])
|
||||
|
||||
an_amount_of_tokens = 1000 * mock_token_deployer._M
|
||||
miner.stake(amount=an_amount_of_tokens, locktime=mock_miner_agent._deployer._min_locked_periods)
|
||||
def test_miner_locking_tokens(chain, miner, mock_miner_agent):
|
||||
|
||||
# Verify that the escrow is allowed to receive tokens
|
||||
# assert mock_miner_agent.token_agent.read().allowance(miner.address, mock_miner_agent.contract_address) == 0
|
||||
assert mock_miner_agent.min_allowed_locked < miner.token_balance(), "Insufficient miner balance"
|
||||
|
||||
# Stake starts after one period
|
||||
# assert miner.token_balance() == 0
|
||||
# assert mock_miner_agent.read().getLockedTokens(miner.address) == 0
|
||||
miner.stake(amount=mock_miner_agent.min_allowed_locked, # Lock the minimum amount of tokens
|
||||
lock_periods=mock_miner_agent.min_locked_periods) # ... for the fewest number of periods
|
||||
|
||||
# Verify that the escrow is "approved" to receive tokens
|
||||
assert mock_miner_agent.token_agent.contract.functions.allowance(miner.address, mock_miner_agent.contract_address).call() == 0
|
||||
|
||||
# Staking starts after one period
|
||||
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == 0
|
||||
|
||||
# Wait for it...
|
||||
chain.time_travel(mock_miner_agent._deployer._hours_per_period)
|
||||
|
||||
assert mock_miner_agent.read().getLockedTokens(miner.address) == an_amount_of_tokens
|
||||
chain.time_travel(periods=1)
|
||||
assert mock_miner_agent.contract.functions.getLockedTokens(miner.address).call() == mock_miner_agent.min_allowed_locked
|
||||
|
||||
|
||||
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
|
||||
def test_mine_then_withdraw_tokens(chain, mock_token_deployer, token_agent, mock_miner_agent, mock_miner_escrow_deployer):
|
||||
"""
|
||||
- Airdrop tokens to everyone
|
||||
- Create a Miner (Ursula)
|
||||
- Spawn additional miners
|
||||
- All miners lock tokens
|
||||
- Wait (with time)
|
||||
- Miner (Ursula) mints new tokens
|
||||
"""
|
||||
def test_miner_collects_staking_reward_tokens(chain, miner, mock_token_agent, mock_miner_agent):
|
||||
|
||||
_origin, *everybody = chain.provider.w3.eth.accounts
|
||||
|
||||
ursula_address, *everyone_else = everybody
|
||||
|
||||
miner = Miner(miner_agent=mock_miner_agent, address=ursula_address)
|
||||
|
||||
# Miner has no locked tokens
|
||||
assert miner.locked_tokens == 0
|
||||
|
||||
# Capture the initial token balance of the miner
|
||||
# Capture the current token balance of the miner
|
||||
initial_balance = miner.token_balance()
|
||||
assert token_agent.get_balance(miner.address) == miner.token_balance()
|
||||
|
||||
# Stake a random amount of tokens
|
||||
# stake_amount = (10 + random.randrange(9000)) * mock_token_deployer._M
|
||||
half_of_stake = initial_balance // 2
|
||||
|
||||
miner.stake(amount=half_of_stake, locktime=1)
|
||||
|
||||
# Ensure the miner has the right amount of staked tokens
|
||||
assert miner.locked_tokens == half_of_stake
|
||||
|
||||
# Ensure the MinerEscrow contract is allowed to receive tokens form Alice
|
||||
# assert miner.token_agent.read().allowance(miner.address, miner.miner_agent.contract_address) == half_of_stake
|
||||
|
||||
# Blockchain staking starts after one period
|
||||
# assert mock_miner_agent.read().getAllLockedTokens() == 0
|
||||
|
||||
# Wait for it...
|
||||
# chain.wait_time(2)
|
||||
assert mock_token_agent.get_balance(miner.address) == miner.token_balance()
|
||||
|
||||
# Have other address lock tokens
|
||||
chain.spawn_miners(miner_agent=mock_miner_agent,
|
||||
addresses=everyone_else,
|
||||
locktime=1,
|
||||
m=mock_token_deployer._M)
|
||||
|
||||
# The miner starts unlocking periods...
|
||||
_origin, ursula, *everybody_else = chain.provider.w3.eth.accounts
|
||||
mock_miner_agent.spawn_random_miners(addresses=everybody_else)
|
||||
|
||||
# ...wait out the lock period...
|
||||
for _ in range(28):
|
||||
chain.time_travel(periods=1)
|
||||
miner.confirm_activity()
|
||||
|
||||
# ...wait more...
|
||||
chain.time_travel(mock_miner_agent._deployer._hours_per_period)
|
||||
|
||||
# miner.confirm_activity()
|
||||
|
||||
# ...wait more...
|
||||
chain.time_travel(mock_miner_agent._deployer._hours_per_period)
|
||||
|
||||
chain.time_travel(periods=2)
|
||||
miner.mint()
|
||||
miner.collect_staking_reward()
|
||||
|
||||
final_balance = token_agent.get_balance(miner.address)
|
||||
final_balance = mock_token_agent.get_balance(miner.address)
|
||||
assert final_balance > initial_balance
|
||||
|
||||
|
||||
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
|
||||
def test_sample_miners(chain, mock_miner_agent):
|
||||
@pytest.mark.slow()
|
||||
def test_sample_miners(chain, mock_miner_agent, mock_token_agent):
|
||||
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent._M)
|
||||
|
||||
_origin, *everyone_else = chain.provider.w3.eth.accounts[1:]
|
||||
# Have other address lock tokens
|
||||
_origin, ursula, *everybody_else = chain.provider.w3.eth.accounts
|
||||
mock_miner_agent.spawn_random_miners(addresses=everybody_else)
|
||||
|
||||
chain.spawn_miners(addresses=everyone_else, locktime=100,
|
||||
miner_agent=mock_miner_agent, m=mock_miner_agent.token_agent._deployer._M)
|
||||
|
||||
chain.time_travel(mock_miner_agent._deployer._hours_per_period)
|
||||
chain.time_travel(periods=1)
|
||||
|
||||
with pytest.raises(MinerAgent.NotEnoughUrsulas):
|
||||
mock_miner_agent.sample(quantity=100) # Waay more than we have deployed
|
||||
|
@ -110,15 +74,7 @@ def test_sample_miners(chain, mock_miner_agent):
|
|||
assert len(set(miners)) == 3
|
||||
|
||||
|
||||
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
|
||||
def test_publish_miner_datastore(chain, mock_miner_agent):
|
||||
|
||||
miner_addr = chain.provider.w3.eth.accounts[1]
|
||||
|
||||
miner = Miner(miner_agent=mock_miner_agent, address=miner_addr)
|
||||
|
||||
balance = miner.token_balance()
|
||||
miner.stake(amount=balance, locktime=1)
|
||||
def test_publish_miner_datastore(miner):
|
||||
|
||||
# Publish Miner IDs to the DHT
|
||||
some_data = os.urandom(32)
|
||||
|
@ -139,6 +95,5 @@ def test_publish_miner_datastore(chain, mock_miner_agent):
|
|||
assert len(stored_miner_ids) == 2
|
||||
assert another_mock_miner_id == stored_miner_ids[1]
|
||||
|
||||
supposedly_the_same_miner_id = mock_miner_agent.read().getMinerId(miner_addr, 1)
|
||||
supposedly_the_same_miner_id = miner.miner_agent.contract.functions.getMinerId(miner.address, 1).call()
|
||||
assert another_mock_miner_id == supposedly_the_same_miner_id
|
||||
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
import pytest
|
||||
|
||||
from tests.blockchain.eth.utilities import MockNuCypherMinerConfig
|
||||
|
||||
M = 10 ** 6
|
||||
|
||||
|
||||
@pytest.mark.skip("Last 5 stubborn blockchain tests.")
|
||||
def test_get_swarm(chain, mock_miner_agent):
|
||||
def test_get_swarm(chain, mock_token_agent, mock_miner_agent):
|
||||
|
||||
# chain._global_airdrop(amount=10000)
|
||||
mock_token_agent.token_airdrop(amount=100000 * mock_token_agent._M)
|
||||
|
||||
creator, *addresses = chain.provider.w3.eth.accounts
|
||||
|
||||
chain.spawn_miners(addresses=addresses, miner_agent=mock_miner_agent, locktime=1)
|
||||
mock_miner_agent.spawn_random_miners(addresses=addresses)
|
||||
|
||||
default_period_duration = MockNuCypherMinerConfig._hours_per_period
|
||||
chain.time_travel(default_period_duration)
|
||||
chain.time_travel(periods=1)
|
||||
|
||||
swarm = mock_miner_agent.swarm()
|
||||
swarm_addresses = list(swarm)
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import pytest
|
||||
from pytest import raises
|
||||
|
||||
from nucypher.blockchain.eth.agents import NucypherTokenAgent, MinerAgent
|
||||
from nucypher.blockchain.eth.deployers import NucypherTokenDeployer, MinerEscrowDeployer, PolicyManagerDeployer
|
||||
from nucypher.blockchain.eth.interfaces import Registrar
|
||||
|
||||
|
||||
def test_token_deployer_and_agent(chain):
|
||||
|
||||
# Trying to get token from blockchain before it's been published fails
|
||||
# with raises(NoKnownAddress):
|
||||
# NucypherTokenAgent(blockchain=chain)
|
||||
with pytest.raises(Registrar.UnknownContract):
|
||||
NucypherTokenAgent(blockchain=chain)
|
||||
|
||||
# The big day...
|
||||
deployer = NucypherTokenDeployer(blockchain=chain)
|
||||
|
@ -31,7 +33,7 @@ def test_token_deployer_and_agent(chain):
|
|||
assert len(token_agent.contract_address) == 42
|
||||
|
||||
# Check that the token contract has tokens
|
||||
assert token_agent.read().totalSupply() != 0
|
||||
assert token_agent.contract.functions.totalSupply().call() != 0
|
||||
# assert token().totalSupply() == int(1e9) * _M # TODO
|
||||
|
||||
# Retrieve the token from the blockchain
|
||||
|
@ -42,7 +44,6 @@ def test_token_deployer_and_agent(chain):
|
|||
assert token_agent == same_token_agent # __eq__
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_deploy_ethereum_contracts(chain):
|
||||
"""
|
||||
Launch all ethereum contracts:
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import random
|
||||
from typing import List
|
||||
|
||||
import pkg_resources
|
||||
|
@ -6,24 +7,73 @@ from eth_tester.backends import is_pyevm_available
|
|||
from eth_tester.backends.pyevm.main import get_default_genesis_params, get_default_account_keys, generate_genesis_state
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.eth.agents import MinerAgent
|
||||
from nucypher.blockchain.eth.constants import NuCypherMinerConfig
|
||||
from nucypher.blockchain.eth.deployers import MinerEscrowDeployer
|
||||
from nucypher.blockchain.eth.agents import MinerAgent, NucypherTokenAgent
|
||||
from nucypher.blockchain.eth.constants import NucypherMinerConfig
|
||||
from nucypher.blockchain.eth.deployers import MinerEscrowDeployer, NucypherTokenDeployer
|
||||
|
||||
|
||||
class MockNuCypherMinerConfig(NuCypherMinerConfig):
|
||||
class MockNucypherMinerConfig(NucypherMinerConfig):
|
||||
"""Speed things up a bit"""
|
||||
_hours_per_period = 1 # Hours
|
||||
_min_locked_periods = 1 # Minimum locked periods
|
||||
# _hours_per_period = 24 # Hours
|
||||
# min_locked_periods = 1 # Minimum locked periods
|
||||
|
||||
|
||||
class MockMinerEscrowDeployer(MinerEscrowDeployer, MockNuCypherMinerConfig):
|
||||
"""Helper class for MockMinerAgent, using a mock miner config"""
|
||||
class MockTokenAgent(NucypherTokenAgent):
|
||||
|
||||
def token_airdrop(self, amount: int, addresses: List[str]=None):
|
||||
"""Airdrops tokens from creator address to all other addresses!"""
|
||||
|
||||
if addresses is None:
|
||||
_creator, *addresses = self.blockchain.provider.w3.eth.accounts
|
||||
|
||||
def txs():
|
||||
for address in addresses:
|
||||
txhash = self.contract.functions.transfer(address, amount).transact({'from': self.origin})
|
||||
yield txhash
|
||||
|
||||
receipts = list()
|
||||
for tx in txs(): # One at a time
|
||||
receipt = self.blockchain.wait_for_receipt(tx)
|
||||
receipts.append(receipt)
|
||||
return receipts
|
||||
|
||||
|
||||
class MockMinerAgent(MinerAgent):
|
||||
class MockMinerAgent(MinerAgent, MockNucypherMinerConfig):
|
||||
"""MinerAgent with faked config subclass"""
|
||||
_deployer = MockMinerEscrowDeployer
|
||||
|
||||
def spawn_random_miners(self, addresses: list) -> list:
|
||||
"""
|
||||
Deposit and lock a random amount of tokens in the miner escrow
|
||||
from each address, "spawning" new Miners.
|
||||
"""
|
||||
from nucypher.blockchain.eth.actors import Miner
|
||||
|
||||
miners = list()
|
||||
for address in addresses:
|
||||
miner = Miner(miner_agent=self, address=address)
|
||||
miners.append(miner)
|
||||
|
||||
# stake a random amount
|
||||
min_stake, balance = self.min_allowed_locked, miner.token_balance()
|
||||
amount = random.randint(min_stake, balance)
|
||||
|
||||
# for a random lock duration
|
||||
min_locktime, max_locktime = self.min_locked_periods, self.max_minting_periods
|
||||
periods = random.randint(min_locktime, max_locktime)
|
||||
|
||||
miner.stake(amount=amount, lock_periods=periods)
|
||||
|
||||
return miners
|
||||
|
||||
|
||||
class MockNucypherTokenDeployer(NucypherTokenDeployer):
|
||||
"""Mock deployer with mock agency"""
|
||||
agency = MockTokenAgent
|
||||
|
||||
|
||||
class MockMinerEscrowDeployer(MinerEscrowDeployer, MockNucypherMinerConfig):
|
||||
"""Helper class for MockMinerAgent, using a mock miner config"""
|
||||
agency = MockMinerAgent
|
||||
|
||||
|
||||
def generate_accounts(w3: Web3, quantity: int) -> List[str]:
|
||||
|
@ -61,7 +111,7 @@ class TesterPyEVMBackend(PyEVMBackend):
|
|||
timestamp: int(epoch)
|
||||
"""
|
||||
|
||||
self.fork_config = {}
|
||||
self.fork_config = dict()
|
||||
|
||||
if not is_pyevm_available():
|
||||
raise pkg_resources.DistributionNotFound(
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
from os.path import abspath, dirname
|
||||
|
||||
import pytest
|
||||
import shutil
|
||||
import time
|
||||
from eth_tester import EthereumTester
|
||||
from geth import LoggingMixin, DevGethProcess
|
||||
from os.path import abspath, dirname
|
||||
from web3 import EthereumTesterProvider, IPCProvider, Web3
|
||||
from web3.middleware import geth_poa_middleware
|
||||
|
||||
from nucypher.blockchain.eth.agents import NucypherTokenAgent, MinerAgent
|
||||
from nucypher.blockchain.eth.agents import PolicyAgent
|
||||
from nucypher.blockchain.eth.chains import TheBlockchain, TesterBlockchain
|
||||
from nucypher.blockchain.eth.deployers import PolicyManagerDeployer, NucypherTokenDeployer
|
||||
from nucypher.blockchain.eth.deployers import PolicyManagerDeployer
|
||||
from nucypher.blockchain.eth.interfaces import Registrar, ContractProvider
|
||||
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
||||
from tests.blockchain.eth import contracts, utilities
|
||||
from tests.blockchain.eth.utilities import MockMinerEscrowDeployer, TesterPyEVMBackend
|
||||
from tests.blockchain.eth.utilities import MockMinerEscrowDeployer, TesterPyEVMBackend, MockNucypherTokenDeployer
|
||||
|
||||
|
||||
#
|
||||
|
@ -172,15 +171,15 @@ def chain(contract_provider, airdrop=False):
|
|||
|
||||
@pytest.fixture(scope='module')
|
||||
def mock_token_deployer(chain):
|
||||
token_deployer = NucypherTokenDeployer(blockchain=chain)
|
||||
token_deployer = MockNucypherTokenDeployer(blockchain=chain)
|
||||
token_deployer.arm()
|
||||
token_deployer.deploy()
|
||||
yield token_deployer
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def mock_miner_escrow_deployer(token_agent):
|
||||
escrow = MockMinerEscrowDeployer(token_agent=token_agent)
|
||||
def mock_miner_escrow_deployer(mock_token_agent):
|
||||
escrow = MockMinerEscrowDeployer(token_agent=mock_token_agent)
|
||||
escrow.arm()
|
||||
escrow.deploy()
|
||||
yield escrow
|
||||
|
@ -200,18 +199,24 @@ def mock_policy_manager_deployer(mock_miner_escrow_deployer):
|
|||
#
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def token_agent(chain, mock_token_deployer):
|
||||
token = NucypherTokenAgent(blockchain=chain)
|
||||
yield token
|
||||
def mock_token_agent(mock_token_deployer):
|
||||
token_agent = mock_token_deployer.make_agent()
|
||||
|
||||
assert mock_token_deployer._contract.address == token_agent.contract_address
|
||||
yield token_agent
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def mock_miner_agent(token_agent, mock_token_deployer, mock_miner_escrow_deployer):
|
||||
miner_agent = MinerAgent(token_agent=token_agent)
|
||||
def mock_miner_agent(mock_miner_escrow_deployer):
|
||||
miner_agent = mock_miner_escrow_deployer.make_agent()
|
||||
|
||||
assert mock_miner_escrow_deployer._contract.address == miner_agent.contract_address
|
||||
yield miner_agent
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def mock_policy_agent(mock_miner_agent, token_agent, mock_token_deployer, mock_miner_escrow_deployer):
|
||||
def mock_policy_agent(mock_miner_agent, mock_token_agent, mock_token_deployer, mock_miner_escrow_deployer):
|
||||
policy_agent = PolicyAgent(miner_agent=mock_miner_agent)
|
||||
|
||||
assert mock_token_deployer._contract.address == policy_agent.contract_address
|
||||
yield policy_agent
|
||||
|
|
Loading…
Reference in New Issue