Merge pull request #270 from KPrasch/sol-client

Blockchain client updates and bugfixes
pull/273/head
K Prasch 2018-05-16 22:50:14 -07:00 committed by GitHub
commit 31503e50e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1716 additions and 356 deletions

1077
Pipfile.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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]

View File

@ -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())

View File

@ -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.
"""

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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):

View File

@ -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):

View File

@ -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;
}

View File

@ -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)
{
}

View File

@ -29,4 +29,8 @@ contract GovernmentV2Mock is Government {
super.verifyState(_testTarget);
require(uint256(delegateGet(_testTarget, "valueToCheck()")) == valueToCheck);
}
function setPreviousTarget(address _target) public {
previousTarget = _target;
}
}

View File

@ -43,4 +43,8 @@ contract MinersEscrowV1Mock is Upgradeable {
lockedTokens[node] = escrow.lockedTokens(node);
}
}
function setPreviousTarget(address _target) public {
previousTarget = _target;
}
}

View File

@ -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;
}
}

View File

@ -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 {}
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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(

View File

@ -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