Merge pull request #1092 from KPrasch/transacting-power

TransactingPower
pull/1107/head
K Prasch 2019-07-15 15:57:28 -07:00 committed by GitHub
commit 65f6fcdb17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 737 additions and 489 deletions

View File

@ -21,22 +21,15 @@ from datetime import datetime
from decimal import Decimal
from json import JSONDecodeError
from typing import Tuple, List, Dict, Union
from eth_utils import keccak
import maya
from constant_sorrow.constants import (
CONTRACT_NOT_DEPLOYED,
NO_DEPLOYER_ADDRESS,
EMPTY_STAKING_SLOT,
UNKNOWN_STAKES,
NOT_STAKING,
NO_STAKES,
STRANGER_STAKER,
NO_STAKING_DEVICE,
STRANGER_WORKER,
WORKER_NOT_RUNNING
)
from eth_tester.exceptions import TransactionFailed
from eth_utils import keccak
from twisted.logger import Logger
from nucypher.blockchain.economics import TokenEconomics
@ -47,7 +40,6 @@ from nucypher.blockchain.eth.agents import (
AdjudicatorAgent,
EthereumContractAgent
)
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.deployers import (
NucypherTokenDeployer,
StakingEscrowDeployer,
@ -57,10 +49,12 @@ from nucypher.blockchain.eth.deployers import (
AdjudicatorDeployer,
ContractDeployer)
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.registry import AllocationRegistry
from nucypher.blockchain.eth.token import NU, Stake, StakeTracker
from nucypher.blockchain.eth.utils import datetime_to_period, calculate_period_duration
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.powers import TransactingPower
def only_me(func):
@ -146,6 +140,7 @@ class Deployer(NucypherTokenActor):
def __init__(self,
blockchain: BlockchainInterface,
deployer_address: str = None,
client_password: str = None,
bare: bool = True
) -> None:
@ -162,6 +157,11 @@ class Deployer(NucypherTokenActor):
self.user_escrow_deployers = dict()
self.deployers = {d.contract_name: d for d in self.deployer_classes}
blockchain.transacting_power = TransactingPower(blockchain=blockchain,
account=deployer_address,
password=client_password)
blockchain.transacting_power.activate()
self.log = Logger("Deployment-Actor")
def __repr__(self):
@ -170,12 +170,6 @@ class Deployer(NucypherTokenActor):
deployer_address=self.deployer_address)
return r
@classmethod
def from_blockchain(cls, provider_uri: str, registry=None, *args, **kwargs):
blockchain = BlockchainInterface.connect(provider_uri=provider_uri, registry=registry)
instance = cls(blockchain=blockchain, *args, **kwargs)
return instance
@property
def deployer_address(self):
return self.blockchain.deployer_address

View File

@ -118,7 +118,7 @@ class NucypherTokenAgent(EthereumContractAgent, metaclass=Agency):
"""Approve the transfer of token from the sender address to the target address."""
payload = {'gas': 500_000} # TODO #413: gas needed for use with geth.
contract_function = self.contract.functions.approve(target_address, amount)
receipt = self.blockchain.send_transaction(transaction_function=contract_function,
receipt = self.blockchain.send_transaction(contract_function=contract_function,
payload=payload,
sender_address=sender_address)
return receipt
@ -126,7 +126,7 @@ class NucypherTokenAgent(EthereumContractAgent, metaclass=Agency):
def transfer(self, amount: int, target_address: str, sender_address: str):
self.approve_transfer(amount=amount, target_address=target_address, sender_address=sender_address)
contract_function = self.contract.functions.transfer(target_address, amount)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=sender_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=sender_address)
return receipt
@ -186,13 +186,13 @@ class StakingEscrowAgent(EthereumContractAgent, metaclass=Agency):
def deposit_tokens(self, amount: int, lock_periods: int, sender_address: str):
"""Send tokens to the escrow from the staker's address"""
contract_function = self.contract.functions.deposit(amount, lock_periods)
receipt = self.blockchain.send_transaction(transaction_function=contract_function,
receipt = self.blockchain.send_transaction(contract_function=contract_function,
sender_address=sender_address)
return receipt
def divide_stake(self, staker_address: str, stake_index: int, target_value: int, periods: int):
contract_function = self.contract.functions.divideStake(stake_index, target_value, periods)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=staker_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=staker_address)
return receipt
def get_last_active_period(self, address: str) -> int:
@ -209,7 +209,7 @@ class StakingEscrowAgent(EthereumContractAgent, metaclass=Agency):
def set_worker(self, staker_address: str, worker_address: str):
contract_function = self.contract.functions.setWorker(worker_address)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=staker_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=staker_address)
return receipt
def release_worker(self, staker_address: str):
@ -220,7 +220,7 @@ class StakingEscrowAgent(EthereumContractAgent, metaclass=Agency):
For each period that the worker confirms activity, the staker is rewarded.
"""
contract_function = self.contract.functions.confirmActivity()
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=worker_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=worker_address)
return receipt
def mint(self, staker_address: str):
@ -230,7 +230,7 @@ class StakingEscrowAgent(EthereumContractAgent, metaclass=Agency):
when you intend to withdraw 100% of tokens.
"""
contract_function = self.contract.functions.mint()
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=staker_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=staker_address)
return receipt
@validate_checksum_address
@ -252,7 +252,7 @@ class StakingEscrowAgent(EthereumContractAgent, metaclass=Agency):
"""Withdraw tokens"""
payload = {'gas': 500_000} # TODO: #842 Gas Management
contract_function = self.contract.functions.withdraw(amount)
receipt = self.blockchain.send_transaction(transaction_function=contract_function,
receipt = self.blockchain.send_transaction(contract_function=contract_function,
payload=payload,
sender_address=staker_address)
return receipt
@ -322,7 +322,7 @@ class PolicyAgent(EthereumContractAgent, metaclass=Agency):
payload = {'value': value}
contract_function = self.contract.functions.createPolicy(policy_id, periods, initial_reward, node_addresses)
receipt = self.blockchain.send_transaction(transaction_function=contract_function,
receipt = self.blockchain.send_transaction(contract_function=contract_function,
payload=payload,
sender_address=author_address)
return receipt
@ -335,13 +335,13 @@ class PolicyAgent(EthereumContractAgent, metaclass=Agency):
def revoke_policy(self, policy_id: bytes, author_address: str):
"""Revoke by arrangement ID; Only the policy's author_address can revoke the policy."""
contract_function = self.contract.functions.revokePolicy(policy_id)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=author_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=author_address)
return receipt
def collect_policy_reward(self, collector_address: str, staker_address: str):
"""Collect rewarded ETH"""
contract_function = self.contract.functions.withdraw(collector_address)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=staker_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=staker_address)
return receipt
def fetch_policy_arrangements(self, policy_id):
@ -352,17 +352,17 @@ class PolicyAgent(EthereumContractAgent, metaclass=Agency):
def revoke_arrangement(self, policy_id: str, node_address: str, author_address: str):
contract_function = self.contract.functions.revokeArrangement(policy_id, node_address)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=author_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=author_address)
return receipt
def calculate_refund(self, policy_id: str, author_address: str):
contract_function = self.contract.functions.calculateRefundValue(policy_id)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=author_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=author_address)
return receipt
def collect_refund(self, policy_id: str, author_address: str):
contract_function = self.contract.functions.refund(policy_id)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=author_address)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=author_address)
return receipt
@ -455,47 +455,47 @@ class UserEscrowAgent(EthereumContractAgent):
def lock(self, amount: int, periods: int):
contract_function = self.__proxy_contract.functions.lock(amount, periods)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def withdraw_tokens(self, value: int):
contract_function = self.principal_contract.functions.withdrawTokens(value)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def withdraw_eth(self):
contract_function = self.principal_contract.functions.withdrawETH()
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def deposit_as_staker(self, value: int, periods: int):
contract_function = self.__proxy_contract.functions.depositAsStaker(value, periods)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def withdraw_as_staker(self, value: int):
contract_function = self.__proxy_contract.functions.withdrawAsStaker(value)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def set_worker(self, worker_address: str):
contract_function = self.__proxy_contract.functions.setWorker(worker_address)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def mint(self):
contract_function = self.__proxy_contract.functions.mint()
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def collect_policy_reward(self):
contract_function = self.__proxy_contract.functions.withdrawPolicyReward()
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
def set_min_reward_rate(self, rate: int):
contract_function = self.__proxy_contract.functions.setMinRewardRate(rate)
receipt = self.blockchain.send_transaction(transaction_function=contract_function, sender_address=self.__beneficiary)
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=self.__beneficiary)
return receipt
@ -513,7 +513,7 @@ class AdjudicatorAgent(EthereumContractAgent, metaclass=Agency):
"""
payload = {'gas': 500_000} # TODO #413: gas needed for use with geth.
contract_function = self.contract.functions.evaluateCFrag(*evidence.evaluation_arguments())
receipt = self.blockchain.send_transaction(transaction_function=contract_function,
receipt = self.blockchain.send_transaction(contract_function=contract_function,
sender_address=sender_address,
payload=payload)
return receipt

View File

@ -6,6 +6,7 @@ from typing import Union
import maya
from constant_sorrow.constants import NOT_RUNNING, UNKNOWN_DEVELOPMENT_CHAIN_ID
from cytoolz.dicttoolz import dissoc
from eth_account import Account
from eth_account.messages import encode_defunct
from eth_utils import to_canonical_address
@ -59,7 +60,7 @@ LOCAL_CHAINS = {1337: "GethDev",
5777: "Ganache/TestRPC"}
class Web3Client(object):
class Web3Client:
is_local = False
@ -151,6 +152,10 @@ class Web3Client(object):
def syncing(self) -> Union[bool, dict]:
return self.w3.eth.syncing
def lock_account(self, address):
if not self.is_local:
return self.lock_account(address=address)
def unlock_account(self, address, password) -> bool:
if not self.is_local:
return self.unlock_account(address, password)
@ -195,6 +200,9 @@ class Web3Client(object):
receipt = self.w3.eth.waitForTransactionReceipt(transaction_hash, timeout=timeout)
return receipt
def sign_transaction(self, transaction: dict):
raise NotImplementedError
def get_transaction(self, transaction_hash) -> str:
return self.w3.eth.getTransaction(transaction_hash)
@ -264,6 +272,19 @@ class GethClient(Web3Client):
def unlock_account(self, address, password):
return self.w3.geth.personal.unlockAccount(address, password)
def sign_transaction(self, transaction: dict) -> bytes:
# Do not include a 'to' field for contract creation.
if transaction['to'] == b'':
transaction = dissoc(transaction, 'to')
# Sign
result = self.w3.eth.signTransaction(transaction=transaction)
# Return RLP bytes
rlp_encoded_transaction = result.raw
return rlp_encoded_transaction
class ParityClient(Web3Client):
@ -293,15 +314,18 @@ class EthereumTesterClient(Web3Client):
is_local = True
def unlock_account(self, address, password):
return True
def unlock_account(self, address, password) -> bool:
"""Returns True if the testing backend keyring has control of the given address."""
address = to_canonical_address(address)
keystore = self.w3.provider.ethereum_tester.backend._key_lookup
return address in keystore
def sync(self, *args, **kwargs):
return True
def sign_transaction(self, account: str, transaction: dict):
def sign_transaction(self, transaction: dict):
# Get signing key of test account
address = to_canonical_address(account)
address = to_canonical_address(transaction['from'])
signing_key = self.w3.provider.ethereum_tester.backend._key_lookup[address]._raw_key
# Sign using a local private key

View File

@ -204,7 +204,7 @@ class DispatcherDeployer(ContractDeployer):
origin_args = {'from': self.deployer_address, 'gasPrice': self.blockchain.client.gas_price} # TODO: Gas management
upgrade_function = self._contract.functions.upgrade(new_target, existing_secret_plaintext, new_secret_hash)
upgrade_receipt = self.blockchain.send_transaction(transaction_function=upgrade_function,
upgrade_receipt = self.blockchain.send_transaction(contract_function=upgrade_function,
sender_address=self.deployer_address,
payload=origin_args)
return upgrade_receipt
@ -212,7 +212,7 @@ class DispatcherDeployer(ContractDeployer):
def rollback(self, existing_secret_plaintext: bytes, new_secret_hash: bytes) -> dict:
origin_args = {'from': self.deployer_address, 'gasPrice': self.blockchain.client.gas_price} # TODO: Gas management
rollback_function = self._contract.functions.rollback(existing_secret_plaintext, new_secret_hash)
rollback_receipt = self.blockchain.send_transaction(transaction_function=rollback_function,
rollback_receipt = self.blockchain.send_transaction(contract_function=rollback_function,
sender_address=self.deployer_address,
payload=origin_args)
return rollback_receipt
@ -294,7 +294,7 @@ class StakingEscrowDeployer(ContractDeployer):
reward_function = self.token_agent.contract.functions.transfer(the_escrow_contract.address,
self.__economics.erc20_reward_supply)
reward_receipt = self.blockchain.send_transaction(transaction_function=reward_function,
reward_receipt = self.blockchain.send_transaction(contract_function=reward_function,
sender_address=self.deployer_address,
payload=origin_args)
@ -304,7 +304,7 @@ class StakingEscrowDeployer(ContractDeployer):
# 4 - Initialize the Staker Escrow contract
init_function = the_escrow_contract.functions.initialize()
init_receipt = self.blockchain.send_transaction(transaction_function=init_function,
init_receipt = self.blockchain.send_transaction(contract_function=init_function,
sender_address=self.deployer_address,
payload=origin_args)
@ -418,7 +418,7 @@ class PolicyManagerDeployer(ContractDeployer):
if gas_limit:
tx_args.update({'gas': gas_limit})
set_policy_manager_function = self.staking_agent.contract.functions.setPolicyManager(policy_manager_contract.address)
set_policy_manager_receipt = self.blockchain.send_transaction(transaction_function=set_policy_manager_function,
set_policy_manager_receipt = self.blockchain.send_transaction(contract_function=set_policy_manager_function,
sender_address=self.deployer_address,
payload=tx_args)
@ -505,7 +505,7 @@ class LibraryLinkerDeployer(ContractDeployer):
origin_args = {'from': self.deployer_address} # TODO: Gas management
retarget_function = self._contract.functions.upgrade(new_target, existing_secret_plaintext, new_secret_hash)
retarget_receipt = self.blockchain.send_transaction(transaction_function=retarget_function,
retarget_receipt = self.blockchain.send_transaction(contract_function=retarget_function,
sender_address=self.deployer_address,
payload=origin_args)
return retarget_receipt
@ -636,7 +636,7 @@ class UserEscrowDeployer(ContractDeployer):
# TODO: #413, #842 - Gas Management
payload = {'from': self.deployer_address, 'gas': 500_000, 'gasPrice': self.blockchain.client.gas_price}
transfer_owner_function = self.contract.functions.transferOwnership(beneficiary_address)
transfer_owner_receipt = self.blockchain.send_transaction(transaction_function=transfer_owner_function,
transfer_owner_receipt = self.blockchain.send_transaction(contract_function=transfer_owner_function,
payload=payload,
sender_address=self.deployer_address)
self.__beneficiary_address = beneficiary_address
@ -657,7 +657,7 @@ class UserEscrowDeployer(ContractDeployer):
'gasPrice': self.blockchain.client.gas_price,
'gas': 200_000}
deposit_function = self.contract.functions.initialDeposit(value, duration)
deposit_receipt = self.blockchain.send_transaction(transaction_function=deposit_function,
deposit_receipt = self.blockchain.send_transaction(contract_function=deposit_function,
sender_address=self.deployer_address,
payload=args)
@ -748,7 +748,7 @@ class AdjudicatorDeployer(ContractDeployer):
if gas_limit:
tx_args.update({'gas': gas_limit})
set_adjudicator_function = self.staking_agent.contract.functions.setAdjudicator(adjudicator_contract.address)
set_adjudicator_receipt = self.blockchain.send_transaction(transaction_function=set_adjudicator_function,
set_adjudicator_receipt = self.blockchain.send_transaction(contract_function=set_adjudicator_function,
sender_address=self.deployer_address,
payload=tx_args)

View File

@ -31,10 +31,12 @@ from constant_sorrow.constants import (
READ_ONLY_INTERFACE
)
from eth_tester import EthereumTester
from eth_utils import to_checksum_address, to_canonical_address
from twisted.logger import Logger
from web3 import Web3, WebsocketProvider, HTTPProvider, IPCProvider
from web3.contract import Contract, ConciseContract, ContractFunction
from web3.exceptions import TimeExhausted
from web3.contract import ConciseContract
from web3.contract import Contract, ContractFunction, ContractConstructor
from web3.exceptions import TimeExhausted, ValidationError
from web3.middleware import geth_poa_middleware
from nucypher.blockchain.eth.clients import Web3Client, NuCypherGethProcess
@ -49,7 +51,7 @@ from nucypher.blockchain.eth.providers import (
)
from nucypher.blockchain.eth.registry import EthereumContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
Web3Providers = Union[IPCProvider, WebsocketProvider, HTTPProvider, EthereumTester]
@ -83,13 +85,11 @@ class BlockchainInterface:
def __init__(self,
poa: bool = True,
sync_now: bool = True,
provider_process: NuCypherGethProcess = NO_PROVIDER_PROCESS,
provider_uri: str = NO_BLOCKCHAIN_CONNECTION,
transacting_power: BlockchainPower = READ_ONLY_INTERFACE,
transacting_power: TransactingPower = READ_ONLY_INTERFACE,
provider: Web3Providers = NO_BLOCKCHAIN_CONNECTION,
registry: EthereumContractRegistry = None,
fetch_registry: bool = True):
registry: EthereumContractRegistry = None):
"""
A blockchain "network interface"; The circumflex wraps entirely around the bounds of
@ -164,11 +164,6 @@ class BlockchainInterface:
self.transacting_power = transacting_power
self.registry = registry
self.connect(provider=provider,
provider_uri=provider_uri,
fetch_registry=fetch_registry,
sync_now=sync_now)
BlockchainInterface._instance = self
def __repr__(self):
@ -188,6 +183,8 @@ class BlockchainInterface:
"""
https://web3py.readthedocs.io/en/stable/__provider.html#examples-using-automated-detection
"""
if self.client is NO_BLOCKCHAIN_CONNECTION:
return False
return self.client.is_connected
def disconnect(self):
@ -208,21 +205,18 @@ class BlockchainInterface:
self.log.debug('Injecting POA middleware at layer 0')
self.client.inject_middleware(geth_poa_middleware, layer=0)
def connect(self,
provider: Web3Providers = None,
provider_uri: str = None,
fetch_registry: bool = True,
sync_now: bool = True):
def connect(self, fetch_registry: bool = True, sync_now: bool = True):
# Spawn child process
if self._provider_process:
self._provider_process.start()
provider_uri = self._provider_process.provider_uri(scheme='file')
else:
self.log.info(f"Using external Web3 Provider '{provider_uri}'")
provider_uri = self.provider_uri
self.log.info(f"Using external Web3 Provider '{self.provider_uri}'")
# Attach Provider
self._attach_provider(provider=provider, provider_uri=provider_uri)
self._attach_provider(provider=self._provider, provider_uri=provider_uri)
self.log.info("Connecting to {}".format(self.provider_uri))
if self._provider is NO_BLOCKCHAIN_CONNECTION:
raise self.NoProvider("There are no configured blockchain providers")
@ -291,7 +285,7 @@ class BlockchainInterface:
self._provider = provider
def send_transaction(self,
transaction_function: ContractFunction,
contract_function: ContractFunction,
sender_address: str,
payload: dict = None,
) -> dict:
@ -312,7 +306,21 @@ class BlockchainInterface:
'from': sender_address,
'gasPrice': self.client.w3.eth.gasPrice})
unsigned_transaction = transaction_function.buildTransaction(payload)
# Get interface name
try:
transaction_name = contract_function.fn_name.upper()
except AttributeError:
if isinstance(contract_function, ContractConstructor):
transaction_name = 'DEPLOY'
else:
transaction_name = 'UNKNOWN'
# Build transaction payload
try:
unsigned_transaction = contract_function.buildTransaction(payload)
except ValidationError:
# TODO: Handle validation failures for gas limits, invalid fields, etc.
raise
#
# Broadcast
@ -320,11 +328,15 @@ class BlockchainInterface:
signed_raw_transaction = self.transacting_power.sign_transaction(unsigned_transaction)
txhash = self.client.send_raw_transaction(signed_raw_transaction)
self.log.debug(f"[TX-{transaction_name}] | {to_checksum_address(payload['from'])}")
try:
receipt = self.client.wait_for_receipt(txhash, timeout=self.TIMEOUT)
except TimeExhausted:
# TODO: Handle transaction timeout
raise
else:
self.log.debug(f"[RECEIPT-{transaction_name}] | {receipt['transactionHash'].hex()}")
#
# Confirm
@ -427,9 +439,13 @@ class BlockchainDeployerInterface(BlockchainInterface):
super().__init__(*args, **kwargs)
self.compiler = compiler or SolidityCompiler()
self._setup_solidity(compiler=self.compiler)
self.__deployer_address = deployer_address or NO_DEPLOYER_CONFIGURED
def connect(self, *args, **kwargs):
super().connect(*args, **kwargs)
self._setup_solidity(compiler=self.compiler)
return self.is_connected
@property
def deployer_address(self):
return self.__deployer_address
@ -488,7 +504,7 @@ class BlockchainDeployerInterface(BlockchainInterface):
# Transmit the deployment tx #
#
receipt = self.send_transaction(transaction_function=transaction_function,
receipt = self.send_transaction(contract_function=transaction_function,
sender_address=self.deployer_address,
payload=deploy_transaction)

View File

@ -285,7 +285,7 @@ class BlockchainPolicy(Policy):
# Transact
contract_function = self.author.policy_agent.contract.functions.createPolicy(*policy_args)
receipt = self.author.blockchain.send_transaction(transaction_function=contract_function,
receipt = self.author.blockchain.send_transaction(contract_function=contract_function,
sender_address=self.author.checksum_address,
payload=payload)
txhash = receipt['transactionHash']

View File

@ -176,10 +176,12 @@ class Character(Learner):
# Decentralized
#
if not federated_only:
if not blockchain and is_me:
raise ValueError('No blockchain interface provided to run decentralized mode.')
if not checksum_address:
raise ValueError("No checksum_address provided while running in a non-federated mode.")
raise ValueError("No checksum_address provided to run in decentralized mode.")
else:
self._checksum_address = checksum_address # TODO: Check that this matches BlockchainPower
self._checksum_address = checksum_address # TODO: Check that this matches TransactingPower
#
# Federated
#
@ -304,7 +306,7 @@ class Character(Learner):
except TypeError:
umbral_key = public_key
crypto_power.consume_power_up(power_up(pubkey=umbral_key))
crypto_power.consume_power_up(power_up(public_key=umbral_key))
return cls(is_me=False, federated_only=federated_only, crypto_power=crypto_power, *args, **kwargs)

View File

@ -27,7 +27,7 @@ from nucypher.blockchain.eth.token import NU
from nucypher.characters.banners import MOE_BANNER, FELIX_BANNER, NU_BANNER
from nucypher.characters.base import Character
from nucypher.config.constants import TEMPLATES_DIR
from nucypher.crypto.powers import SigningPower, BlockchainPower
from nucypher.crypto.powers import SigningPower, TransactingPower
from nucypher.keystore.threading import ThreadedSession
from nucypher.network.nodes import FleetStateTracker
@ -161,6 +161,7 @@ class Felix(Character, NucypherTokenActor):
db_filepath: str,
rest_host: str,
rest_port: int,
client_password: str = None,
crash_on_error: bool = False,
economics: TokenEconomics = None,
distribute_ether: bool = True,
@ -182,9 +183,10 @@ class Felix(Character, NucypherTokenActor):
self.db_engine = create_engine(f'sqlite:///{self.db_filepath}', convert_unicode=True)
# Blockchain
blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_address)
self._crypto_power.consume_power_up(blockchain_power)
# blockchain_power.unlock_account(password=None) # TODO: TransactingPower
transacting_power = TransactingPower(blockchain=self.blockchain,
password=client_password,
account=self.checksum_address)
self._crypto_power.consume_power_up(transacting_power)
self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
self.reserved_addresses = [self.checksum_address, BlockchainInterface.NULL_ADDRESS]

View File

@ -28,8 +28,7 @@ import requests
from bytestring_splitter import BytestringKwargifier, BytestringSplittingError
from bytestring_splitter import BytestringSplitter, VariableLengthBytestring
from constant_sorrow import constants
from constant_sorrow.constants import FEDERATED_POLICY, STRANGER_ALICE
from constant_sorrow.constants import INCLUDED_IN_BYTESTRING, PUBLIC_ONLY
from constant_sorrow.constants import INCLUDED_IN_BYTESTRING, PUBLIC_ONLY, FEDERATED_POLICY, STRANGER_ALICE
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve
from cryptography.hazmat.primitives.serialization import Encoding
@ -60,7 +59,7 @@ from nucypher.config.storages import NodeStorage, ForgetfulNodeStorage
from nucypher.crypto.api import keccak_digest, encrypt_and_sign
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, PUBLIC_ADDRESS_LENGTH
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import SigningPower, DecryptingPower, DelegatingPower, BlockchainPower, PowerUpError
from nucypher.crypto.powers import SigningPower, DecryptingPower, DelegatingPower, TransactingPower, PowerUpError
from nucypher.crypto.signing import InvalidSignature
from nucypher.keystore.keypairs import HostingKeypair
from nucypher.network.exceptions import NodeSeemsToBeDown
@ -90,6 +89,7 @@ class Alice(Character, PolicyAuthor):
network_middleware=None,
controller=True,
policy_agent=None,
client_password: str = None,
*args, **kwargs) -> None:
#
@ -119,16 +119,16 @@ class Alice(Character, PolicyAuthor):
*args, **kwargs)
if is_me and not federated_only: # TODO: #289
transacting_power = TransactingPower(account=self.checksum_address,
password=client_password,
blockchain=self.blockchain)
self._crypto_power.consume_power_up(transacting_power)
PolicyAuthor.__init__(self,
blockchain=self.blockchain,
policy_agent=policy_agent,
checksum_address=checksum_address)
# TODO: #1092 - TransactingPower
blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_address)
self._crypto_power.consume_power_up(blockchain_power)
self.blockchain.transacting_power = blockchain_power # TODO: Embed in Powerups
if is_me and controller:
self.controller = self._controller_class(alice=self)
@ -842,6 +842,7 @@ class Ursula(Teacher, Character, Worker):
checksum_address: str = None, # Staker address
worker_address: str = None,
stake_tracker: StakeTracker = None,
client_password: str = None,
# Character
password: str = None,
@ -888,6 +889,16 @@ class Ursula(Teacher, Character, Worker):
# Ursula is a Decentralized Worker
#
if not federated_only:
# Access staking node via node's transacting keys
transacting_power = TransactingPower(account=worker_address,
password=client_password,
blockchain=self.blockchain)
self._crypto_power.consume_power_up(transacting_power)
# Use blockchain power to substantiate stamp
self.substantiate_stamp(client_password=password)
Worker.__init__(self,
is_me=is_me,
blockchain=self.blockchain,
@ -895,17 +906,10 @@ class Ursula(Teacher, Character, Worker):
worker_address=worker_address,
stake_tracker=stake_tracker)
# Access to worker's ETH client via node's transacting keys
# TODO: #1092 - TransactingPower
blockchain_power = BlockchainPower(blockchain=self.blockchain, account=worker_address)
self._crypto_power.consume_power_up(blockchain_power)
self.blockchain.transacting_power = blockchain_power # TODO: Embed in powerups
self.substantiate_stamp(client_password=password) # TODO: Use PowerUp / Derive from keyring
#
# ProxyRESTServer and TLSHostingPower # TODO: Maybe we want _power_ups to be public after all?
# ProxyRESTServer and TLSHostingPower #
#
if not crypto_power or (TLSHostingPower not in crypto_power._power_ups):
if not crypto_power or (TLSHostingPower not in crypto_power):
#
# Ephemeral Self-Ursula
@ -966,15 +970,16 @@ class Ursula(Teacher, Character, Worker):
certificate = self._crypto_power.power_ups(TLSHostingPower).keypair.certificate
Teacher.__init__(self,
password=password,
worker_address=worker_address,
domains=domains,
certificate=certificate,
certificate_filepath=certificate_filepath,
interface_signature=interface_signature,
timestamp=timestamp,
decentralized_identity_evidence=decentralized_identity_evidence,
# TODO: #1091 When is_me and not federated_only, the stamp is substantiated twice
worker_address=worker_address,
substantiate_immediately=is_me and not federated_only,
# FIXME: When is_me and not federated_only, the stamp is substantiated twice
)
#

View File

@ -23,7 +23,8 @@ from typing import List
import click
import requests
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION, NO_PASSWORD
from nacl.exceptions import CryptoError
from twisted.logger import Logger
from nucypher.blockchain.eth.clients import NuCypherGethGoerliProcess
@ -32,6 +33,7 @@ from nucypher.characters.lawful import Ursula
from nucypher.cli.config import NucypherClickConfig
from nucypher.cli.types import IPV4_ADDRESS
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, USER_LOG_DIR
from nucypher.config.node import CharacterConfiguration
from nucypher.network.middleware import RestMiddleware
from nucypher.network.teachers import TEACHER_NODES
@ -67,6 +69,27 @@ class UnknownIPAddress(RuntimeError):
pass
def get_password(confirm: bool = False) -> str:
keyring_password = os.environ.get("NUCYPHER_KEYRING_PASSWORD", NO_PASSWORD)
if keyring_password is NO_PASSWORD: # Collect password, prefer env var
prompt = "Enter keyring password"
keyring_password = click.prompt(prompt, confirmation_prompt=confirm, hide_input=True)
return keyring_password
def unlock_nucypher_keyring(password: str, character_configuration: CharacterConfiguration):
console_emitter(message='Decrypting NuCypher keyring...', color='yellow')
if character_configuration.dev_mode:
return True # Dev accounts are always unlocked
# NuCypher
try:
character_configuration.attach_keyring()
character_configuration.keyring.unlock(password=password) # Takes ~3 seconds, ~1GB Ram
except CryptoError:
raise character_configuration.keyring.AuthenticationFailed
def load_seednodes(min_stake: int,
federated_only: bool,
network_domains: set,
@ -104,25 +127,6 @@ def load_seednodes(min_stake: int,
return teacher_nodes
def destroy_configuration_root(config_root=None, force=False, logs: bool = False) -> str:
"""CAUTION: This will destroy *all* nucypher configuration files from the configuration root"""
config_root = config_root or DEFAULT_CONFIG_ROOT
if not force:
click.confirm(DESTRUCTION.format(config_root), abort=True) # ABORT
if os.path.isdir(config_root):
shutil.rmtree(config_root, ignore_errors=force) # config
else:
console_emitter(message=f'No NuCypher configuration root directory found at \'{config_root}\'')
if logs:
shutil.rmtree(USER_LOG_DIR, ignore_errors=force) # logs
return config_root
def get_external_ip_from_centralized_source() -> str:
ip_request = requests.get('https://ifconfig.me/')
if ip_request.status_code == 200:
@ -226,7 +230,6 @@ def make_cli_character(character_config,
dev: bool = False,
teacher_uri: str = None,
min_stake: int = 0,
sync: bool = True,
**config_args):
#
@ -235,13 +238,13 @@ def make_cli_character(character_config,
# Handle Blockchain
if not character_config.federated_only:
click_config.connect_to_blockchain(character_configuration=character_config, sync_now=sync)
character_config.get_blockchain_interface()
# Handle Keyring
if not dev:
character_config.attach_keyring()
click_config.unlock_keyring(character_configuration=character_config,
password=click_config.get_password(confirm=False))
unlock_nucypher_keyring(character_configuration=character_config,
password=get_password(confirm=False))
# Handle Teachers
teacher_nodes = None
@ -250,7 +253,7 @@ def make_cli_character(character_config,
min_stake=min_stake,
federated_only=character_config.federated_only,
network_domains=character_config.domains,
network_middleware=click_config.middleware)
network_middleware=character_config.network_middleware)
#
# Character Init
@ -258,19 +261,20 @@ def make_cli_character(character_config,
# Produce Character
CHARACTER = character_config(known_nodes=teacher_nodes,
network_middleware=click_config.middleware,
network_middleware=character_config.network_middleware,
**config_args)
#
# Post-Init
#
# TODO: Move to character configuration
# Switch to character control emitter
if click_config.json_ipc:
CHARACTER.controller.emitter = JSONRPCStdoutEmitter(quiet=click_config.quiet)
# Federated
if character_config.federated_only:
click_config.emit(message="WARNING: Running in Federated mode", color='yellow')
console_emitter(message="WARNING: Running in Federated mode", color='yellow')
return CHARACTER

View File

@ -1,12 +1,15 @@
import click
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.registry import EthereumContractRegistry
from nucypher.characters.banners import ALICE_BANNER
from nucypher.cli import actions, painting
from nucypher.cli import types
from nucypher.cli import actions, painting, types
from nucypher.cli.actions import get_password
from nucypher.cli.config import nucypher_click_config
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE, EIP55_CHECKSUM_ADDRESS
from nucypher.config.characters import AliceConfiguration
from nucypher.crypto.powers import TransactingPower
@click.command()
@ -24,6 +27,7 @@ from nucypher.config.characters import AliceConfiguration
@click.option('--config-file', help="Path to configuration file", type=EXISTING_READABLE_FILE)
@click.option('--provider-uri', help="Blockchain provider's URI", type=click.STRING)
@click.option('--sync/--no-sync', default=True)
@click.option('--device/--no-device', default=False)
@click.option('--geth', '-G', help="Run using the built-in geth node", is_flag=True)
@click.option('--poa', help="Inject POA middleware", is_flag=True, default=None)
@click.option('--no-registry', help="Skip importing the default contract registry", is_flag=True)
@ -68,6 +72,7 @@ def alice(click_config,
poa,
no_registry,
registry_filepath,
device,
# Alice
bob_encrypting_key,
@ -79,7 +84,7 @@ def alice(click_config,
rate,
duration,
expiration,
message_kit
message_kit,
):
@ -117,7 +122,7 @@ def alice(click_config,
if not config_root: # Flag
config_root = click_config.config_file # Envvar
new_alice_config = AliceConfiguration.generate(password=click_config.get_password(confirm=True),
new_alice_config = AliceConfiguration.generate(password=get_password(confirm=True),
config_root=config_root,
checksum_address=pay_with,
domains={network} if network else None,
@ -168,13 +173,12 @@ def alice(click_config,
except FileNotFoundError:
return actions.handle_missing_configuration_file(character_config_class=AliceConfiguration,
config_file=config_file)
ALICE = actions.make_cli_character(character_config=alice_config,
click_config=click_config,
dev=dev,
teacher_uri=teacher_uri,
min_stake=min_stake,
sync=sync)
min_stake=min_stake)
#
# Admin Actions

View File

@ -2,6 +2,7 @@ import click
from nucypher.characters.banners import BOB_BANNER
from nucypher.cli import actions, painting
from nucypher.cli.actions import get_password
from nucypher.cli.config import nucypher_click_config
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE, EIP55_CHECKSUM_ADDRESS
from nucypher.config.characters import BobConfiguration
@ -78,7 +79,7 @@ def bob(click_config,
if not config_root: # Flag
config_root = click_config.config_file # Envvar
new_bob_config = BobConfiguration.generate(password=click_config.get_password(confirm=True),
new_bob_config = BobConfiguration.generate(password=get_password(confirm=True),
config_root=config_root or DEFAULT_CONFIG_ROOT,
checksum_address=pay_with,
domains={network} if network else None,

View File

@ -3,9 +3,9 @@ import os
import click
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
from nucypher.blockchain.eth.clients import NuCypherGethDevnetProcess
from nucypher.characters.banners import FELIX_BANNER
from nucypher.cli import actions, painting
from nucypher.cli.actions import get_password, unlock_nucypher_keyring
from nucypher.cli.config import nucypher_click_config
from nucypher.cli.types import NETWORK_PORT, EXISTING_READABLE_FILE, EIP55_CHECKSUM_ADDRESS
from nucypher.config.characters import FelixConfiguration
@ -72,11 +72,8 @@ def felix(click_config,
if not config_root: # Flag
config_root = DEFAULT_CONFIG_ROOT # Envvar or init-only default
# Acquire Keyring Password
new_password = click_config.get_password(confirm=True)
try:
new_felix_config = FelixConfiguration.generate(password=new_password,
new_felix_config = FelixConfiguration.generate(password=get_password(confirm=True),
config_root=config_root,
rest_host=host,
rest_port=discovery_port,
@ -123,12 +120,10 @@ def felix(click_config,
try:
# Connect to Blockchain
felix_config.connect_to_blockchain()
felix_config.get_blockchain_interface()
# Authenticate
password = click_config.get_password(confirm=False)
click_config.unlock_keyring(character_configuration=felix_config,
password=password)
unlock_nucypher_keyring(character_configuration=felix_config, password=get_password(confirm=False))
# Produce Teacher Ursulas
teacher_nodes = actions.load_seednodes(teacher_uris=[teacher_uri] if teacher_uri else None,

View File

@ -18,16 +18,13 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import click
import socket
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
from twisted.internet import stdio
from nucypher.blockchain.eth.clients import NuCypherGethDevnetProcess, NuCypherGethGoerliProcess
from nucypher.blockchain.eth.token import NU
from nucypher.characters.banners import URSULA_BANNER
from nucypher.cli import actions, painting
from nucypher.cli.actions import UnknownIPAddress
from nucypher.cli.actions import get_password
from nucypher.cli.config import nucypher_click_config
from nucypher.cli.processes import UrsulaCommandProtocol
from nucypher.cli.types import (
@ -36,8 +33,7 @@ from nucypher.cli.types import (
EXISTING_READABLE_FILE,
STAKE_DURATION,
STAKE_EXTENSION,
STAKE_VALUE,
IPV4_ADDRESS)
STAKE_VALUE)
from nucypher.config.characters import UrsulaConfiguration
from nucypher.utilities.sandbox.constants import (
TEMPORARY_DOMAIN,
@ -66,9 +62,9 @@ from nucypher.utilities.sandbox.constants import (
@click.option('--config-file', help="Path to configuration file", type=EXISTING_READABLE_FILE)
@click.option('--poa', help="Inject POA middleware", is_flag=True, default=None)
@click.option('--sync/--no-sync', default=True)
@click.option('--device/--no-device', default=False)
@click.option('--geth', '-G', help="Run using the built-in geth node", is_flag=True)
@click.option('--provider-uri', help="Blockchain provider's URI", type=click.STRING)
@click.option('--recompile-solidity', help="Compile solidity from source when making a web3 connection", is_flag=True)
@click.option('--no-registry', help="Skip importing the default contract registry", is_flag=True)
@click.option('--registry-filepath', help="Custom contract registry filepath", type=EXISTING_READABLE_FILE)
@click.option('--value', help="Token value of stake", type=click.INT)
@ -98,7 +94,6 @@ def ursula(click_config,
config_file,
provider_uri,
geth,
recompile_solidity,
no_registry,
registry_filepath,
value,
@ -107,6 +102,7 @@ def ursula(click_config,
list_,
divide,
sync,
device,
interactive,
) -> None:
@ -178,9 +174,7 @@ def ursula(click_config,
if not rest_host:
rest_host = actions.determine_external_ip_address(force=force)
new_password = click_config.get_password(confirm=True)
ursula_config = UrsulaConfiguration.generate(password=new_password,
ursula_config = UrsulaConfiguration.generate(password=get_password(confirm=True),
config_root=config_root,
rest_host=rest_host,
rest_port=rest_port,
@ -248,14 +242,12 @@ def ursula(click_config,
raise click.BadOptionUsage(option_name='--dev', message=message)
return actions.destroy_configuration(character_config=ursula_config, force=force)
#
# Make Ursula
#
URSULA = actions.make_cli_character(character_config=ursula_config,
click_config=click_config,
sync=sync,
min_stake=min_stake,
teacher_uri=teacher_uri,
dev=dev,

View File

@ -21,13 +21,10 @@ import collections
import os
import click
from constant_sorrow.constants import NO_PASSWORD, NO_BLOCKCHAIN_CONNECTION
from nacl.exceptions import CryptoError
from twisted.logger import Logger
from twisted.logger import globalLogPublisher
from nucypher.config.constants import NUCYPHER_SENTRY_ENDPOINT
from nucypher.config.node import CharacterConfiguration
from nucypher.utilities.logging import (
logToSentry,
getTextFileObserver,
@ -68,53 +65,6 @@ class NucypherClickConfig:
self.quiet = False
self.log = Logger(self.__class__.__name__)
# Auth
self.__keyring_password = NO_PASSWORD
# Blockchain
self.accounts = NO_BLOCKCHAIN_CONNECTION
self.blockchain = NO_BLOCKCHAIN_CONNECTION
def connect_to_blockchain(self, character_configuration, sync_now: bool = True):
character_configuration.connect_to_blockchain(sync_now=sync_now)
character_configuration.connect_to_contracts()
self.blockchain = character_configuration.blockchain
self.accounts = self.blockchain.client.accounts
def get_password(self, confirm: bool = False) -> str:
keyring_password = os.environ.get("NUCYPHER_KEYRING_PASSWORD", NO_PASSWORD)
if keyring_password is NO_PASSWORD: # Collect password, prefer env var
prompt = "Enter keyring password"
keyring_password = click.prompt(prompt, confirmation_prompt=confirm, hide_input=True)
self.__keyring_password = keyring_password
return self.__keyring_password
def unlock_keyring(self,
password: str,
character_configuration: CharacterConfiguration,
unlock_wallet: bool = True):
if not self.quiet:
self.emit(message='Decrypting NuCypher keyring...', color='yellow')
if character_configuration.dev_mode:
return True # Dev accounts are always unlocked
# NuCypher
try:
character_configuration.attach_keyring()
character_configuration.keyring.unlock(password=password) # Takes ~3 seconds, ~1GB Ram
except CryptoError:
raise character_configuration.keyring.AuthenticationFailed
# Ethereum Client # TODO : Integrate with Powers API
if not character_configuration.federated_only and unlock_wallet:
self.emit(message='Decrypting Ethereum Node Keyring...', color='yellow')
character_configuration.blockchain.client.unlock_account(address=character_configuration.checksum_address,
password=password)
@classmethod
def attach_emitter(cls, emitter) -> None:
cls.__emitter = emitter
@ -129,9 +79,6 @@ class NucypherDeployerClickConfig(NucypherClickConfig):
__secrets = ('staker_secret', 'policy_secret', 'escrow_proxy_secret', 'adjudicator_secret')
Secrets = collections.namedtuple('Secrets', __secrets)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def collect_deployment_secrets(self) -> Secrets:
# Deployment Environment Variables
@ -159,10 +106,10 @@ class NucypherDeployerClickConfig(NucypherClickConfig):
hide_input=True,
confirmation_prompt=True)
secrets = self.Secrets(staker_secret=self.staking_escrow_deployment_secret, # type: str
policy_secret=self.policy_manager_deployment_secret, # type: str
escrow_proxy_secret=self.user_escrow_proxy_deployment_secret, # type: str
adjudicator_secret=self.adjudicator_deployment_secret # type: str
secrets = self.Secrets(staker_secret=self.staking_escrow_deployment_secret, # type: str
policy_secret=self.policy_manager_deployment_secret, # type: str
escrow_proxy_secret=self.user_escrow_proxy_deployment_secret, # type: str
adjudicator_secret=self.adjudicator_deployment_secret # type: str
)
return secrets

View File

@ -19,19 +19,18 @@ import time
import click
import maya
from web3.exceptions import TimeExhausted
from nucypher.blockchain.eth.actors import Deployer
from nucypher.blockchain.eth.agents import NucypherTokenAgent
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainDeployerInterface
from nucypher.blockchain.eth.clients import NuCypherGethDevnetProcess
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import EthereumContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.characters.banners import NU_BANNER
from nucypher.cli.actions import get_password
from nucypher.cli.config import nucypher_deployer_config
from nucypher.cli.types import EIP55_CHECKSUM_ADDRESS, EXISTING_READABLE_FILE
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.powers import BlockchainPower
@click.command()
@ -42,6 +41,7 @@ from nucypher.crypto.powers import BlockchainPower
@click.option('--provider-uri', help="Blockchain provider's URI", type=click.STRING)
@click.option('--geth', '-G', help="Run using the built-in geth node", is_flag=True)
@click.option('--sync/--no-sync', default=True)
@click.option('--device/--no-device', default=False) # TODO: Make True by default.
@click.option('--enode', help="An ethereum bootnode enode address to start learning from", type=click.STRING)
@click.option('--config-root', help="Custom configuration directory", type=click.Path())
@click.option('--contract-name', help="Deploy a single contract by name", type=click.STRING)
@ -70,6 +70,7 @@ def deploy(click_config,
recipient_address,
config_root,
sync,
device,
force):
"""Manage contract and registry deployment"""
@ -104,9 +105,9 @@ def deploy(click_config,
blockchain = BlockchainDeployerInterface(provider_uri=provider_uri,
poa=poa,
registry=registry,
compiler=SolidityCompiler(),
fetch_registry=False,
sync_now=sync)
compiler=SolidityCompiler())
blockchain.connect(fetch_registry=False, sync_now=sync)
#
# Deployment Actor
@ -123,9 +124,13 @@ def deploy(click_config,
if not force:
click.confirm("Selected {} - Continue?".format(deployer_address), abort=True)
# TODO: Integrate with Deployer Actor (Character)
blockchain.transacting_power = BlockchainPower(blockchain=blockchain, account=deployer_address)
deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address)
password = None
if not device and not blockchain.client.is_local:
password = get_password(confirm=False)
deployer = Deployer(blockchain=blockchain,
client_password=password,
deployer_address=deployer_address)
# Verify ETH Balance
click.secho(f"\n\nDeployer ETH balance: {deployer.eth_balance}")
@ -133,11 +138,6 @@ def deploy(click_config,
click.secho("Deployer address has no ETH.", fg='red', bold=True)
raise click.Abort()
if not blockchain.client.is_local:
# (~ dev mode; Assume accounts are already unlocked)
password = click.prompt("Enter ETH node password", hide_input=True)
blockchain.client.unlockAccount(deployer_address, password)
# Add ETH Bootnode or Peer
if enode:
if geth:
@ -297,12 +297,6 @@ def deploy(click_config,
txhash = token_agent.transfer(amount=amount, sender_address=token_agent.contract_address, target_address=recipient_address)
click.secho(f"OK | {txhash}")
elif action == "destroy-registry":
registry_filepath = deployer.blockchain.registry.filepath
click.confirm(f"Are you absolutely sure you want to destroy the contract registry at {registry_filepath}?", abort=True)
os.remove(registry_filepath)
click.secho(f"Successfully destroyed {registry_filepath}", fg='red')
else:
raise click.BadArgumentUsage(message=f"Unknown action '{action}'")

View File

@ -38,8 +38,8 @@ def status(click_config, config_file):
#
ursula_config = UrsulaConfiguration.from_configuration_file(filepath=config_file)
if not ursula_config.federated_only:
ursula_config.connect_to_blockchain(provider_uri=ursula_config.provider_uri)
ursula_config.connect_to_contracts()
ursula_config.get_blockchain_interface(provider_uri=ursula_config.provider_uri)
ursula_config.acquire_agency()
# Contracts
paint_contract_status(ursula_config=ursula_config, click_config=click_config)

View File

@ -22,6 +22,7 @@ import stat
from json import JSONDecodeError
from typing import ClassVar, Tuple, Callable, Union, Dict, List
from constant_sorrow.constants import FEDERATED_ADDRESS
from constant_sorrow.constants import KEYRING_LOCKED
from cryptography import x509
from cryptography.hazmat.backends import default_backend
@ -34,13 +35,12 @@ from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.x509 import Certificate
from eth_account import Account
from eth_keys import KeyAPI as EthKeyAPI
from eth_utils import to_checksum_address, is_checksum_address
from eth_utils import to_checksum_address
from nacl.exceptions import CryptoError
from nacl.secret import SecretBox
from twisted.logger import Logger
from umbral.keys import UmbralPrivateKey, UmbralPublicKey, UmbralKeyingMaterial, derive_key_from_password
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.api import generate_self_signed_certificate
from nucypher.crypto.constants import BLAKE2B
@ -49,10 +49,8 @@ from nucypher.crypto.powers import (
DecryptingPower,
KeyPairBasedPower,
DerivedKeyBasedPower,
BlockchainPower
TransactingPower
)
from constant_sorrow.constants import FEDERATED_ADDRESS
from nucypher.network.server import TLSHostingPower
FILE_ENCODING = 'utf-8'
@ -499,10 +497,6 @@ class NucypherKeyring:
keying_material = SecretBox(wrap_key).decrypt(key_data['key'])
new_cryptopower = power_class(keying_material=keying_material)
elif power_class is BlockchainPower:
# new_cryptopower = power_class(account=self.checksum_address)
pass # TODO: Needs refactoring with TransactingPower
else:
failure_message = "{} is an invalid type for deriving a CryptoPower.".format(power_class.__name__)
raise ValueError(failure_message)
@ -514,13 +508,12 @@ class NucypherKeyring:
#
@classmethod
def generate(cls,
checksum_address: str,
password: str,
encrypting: bool,
rest: bool,
host: str = None,
curve: EllipticCurve = None,
federated: bool = False,
checksum_address: str = None,
keyring_root: str = None,
) -> 'NucypherKeyring':
"""
@ -550,14 +543,14 @@ class NucypherKeyring:
keyring_args = dict()
if checksum_address:
if checksum_address is not FEDERATED_ADDRESS:
# Addresses read from some node keyrings (clients) are *not* returned in checksum format.
checksum_address = to_checksum_address(checksum_address)
if encrypting is True:
signing_private_key, signing_public_key = _generate_signing_keys()
if federated and not checksum_address:
if checksum_address is FEDERATED_ADDRESS:
uncompressed_bytes = signing_public_key.to_bytes(is_compressed=False)
without_prefix = uncompressed_bytes[1:]
verifying_key_as_eth_key = EthKeyAPI.PublicKey(without_prefix)

View File

@ -26,7 +26,8 @@ from constant_sorrow.constants import (
NO_BLOCKCHAIN_CONNECTION,
LIVE_CONFIGURATION,
NO_KEYRING_ATTACHED,
DEVELOPMENT_CONFIGURATION
DEVELOPMENT_CONFIGURATION,
FEDERATED_ADDRESS
)
from twisted.logger import Logger
from umbral.signing import Signature
@ -126,7 +127,6 @@ class CharacterConfiguration(BaseConfiguration):
self.provider_uri = provider_uri or self.DEFAULT_PROVIDER_URI
self.provider_process = provider_process or NO_BLOCKCHAIN_CONNECTION
self.blockchain = NO_BLOCKCHAIN_CONNECTION.bool_value(False)
self.accounts = NO_BLOCKCHAIN_CONNECTION
self.token_agent = NO_BLOCKCHAIN_CONNECTION
self.staking_agent = NO_BLOCKCHAIN_CONNECTION
self.policy_agent = NO_BLOCKCHAIN_CONNECTION
@ -178,24 +178,15 @@ class CharacterConfiguration(BaseConfiguration):
def dev_mode(self) -> bool:
return self.__dev_mode
@property
def known_nodes(self):
return self.__fleet_state
def connect_to_blockchain(self, sync_now: bool = False) -> None:
def get_blockchain_interface(self) -> None:
if self.federated_only:
raise CharacterConfiguration.ConfigurationError("Cannot connect to blockchain in federated mode")
self.blockchain = BlockchainInterface(provider_uri=self.provider_uri,
poa=self.poa,
fetch_registry=True,
provider_process=self.provider_process,
sync_now=sync_now)
provider_process=self.provider_process)
# Read Ethereum Node Keyring
self.accounts = self.blockchain.client.accounts
def connect_to_contracts(self) -> None:
def acquire_agency(self) -> None:
self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
self.staking_agent = StakingEscrowAgent(blockchain=self.blockchain)
self.policy_agent = PolicyAgent(blockchain=self.blockchain)
@ -327,7 +318,7 @@ class CharacterConfiguration(BaseConfiguration):
node_storage=self.node_storage,
crypto_power_ups=self.derive_node_power_ups())
if not self.federated_only:
self.connect_to_blockchain()
self.get_blockchain_interface()
payload.update(blockchain=self.blockchain)
return payload
@ -424,9 +415,11 @@ class CharacterConfiguration(BaseConfiguration):
def write_keyring(self, password: str, **generation_kwargs) -> NucypherKeyring:
# Note: It is assumed the blockchain is not yet available.
if not self.federated_only:
if self.federated_only:
checksum_address = FEDERATED_ADDRESS
else:
# Note: It is assumed the blockchain interface is not yet connected.
if self.provider_process:
# Generate Geth's "datadir"
@ -439,17 +432,14 @@ class CharacterConfiguration(BaseConfiguration):
elif self.checksum_address not in self.provider_process.accounts():
raise self.ConfigurationError(f'Unknown Account {self.checksum_address}')
# Determine etherbase (web3)
elif not self.checksum_address:
self.connect_to_blockchain()
if not self.blockchain.client.accounts:
raise self.ConfigurationError(f'Web3 provider "{self.provider_uri}" does not have any accounts')
self.checksum_address = self.blockchain.client.etherbase
raise self.ConfigurationError(f'No checksum address provided for decentralized configuration.')
checksum_address = self.checksum_address
self.keyring = NucypherKeyring.generate(password=password,
keyring_root=self.keyring_root,
checksum_address=self.checksum_address,
federated=self.federated_only,
checksum_address=checksum_address,
**generation_kwargs)
self.checksum_address = self.keyring.account

View File

@ -14,16 +14,18 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import inspect
from eth_keys.datatypes import PublicKey, Signature as EthSignature
from eth_keys.exceptions import BadSignature
from eth_utils import keccak
import inspect
from typing import List, Tuple, Optional
from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION
from cytoolz.dicttoolz import dissoc
from eth_account._utils.transactions import assert_valid_fields
from hexbytes import HexBytes
from umbral import pre
from umbral.keys import UmbralPublicKey, UmbralPrivateKey, UmbralKeyingMaterial
from nucypher.crypto.signing import InvalidSignature
from nucypher.keystore import keypairs
from nucypher.keystore.keypairs import SigningKeypair, DecryptingKeypair
@ -40,25 +42,32 @@ class NoDecryptingPower(PowerUpError):
pass
class NoBlockchainPower(PowerUpError):
class NoTransactingPower(PowerUpError):
pass
class CryptoPower(object):
def __init__(self, power_ups: list = None) -> None:
self._power_ups = {} # type: dict
self.__power_ups = {} # type: dict
# TODO: The keys here will actually be IDs for looking up in a KeyStore.
self.public_keys = {} # type: dict
if power_ups is not None:
for power_up in power_ups:
self.consume_power_up(power_up)
else:
power_ups = [] # default
def consume_power_up(self, power_up):
def __contains__(self, item):
try:
self.power_ups(item)
except PowerUpError:
return False
else:
return True
def consume_power_up(self, power_up, *args, **kwargs):
if isinstance(power_up, CryptoPowerUp):
power_up_class = power_up.__class__
power_up.activate(*args, **kwargs)
power_up_instance = power_up
elif CryptoPowerUp in inspect.getmro(power_up):
power_up_class = power_up
@ -67,69 +76,115 @@ class CryptoPower(object):
raise TypeError(
("power_up must be a subclass of CryptoPowerUp or an instance "
"of a CryptoPowerUp subclass."))
self._power_ups[power_up_class] = power_up_instance
self.__power_ups[power_up_class] = power_up_instance
if power_up.confers_public_key:
self.public_keys[power_up_class] = power_up_instance.public_key()
def power_ups(self, power_up_class):
try:
return self._power_ups[power_up_class]
return self.__power_ups[power_up_class]
except KeyError:
raise power_up_class.not_found_error
class CryptoPowerUp(object):
class CryptoPowerUp:
"""
Gives you MORE CryptoPower!
"""
confers_public_key = False
def activate(self, *args, **kwargs):
return
class BlockchainPower(CryptoPowerUp):
class TransactingPower(CryptoPowerUp):
"""
Allows for transacting on a Blockchain via web3 backend.
"""
not_found_error = NoBlockchainPower
not_found_error = NoTransactingPower
def __init__(self, blockchain: 'Blockchain', account: str, device = None) -> None:
class NoBlockchainConnection(PowerUpError):
pass
class AccountLocked(PowerUpError):
pass
class InvalidSigningRequest(PowerUpError):
pass
def __init__(self,
blockchain,
account: str,
password: str = None):
"""
Instantiates a BlockchainPower for the given account id.
Instantiates a TransactingPower for the given checksum_address.
"""
self.blockchain = blockchain
self.account = account
self.device = device
self.is_unlocked = False
if blockchain.is_connected:
self.client = blockchain.client
else:
self.client = NO_BLOCKCHAIN_CONNECTION
def unlock_account(self, password: str):
"""
Unlocks the account for the specified duration. If no duration is
provided, it will remain unlocked indefinitely.
"""
self.is_unlocked = self.blockchain.client.unlock_account(self.account, password)
if not self.is_unlocked:
raise PowerUpError("Failed to unlock account {}".format(self.account))
self.account = account
self.device = True if not password else False
self.__password = password
self.__unlocked = False
def __del__(self):
self.lock_account()
@property
def is_unlocked(self) -> bool:
return self.__unlocked
@property
def is_active(self) -> bool:
"""Returns True if the blockchain currently has this transacting power attached."""
return self.blockchain.transacting_power == self
def activate(self, password: str = None):
"""Be Consumed"""
self.blockchain.connect()
self.client = self.blockchain.client # Connect
self.unlock_account(password=password or self.__password)
self.blockchain.transacting_power = self # Attach
self.__password = None # Discard
def lock_account(self):
if self.device:
# TODO: Force Disconnect Devices
pass
else:
_result = self.client.lock_account(address=self.account)
self.__unlocked = False
def unlock_account(self, password: str = None):
if self.device:
unlocked = True
else:
if self.client is NO_BLOCKCHAIN_CONNECTION:
raise self.NoBlockchainConnection
unlocked = self.client.unlock_account(address=self.account, password=password)
self.__unlocked = unlocked
def sign_message(self, message: bytes) -> bytes:
"""
Signs the message with the private key of the BlockchainPower.
Signs the message with the private key of the TransactingPower.
"""
if not self.is_unlocked:
raise PowerUpError("Account is not unlocked.")
signature = self.blockchain.client.sign_message(account=self.account, message=message)
raise self.AccountLocked("Failed to unlock account {}".format(self.account))
signature = self.client.sign_message(account=self.account, message=message)
return signature
def sign_transaction(self, unsigned_transaction: dict):
if self.device:
# TODO: Implement TrustedDevice
raise NotImplementedError
# This check is also performed client-side.
sender_address = unsigned_transaction['from']
if sender_address != self.account:
raise PowerUpError(f"'from' field must match account {self.account}, but it was {sender_address}")
signed_transaction = self.blockchain.client.sign_transaction(transaction=unsigned_transaction, account=self.account)
return signed_transaction
def sign_transaction(self, unsigned_transaction: dict) -> HexBytes:
"""
Signs the transaction with the private key of the TransactingPower.
"""
if not self.is_unlocked:
raise self.AccountLocked("Failed to unlock account {}".format(self.account))
signed_raw_transaction = self.blockchain.client.sign_transaction(transaction=unsigned_transaction)
return signed_raw_transaction
class KeyPairBasedPower(CryptoPowerUp):
@ -138,25 +193,24 @@ class KeyPairBasedPower(CryptoPowerUp):
_default_private_key_class = UmbralPrivateKey
def __init__(self,
pubkey: UmbralPublicKey = None,
public_key: UmbralPublicKey = None,
keypair: keypairs.Keypair = None,
) -> None:
if keypair and pubkey:
raise ValueError(
"Pass keypair or pubkey_bytes (or neither), but not both.")
if keypair and public_key:
raise ValueError("Pass keypair or pubkey_bytes (or neither), but not both.")
elif keypair:
self.keypair = keypair
else:
# They didn't pass a keypair; we'll make one with the bytes or
# UmbralPublicKey if they provided such a thing.
if pubkey:
if public_key:
try:
public_key = pubkey.as_umbral_pubkey()
public_key = public_key.as_umbral_pubkey()
except AttributeError:
try:
public_key = UmbralPublicKey.from_bytes(pubkey)
public_key = UmbralPublicKey.from_bytes(public_key)
except TypeError:
public_key = pubkey
public_key = public_key
self.keypair = self._keypair_class(
public_key=public_key)
else:
@ -168,10 +222,8 @@ class KeyPairBasedPower(CryptoPowerUp):
try:
return getattr(self.keypair, item)
except AttributeError:
raise PowerUpError(
"This {} has a keypair, {}, which doesn't provide {}.".format(self.__class__,
self.keypair.__class__,
item))
message = f"This {self.__class__} has a keypair, {self.keypair.__class__}, which doesn't provide {item}."
raise PowerUpError(message)
else:
raise PowerUpError("This {} doesn't provide {}.".format(self.__class__, item))

View File

@ -49,7 +49,7 @@ from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.config.constants import SeednodeMetadata
from nucypher.config.storages import ForgetfulNodeStorage
from nucypher.crypto.api import keccak_digest, verify_eip_191, recover_address_eip_191
from nucypher.crypto.powers import BlockchainPower, SigningPower, DecryptingPower, NoSigningPower
from nucypher.crypto.powers import TransactingPower, SigningPower, DecryptingPower, NoSigningPower
from nucypher.crypto.signing import signature_splitter
from nucypher.network import LEARNING_LOOP_VERSION
from nucypher.network.exceptions import NodeSeemsToBeDown
@ -915,7 +915,7 @@ class Teacher:
self.__worker_address = None
if substantiate_immediately:
# TODO: #1091
# TODO: #1091 When is_me and not federated_only, the stamp is substantiated twice
self.substantiate_stamp(client_password=password)
class InvalidNode(SuspiciousActivity):
@ -1149,13 +1149,12 @@ class Teacher:
signature=self.decentralized_identity_evidence)
return self.__worker_address
def substantiate_stamp(self, client_password: str):
# TODO: #1092 - TransactingPower
blockchain_power = self._crypto_power.power_ups(BlockchainPower)
blockchain_power.unlock_account(password=client_password) # TODO: #349
signature = blockchain_power.sign_message(message=bytes(self.stamp))
def substantiate_stamp(self, client_password: str = None):
transacting_power = self._crypto_power.power_ups(TransactingPower)
transacting_power.unlock_account(password=client_password) # TODO: #349
signature = transacting_power.sign_message(message=bytes(self.stamp))
self.__decentralized_identity_evidence = signature
self.__worker_address = blockchain_power.account
self.__worker_address = transacting_power.account
#
# Interface

View File

@ -32,7 +32,7 @@ from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.blockchain.eth.token import NU
from nucypher.blockchain.eth.utils import epoch_to_period
from nucypher.config.constants import BASE_DIR
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import (
NUMBER_OF_ETH_TEST_ACCOUNTS,
NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS,
@ -52,7 +52,7 @@ def token_airdrop(token_agent, amount: NU, origin: str, addresses: List[str]):
args = {'from': origin, 'gasPrice': token_agent.blockchain.client.gas_price}
for address in addresses:
contract_function = token_agent.contract.functions.transfer(address, int(amount))
_receipt = token_agent.blockchain.send_transaction(transaction_function=contract_function,
_receipt = token_agent.blockchain.send_transaction(contract_function=contract_function,
sender_address=origin,
payload=args)
yield _receipt
@ -110,12 +110,13 @@ class TesterBlockchain(BlockchainDeployerInterface):
*args, **kwargs)
self.log = Logger("test-blockchain")
self.connect()
# Generate additional ethereum accounts for testing
population = test_accounts
enough_accounts = len(self.w3.eth.accounts) >= population
enough_accounts = len(self.client.accounts) >= population
if not enough_accounts:
accounts_to_make = population - len(self.w3.eth.accounts)
accounts_to_make = population - len(self.client.accounts)
self.__generate_insecure_unlocked_accounts(quantity=accounts_to_make)
assert test_accounts == len(self.w3.eth.accounts)
@ -212,8 +213,10 @@ class TesterBlockchain(BlockchainDeployerInterface):
"""For use with metric testing scripts"""
testerchain = cls(compiler=SolidityCompiler())
power = BlockchainPower(blockchain=testerchain, account=testerchain.client.etherbase)
power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD)
power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
power.activate()
testerchain.transacting_power = power
origin = testerchain.client.etherbase

View File

@ -23,11 +23,12 @@ from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.token import StakeTracker
from nucypher.characters.lawful import Ursula
from nucypher.config.characters import UrsulaConfiguration
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import (
MOCK_KNOWN_URSULAS_CACHE,
MOCK_URSULA_STARTING_PORT,
NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK,
MOCK_URSULA_DB_FILEPATH)
MOCK_URSULA_DB_FILEPATH, INSECURE_DEVELOPMENT_PASSWORD)
def make_federated_ursulas(ursula_config: UrsulaConfiguration,

View File

@ -15,7 +15,8 @@ def test_geth_EIP_191_client_signature_integration(geth_dev_node):
pytest.skip("Do not run Geth nodes in CI")
# Start a geth process
blockchain = BlockchainInterface(provider_process=geth_dev_node, sync_now=False)
blockchain = BlockchainInterface(provider_process=geth_dev_node)
blockchain.connect(fetch_registry=False, sync_now=False)
# Sign a message (RPC) and verify it.
etherbase = blockchain.client.accounts[0]

View File

@ -86,7 +86,9 @@ class GanacheClientTestInterface(BlockchainInterfaceTestBase):
def test_geth_web3_client():
interface = GethClientTestBlockchain(provider_uri='file:///ipc.geth', sync_now=False)
interface = GethClientTestBlockchain(provider_uri='file:///ipc.geth')
interface.connect(fetch_registry=False, sync_now=False)
assert isinstance(interface.client, GethClient)
assert interface.client.node_technology == 'Geth'
assert interface.client.node_version == 'v1.4.11-stable-fed692f6'
@ -98,7 +100,9 @@ def test_geth_web3_client():
def test_parity_web3_client():
interface = ParityClientTestInterface(provider_uri='file:///ipc.parity', sync_now=False)
interface = ParityClientTestInterface(provider_uri='file:///ipc.parity')
interface.connect(fetch_registry=False, sync_now=False)
assert isinstance(interface.client, ParityClient)
assert interface.client.node_technology == 'Parity-Ethereum'
assert interface.client.node_version == 'v2.5.1-beta-e0141f8-20190510'
@ -107,7 +111,9 @@ def test_parity_web3_client():
def test_ganache_web3_client():
interface = GanacheClientTestInterface(provider_uri='http://ganache:8445', sync_now=False)
interface = GanacheClientTestInterface(provider_uri='http://ganache:8445')
interface.connect(fetch_registry=False, sync_now=False)
assert isinstance(interface.client, GanacheClient)
assert interface.client.node_technology == 'EthereumJS TestRPC'
assert interface.client.node_version == 'v2.1.5'

View File

@ -21,13 +21,17 @@ import pytest
from web3.contract import Contract
from nucypher.blockchain.eth.deployers import DispatcherDeployer
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
@pytest.fixture()
def escrow(testerchain):
# Mock Powerup consumption (Deployer)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
testerchain.transacting_power.activate()
escrow, _ = testerchain.deploy_contract('StakingEscrowForAdjudicatorMock')
return escrow

View File

@ -22,10 +22,9 @@ import pytest
from web3.auto import w3
from nucypher.blockchain.eth.actors import Deployer
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import InMemoryEthereumContractRegistry, InMemoryAllocationRegistry
from nucypher.blockchain.eth.registry import InMemoryAllocationRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
# Prevents TesterBlockchain to be picked up by py.test as a test class
from nucypher.utilities.sandbox.blockchain import TesterBlockchain as _TesterBlockchain
from nucypher.utilities.sandbox.constants import (
@ -35,7 +34,8 @@ from nucypher.utilities.sandbox.constants import (
POLICY_MANAGER_DEPLOYMENT_SECRET,
STAKING_ESCROW_DEPLOYMENT_SECRET,
NUMBER_OF_ALLOCATIONS_IN_TESTS,
TEST_PROVIDER_URI)
INSECURE_DEVELOPMENT_PASSWORD
)
@pytest.mark.slow()
@ -48,7 +48,10 @@ def test_rapid_deployment(token_economics):
compiler=compiler)
# TODO: #1092 - TransactingPower
blockchain.transacting_power = BlockchainPower(blockchain=blockchain, account=blockchain.etherbase_account)
blockchain.transacting_power = TransactingPower(blockchain=blockchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=blockchain.etherbase_account)
blockchain.transacting_power.activate()
deployer_address = blockchain.etherbase_account
deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address)

View File

@ -20,9 +20,9 @@ import pytest
from nucypher.blockchain.eth.actors import Staker
from nucypher.blockchain.eth.token import NU, Stake
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.blockchain import token_airdrop
from nucypher.utilities.sandbox.constants import DEVELOPMENT_TOKEN_AIRDROP_AMOUNT
from nucypher.utilities.sandbox.constants import DEVELOPMENT_TOKEN_AIRDROP_AMOUNT, INSECURE_DEVELOPMENT_PASSWORD
from nucypher.utilities.sandbox.ursula import make_decentralized_ursulas
@ -43,7 +43,10 @@ def test_staker_locking_tokens(testerchain, agency, staker, token_economics):
token_agent, staking_agent, policy_agent = agency
# Mock Powerup consumption (Ursula-Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker.checksum_address)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker.checksum_address)
testerchain.transacting_power.activate()
assert NU(token_economics.minimum_allowed_locked, 'NuNit') < staker.token_balance, "Insufficient staker balance"
@ -106,8 +109,11 @@ def test_staker_collects_staking_reward(testerchain, staker, blockchain_ursulas,
initial_balance = staker.token_balance
assert token_agent.get_balance(staker.checksum_address) == initial_balance
# Mock Powerup consumption (Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker.checksum_address)
# Mock Powerup consumption (Ursula-Worker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker.checksum_address)
testerchain.transacting_power.activate()
staker.initialize_stake(amount=NU(token_economics.minimum_allowed_locked, 'NuNit'), # Lock the minimum amount of tokens
lock_periods=int(token_economics.minimum_locked_periods)) # ... for the fewest number of periods
@ -131,8 +137,11 @@ def test_staker_collects_staking_reward(testerchain, staker, blockchain_ursulas,
# ...wait more...
testerchain.time_travel(periods=2)
# Mock Powerup consumption (Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker.checksum_address)
# Mock Powerup consumption (Ursula-Worker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker.checksum_address)
testerchain.transacting_power.activate()
# Profit!
staker.collect_staking_reward()

View File

@ -25,8 +25,9 @@ from nucypher.blockchain.eth.actors import NucypherTokenActor, Staker
from nucypher.blockchain.eth.agents import AdjudicatorAgent
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.token import NU
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.crypto.signing import SignatureStamp
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
def mock_ursula(testerchain, account):
@ -58,7 +59,10 @@ def test_adjudicator_slashes(agency,
locked_tokens = token_economics.minimum_allowed_locked * 5
# Mock Powerup consumption (Deployer)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
testerchain.transacting_power.activate()
# The staker receives an initial amount of tokens
_txhash = token_agent.transfer(amount=locked_tokens,
@ -66,7 +70,10 @@ def test_adjudicator_slashes(agency,
sender_address=testerchain.etherbase_account)
# Mock Powerup consumption (Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker_account)
testerchain.transacting_power.activate()
# Deposit: The staker deposits tokens in the StakingEscrow contract.
staker = Staker(checksum_address=staker_account, is_me=True, blockchain=testerchain)
@ -97,7 +104,11 @@ def test_adjudicator_slashes(agency,
bobby_old_balance = bobby.token_balance
# Mock Powerup consumption (Bob)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=bob_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=bob_account)
testerchain.transacting_power.activate()
adjudicator_agent.evaluate_cfrag(evidence=evidence, sender_address=bob_account)
assert adjudicator_agent.was_this_evidence_evaluated(evidence)

View File

@ -20,7 +20,8 @@ import collections
import pytest
from eth_utils import is_checksum_address
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
MockPolicyMetadata = collections.namedtuple('MockPolicyMetadata', 'policy_id author addresses')
@ -50,7 +51,10 @@ def test_create_policy(testerchain, agency, token_economics):
agent = policy_agent
# Mock Powerup consumption
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.alice_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.alice_account)
testerchain.transacting_power.activate()
policy_id = os.urandom(16)
node_addresses = list(staking_agent.sample(quantity=3, duration=1))
@ -111,13 +115,19 @@ def test_calculate_refund(testerchain, agency, policy_meta):
worker = staking_agent.get_worker_from_staker(staker)
# Mock Powerup consumption (Ursula-Worker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=worker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=worker)
testerchain.transacting_power.activate()
testerchain.time_travel(hours=9)
_receipt = staking_agent.confirm_activity(worker_address=worker)
# Mock Powerup consumption (Alice)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.alice_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.alice_account)
testerchain.transacting_power.activate()
receipt = agent.calculate_refund(policy_id=policy_meta.policy_id, author_address=policy_meta.author)
assert receipt['status'] == 1, "Transaction Rejected"
@ -144,7 +154,10 @@ def test_collect_policy_reward(testerchain, agency, policy_meta, token_economics
worker = staking_agent.get_worker_from_staker(staker)
# Mock Powerup consumption (Ursula-Worker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=worker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=worker)
testerchain.transacting_power.activate()
old_eth_balance = token_agent.blockchain.client.get_balance(staker)
@ -153,7 +166,10 @@ def test_collect_policy_reward(testerchain, agency, policy_meta, token_economics
testerchain.time_travel(periods=1)
# Mock Powerup consumption (Ursula-Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker)
testerchain.transacting_power.activate()
receipt = agent.collect_policy_reward(collector_address=staker, staker_address=staker)
assert receipt['status'] == 1, "Transaction Rejected"

View File

@ -22,7 +22,8 @@ from eth_utils.address import to_checksum_address, is_address
from nucypher.blockchain.eth.agents import StakingEscrowAgent
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
@pytest.mark.slow()
@ -34,7 +35,10 @@ def test_deposit_tokens(testerchain, agency, token_economics):
staker_account = testerchain.unassigned_accounts[0]
# Mock Powerup consumption (Deployer)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
testerchain.transacting_power.activate()
balance = token_agent.get_balance(address=staker_account)
assert balance == 0
@ -45,7 +49,10 @@ def test_deposit_tokens(testerchain, agency, token_economics):
sender_address=testerchain.etherbase_account)
# Mock Powerup consumption (Ursula-Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker_account)
testerchain.transacting_power.activate()
#
# Deposit: The staker deposits tokens in the StakingEscrow contract.
@ -165,7 +172,10 @@ def test_confirm_activity(agency, testerchain):
staker_account, worker_account, *other = testerchain.unassigned_accounts
# Mock Powerup consumption (Ursula-Worker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=worker_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=worker_account)
testerchain.transacting_power.activate()
receipt = staking_agent.confirm_activity(worker_address=worker_account)
assert receipt['status'] == 1, "Transaction Rejected"
@ -219,7 +229,10 @@ def test_collect_staking_reward(agency, testerchain):
testerchain.time_travel(periods=2)
# Mock Powerup consumption (Ursula-Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=staker_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=staker_account)
testerchain.transacting_power.activate()
# Mint
_receipt = staking_agent.mint(staker_address=staker_account)

View File

@ -19,7 +19,8 @@ from eth_tester.exceptions import TransactionFailed
from nucypher.blockchain.eth.agents import NucypherTokenAgent
from nucypher.blockchain.eth.deployers import NucypherTokenDeployer, DispatcherDeployer
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
@pytest.fixture(scope='module')
@ -67,7 +68,10 @@ def test_approve_transfer(agent, token_economics):
deployer, someone, *everybody_else = testerchain.client.accounts
# Mock Powerup consumption
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=someone)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=someone)
testerchain.transacting_power.activate()
# Approve
receipt = agent.approve_transfer(amount=token_economics.minimum_allowed_locked,
@ -83,7 +87,10 @@ def test_transfer(agent, token_economics):
origin, someone, *everybody_else = testerchain.client.accounts
# Mock Powerup consumption (Deployer)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=origin)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=origin)
testerchain.transacting_power.activate()
old_balance = agent.get_balance(someone)
receipt = agent.transfer(amount=token_economics.minimum_allowed_locked,

View File

@ -25,7 +25,8 @@ from nucypher.blockchain.eth.agents import UserEscrowAgent
from nucypher.blockchain.eth.interfaces import BlockchainInterface
from nucypher.blockchain.eth.deployers import UserEscrowDeployer, UserEscrowProxyDeployer, DispatcherDeployer
from nucypher.blockchain.eth.registry import InMemoryAllocationRegistry
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
TEST_DURATION = 60*60
TEST_ALLOCATION_REGISTRY = InMemoryAllocationRegistry()
@ -56,7 +57,10 @@ def agent(testerchain, proxy_deployer, allocation_value) -> UserEscrowAgent:
deployer_address, beneficiary_address, *everybody_else = testerchain.client.accounts
# Mock Powerup consumption (Deployer)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=deployer_address)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=deployer_address)
testerchain.transacting_power.activate()
# Escrow
escrow_deployer = UserEscrowDeployer(deployer_address=deployer_address,
@ -141,7 +145,10 @@ def test_deposit_and_withdraw_as_staker(testerchain, agent, agency, allocation_v
assert token_agent.get_balance(address=agent.contract_address) == allocation_value
# Mock Powerup consumption (Beneficiary)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=agent.beneficiary)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=agent.beneficiary)
testerchain.transacting_power.activate()
# Move the tokens to the StakingEscrow
receipt = agent.deposit_as_staker(value=token_economics.minimum_allowed_locked, periods=token_economics.minimum_locked_periods)
@ -159,7 +166,10 @@ def test_deposit_and_withdraw_as_staker(testerchain, agent, agency, allocation_v
assert staking_agent.get_locked_tokens(staker_address=agent.contract_address, periods=token_economics.minimum_locked_periods+1) == 0
# Mock Powerup consumption (Beneficiary-Worker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=worker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=worker)
testerchain.transacting_power.activate()
for _ in range(token_economics.minimum_locked_periods):
staking_agent.confirm_activity(worker_address=worker)
@ -167,7 +177,10 @@ def test_deposit_and_withdraw_as_staker(testerchain, agent, agency, allocation_v
testerchain.time_travel(periods=1)
# Mock Powerup consumption (Beneficiary)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=agent.beneficiary)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=agent.beneficiary)
testerchain.transacting_power.activate()
agent.mint()
@ -190,7 +203,10 @@ def test_collect_policy_reward(testerchain, agent, agency, token_economics):
deployer_address, beneficiary_address, author, ursula, *everybody_else = testerchain.client.accounts
# Mock Powerup consumption (Beneficiary)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=agent.beneficiary)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=agent.beneficiary)
testerchain.transacting_power.activate()
_txhash = agent.deposit_as_staker(value=token_economics.minimum_allowed_locked, periods=token_economics.minimum_locked_periods)
@ -201,7 +217,10 @@ def test_collect_policy_reward(testerchain, agent, agency, token_economics):
testerchain.time_travel(periods=1)
# Mock Powerup consumption (Alice)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=author)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=author)
testerchain.transacting_power.activate()
_txhash = policy_agent.create_policy(policy_id=os.urandom(16),
author_address=author,
@ -211,7 +230,10 @@ def test_collect_policy_reward(testerchain, agent, agency, token_economics):
node_addresses=[agent.contract_address])
# Mock Powerup consumption (Beneficiary-Worker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=worker)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=worker)
testerchain.transacting_power.activate()
_txhash = staking_agent.confirm_activity(worker_address=worker)
testerchain.time_travel(periods=2)
@ -220,7 +242,10 @@ def test_collect_policy_reward(testerchain, agent, agency, token_economics):
old_balance = testerchain.client.get_balance(account=agent.beneficiary)
# Mock Powerup consumption (Beneficiary)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=agent.beneficiary)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=agent.beneficiary)
testerchain.transacting_power.activate()
txhash = agent.collect_policy_reward()
assert txhash # TODO
@ -232,7 +257,10 @@ def test_withdraw_tokens(testerchain, agent, agency, allocation_value):
deployer_address, beneficiary_address, *everybody_else = testerchain.client.accounts
# Mock Powerup consumption (Beneficiary)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=agent.beneficiary)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=agent.beneficiary)
testerchain.transacting_power.activate()
assert token_agent.get_balance(address=agent.contract_address) == agent.unvested_tokens
with pytest.raises((TransactionFailed, ValueError)):

View File

@ -14,11 +14,13 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import eth_utils
import pytest
import pytest
from constant_sorrow import constants
from cryptography.exceptions import InvalidSignature
from eth_account._utils.transactions import Transaction
from eth_utils import to_checksum_address
from nucypher.characters.lawful import Alice, Character, Bob
from nucypher.characters.lawful import Enrico
@ -27,8 +29,8 @@ from nucypher.crypto.api import verify_eip_191
from nucypher.crypto.powers import (CryptoPower,
SigningPower,
NoSigningPower,
BlockchainPower,
PowerUpError)
TransactingPower)
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
"""
Chapter 1: SIGNING
@ -116,39 +118,51 @@ def test_anybody_can_verify():
assert cleartext is constants.NO_DECRYPTION_PERFORMED
def test_character_blockchain_power(testerchain, agency):
# TODO: Handle multiple providers
eth_address = testerchain.client.accounts[0]
canonical_address = eth_utils.to_canonical_address(eth_address)
sig_privkey = testerchain.provider.ethereum_tester.backend._key_lookup[canonical_address]
def test_character_transacting_power_signing(testerchain, agency):
# Pretend to be a character.
eth_address = testerchain.etherbase_account
signer = Character(is_me=True, blockchain=testerchain, checksum_address=eth_address)
signer._crypto_power.consume_power_up(BlockchainPower(blockchain=testerchain, account=eth_address))
# Due to testing backend, the account is already unlocked.
power = signer._crypto_power.power_ups(BlockchainPower)
power.is_unlocked = True
# power.unlock_account('this-is-not-a-secure-password')
# Manually consume the power up
transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=eth_address)
data_to_sign = b'What does Ursula look like?!?'
sig = power.sign_message(message=data_to_sign)
assert testerchain.transacting_power != transacting_power
signer._crypto_power.consume_power_up(transacting_power)
is_verified = verify_eip_191(address=eth_address, message=data_to_sign, signature=sig)
# Retrieve the power up
power = signer._crypto_power.power_ups(TransactingPower)
assert power == transacting_power
assert testerchain.transacting_power == power
assert power.is_active is True
assert power.is_unlocked is True
assert testerchain.transacting_power.is_unlocked is True
# Sign Message
data_to_sign = b'Premium Select Luxury Pencil Holder'
signature = power.sign_message(message=data_to_sign)
is_verified = verify_eip_191(address=eth_address, message=data_to_sign, signature=signature)
assert is_verified is True
# Test a bad address/pubkey pair
is_verified = verify_eip_191(address=testerchain.client.accounts[1],
message=data_to_sign,
signature=sig)
assert is_verified is False
# Sign Transaction
transaction_dict = {'nonce': testerchain.client.w3.eth.getTransactionCount(eth_address),
'gasPrice': testerchain.client.w3.eth.gasPrice,
'gas': 100000,
'from': eth_address,
'to': testerchain.unassigned_accounts[1],
'value': 1,
'data': b''}
# Test a signature without unlocking the account
power.is_unlocked = False
with pytest.raises(PowerUpError):
power.sign_message(message=b'test')
signed_transaction = power.sign_transaction(unsigned_transaction=transaction_dict)
# Test lockAccount call
del power
# Demonstrate that the transaction is valid RLP encoded.
restored_transaction = Transaction.from_bytes(serialized_bytes=signed_transaction)
restored_dict = restored_transaction.as_dict()
assert to_checksum_address(restored_dict['to']) == transaction_dict['to']
"""
@ -174,7 +188,7 @@ def test_anybody_can_encrypt():
def test_node_deployer(federated_ursulas):
for ursula in federated_ursulas:
deployer = ursula.get_deployer()
assert deployer.options['https_port'] == ursula.rest_interface.port
assert deployer.options['https_port'] == ursula.rest_information()[0].port
assert deployer.application == ursula.rest_app

View File

@ -44,7 +44,7 @@ def test_alice_control_starts_with_mocked_keyring(click_runner, mocker):
user_input = '{password}\n{password}\n'.format(password=INSECURE_DEVELOPMENT_PASSWORD)
result = click_runner.invoke(nucypher_cli, init_args, input=user_input)
assert result.exit_code == 0
assert result.exit_code == 0, result.exception
def test_initialize_alice_with_custom_configuration_root(custom_filepath, click_runner):

View File

@ -73,6 +73,7 @@ class MockSideChannel:
@pt.inlineCallbacks
@pytest.mark.parametrize('federated', (True, False))
def test_cli_lifecycle(click_runner,
testerchain,
random_policy_label,
federated_ursulas,
blockchain_ursulas,
@ -108,7 +109,8 @@ def test_cli_lifecycle(click_runner,
if federated:
alice_init_args += ('--federated-only', )
else:
alice_init_args += ('--provider-uri', TEST_PROVIDER_URI)
alice_init_args += ('--provider-uri', TEST_PROVIDER_URI,
'--pay-with', testerchain.alice_account)
alice_init_response = click_runner.invoke(nucypher_cli, alice_init_args, catch_exceptions=False, env=envvars)
assert alice_init_response.exit_code == 0
@ -137,7 +139,8 @@ def test_cli_lifecycle(click_runner,
if federated:
bob_init_args += ('--federated-only', )
else:
bob_init_args += ('--provider-uri', TEST_PROVIDER_URI)
bob_init_args += ('--provider-uri', TEST_PROVIDER_URI,
'--pay-with', testerchain.bob_account)
bob_init_response = click_runner.invoke(nucypher_cli, bob_init_args, catch_exceptions=False, env=envvars)
assert bob_init_response.exit_code == 0

View File

@ -11,12 +11,12 @@ from nucypher.blockchain.eth.agents import (
StakingEscrowAgent,
UserEscrowAgent,
PolicyAgent,
Agency, AdjudicatorAgent)
Agency)
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import AllocationRegistry, EthereumContractRegistry
from nucypher.blockchain.eth.registry import AllocationRegistry
from nucypher.cli.deploy import deploy
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
# Prevents TesterBlockchain to be picked up by py.test as a test class
from nucypher.utilities.sandbox.blockchain import TesterBlockchain as _TesterBlockchain
from nucypher.utilities.sandbox.constants import (
@ -44,7 +44,7 @@ def make_testerchain():
# Set the deployer address from a freshly created test account
testerchain.deployer_address = testerchain.etherbase_account
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain, account=testerchain.etherbase_account)
return testerchain
@ -356,8 +356,7 @@ def test_nucypher_deploy_allocation_contracts(click_runner,
account_index = '0\n'
yes = 'Y\n'
node_password = f'{INSECURE_DEVELOPMENT_PASSWORD}\n'
user_input = account_index + yes + node_password + yes
user_input = account_index + yes + yes
result = click_runner.invoke(deploy,
deploy_command,
@ -379,24 +378,3 @@ def test_nucypher_deploy_allocation_contracts(click_runner,
# Destroy existing blockchain
testerchain.disconnect()
def test_destroy_registry(click_runner, mock_primary_registry_filepath):
# ... I changed my mind, destroy the registry!
destroy_command = ('destroy-registry',
'--registry-infile', mock_primary_registry_filepath,
'--provider-uri', TEST_PROVIDER_URI,
'--poa')
# TODO: #1036 - Providers and unlocking are not needed for this command
account_index = '0\n'
yes = 'Y\n'
user_input = account_index + yes + yes
result = click_runner.invoke(deploy, destroy_command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
assert mock_primary_registry_filepath in result.output
assert DEFAULT_CONFIG_ROOT not in result.output, 'WARNING: Deploy CLI tests are using default config root dir!'
assert f'Successfully destroyed {mock_primary_registry_filepath}' in result.output
assert not os.path.isfile(mock_primary_registry_filepath)

View File

@ -5,17 +5,18 @@ from umbral.signing import Signer
from nucypher.config.keyring import NucypherKeyring
from nucypher.crypto.powers import DelegatingPower, DecryptingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
from constant_sorrow.constants import FEDERATED_ADDRESS
def test_generate_alice_keyring(tmpdir):
password = 'x' * 16
keyring = NucypherKeyring.generate(
password=password,
checksum_address=FEDERATED_ADDRESS,
password=INSECURE_DEVELOPMENT_PASSWORD,
encrypting=True,
rest=False,
keyring_root=tmpdir,
federated=True
keyring_root=tmpdir
)
enc_pubkey = keyring.encrypting_public_key
@ -24,7 +25,7 @@ def test_generate_alice_keyring(tmpdir):
with pytest.raises(NucypherKeyring.KeyringLocked):
_dec_keypair = keyring.derive_crypto_power(DecryptingPower).keypair
keyring.unlock(password)
keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
dec_keypair = keyring.derive_crypto_power(DecryptingPower).keypair
assert enc_pubkey == dec_keypair.pubkey

131
tests/crypto/test_powers.py Normal file
View File

@ -0,0 +1,131 @@
import pytest
from eth_account._utils.transactions import Transaction
from eth_utils import to_checksum_address
from nucypher.blockchain.eth.agents import NucypherTokenAgent
from nucypher.crypto.api import verify_eip_191
from nucypher.crypto.powers import (PowerUpError)
from nucypher.crypto.powers import TransactingPower
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
def test_transacting_power_sign_message(testerchain):
# Manually create a TransactingPower
testerchain.connect()
eth_address = testerchain.etherbase_account
power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=eth_address)
# The default state of the account is locked.
# Test a signature without unlocking the account
with pytest.raises(PowerUpError):
power.sign_message(message=b'test')
# Manually unlock
power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD)
# Sign
data_to_sign = b'Premium Select Luxury Pencil Holder'
signature = power.sign_message(message=data_to_sign)
# Verify
is_verified = verify_eip_191(address=eth_address, message=data_to_sign, signature=signature)
assert is_verified is True
# Test invalid address/pubkey pair
is_verified = verify_eip_191(address=testerchain.client.accounts[1],
message=data_to_sign,
signature=signature)
assert is_verified is False
# Test lockAccount call
power.lock_account()
# Test a signature without unlocking the account
with pytest.raises(PowerUpError):
power.sign_message(message=b'test')
del power # Locks account
def test_transacting_power_sign_transaction(testerchain):
eth_address = testerchain.unassigned_accounts[2]
power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=eth_address)
assert power.is_active is False
assert power.is_unlocked is False
transaction_dict = {'nonce': testerchain.client.w3.eth.getTransactionCount(eth_address),
'gasPrice': testerchain.client.w3.eth.gasPrice,
'gas': 100000,
'from': eth_address,
'to': testerchain.unassigned_accounts[1],
'value': 1,
'data': b''}
# The default state of the account is locked.
# Test a signature without unlocking the account
with pytest.raises(TransactingPower.AccountLocked):
power.sign_transaction(unsigned_transaction=transaction_dict)
# Sign
power.activate()
assert power.is_active is True
assert power.is_unlocked is True
signed_transaction = power.sign_transaction(unsigned_transaction=transaction_dict)
# Demonstrate that the transaction is valid RLP encoded.
from eth_account._utils.transactions import Transaction
restored_transaction = Transaction.from_bytes(serialized_bytes=signed_transaction)
restored_dict = restored_transaction.as_dict()
assert to_checksum_address(restored_dict['to']) == transaction_dict['to']
# Try signing with missing transaction fields
del transaction_dict['gas']
del transaction_dict['nonce']
with pytest.raises(TypeError):
power.sign_transaction(unsigned_transaction=transaction_dict)
# Try signing with a re-locked account.
power.lock_account()
with pytest.raises(TransactingPower.AccountLocked):
power.sign_transaction(unsigned_transaction=transaction_dict)
power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD)
assert power.is_unlocked is True
# Tear-Down Test
power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
power.activate(password=INSECURE_DEVELOPMENT_PASSWORD)
def test_transacting_power_sign_agent_transaction(testerchain, agency):
token_agent = NucypherTokenAgent(blockchain=testerchain)
contract_function = token_agent.contract.functions.approve(testerchain.etherbase_account, 100)
payload = {'chainId': int(testerchain.client.chain_id),
'nonce': testerchain.client.w3.eth.getTransactionCount(testerchain.etherbase_account),
'from': testerchain.etherbase_account,
'gasPrice': testerchain.client.gas_price}
unsigned_transaction = contract_function.buildTransaction(payload)
# Sign with Transacting Power
transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
transacting_power.activate()
signed_raw_transaction = transacting_power.sign_transaction(unsigned_transaction)
# Demonstrate that the transaction is valid RLP encoded.
restored_transaction = Transaction.from_bytes(serialized_bytes=signed_raw_transaction)
restored_dict = restored_transaction.as_dict()
assert to_checksum_address(restored_dict['to']) == unsigned_transaction['to']

View File

@ -39,15 +39,12 @@ from nucypher.blockchain.eth.deployers import (NucypherTokenDeployer,
PolicyManagerDeployer,
DispatcherDeployer,
AdjudicatorDeployer)
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import InMemoryEthereumContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.blockchain.eth.token import NU
from nucypher.characters.lawful import Enrico, Bob
from nucypher.config.characters import UrsulaConfiguration, AliceConfiguration, BobConfiguration
from nucypher.config.constants import BASE_DIR
from nucypher.config.node import CharacterConfiguration
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.crypto.utils import canonical_address_from_umbral_key
from nucypher.keystore import keystore
from nucypher.keystore.db import Base
@ -59,8 +56,8 @@ from nucypher.utilities.sandbox.constants import (DEVELOPMENT_ETH_AIRDROP_AMOUNT
MOCK_URSULA_STARTING_PORT,
NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK,
TEMPORARY_DOMAIN,
TEST_PROVIDER_URI
)
TEST_PROVIDER_URI,
INSECURE_DEVELOPMENT_PASSWORD)
from nucypher.utilities.sandbox.middleware import MockRestMiddleware
from nucypher.utilities.sandbox.policy import generate_random_label
from nucypher.utilities.sandbox.ursula import (make_decentralized_ursulas,
@ -296,7 +293,6 @@ def capsule_side_channel(enacted_federated_policy):
self.messages = []
self()
return _CapsuleSideChannel()
@ -316,7 +312,7 @@ def federated_alice(alice_federated_test_config):
@pytest.fixture(scope="module")
def blockchain_alice(alice_blockchain_test_config):
def blockchain_alice(alice_blockchain_test_config, testerchain):
_alice = alice_blockchain_test_config.produce()
return _alice
@ -328,7 +324,7 @@ def federated_bob(bob_federated_test_config):
@pytest.fixture(scope="module")
def blockchain_bob(bob_blockchain_test_config):
def blockchain_bob(bob_blockchain_test_config, testerchain):
_bob = bob_blockchain_test_config.produce()
return _bob
@ -371,20 +367,21 @@ def testerchain():
# Create the blockchain
testerchain = TesterBlockchain(eth_airdrop=True, free_transactions=True)
# TODO: TransactingPower
# Mock TransactingPower Consumption
testerchain.transacting_power = BlockchainPower(blockchain=testerchain, account=testerchain.etherbase_account)
# Mock TransactingPower Consumption (Deployer)
testerchain.deployer_address = testerchain.etherbase_account
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.deployer_address)
testerchain.transacting_power.activate()
yield testerchain
testerchain.disconnect()
@pytest.fixture(scope='module')
def agency(testerchain):
"""
Launch the big three contracts on provided chain,
make agents for each and return them.
"""
"""Launch all Nucypher ethereum contracts"""
origin = testerchain.etherbase_account
token_deployer = NucypherTokenDeployer(blockchain=testerchain, deployer_address=origin)
@ -399,18 +396,22 @@ def agency(testerchain):
adjudicator_deployer = AdjudicatorDeployer(deployer_address=origin, blockchain=testerchain)
adjudicator_deployer.deploy(secret_hash=os.urandom(DispatcherDeployer.DISPATCHER_SECRET_LENGTH))
token_agent = token_deployer.make_agent() # 1: Token
staking_agent = staking_escrow_deployer.make_agent() # 2 Miner Escrow
policy_agent = policy_manager_deployer.make_agent() # 3 Policy Agent
adjudicator_agent = adjudicator_deployer.make_agent() # 4
token_agent = token_deployer.make_agent() # 1 Token
staking_agent = staking_escrow_deployer.make_agent() # 2 Miner Escrow
policy_agent = policy_manager_deployer.make_agent() # 3 Policy Agent
_adjudicator_agent = adjudicator_deployer.make_agent() # 4 Adjudicator
# TODO: Perhaps we should get rid of returning these agents here.
# What's important is deploying and creating the first agent for each contract,
# and since agents are singletons, in tests it's only necessary to call the agent
# constructor again to receive the existing agent. For example:
# constructor again to receive the existing agent.
#
# For example:
# staking_agent = StakingEscrowAgent()
#
# This is more clear than how we currently obtain an agent instance in tests:
# _, staking_agent, _ = agency
#
# Other advantages is that it's closer to how agents should be use (i.e., there
# are no fixtures IRL) and it's more extensible (e.g., AdjudicatorAgent)
@ -425,13 +426,15 @@ def clear_out_agency():
@pytest.fixture(scope="module")
def stakers(agency, token_economics):
def stakers(testerchain, agency, token_economics):
token_agent, _staking_agent, _policy_agent = agency
blockchain = token_agent.blockchain
# Mock Powerup consumption (Deployer)
blockchain.transacting_power = BlockchainPower(blockchain=blockchain,
account=blockchain.etherbase_account)
blockchain.transacting_power = TransactingPower(blockchain=blockchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=blockchain.etherbase_account)
blockchain.transacting_power.activate()
token_airdrop(origin=blockchain.etherbase_account,
addresses=blockchain.stakers_accounts,
@ -442,9 +445,11 @@ def stakers(agency, token_economics):
for index, account in enumerate(blockchain.stakers_accounts):
staker = Staker(is_me=True, checksum_address=account, blockchain=blockchain)
# Mock TransactingPower consumption (Ursula-Staker)
staker.blockchain.transacting_power = BlockchainPower(blockchain=staker.blockchain,
account=staker.checksum_address)
# Mock TransactingPower consumption
staker.blockchain.transacting_power = TransactingPower(blockchain=blockchain,
password=INSECURE_DEVELOPMENT_PASSWORD,
account=account)
staker.blockchain.transacting_power.activate()
min_stake, balance = token_economics.minimum_allowed_locked, staker.token_balance
amount = random.randint(min_stake, balance)
@ -494,8 +499,8 @@ def idle_staker(testerchain, agency):
idle_staker_account = testerchain.unassigned_accounts[-2]
# Mock Powerup consumption (Deployer)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain,
account=testerchain.etherbase_account)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
account=testerchain.etherbase_account)
token_airdrop(origin=testerchain.etherbase_account,
addresses=[idle_staker_account],

View File

@ -9,7 +9,7 @@ from bytestring_splitter import VariableLengthBytestring
from constant_sorrow.constants import NOT_SIGNED
from nucypher.characters.base import Character
from nucypher.crypto.powers import BlockchainPower
from nucypher.crypto.powers import TransactingPower
from nucypher.network.nicknames import nickname_from_seed
from nucypher.network.nodes import FleetStateTracker
from nucypher.utilities.sandbox.middleware import MockRestMiddleware
@ -84,8 +84,8 @@ def test_invalid_workers_tolerance(testerchain,
periods = token_economics.minimum_locked_periods
# Mock Powerup consumption (Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain,
account=idle_staker.checksum_address)
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
account=idle_staker.checksum_address)
idle_staker.initialize_stake(amount=amount, lock_periods=periods)
@ -122,7 +122,7 @@ def test_invalid_workers_tolerance(testerchain,
# She withdraws up to the last penny (well, last nunit, actually).
# Mock Powerup consumption (Staker)
testerchain.transacting_power = BlockchainPower(blockchain=testerchain,
testerchain.transacting_power = TransactingPower(blockchain=testerchain,
account=idle_staker.checksum_address)
idle_staker.mint()
testerchain.time_travel(periods=1)

View File

@ -24,9 +24,9 @@ import os
import re
import sys
import time
from mock import Mock
from os.path import abspath, dirname
from mock import Mock
from twisted.logger import globalLogPublisher, Logger, jsonFileLogObserver, ILogObserver
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer