mirror of https://github.com/nucypher/nucypher.git
commit
498b9887a1
|
@ -0,0 +1 @@
|
|||
RPC connectivity is now agnostic to the EVM client fingerprint and implementation technology.
|
|
@ -0,0 +1 @@
|
|||
Wallet management and transaction signing via blockchain clients is deprecated for production use.
|
|
@ -1,14 +1,9 @@
|
|||
import os
|
||||
import time
|
||||
from functools import cached_property
|
||||
from typing import Union
|
||||
|
||||
from constant_sorrow.constants import UNKNOWN_DEVELOPMENT_CHAIN_ID
|
||||
from cytoolz.dicttoolz import dissoc
|
||||
from eth_account import Account
|
||||
from eth_account.messages import encode_defunct
|
||||
from eth_typing.evm import BlockNumber, ChecksumAddress
|
||||
from eth_utils import to_canonical_address, to_checksum_address
|
||||
from eth_typing.evm import BlockNumber
|
||||
from web3 import Web3
|
||||
from web3._utils.threads import Timeout
|
||||
from web3.contract.contract import Contract
|
||||
|
@ -38,30 +33,13 @@ class Web3ClientUnexpectedVersionString(Web3ClientError):
|
|||
pass
|
||||
|
||||
|
||||
# TODO: Consider creating a ChainInventory class and/or moving this to a separate module
|
||||
|
||||
PUBLIC_CHAINS = {
|
||||
0: "Olympic",
|
||||
1: "Mainnet",
|
||||
2: "Morden",
|
||||
3: "Ropsten",
|
||||
4: "Rinkeby",
|
||||
5: "Goerli",
|
||||
6: "Kotti",
|
||||
42: "Kovan",
|
||||
77: "Sokol",
|
||||
100: "xDai",
|
||||
137: "Polygon/Mainnet",
|
||||
11155111: "Sepolia",
|
||||
80001: "Polygon/Mumbai",
|
||||
80002: "Polygon/Amoy",
|
||||
}
|
||||
|
||||
LOCAL_CHAINS = {
|
||||
1337: "GethDev",
|
||||
5777: "Ganache/TestRPC"
|
||||
}
|
||||
|
||||
# This list is not exhaustive,
|
||||
# but is sufficient for the current needs of the project.
|
||||
POA_CHAINS = {
|
||||
|
@ -78,18 +56,6 @@ POA_CHAINS = {
|
|||
|
||||
|
||||
class EthereumClient:
|
||||
is_local = False
|
||||
|
||||
# These two are used by Infura
|
||||
GETH = 'Geth'
|
||||
BOR = 'bor'
|
||||
|
||||
PARITY = 'Parity'
|
||||
ALT_PARITY = 'Parity-Ethereum'
|
||||
GANACHE = 'EthereumJS TestRPC'
|
||||
|
||||
ETHEREUM_TESTER = 'EthereumTester' # (PyEVM)
|
||||
|
||||
BLOCK_CONFIRMATIONS_POLLING_TIME = 3 # seconds
|
||||
TRANSACTION_POLLING_TIME = 0.5 # seconds
|
||||
COOLING_TIME = 5 # seconds
|
||||
|
@ -125,107 +91,33 @@ class EthereumClient:
|
|||
block_hash=Web3.to_hex(receipt['blockHash']))
|
||||
super().__init__(self.message)
|
||||
|
||||
def __init__(self,
|
||||
w3,
|
||||
node_technology: str,
|
||||
version: str,
|
||||
platform: str,
|
||||
backend: str):
|
||||
|
||||
def __init__(self, w3):
|
||||
self.w3 = w3
|
||||
self.node_technology = node_technology
|
||||
self.node_version = version
|
||||
self.platform = platform
|
||||
self.backend = backend
|
||||
self.log = Logger(self.__class__.__name__)
|
||||
|
||||
self._add_default_middleware()
|
||||
|
||||
def _add_default_middleware(self):
|
||||
# default retry functionality
|
||||
self.log.debug('Adding RPC retry middleware to client')
|
||||
self.add_middleware(RetryRequestMiddleware)
|
||||
|
||||
@classmethod
|
||||
def _get_variant(cls, w3):
|
||||
return cls
|
||||
|
||||
@classmethod
|
||||
def from_w3(cls, w3: Web3) -> 'EthereumClient':
|
||||
"""
|
||||
|
||||
Client version strings:
|
||||
|
||||
Geth -> 'Geth/v1.4.11-stable-fed692f6/darwin/go1.7'
|
||||
Parity -> 'Parity-Ethereum/v2.5.1-beta-e0141f8-20190510/x86_64-linux-gnu/rustc1.34.1'
|
||||
Ganache -> 'EthereumJS TestRPC/v2.1.5/ethereum-js'
|
||||
PyEVM -> 'EthereumTester/0.1.0b39/linux/python3.6.7'
|
||||
Bor -> 'bor/v0.2.13-beta2-c227a072/linux-amd64/go1.17.5'
|
||||
"""
|
||||
clients = {
|
||||
|
||||
# Geth
|
||||
cls.GETH: GethClient,
|
||||
cls.BOR: BorClient,
|
||||
|
||||
# Parity
|
||||
cls.PARITY: ParityClient,
|
||||
cls.ALT_PARITY: ParityClient,
|
||||
|
||||
# Test Clients
|
||||
cls.GANACHE: GanacheClient,
|
||||
cls.ETHEREUM_TESTER: EthereumTesterClient,
|
||||
}
|
||||
|
||||
try:
|
||||
client_data = w3.client_version.split('/')
|
||||
node_technology = client_data[0]
|
||||
ClientSubclass = clients[node_technology]
|
||||
|
||||
except (ValueError, IndexError):
|
||||
raise ValueError(f"Invalid client version string. Got '{w3.client_version}'")
|
||||
|
||||
except KeyError:
|
||||
raise NotImplementedError(f'{w3.client_version} is not a supported ethereum client')
|
||||
|
||||
client_kwargs = {
|
||||
'node_technology': node_technology,
|
||||
'version': client_data[1],
|
||||
'backend': client_data[-1],
|
||||
'platform': client_data[2] if len(client_data) == 4 else None # Platform is optional
|
||||
}
|
||||
|
||||
instance = ClientSubclass._get_variant(w3)(w3, **client_kwargs)
|
||||
return instance
|
||||
|
||||
@property
|
||||
def peers(self):
|
||||
raise NotImplementedError
|
||||
endpoint_uri = getattr(self.w3.provider, "endpoint_uri", "")
|
||||
if "infura" in endpoint_uri:
|
||||
self.log.debug("Adding Infura RPC retry middleware to client")
|
||||
self.add_middleware(InfuraRetryRequestMiddleware)
|
||||
elif "alchemyapi.io" in endpoint_uri:
|
||||
self.log.debug("Adding Alchemy RPC retry middleware to client")
|
||||
self.add_middleware(AlchemyRetryRequestMiddleware)
|
||||
else:
|
||||
self.log.debug("Adding RPC retry middleware to client")
|
||||
self.add_middleware(RetryRequestMiddleware)
|
||||
|
||||
@property
|
||||
def chain_name(self) -> str:
|
||||
chain_inventory = LOCAL_CHAINS if self.is_local else PUBLIC_CHAINS
|
||||
name = chain_inventory.get(self.chain_id, UNKNOWN_DEVELOPMENT_CHAIN_ID)
|
||||
name = PUBLIC_CHAINS.get(self.chain_id, UNKNOWN_DEVELOPMENT_CHAIN_ID)
|
||||
return name
|
||||
|
||||
def lock_account(self, account) -> bool:
|
||||
if self.is_local:
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
def unlock_account(self, account, password, duration=None) -> bool:
|
||||
if self.is_local:
|
||||
return True
|
||||
return NotImplemented
|
||||
|
||||
@property
|
||||
def is_connected(self):
|
||||
return self.w3.is_connected()
|
||||
|
||||
@property
|
||||
def etherbase(self) -> str:
|
||||
return self.w3.eth.accounts[0]
|
||||
|
||||
@property
|
||||
def accounts(self):
|
||||
return self.w3.eth.accounts
|
||||
|
@ -244,15 +136,8 @@ class EthereumClient:
|
|||
|
||||
@cached_property
|
||||
def chain_id(self) -> int:
|
||||
result = self.w3.eth.chain_id
|
||||
try:
|
||||
# from hex-str
|
||||
chain_id = int(result, 16)
|
||||
except TypeError:
|
||||
# from str
|
||||
chain_id = int(result)
|
||||
|
||||
return chain_id
|
||||
_chain_id = self._get_chain_id(self.w3)
|
||||
return _chain_id
|
||||
|
||||
@property
|
||||
def net_version(self) -> int:
|
||||
|
@ -279,10 +164,6 @@ class EthereumClient:
|
|||
def block_number(self) -> BlockNumber:
|
||||
return self.w3.eth.block_number
|
||||
|
||||
@property
|
||||
def coinbase(self) -> ChecksumAddress:
|
||||
return self.w3.eth.coinbase
|
||||
|
||||
def wait_for_receipt(self,
|
||||
transaction_hash: str,
|
||||
timeout: float,
|
||||
|
@ -361,9 +242,6 @@ class EthereumClient:
|
|||
# TODO: Consider adding an optional param in this exception to include extra info (e.g. new block)
|
||||
return True
|
||||
|
||||
def sign_transaction(self, transaction_dict: dict) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_transaction(self, transaction_hash) -> dict:
|
||||
return self.w3.eth.get_transaction(transaction_hash)
|
||||
|
||||
|
@ -380,14 +258,6 @@ class EthereumClient:
|
|||
def send_raw_transaction(self, transaction_bytes: bytes) -> str:
|
||||
return self.w3.eth.send_raw_transaction(transaction_bytes)
|
||||
|
||||
def sign_message(self, account: str, message: bytes) -> str:
|
||||
"""
|
||||
Calls the appropriate signing function for the specified account on the
|
||||
backend. If the backend is based on eth-tester, then it uses the
|
||||
eth-tester signing interface to do so.
|
||||
"""
|
||||
return self.w3.eth.sign(account, data=message)
|
||||
|
||||
def get_blocktime(self):
|
||||
highest_block = self.w3.eth.get_block('latest')
|
||||
now = highest_block['timestamp']
|
||||
|
@ -401,177 +271,14 @@ class EthereumClient:
|
|||
# check that our local chain data is up to date
|
||||
return (time.time() - self.get_blocktime()) < self.STALECHECK_ALLOWABLE_DELAY
|
||||
|
||||
def parse_transaction_data(self, transaction):
|
||||
return transaction.input
|
||||
|
||||
|
||||
class GethClient(EthereumClient):
|
||||
|
||||
@classmethod
|
||||
def _get_variant(cls, w3):
|
||||
endpoint_uri = getattr(w3.provider, 'endpoint_uri', '')
|
||||
if 'infura' in endpoint_uri:
|
||||
return InfuraClient
|
||||
elif 'alchemyapi.io' in endpoint_uri:
|
||||
return AlchemyClient
|
||||
|
||||
return cls
|
||||
|
||||
@property
|
||||
def is_local(self):
|
||||
return self.chain_id not in PUBLIC_CHAINS
|
||||
|
||||
@property
|
||||
def peers(self):
|
||||
return self.w3.geth.admin.peers()
|
||||
|
||||
def new_account(self, password: str) -> str:
|
||||
new_account = self.w3.geth.personal.new_account(password)
|
||||
return to_checksum_address(new_account) # cast and validate
|
||||
|
||||
def unlock_account(self, account: str, password: str, duration: int = None):
|
||||
if self.is_local:
|
||||
return True
|
||||
debug_message = f"Unlocking account {account}"
|
||||
|
||||
if duration is None:
|
||||
debug_message += " for 5 minutes"
|
||||
elif duration == 0:
|
||||
debug_message += " indefinitely"
|
||||
elif duration > 0:
|
||||
debug_message += f" for {duration} seconds"
|
||||
|
||||
if password is None:
|
||||
debug_message += " with no password."
|
||||
|
||||
self.log.debug(debug_message)
|
||||
return self.w3.geth.personal.unlock_account(account, password, duration)
|
||||
|
||||
def lock_account(self, account):
|
||||
return self.w3.geth.personal.lock_account(account)
|
||||
|
||||
def sign_transaction(self, transaction_dict: dict) -> bytes:
|
||||
|
||||
# Do not include a 'to' field for contract creation.
|
||||
if transaction_dict['to'] == b'':
|
||||
transaction_dict = dissoc(transaction_dict, 'to')
|
||||
|
||||
# Sign
|
||||
result = self.w3.eth.sign_transaction(transaction_dict)
|
||||
|
||||
# Return RLP bytes
|
||||
rlp_encoded_transaction = result.raw
|
||||
return rlp_encoded_transaction
|
||||
|
||||
@property
|
||||
def wallets(self):
|
||||
return self.w3.geth.personal.list_wallets()
|
||||
|
||||
|
||||
class BorClient(GethClient):
|
||||
"""Geth to Bor adapter"""
|
||||
|
||||
|
||||
class ParityClient(EthereumClient):
|
||||
|
||||
@property
|
||||
def peers(self) -> list:
|
||||
"""
|
||||
TODO: Look for web3.py support for Parity Peers endpoint
|
||||
"""
|
||||
return self.w3.manager.request_blocking("parity_netPeers", [])
|
||||
|
||||
def new_account(self, password: str) -> str:
|
||||
new_account = self.w3.parity.personal.new_account(password)
|
||||
return to_checksum_address(new_account) # cast and validate
|
||||
|
||||
def unlock_account(self, account, password, duration: int = None) -> bool:
|
||||
return self.w3.parity.personal.unlock_account(account, password, duration)
|
||||
|
||||
def lock_account(self, account):
|
||||
return self.w3.parity.personal.lock_account(account)
|
||||
|
||||
|
||||
class GanacheClient(EthereumClient):
|
||||
is_local = True
|
||||
|
||||
def unlock_account(self, *args, **kwargs) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
class InfuraClient(EthereumClient):
|
||||
is_local = False
|
||||
TRANSACTION_POLLING_TIME = 2 # seconds
|
||||
|
||||
def _add_default_middleware(self):
|
||||
# default retry functionality
|
||||
self.log.debug('Adding Infura RPC retry middleware to client')
|
||||
self.add_middleware(InfuraRetryRequestMiddleware)
|
||||
|
||||
def unlock_account(self, *args, **kwargs) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
class AlchemyClient(EthereumClient):
|
||||
|
||||
def _add_default_middleware(self):
|
||||
# default retry functionality
|
||||
self.log.debug('Adding Alchemy RPC retry middleware to client')
|
||||
self.add_middleware(AlchemyRetryRequestMiddleware)
|
||||
|
||||
|
||||
class EthereumTesterClient(EthereumClient):
|
||||
is_local = True
|
||||
|
||||
def unlock_account(self, account, password, duration: int = None) -> bool:
|
||||
"""Returns True if the testing backend keystore has control of the given address."""
|
||||
account = to_checksum_address(account)
|
||||
keystore_accounts = self.w3.provider.ethereum_tester.get_accounts()
|
||||
if account in keystore_accounts:
|
||||
return True
|
||||
else:
|
||||
return self.w3.provider.ethereum_tester.unlock_account(account=account,
|
||||
password=password,
|
||||
unlock_seconds=duration)
|
||||
|
||||
def lock_account(self, account) -> bool:
|
||||
"""Returns True if the testing backend keystore has control of the given address."""
|
||||
account = to_canonical_address(account)
|
||||
keystore_accounts = self.w3.provider.ethereum_tester.backend.get_accounts()
|
||||
if account in keystore_accounts:
|
||||
return True
|
||||
else:
|
||||
return self.w3.provider.ethereum_tester.lock_account(account=account)
|
||||
|
||||
def new_account(self, password: str) -> str:
|
||||
insecure_account = self.w3.provider.ethereum_tester.add_account(private_key=os.urandom(32).hex(),
|
||||
password=password)
|
||||
return insecure_account
|
||||
|
||||
def __get_signing_key(self, account: bytes):
|
||||
"""Get signing key of test account"""
|
||||
account = to_canonical_address(account)
|
||||
def _get_chain_id(cls, w3: Web3):
|
||||
result = w3.eth.chain_id
|
||||
try:
|
||||
signing_key = self.w3.provider.ethereum_tester.backend._key_lookup[account]._raw_key
|
||||
except KeyError:
|
||||
raise self.UnknownAccount(account)
|
||||
return signing_key
|
||||
# from hex-str
|
||||
chain_id = int(result, 16)
|
||||
except TypeError:
|
||||
# from str
|
||||
chain_id = int(result)
|
||||
|
||||
def sign_transaction(self, transaction_dict: dict) -> bytes:
|
||||
# Sign using a local private key
|
||||
address = to_canonical_address(transaction_dict['from'])
|
||||
signing_key = self.__get_signing_key(account=address)
|
||||
raw_transaction = self.w3.eth.account.sign_transaction(
|
||||
transaction_dict, private_key=signing_key
|
||||
).rawTransaction
|
||||
return raw_transaction
|
||||
|
||||
def sign_message(self, account: str, message: bytes) -> str:
|
||||
"""Sign, EIP-191 (Geth) Style"""
|
||||
signing_key = self.__get_signing_key(account=account)
|
||||
signable_message = encode_defunct(primitive=message)
|
||||
signature_and_stuff = Account.sign_message(signable_message=signable_message, private_key=signing_key)
|
||||
return signature_and_stuff['signature']
|
||||
|
||||
def parse_transaction_data(self, transaction):
|
||||
return transaction.data # See https://github.com/ethereum/eth-tester/issues/173
|
||||
return chain_id
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import math
|
||||
import pprint
|
||||
from pathlib import Path
|
||||
from typing import Callable, Dict, NamedTuple, Optional, Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
@ -22,15 +21,12 @@ from web3.middleware import geth_poa_middleware, simple_cache_middleware
|
|||
from web3.providers import BaseProvider
|
||||
from web3.types import TxParams, TxReceipt
|
||||
|
||||
from nucypher.blockchain.eth.clients import POA_CHAINS, EthereumClient, InfuraClient
|
||||
from nucypher.blockchain.eth.clients import POA_CHAINS, EthereumClient
|
||||
from nucypher.blockchain.eth.decorators import validate_checksum_address
|
||||
from nucypher.blockchain.eth.providers import (
|
||||
_get_auto_provider,
|
||||
_get_HTTP_provider,
|
||||
_get_IPC_provider,
|
||||
_get_http_provider,
|
||||
_get_mock_test_provider,
|
||||
_get_pyevm_test_provider,
|
||||
_get_websocket_provider,
|
||||
)
|
||||
from nucypher.blockchain.eth.registry import ContractRegistry
|
||||
from nucypher.blockchain.eth.utils import (
|
||||
|
@ -42,7 +38,6 @@ from nucypher.crypto.powers import TransactingPower
|
|||
from nucypher.utilities.emitters import StdoutEmitter
|
||||
from nucypher.utilities.gas_strategies import (
|
||||
WEB3_GAS_STRATEGIES,
|
||||
construct_datafeed_median_strategy,
|
||||
max_price_gas_strategy_wrapper,
|
||||
)
|
||||
from nucypher.utilities.logging import Logger
|
||||
|
@ -318,11 +313,6 @@ class BlockchainInterface:
|
|||
def configure_gas_strategy(self, gas_strategy: Optional[Callable] = None) -> None:
|
||||
if gas_strategy:
|
||||
reported_gas_strategy = f"fixed/{gas_strategy.name}"
|
||||
|
||||
elif isinstance(self.client, InfuraClient):
|
||||
gas_strategy = construct_datafeed_median_strategy(speed=self.gas_strategy)
|
||||
reported_gas_strategy = f"datafeed/{self.gas_strategy}"
|
||||
|
||||
else:
|
||||
reported_gas_strategy = f"web3/{self.gas_strategy}"
|
||||
gas_strategy = self.get_gas_strategy(self.gas_strategy)
|
||||
|
@ -362,7 +352,7 @@ class BlockchainInterface:
|
|||
try:
|
||||
self.w3 = self.Web3(provider=self._provider)
|
||||
self.tx_machine.w3 = self.w3 # share this web3 instance with the tracker
|
||||
self.client = EthereumClient.from_w3(w3=self.w3)
|
||||
self.client = EthereumClient(w3=self.w3)
|
||||
except requests.ConnectionError: # RPC
|
||||
raise self.ConnectionFailed(
|
||||
f"Connection Failed - {str(self.endpoint)} - is RPC enabled?"
|
||||
|
@ -394,43 +384,22 @@ class BlockchainInterface:
|
|||
|
||||
if endpoint and not provider:
|
||||
uri_breakdown = urlparse(endpoint)
|
||||
|
||||
if uri_breakdown.scheme == "tester":
|
||||
providers = {
|
||||
"pyevm": _get_pyevm_test_provider,
|
||||
"mock": _get_mock_test_provider,
|
||||
}
|
||||
provider_scheme = uri_breakdown.netloc
|
||||
|
||||
provider_scheme = (
|
||||
uri_breakdown.netloc
|
||||
if uri_breakdown.scheme == "tester"
|
||||
else uri_breakdown.scheme
|
||||
)
|
||||
if provider_scheme == "pyevm":
|
||||
self._provider = _get_pyevm_test_provider(endpoint)
|
||||
elif provider_scheme == "mock":
|
||||
self._provider = _get_mock_test_provider(endpoint)
|
||||
elif provider_scheme == "http" or provider_scheme == "https":
|
||||
self._provider = _get_http_provider(endpoint)
|
||||
else:
|
||||
providers = {
|
||||
"auto": _get_auto_provider,
|
||||
"ipc": _get_IPC_provider,
|
||||
"file": _get_IPC_provider,
|
||||
"ws": _get_websocket_provider,
|
||||
"wss": _get_websocket_provider,
|
||||
"http": _get_HTTP_provider,
|
||||
"https": _get_HTTP_provider,
|
||||
}
|
||||
provider_scheme = uri_breakdown.scheme
|
||||
|
||||
# auto-detect for file based ipc
|
||||
if not provider_scheme:
|
||||
if Path(endpoint).is_file():
|
||||
# file is available - assume ipc/file scheme
|
||||
provider_scheme = "file"
|
||||
self.log.info(
|
||||
f"Auto-detected provider scheme as 'file://' for provider {endpoint}"
|
||||
)
|
||||
|
||||
try:
|
||||
self._provider = providers[provider_scheme](endpoint)
|
||||
except KeyError:
|
||||
raise self.UnsupportedProvider(
|
||||
f"{endpoint} is an invalid or unsupported blockchain provider URI"
|
||||
)
|
||||
else:
|
||||
self.endpoint = endpoint or NO_BLOCKCHAIN_CONNECTION
|
||||
self.endpoint = endpoint or NO_BLOCKCHAIN_CONNECTION
|
||||
else:
|
||||
self._provider = provider
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from urllib.parse import urlparse
|
||||
|
||||
from web3 import HTTPProvider, IPCProvider, WebsocketProvider
|
||||
from web3 import HTTPProvider
|
||||
from web3.providers import BaseProvider
|
||||
|
||||
from nucypher.exceptions import DevelopmentInstallationRequired
|
||||
|
@ -10,15 +9,7 @@ class ProviderError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
def _get_IPC_provider(endpoint) -> BaseProvider:
|
||||
uri_breakdown = urlparse(endpoint)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
return IPCProvider(ipc_path=uri_breakdown.path,
|
||||
timeout=BlockchainInterface.TIMEOUT,
|
||||
request_kwargs={'timeout': BlockchainInterface.TIMEOUT})
|
||||
|
||||
|
||||
def _get_HTTP_provider(endpoint) -> BaseProvider:
|
||||
def _get_http_provider(endpoint) -> BaseProvider:
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
|
||||
return HTTPProvider(
|
||||
|
@ -27,24 +18,6 @@ def _get_HTTP_provider(endpoint) -> BaseProvider:
|
|||
)
|
||||
|
||||
|
||||
def _get_websocket_provider(endpoint) -> BaseProvider:
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
|
||||
return WebsocketProvider(
|
||||
endpoint_uri=endpoint,
|
||||
websocket_kwargs={"timeout": BlockchainInterface.TIMEOUT},
|
||||
)
|
||||
|
||||
|
||||
def _get_auto_provider(endpoint) -> BaseProvider:
|
||||
from web3.auto import w3
|
||||
# how-automated-detection-works: https://web3py.readthedocs.io/en/latest/providers.html
|
||||
connected = w3.isConnected()
|
||||
if not connected:
|
||||
raise ProviderError('Cannot auto-detect node. Provide a full URI instead.')
|
||||
return w3.provider
|
||||
|
||||
|
||||
def _get_pyevm_test_backend():
|
||||
|
||||
try:
|
||||
|
@ -91,8 +64,3 @@ def _get_mock_test_provider(endpoint) -> BaseProvider:
|
|||
mock_backend = MockBackend()
|
||||
provider = _get_ethereum_tester(test_backend=mock_backend)
|
||||
return provider
|
||||
|
||||
|
||||
def _get_tester_ganache(endpoint=None) -> BaseProvider:
|
||||
endpoint_uri = endpoint or "http://localhost:7545"
|
||||
return HTTPProvider(endpoint_uri=endpoint_uri)
|
||||
|
|
|
@ -8,7 +8,7 @@ from cytoolz.dicttoolz import dissoc
|
|||
from eth_account.account import Account
|
||||
from eth_account.messages import encode_defunct
|
||||
from eth_account.signers.local import LocalAccount
|
||||
from eth_utils.address import is_address, to_canonical_address, to_checksum_address
|
||||
from eth_utils.address import is_address, to_checksum_address
|
||||
from hexbytes.main import BytesLike, HexBytes
|
||||
|
||||
from nucypher.blockchain.eth.decorators import validate_checksum_address
|
||||
|
@ -20,12 +20,11 @@ class Web3Signer(Signer):
|
|||
super().__init__()
|
||||
self.__client = client
|
||||
|
||||
def _get_signer(self, account: str) -> LocalAccount:
|
||||
"""Test helper to get a signer from the client's backend"""
|
||||
account = to_canonical_address(account)
|
||||
_eth_tester = self.__client.w3.provider.ethereum_tester
|
||||
signer = Account.from_key(_eth_tester.backend._key_lookup[account]._raw_key)
|
||||
return signer
|
||||
@validate_checksum_address
|
||||
def _get_signer(self, account: str):
|
||||
raise NotImplementedError(
|
||||
"Can't sign via external blockchain clients; provide an explicit Signer"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def uri_scheme(cls) -> str:
|
||||
|
@ -56,53 +55,30 @@ class Web3Signer(Signer):
|
|||
|
||||
@validate_checksum_address
|
||||
def is_device(self, account: str):
|
||||
try:
|
||||
# TODO: Temporary fix for #1128 and #1385. It's ugly af, but it works. Move somewhere else?
|
||||
wallets = self.__client.wallets
|
||||
except AttributeError:
|
||||
return False
|
||||
else:
|
||||
HW_WALLET_URL_PREFIXES = ("trezor", "ledger")
|
||||
hw_accounts = [
|
||||
w["accounts"]
|
||||
for w in wallets
|
||||
if w["url"].startswith(HW_WALLET_URL_PREFIXES)
|
||||
]
|
||||
hw_addresses = [
|
||||
to_checksum_address(account["address"])
|
||||
for sublist in hw_accounts
|
||||
for account in sublist
|
||||
]
|
||||
return account in hw_addresses
|
||||
return False
|
||||
|
||||
@validate_checksum_address
|
||||
def unlock_account(self, account: str, password: str, duration: int = None):
|
||||
if self.is_device(account=account):
|
||||
unlocked = True
|
||||
else:
|
||||
unlocked = self.__client.unlock_account(
|
||||
account=account, password=password, duration=duration
|
||||
)
|
||||
return unlocked
|
||||
raise NotImplementedError(
|
||||
"Can't manage accounts via external blockchain clients; provide an explicit Signer"
|
||||
)
|
||||
|
||||
@validate_checksum_address
|
||||
def lock_account(self, account: str):
|
||||
if self.is_device(account=account):
|
||||
result = None # TODO: Force Disconnect Devices?
|
||||
else:
|
||||
result = self.__client.lock_account(account=account)
|
||||
return result
|
||||
raise NotImplementedError(
|
||||
"Can't manage accounts via external blockchain clients; provide an explicit Signer"
|
||||
)
|
||||
|
||||
@validate_checksum_address
|
||||
def sign_message(self, account: str, message: bytes, **kwargs) -> HexBytes:
|
||||
signature = self.__client.sign_message(account=account, message=message)
|
||||
return HexBytes(signature)
|
||||
raise NotImplementedError(
|
||||
"Can't sign via external blockchain clients; provide an explicit Signer"
|
||||
)
|
||||
|
||||
def sign_transaction(self, transaction_dict: dict) -> bytes:
|
||||
raw_transaction = self.__client.sign_transaction(
|
||||
transaction_dict=transaction_dict
|
||||
raise NotImplementedError(
|
||||
"Can't sign via external blockchain clients; provide an explicit Signer"
|
||||
)
|
||||
return raw_transaction
|
||||
|
||||
|
||||
class KeystoreSigner(Signer):
|
||||
|
@ -343,7 +319,7 @@ class InMemorySigner(Signer):
|
|||
return True
|
||||
|
||||
@validate_checksum_address
|
||||
def __get_signer(self, account: str) -> LocalAccount:
|
||||
def _get_signer(self, account: str) -> LocalAccount:
|
||||
"""Lookup a known keystore account by its checksum address or raise an error"""
|
||||
try:
|
||||
return self.__signers[account]
|
||||
|
@ -356,7 +332,7 @@ class InMemorySigner(Signer):
|
|||
@validate_checksum_address
|
||||
def sign_transaction(self, transaction_dict: dict) -> bytes:
|
||||
sender = transaction_dict["from"]
|
||||
signer = self.__get_signer(account=sender)
|
||||
signer = self._get_signer(account=sender)
|
||||
if not transaction_dict["to"]:
|
||||
transaction_dict = dissoc(transaction_dict, "to")
|
||||
raw_transaction = signer.sign_transaction(
|
||||
|
@ -366,7 +342,7 @@ class InMemorySigner(Signer):
|
|||
|
||||
@validate_checksum_address
|
||||
def sign_message(self, account: str, message: bytes, **kwargs) -> HexBytes:
|
||||
signer = self.__get_signer(account=account)
|
||||
signer = self._get_signer(account=account)
|
||||
signature = signer.sign_message(
|
||||
signable_message=encode_defunct(primitive=message)
|
||||
).signature
|
||||
|
|
|
@ -4,7 +4,7 @@ from unittest.mock import Mock, patch
|
|||
|
||||
from nucypher_core import NodeMetadata
|
||||
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.signers.software import InMemorySigner
|
||||
from nucypher.characters.lawful import Alice, Ursula
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN_NAME
|
||||
from nucypher.crypto.powers import CryptoPower
|
||||
|
@ -49,8 +49,6 @@ class Vladimir(Ursula):
|
|||
|
||||
crypto_power = CryptoPower(power_ups=target_ursula._default_crypto_powerups)
|
||||
|
||||
cls.attach_transacting_key(blockchain=eth_blockchain)
|
||||
|
||||
# Vladimir does not care about payment.
|
||||
bogus_pre_payment_method = FreeReencryptions()
|
||||
bogus_pre_payment_method.provider = Mock()
|
||||
|
@ -73,7 +71,7 @@ class Vladimir(Ursula):
|
|||
network_middleware=cls.network_middleware,
|
||||
checksum_address=cls.fraud_address,
|
||||
operator_address=cls.fraud_address,
|
||||
signer=Web3Signer(eth_blockchain.client),
|
||||
signer=InMemorySigner(private_key=cls.fraud_key),
|
||||
eth_endpoint=eth_blockchain.endpoint,
|
||||
polygon_endpoint=polygon_blockchain.endpoint,
|
||||
pre_payment_method=bogus_pre_payment_method,
|
||||
|
@ -114,28 +112,6 @@ class Vladimir(Ursula):
|
|||
|
||||
return vladimir
|
||||
|
||||
@classmethod
|
||||
def attach_transacting_key(cls, blockchain):
|
||||
"""
|
||||
Upload Vladimir's ETH keys to the keychain via web3.
|
||||
"""
|
||||
try:
|
||||
from eth_tester.exceptions import ValidationError
|
||||
except ImportError:
|
||||
raise DevelopmentInstallationRequired(
|
||||
importable_name="eth_tester.exceptions.ValidationError"
|
||||
)
|
||||
try:
|
||||
password = 'iamverybadass'
|
||||
blockchain.w3.provider.ethereum_tester.add_account(cls.fraud_key, password=password)
|
||||
except (ValidationError,):
|
||||
# check if Vlad's key is already on the keystore...
|
||||
if cls.fraud_address in blockchain.client.accounts:
|
||||
return True
|
||||
else:
|
||||
raise
|
||||
return True
|
||||
|
||||
|
||||
class Amonia(Alice):
|
||||
"""
|
||||
|
|
|
@ -10,7 +10,7 @@ from prometheus_client import REGISTRY
|
|||
from nucypher.blockchain.eth.agents import ContractAgency, SubscriptionManagerAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.models import Coordinator
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.signers.software import InMemorySigner
|
||||
from nucypher.characters.lawful import Enrico, Ursula
|
||||
from nucypher.policy.conditions.evm import ContractCondition, RPCCondition
|
||||
from nucypher.policy.conditions.lingo import (
|
||||
|
@ -49,8 +49,8 @@ def interval(testerchain):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def signer(testerchain):
|
||||
return Web3Signer(testerchain.client)
|
||||
def signer():
|
||||
return InMemorySigner()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.utilities.logging import Logger
|
||||
from tests.utils.ursula import select_test_port
|
||||
|
@ -19,9 +18,10 @@ def test_ursula_operator_confirmation(
|
|||
taco_application_agent,
|
||||
taco_child_application_agent,
|
||||
deployer_account,
|
||||
accounts,
|
||||
):
|
||||
staking_provider = testerchain.stake_provider_account(0)
|
||||
operator_address = testerchain.ursula_account(0)
|
||||
staking_provider = accounts.staking_provider_account(0)
|
||||
operator_address = accounts.ursula_account(0)
|
||||
min_authorization = taco_application_agent.get_min_authorization()
|
||||
|
||||
# make an staking_providers and some stakes
|
||||
|
@ -47,7 +47,7 @@ def test_ursula_operator_confirmation(
|
|||
|
||||
# bond this operator
|
||||
tpower = TransactingPower(
|
||||
account=staking_provider, signer=Web3Signer(testerchain.client)
|
||||
account=staking_provider, signer=accounts.get_account_signer(staking_provider)
|
||||
)
|
||||
taco_application_agent.bond_operator(
|
||||
staking_provider=staking_provider,
|
||||
|
@ -57,7 +57,9 @@ def test_ursula_operator_confirmation(
|
|||
|
||||
# make an ursula.
|
||||
ursula = ursula_test_config.produce(
|
||||
operator_address=operator_address, rest_port=select_test_port()
|
||||
operator_address=operator_address,
|
||||
rest_port=select_test_port(),
|
||||
signer=accounts.get_account_signer(operator_address),
|
||||
)
|
||||
|
||||
# now the worker has a staking provider
|
||||
|
|
|
@ -9,7 +9,6 @@ from twisted.internet.task import deferLater
|
|||
|
||||
from nucypher.blockchain.eth.agents import CoordinatorAgent
|
||||
from nucypher.blockchain.eth.models import Coordinator
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
||||
|
||||
|
@ -44,9 +43,9 @@ def cohort_ursulas(cohort, taco_application_agent):
|
|||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def transacting_powers(testerchain, cohort_ursulas):
|
||||
def transacting_powers(accounts, cohort_ursulas):
|
||||
return [
|
||||
TransactingPower(account=ursula, signer=Web3Signer(testerchain.client))
|
||||
TransactingPower(account=ursula, signer=accounts.get_account_signer(ursula))
|
||||
for ursula in cohort_ursulas
|
||||
]
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ from nucypher.blockchain.eth.agents import (
|
|||
WeightedSampler,
|
||||
)
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
||||
|
||||
|
@ -19,9 +18,10 @@ def test_sampling_distribution(
|
|||
threshold_staking,
|
||||
coordinator_agent,
|
||||
deployer_account,
|
||||
accounts,
|
||||
):
|
||||
# setup
|
||||
stake_provider_accounts = testerchain.stake_providers_accounts
|
||||
stake_provider_accounts = accounts.staking_providers_accounts
|
||||
amount = taco_application_agent.get_min_authorization()
|
||||
all_locked_tokens = len(stake_provider_accounts) * amount
|
||||
|
||||
|
@ -35,7 +35,10 @@ def test_sampling_distribution(
|
|||
provider_address, 0, amount, sender=deployer_account
|
||||
)
|
||||
|
||||
power = TransactingPower(account=provider_address, signer=Web3Signer(testerchain.client))
|
||||
power = TransactingPower(
|
||||
account=provider_address,
|
||||
signer=accounts.get_account_signer(provider_address),
|
||||
)
|
||||
|
||||
# We assume that the staking provider knows in advance the account of her operator
|
||||
taco_application_agent.bond_operator(
|
||||
|
|
|
@ -3,7 +3,6 @@ import random
|
|||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
||||
|
||||
|
@ -29,13 +28,14 @@ def test_authorized_tokens(
|
|||
|
||||
def test_staking_providers_and_operators_relationships(
|
||||
testerchain,
|
||||
accounts,
|
||||
taco_application_agent,
|
||||
threshold_staking,
|
||||
taco_application,
|
||||
deployer_account,
|
||||
get_random_checksum_address,
|
||||
):
|
||||
staking_provider_account, operator_account, *other = testerchain.unassigned_accounts
|
||||
staking_provider_account, operator_account, *other = accounts.unassigned_accounts
|
||||
threshold_staking.setRoles(staking_provider_account, sender=deployer_account)
|
||||
threshold_staking.authorizationIncreased(
|
||||
staking_provider_account,
|
||||
|
@ -50,7 +50,8 @@ def test_staking_providers_and_operators_relationships(
|
|||
)
|
||||
|
||||
tpower = TransactingPower(
|
||||
account=staking_provider_account, signer=Web3Signer(testerchain.client)
|
||||
account=staking_provider_account,
|
||||
signer=accounts.get_account_signer(staking_provider_account),
|
||||
)
|
||||
_txhash = taco_application_agent.bond_operator(
|
||||
transacting_power=tpower,
|
||||
|
|
|
@ -5,11 +5,13 @@ from twisted.logger import LogLevel, globalLogPublisher
|
|||
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN_NAME
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from tests.constants import TEST_ETH_PROVIDER_URI
|
||||
from tests.utils.ursula import make_ursulas, start_pytest_ursula_services
|
||||
from tests.utils.ursula import (
|
||||
make_ursulas,
|
||||
start_pytest_ursula_services,
|
||||
)
|
||||
|
||||
|
||||
def test_ursula_stamp_verification_tolerance(ursulas, mocker):
|
||||
|
@ -79,6 +81,7 @@ def test_ursula_stamp_verification_tolerance(ursulas, mocker):
|
|||
|
||||
def test_invalid_operators_tolerance(
|
||||
testerchain,
|
||||
accounts,
|
||||
test_registry,
|
||||
ursulas,
|
||||
threshold_staking,
|
||||
|
@ -90,15 +93,12 @@ def test_invalid_operators_tolerance(
|
|||
#
|
||||
# Setup
|
||||
#
|
||||
(
|
||||
creator,
|
||||
_staking_provider,
|
||||
operator_address,
|
||||
*everyone_else,
|
||||
) = testerchain.client.accounts
|
||||
|
||||
existing_ursula, other_ursula, *the_others = list(ursulas)
|
||||
|
||||
_staking_provider, operator_address = (
|
||||
accounts.unassigned_accounts[0],
|
||||
accounts.unassigned_accounts[1],
|
||||
)
|
||||
# We start with an ursula with no tokens staked
|
||||
owner, _, _ = threshold_staking.rolesOf(_staking_provider, sender=deployer_account)
|
||||
assert owner == NULL_ADDRESS
|
||||
|
@ -112,7 +112,7 @@ def test_invalid_operators_tolerance(
|
|||
|
||||
# now lets bond this worker
|
||||
tpower = TransactingPower(
|
||||
account=_staking_provider, signer=Web3Signer(testerchain.client)
|
||||
account=_staking_provider, signer=accounts.get_account_signer(_staking_provider)
|
||||
)
|
||||
taco_application_agent.bond_operator(
|
||||
staking_provider=_staking_provider,
|
||||
|
@ -121,7 +121,11 @@ def test_invalid_operators_tolerance(
|
|||
)
|
||||
|
||||
# Make the Operator
|
||||
ursulas = make_ursulas(ursula_test_config, [_staking_provider], [operator_address])
|
||||
ursulas = make_ursulas(
|
||||
ursula_test_config,
|
||||
[_staking_provider],
|
||||
[accounts.get_account_signer(operator_address)],
|
||||
)
|
||||
ursula = ursulas[0]
|
||||
ursula.run(
|
||||
preflight=False,
|
||||
|
|
|
@ -4,7 +4,6 @@ from eth_utils import to_checksum_address
|
|||
from nucypher_core.ferveo import Keypair
|
||||
|
||||
from nucypher.blockchain.eth.models import Ferveo
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.lawful import Character
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN_NAME
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
@ -15,9 +14,9 @@ from tests.constants import INSECURE_DEVELOPMENT_PASSWORD, TEST_ETH_PROVIDER_URI
|
|||
TransactingPower.lock_account = LOCK_FUNCTION
|
||||
|
||||
|
||||
def test_character_transacting_power_signing(testerchain, test_registry):
|
||||
def test_character_transacting_power_signing(testerchain, test_registry, accounts):
|
||||
# Pretend to be a character.
|
||||
eth_address = testerchain.etherbase_account
|
||||
eth_address = accounts.etherbase_account
|
||||
signer = Character(
|
||||
is_me=True,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
|
@ -29,7 +28,7 @@ def test_character_transacting_power_signing(testerchain, test_registry):
|
|||
# Manually consume the power up
|
||||
transacting_power = TransactingPower(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
signer=accounts.get_account_signer(eth_address),
|
||||
account=eth_address,
|
||||
)
|
||||
|
||||
|
@ -54,7 +53,7 @@ def test_character_transacting_power_signing(testerchain, test_registry):
|
|||
"gasPrice": testerchain.client.w3.eth.gas_price,
|
||||
"gas": 100000,
|
||||
"from": eth_address,
|
||||
"to": testerchain.unassigned_accounts[1],
|
||||
"to": accounts.unassigned_accounts[1],
|
||||
"value": 1,
|
||||
"data": b"",
|
||||
}
|
||||
|
@ -67,12 +66,12 @@ def test_character_transacting_power_signing(testerchain, test_registry):
|
|||
assert to_checksum_address(restored_dict["to"]) == transaction_dict["to"]
|
||||
|
||||
|
||||
def test_transacting_power_sign_message(testerchain):
|
||||
def test_transacting_power_sign_message(testerchain, accounts):
|
||||
# Manually create a TransactingPower
|
||||
eth_address = testerchain.etherbase_account
|
||||
eth_address = accounts.etherbase_account
|
||||
power = TransactingPower(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
signer=accounts.get_account_signer(eth_address),
|
||||
account=eth_address,
|
||||
)
|
||||
|
||||
|
@ -91,18 +90,18 @@ def test_transacting_power_sign_message(testerchain):
|
|||
|
||||
# Test invalid address/pubkey pair
|
||||
is_verified = verify_eip_191(
|
||||
address=testerchain.client.accounts[1],
|
||||
address=accounts.accounts_addresses[1],
|
||||
message=data_to_sign,
|
||||
signature=signature,
|
||||
)
|
||||
assert is_verified is False
|
||||
|
||||
|
||||
def test_transacting_power_sign_transaction(testerchain):
|
||||
eth_address = testerchain.unassigned_accounts[2]
|
||||
def test_transacting_power_sign_transaction(testerchain, accounts):
|
||||
eth_address = accounts.unassigned_accounts[2]
|
||||
power = TransactingPower(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
signer=accounts.get_account_signer(eth_address),
|
||||
account=eth_address,
|
||||
)
|
||||
|
||||
|
@ -111,7 +110,7 @@ def test_transacting_power_sign_transaction(testerchain):
|
|||
"gasPrice": testerchain.client.w3.eth.gas_price,
|
||||
"gas": 100000,
|
||||
"from": eth_address,
|
||||
"to": testerchain.unassigned_accounts[1],
|
||||
"to": accounts.unassigned_accounts[1],
|
||||
"value": 1,
|
||||
"data": b"",
|
||||
}
|
||||
|
@ -134,7 +133,9 @@ def test_transacting_power_sign_transaction(testerchain):
|
|||
power.sign_transaction(transaction_dict=transaction_dict)
|
||||
|
||||
|
||||
def test_transacting_power_sign_agent_transaction(testerchain, coordinator_agent):
|
||||
def test_transacting_power_sign_agent_transaction(
|
||||
testerchain, coordinator_agent, accounts
|
||||
):
|
||||
public_key = Keypair.random().public_key()
|
||||
g2_point = Ferveo.G2Point.from_public_key(public_key)
|
||||
contract_function = coordinator_agent.contract.functions.setProviderPublicKey(
|
||||
|
@ -144,9 +145,9 @@ def test_transacting_power_sign_agent_transaction(testerchain, coordinator_agent
|
|||
payload = {
|
||||
"chainId": int(testerchain.client.chain_id),
|
||||
"nonce": testerchain.client.w3.eth.get_transaction_count(
|
||||
testerchain.etherbase_account
|
||||
accounts.etherbase_account
|
||||
),
|
||||
"from": testerchain.etherbase_account,
|
||||
"from": accounts.etherbase_account,
|
||||
"gasPrice": testerchain.client.gas_price,
|
||||
"gas": 500_000,
|
||||
}
|
||||
|
@ -156,8 +157,8 @@ def test_transacting_power_sign_agent_transaction(testerchain, coordinator_agent
|
|||
# Sign with Transacting Power
|
||||
transacting_power = TransactingPower(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
account=testerchain.etherbase_account,
|
||||
signer=accounts.get_account_signer(accounts.etherbase_account),
|
||||
account=accounts.etherbase_account,
|
||||
)
|
||||
signed_raw_transaction = transacting_power.sign_transaction(unsigned_transaction)
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ from web3 import Web3
|
|||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, TACoApplicationAgent
|
||||
from nucypher.blockchain.eth.signers import KeystoreSigner
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.cli.main import nucypher_cli
|
||||
from nucypher.config.characters import UrsulaConfiguration
|
||||
from nucypher.config.constants import (
|
||||
|
@ -34,6 +33,7 @@ def mock_funded_account_password_keystore(
|
|||
taco_application_agent,
|
||||
test_registry,
|
||||
deployer_account,
|
||||
accounts,
|
||||
):
|
||||
"""
|
||||
Generate a random keypair & password and create a local keystore. Then prepare a staking provider
|
||||
|
@ -49,14 +49,14 @@ def mock_funded_account_password_keystore(
|
|||
testerchain.client.w3.eth.send_transaction(
|
||||
{
|
||||
"to": account.address,
|
||||
"from": testerchain.etherbase_account,
|
||||
"from": accounts.etherbase_account,
|
||||
"value": Web3.to_wei("100", "ether"),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# initialize threshold stake
|
||||
provider_address = testerchain.unassigned_accounts[0]
|
||||
provider_address = accounts.unassigned_accounts[0]
|
||||
threshold_staking.setRoles(provider_address, sender=deployer_account)
|
||||
threshold_staking.setStakes(
|
||||
provider_address,
|
||||
|
@ -66,7 +66,7 @@ def mock_funded_account_password_keystore(
|
|||
)
|
||||
|
||||
provider_power = TransactingPower(
|
||||
account=provider_address, signer=Web3Signer(testerchain.client)
|
||||
account=provider_address, signer=accounts.get_account_signer(provider_address)
|
||||
)
|
||||
provider_power.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
|
||||
|
@ -92,6 +92,7 @@ def test_ursula_and_local_keystore_signer_integration(
|
|||
mocker,
|
||||
mock_funded_account_password_keystore,
|
||||
testerchain,
|
||||
accounts,
|
||||
):
|
||||
config_root_path = tmp_path
|
||||
ursula_config_path = config_root_path / UrsulaConfiguration.generate_filename()
|
||||
|
@ -101,7 +102,7 @@ def test_ursula_and_local_keystore_signer_integration(
|
|||
testerchain.client.w3.eth.send_transaction(
|
||||
{
|
||||
"to": worker_account,
|
||||
"from": testerchain.etherbase_account,
|
||||
"from": accounts.etherbase_account,
|
||||
"value": Web3.to_wei("100", "ether"),
|
||||
}
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ from unittest import mock
|
|||
|
||||
import pytest
|
||||
import pytest_twisted as pt
|
||||
from eth_account import Account
|
||||
from twisted.internet import threads
|
||||
|
||||
from nucypher.characters.base import Learner
|
||||
|
@ -16,7 +17,6 @@ from nucypher.utilities.networking import LOOPBACK_ADDRESS
|
|||
from tests.constants import (
|
||||
INSECURE_DEVELOPMENT_PASSWORD,
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
TEST_POLYGON_PROVIDER_URI,
|
||||
)
|
||||
from tests.utils.ursula import select_test_port, start_pytest_ursula_services
|
||||
|
||||
|
@ -32,8 +32,9 @@ def test_missing_configuration_file(_default_filepath_mock, click_runner):
|
|||
|
||||
|
||||
@pt.inlineCallbacks
|
||||
def test_run_lone_default_development_ursula(click_runner, ursulas, testerchain):
|
||||
def test_run_lone_default_development_ursula(click_runner, mocker, ursulas, accounts):
|
||||
deploy_port = select_test_port()
|
||||
operator_address = ursulas[0].operator_address
|
||||
args = (
|
||||
"ursula",
|
||||
"run", # Stat Ursula Command
|
||||
|
@ -44,13 +45,18 @@ def test_run_lone_default_development_ursula(click_runner, ursulas, testerchain)
|
|||
"--dry-run", # Disable twisted reactor in subprocess
|
||||
"--lonely", # Do not load seednodes,
|
||||
"--operator-address",
|
||||
ursulas[0].operator_address,
|
||||
operator_address,
|
||||
"--eth-endpoint",
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
"--polygon-endpoint",
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
"--signer",
|
||||
"memory://",
|
||||
)
|
||||
|
||||
account = Account.from_key(private_key=accounts[operator_address].private_key)
|
||||
mocker.patch.object(Account, "create", return_value=account)
|
||||
|
||||
result = yield threads.deferToThread(
|
||||
click_runner.invoke,
|
||||
nucypher_cli,
|
||||
|
|
|
@ -79,7 +79,9 @@ def test_user_address_context_invalid_eip712_typed_data(valid_user_address_conte
|
|||
get_context_value(USER_ADDRESS_CONTEXT, **context)
|
||||
|
||||
|
||||
def test_user_address_context_variable_verification(testerchain, valid_user_address_context):
|
||||
def test_user_address_context_variable_verification(
|
||||
valid_user_address_context, accounts
|
||||
):
|
||||
# valid user address context - signature matches address
|
||||
address = get_context_value(USER_ADDRESS_CONTEXT, **valid_user_address_context)
|
||||
assert address == valid_user_address_context[USER_ADDRESS_CONTEXT]["address"]
|
||||
|
@ -89,7 +91,7 @@ def test_user_address_context_variable_verification(testerchain, valid_user_addr
|
|||
mismatch_with_address_context = copy.deepcopy(valid_user_address_context)
|
||||
mismatch_with_address_context[USER_ADDRESS_CONTEXT][
|
||||
"address"
|
||||
] = testerchain.etherbase_account
|
||||
] = accounts.etherbase_account
|
||||
with pytest.raises(ContextVariableVerificationFailed):
|
||||
get_context_value(USER_ADDRESS_CONTEXT, **mismatch_with_address_context)
|
||||
|
||||
|
@ -119,9 +121,9 @@ def test_user_address_context_variable_verification(testerchain, valid_user_addr
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_no_providers(
|
||||
get_context_value_mock, testerchain, rpc_condition
|
||||
get_context_value_mock, testerchain, accounts, rpc_condition
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
with pytest.raises(NoConnectionToChain):
|
||||
_ = rpc_condition.verify(providers={}, **context)
|
||||
|
||||
|
@ -136,9 +138,9 @@ def test_rpc_condition_evaluation_no_providers(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_invalid_provider_for_chain(
|
||||
get_context_value_mock, testerchain, rpc_condition
|
||||
get_context_value_mock, testerchain, accounts, rpc_condition
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
new_chain = 23
|
||||
rpc_condition.chain = new_chain
|
||||
condition_providers = {new_chain: {testerchain.provider}}
|
||||
|
@ -152,8 +154,10 @@ def test_rpc_condition_evaluation_invalid_provider_for_chain(
|
|||
GET_CONTEXT_VALUE_IMPORT_PATH,
|
||||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation(get_context_value_mock, testerchain, rpc_condition, condition_providers):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
def test_rpc_condition_evaluation(
|
||||
get_context_value_mock, accounts, rpc_condition, condition_providers
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
condition_result, call_result = rpc_condition.verify(
|
||||
providers=condition_providers, **context
|
||||
)
|
||||
|
@ -168,9 +172,9 @@ def test_rpc_condition_evaluation(get_context_value_mock, testerchain, rpc_condi
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_multiple_chain_providers(
|
||||
get_context_value_mock, testerchain, rpc_condition
|
||||
get_context_value_mock, testerchain, accounts, rpc_condition
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
|
||||
condition_providers = {
|
||||
"1": {"fake1a", "fake1b"},
|
||||
|
@ -194,9 +198,9 @@ def test_rpc_condition_evaluation_multiple_chain_providers(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_multiple_providers_no_valid_fallback(
|
||||
get_context_value_mock, mocker, testerchain, rpc_condition
|
||||
get_context_value_mock, mocker, accounts, rpc_condition
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
|
||||
def my_configure_w3(provider: BaseProvider):
|
||||
return Web3(provider)
|
||||
|
@ -223,9 +227,9 @@ def test_rpc_condition_evaluation_multiple_providers_no_valid_fallback(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_multiple_providers_valid_fallback(
|
||||
get_context_value_mock, mocker, testerchain, rpc_condition
|
||||
get_context_value_mock, mocker, testerchain, accounts, rpc_condition
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
|
||||
def my_configure_w3(provider: BaseProvider):
|
||||
return Web3(provider)
|
||||
|
@ -260,9 +264,9 @@ def test_rpc_condition_evaluation_multiple_providers_valid_fallback(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_no_connection_to_chain(
|
||||
get_context_value_mock, testerchain, rpc_condition
|
||||
get_context_value_mock, testerchain, accounts, rpc_condition
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
|
||||
# condition providers for other unrelated chains
|
||||
providers = {
|
||||
|
@ -279,9 +283,9 @@ def test_rpc_condition_evaluation_no_connection_to_chain(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_rpc_condition_evaluation_with_context_var_in_return_value_test(
|
||||
get_context_value_mock, testerchain, condition_providers
|
||||
get_context_value_mock, testerchain, accounts, condition_providers
|
||||
):
|
||||
account, *other_accounts = testerchain.client.accounts
|
||||
account, *other_accounts = accounts.accounts_addresses
|
||||
balance = testerchain.client.get_balance(account)
|
||||
|
||||
# we have balance stored, use for rpc condition with context variable
|
||||
|
@ -318,15 +322,15 @@ def test_rpc_condition_evaluation_with_context_var_in_return_value_test(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_erc20_evm_condition_evaluation(
|
||||
get_context_value_mock, testerchain, erc20_evm_condition_balanceof, condition_providers
|
||||
get_context_value_mock, erc20_evm_condition_balanceof, condition_providers, accounts
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.unassigned_accounts[0]}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.unassigned_accounts[0]}}
|
||||
condition_result, call_result = erc20_evm_condition_balanceof.verify(
|
||||
providers=condition_providers, **context
|
||||
)
|
||||
assert condition_result is True
|
||||
|
||||
context[USER_ADDRESS_CONTEXT]["address"] = testerchain.etherbase_account
|
||||
context[USER_ADDRESS_CONTEXT]["address"] = accounts.etherbase_account
|
||||
condition_result, call_result = erc20_evm_condition_balanceof.verify(
|
||||
providers=condition_providers, **context
|
||||
)
|
||||
|
@ -334,15 +338,15 @@ def test_erc20_evm_condition_evaluation(
|
|||
|
||||
|
||||
def test_erc20_evm_condition_evaluation_with_custom_context_variable(
|
||||
testerchain, custom_context_variable_erc20_condition, condition_providers
|
||||
custom_context_variable_erc20_condition, condition_providers, accounts
|
||||
):
|
||||
context = {":addressToUse": testerchain.unassigned_accounts[0]}
|
||||
context = {":addressToUse": accounts.unassigned_accounts[0]}
|
||||
condition_result, call_result = custom_context_variable_erc20_condition.verify(
|
||||
providers=condition_providers, **context
|
||||
)
|
||||
assert condition_result is True
|
||||
|
||||
context[":addressToUse"] = testerchain.etherbase_account
|
||||
context[":addressToUse"] = accounts.etherbase_account
|
||||
condition_result, call_result = custom_context_variable_erc20_condition.verify(
|
||||
providers=condition_providers, **context
|
||||
)
|
||||
|
@ -354,9 +358,13 @@ def test_erc20_evm_condition_evaluation_with_custom_context_variable(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_erc721_evm_condition_owner_evaluation(
|
||||
get_context_value_mock, testerchain, test_registry, erc721_evm_condition_owner, condition_providers
|
||||
get_context_value_mock,
|
||||
accounts,
|
||||
test_registry,
|
||||
erc721_evm_condition_owner,
|
||||
condition_providers,
|
||||
):
|
||||
account, *other_accounts = testerchain.client.accounts
|
||||
account, *other_accounts = accounts.accounts_addresses
|
||||
# valid owner of nft
|
||||
context = {
|
||||
USER_ADDRESS_CONTEXT: {"address": account},
|
||||
|
@ -393,9 +401,13 @@ def test_erc721_evm_condition_owner_evaluation(
|
|||
side_effect=_dont_validate_user_address,
|
||||
)
|
||||
def test_erc721_evm_condition_balanceof_evaluation(
|
||||
get_context_value_mock, testerchain, test_registry, erc721_evm_condition_balanceof, condition_providers
|
||||
get_context_value_mock,
|
||||
accounts,
|
||||
test_registry,
|
||||
erc721_evm_condition_balanceof,
|
||||
condition_providers,
|
||||
):
|
||||
account, *other_accounts = testerchain.client.accounts
|
||||
account, *other_accounts = accounts.accounts_addresses
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": account}} # owner of NFT
|
||||
condition_result, call_result = erc721_evm_condition_balanceof.verify(
|
||||
providers=condition_providers, **context
|
||||
|
@ -723,11 +735,11 @@ def test_not_of_simple_compound_conditions_lingo_evaluation(
|
|||
)
|
||||
def test_onchain_conditions_lingo_evaluation(
|
||||
get_context_value_mock,
|
||||
testerchain,
|
||||
compound_lingo,
|
||||
condition_providers,
|
||||
accounts,
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.etherbase_account}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.etherbase_account}}
|
||||
result = compound_lingo.eval(providers=condition_providers, **context)
|
||||
assert result is True
|
||||
|
||||
|
@ -738,11 +750,11 @@ def test_onchain_conditions_lingo_evaluation(
|
|||
)
|
||||
def test_not_of_onchain_conditions_lingo_evaluation(
|
||||
get_context_value_mock,
|
||||
testerchain,
|
||||
compound_lingo,
|
||||
condition_providers,
|
||||
accounts,
|
||||
):
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": testerchain.etherbase_account}}
|
||||
context = {USER_ADDRESS_CONTEXT: {"address": accounts.etherbase_account}}
|
||||
result = compound_lingo.eval(providers=condition_providers, **context)
|
||||
assert result is True
|
||||
|
||||
|
|
|
@ -14,19 +14,16 @@ from nucypher.blockchain.eth.agents import (
|
|||
)
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import ContractRegistry, RegistrySourceManager
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.policy.conditions.evm import RPCCondition
|
||||
from nucypher.utilities.logging import Logger
|
||||
from tests.constants import (
|
||||
BONUS_TOKENS_FOR_TESTS,
|
||||
INSECURE_DEVELOPMENT_PASSWORD,
|
||||
MIN_OPERATOR_SECONDS,
|
||||
TEMPORARY_DOMAIN,
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
TESTERCHAIN_CHAIN_ID,
|
||||
)
|
||||
from tests.utils.blockchain import TesterBlockchain
|
||||
from tests.utils.blockchain import ReservedTestAccountManager, TesterBlockchain
|
||||
from tests.utils.registry import ApeRegistrySource
|
||||
from tests.utils.ursula import (
|
||||
mock_permitted_multichain_connections,
|
||||
|
@ -78,6 +75,11 @@ def monkeymodule():
|
|||
#
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def accounts():
|
||||
return ReservedTestAccountManager()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def deployer_account(accounts):
|
||||
return accounts[0]
|
||||
|
@ -301,7 +303,7 @@ def test_registry(deployed_contracts, module_mocker):
|
|||
|
||||
@pytest.mark.usefixtures("test_registry")
|
||||
@pytest.fixture(scope="module")
|
||||
def testerchain(project, clock) -> TesterBlockchain:
|
||||
def testerchain(project, clock, accounts) -> TesterBlockchain:
|
||||
# Extract the web3 provider containing EthereumTester from the ape project's chain manager
|
||||
provider = project.chain_manager.provider.web3.provider
|
||||
testerchain = TesterBlockchain(provider=provider)
|
||||
|
@ -327,13 +329,8 @@ def staking_providers(
|
|||
|
||||
staking_providers = list()
|
||||
for provider_address, operator_address in zip(
|
||||
testerchain.stake_providers_accounts, testerchain.ursulas_accounts
|
||||
accounts.staking_providers_accounts, accounts.ursulas_accounts
|
||||
):
|
||||
provider_power = TransactingPower(
|
||||
account=provider_address, signer=Web3Signer(testerchain.client)
|
||||
)
|
||||
provider_power.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
|
||||
# for a random amount
|
||||
amount = minimum_stake + random.randrange(BONUS_TOKENS_FOR_TESTS)
|
||||
|
||||
|
@ -345,7 +342,9 @@ def staking_providers(
|
|||
)
|
||||
|
||||
taco_application.bondOperator(
|
||||
provider_address, operator_address, sender=accounts[provider_address]
|
||||
provider_address,
|
||||
operator_address,
|
||||
sender=accounts[provider_address],
|
||||
)
|
||||
|
||||
# track
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from tests.constants import (
|
||||
DEVELOPMENT_ETH_AIRDROP_AMOUNT,
|
||||
NUMBER_OF_ETH_TEST_ACCOUNTS,
|
||||
NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS,
|
||||
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS,
|
||||
)
|
||||
|
||||
# Prevents TesterBlockchain to be picked up by py.test as a test class
|
||||
from tests.utils.blockchain import TesterBlockchain as _TesterBlockchain
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def another_testerchain():
|
||||
testerchain = _TesterBlockchain(eth_airdrop=True, light=True)
|
||||
testerchain.deployer_address = testerchain.etherbase_account
|
||||
assert testerchain.is_light
|
||||
yield testerchain
|
||||
|
||||
|
||||
def test_testerchain_creation(testerchain, another_testerchain):
|
||||
|
||||
chains = (testerchain, another_testerchain)
|
||||
|
||||
for chain in chains:
|
||||
|
||||
# Ensure we are testing on the correct network...
|
||||
assert "tester" in chain.endpoint
|
||||
|
||||
# ... and that there are already some blocks mined
|
||||
chain.w3.eth.w3.testing.mine(1)
|
||||
assert chain.w3.eth.block_number > 0
|
||||
|
||||
# Check that we have enough test accounts
|
||||
assert len(chain.client.accounts) >= NUMBER_OF_ETH_TEST_ACCOUNTS
|
||||
|
||||
# Check that distinguished accounts are assigned
|
||||
etherbase = chain.etherbase_account
|
||||
assert etherbase == chain.client.accounts[0]
|
||||
|
||||
alice = chain.alice_account
|
||||
assert alice == chain.client.accounts[1]
|
||||
|
||||
bob = chain.bob_account
|
||||
assert bob == chain.client.accounts[2]
|
||||
|
||||
stakers = [chain.stake_provider_account(i) for i in range(NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS)]
|
||||
assert stakers == chain.stake_providers_accounts
|
||||
|
||||
ursulas = [chain.ursula_account(i) for i in range(NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)]
|
||||
assert ursulas == chain.ursulas_accounts
|
||||
|
||||
# Check that the remaining accounts are different from the previous ones:
|
||||
assert set([etherbase, alice, bob] + ursulas + stakers).isdisjoint(set(chain.unassigned_accounts))
|
||||
|
||||
# Check that accounts are funded
|
||||
for account in chain.client.accounts:
|
||||
assert chain.client.get_balance(account) >= DEVELOPMENT_ETH_AIRDROP_AMOUNT
|
||||
|
||||
# Check that accounts can send transactions
|
||||
for account in chain.client.accounts:
|
||||
balance = chain.client.get_balance(account)
|
||||
assert balance
|
||||
|
||||
tx = {'to': etherbase, 'from': account, 'value': 100}
|
||||
txhash = chain.client.send_transaction(tx)
|
||||
_receipt = chain.wait_for_receipt(txhash)
|
|
@ -32,14 +32,8 @@ GLOBAL_ALLOW_LIST = "GlobalAllowList"
|
|||
# Ursula
|
||||
#
|
||||
|
||||
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS = 10
|
||||
|
||||
NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS = NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS
|
||||
|
||||
# Ursulas (Operators) and Staking Providers have their own account
|
||||
NUMBER_OF_ETH_TEST_ACCOUNTS = NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS + NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS + 10
|
||||
|
||||
NUMBER_OF_URSULAS_IN_DEVELOPMENT_DOMAIN = NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS
|
||||
NUMBER_OF_ETH_TEST_ACCOUNTS = 30
|
||||
|
||||
#
|
||||
# Local Signer Keystore
|
||||
|
|
|
@ -68,6 +68,7 @@ from tests.mock.performance_mocks import (
|
|||
mock_rest_app_creation,
|
||||
mock_verify_node,
|
||||
)
|
||||
from tests.utils.blockchain import ReservedTestAccountManager
|
||||
from tests.utils.config import (
|
||||
make_alice_test_configuration,
|
||||
make_bob_test_configuration,
|
||||
|
@ -78,7 +79,12 @@ from tests.utils.middleware import (
|
|||
MockRestMiddlewareForLargeFleetTests,
|
||||
)
|
||||
from tests.utils.policy import generate_random_label
|
||||
from tests.utils.ursula import MOCK_KNOWN_URSULAS_CACHE, make_ursulas, select_test_port
|
||||
from tests.utils.ursula import (
|
||||
MOCK_KNOWN_URSULAS_CACHE,
|
||||
make_random_ursulas,
|
||||
make_reserved_ursulas,
|
||||
select_test_port,
|
||||
)
|
||||
|
||||
test_logger = Logger("test-logger")
|
||||
|
||||
|
@ -111,6 +117,12 @@ def temp_dir_path():
|
|||
#
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def accounts():
|
||||
"""a la ape"""
|
||||
return ReservedTestAccountManager()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def random_account():
|
||||
key = Account.create(extra_entropy="lamborghini mercy")
|
||||
|
@ -129,13 +141,12 @@ def random_address(random_account):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def ursula_test_config(test_registry, temp_dir_path, testerchain):
|
||||
def ursula_test_config(test_registry, temp_dir_path):
|
||||
config = make_ursula_test_configuration(
|
||||
eth_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
test_registry=test_registry,
|
||||
rest_port=select_test_port(),
|
||||
operator_address=testerchain.ursulas_accounts.pop(),
|
||||
)
|
||||
yield config
|
||||
config.cleanup()
|
||||
|
@ -144,12 +155,12 @@ def ursula_test_config(test_registry, temp_dir_path, testerchain):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def alice_test_config(ursulas, testerchain, test_registry):
|
||||
def alice_test_config(ursulas, accounts, test_registry):
|
||||
config = make_alice_test_configuration(
|
||||
eth_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
known_nodes=ursulas,
|
||||
checksum_address=testerchain.alice_account,
|
||||
checksum_address=accounts.alice_account,
|
||||
test_registry=test_registry,
|
||||
)
|
||||
yield config
|
||||
|
@ -157,11 +168,11 @@ def alice_test_config(ursulas, testerchain, test_registry):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def bob_test_config(testerchain, test_registry):
|
||||
def bob_test_config(accounts, test_registry):
|
||||
config = make_bob_test_configuration(
|
||||
eth_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
test_registry=test_registry,
|
||||
checksum_address=testerchain.bob_account,
|
||||
checksum_address=accounts.bob_account,
|
||||
)
|
||||
yield config
|
||||
config.cleanup()
|
||||
|
@ -259,30 +270,32 @@ def random_policy_label():
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def alice(alice_test_config, ursulas, testerchain):
|
||||
alice = alice_test_config.produce()
|
||||
def alice(alice_test_config, accounts):
|
||||
alice = alice_test_config.produce(
|
||||
signer=accounts.get_account_signer(accounts.alice_account)
|
||||
)
|
||||
yield alice
|
||||
alice.disenchant()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def bob(bob_test_config, testerchain):
|
||||
def bob(bob_test_config, accounts):
|
||||
bob = bob_test_config.produce(
|
||||
polygon_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
signer=accounts.get_account_signer(accounts.bob_account),
|
||||
)
|
||||
yield bob
|
||||
bob.disenchant()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def lonely_ursula_maker(ursula_test_config, testerchain):
|
||||
def lonely_ursula_maker(ursula_test_config, accounts):
|
||||
class _PartialUrsulaMaker:
|
||||
_partial = partial(
|
||||
make_ursulas,
|
||||
make_reserved_ursulas,
|
||||
accounts=accounts,
|
||||
ursula_config=ursula_test_config,
|
||||
know_each_other=False,
|
||||
staking_provider_addresses=testerchain.stake_providers_accounts,
|
||||
operator_addresses=testerchain.ursulas_accounts,
|
||||
)
|
||||
_made = []
|
||||
|
||||
|
@ -399,24 +412,15 @@ def fleet_of_highperf_mocked_ursulas(ursula_test_config, request, testerchain):
|
|||
except AttributeError:
|
||||
quantity = 5000 # Bigass fleet by default; that's kinda the point.
|
||||
|
||||
staking_addresses = (
|
||||
to_checksum_address("0x" + os.urandom(20).hex()) for _ in range(5000)
|
||||
)
|
||||
operator_addresses = (
|
||||
to_checksum_address("0x" + os.urandom(20).hex()) for _ in range(5000)
|
||||
)
|
||||
|
||||
with GlobalLoggerSettings.pause_all_logging_while():
|
||||
with contextlib.ExitStack() as stack:
|
||||
for mock in mocks:
|
||||
stack.enter_context(mock)
|
||||
|
||||
_ursulas = make_ursulas(
|
||||
_ursulas = make_random_ursulas(
|
||||
ursula_config=ursula_test_config,
|
||||
quantity=quantity,
|
||||
know_each_other=False,
|
||||
staking_provider_addresses=staking_addresses,
|
||||
operator_addresses=operator_addresses,
|
||||
)
|
||||
all_ursulas = {u.checksum_address: u for u in _ursulas}
|
||||
|
||||
|
@ -439,13 +443,13 @@ def fleet_of_highperf_mocked_ursulas(ursula_test_config, request, testerchain):
|
|||
def highperf_mocked_alice(
|
||||
fleet_of_highperf_mocked_ursulas,
|
||||
monkeymodule,
|
||||
testerchain,
|
||||
accounts,
|
||||
):
|
||||
config = AliceConfiguration(
|
||||
dev_mode=True,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
eth_endpoint=TEST_ETH_PROVIDER_URI,
|
||||
checksum_address=testerchain.alice_account,
|
||||
checksum_address=accounts.alice_account,
|
||||
network_middleware=MockRestMiddlewareForLargeFleetTests(
|
||||
eth_endpoint=TEST_ETH_PROVIDER_URI
|
||||
),
|
||||
|
@ -691,16 +695,15 @@ def clock():
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def ursulas(testerchain, ursula_test_config, staking_providers):
|
||||
def ursulas(accounts, ursula_test_config, staking_providers):
|
||||
if MOCK_KNOWN_URSULAS_CACHE:
|
||||
# TODO: Is this a safe assumption / test behaviour?
|
||||
# raise RuntimeError("Ursulas cache was unclear at fixture loading time. Did you use one of the ursula maker functions without cleaning up?")
|
||||
MOCK_KNOWN_URSULAS_CACHE.clear()
|
||||
|
||||
_ursulas = make_ursulas(
|
||||
_ursulas = make_reserved_ursulas(
|
||||
accounts=accounts,
|
||||
ursula_config=ursula_test_config,
|
||||
staking_provider_addresses=testerchain.stake_providers_accounts,
|
||||
operator_addresses=testerchain.ursulas_accounts,
|
||||
know_each_other=True,
|
||||
)
|
||||
for u in _ursulas:
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
||||
|
||||
@pytest.mark.skip(
|
||||
"This test need to be refactored to use some other transaction than deployment"
|
||||
)
|
||||
def test_block_confirmations(testerchain, test_registry, mocker):
|
||||
origin = testerchain.etherbase_account
|
||||
def test_block_confirmations(testerchain, test_registry, mocker, accounts):
|
||||
origin = accounts.etherbase_account
|
||||
transacting_power = TransactingPower(
|
||||
account=origin, signer=Web3Signer(testerchain.client)
|
||||
account=origin, signer=accounts.get_account_signer(origin)
|
||||
)
|
||||
|
||||
# Mocks and test adjustments
|
||||
|
|
|
@ -11,7 +11,7 @@ from web3.datastructures import AttributeDict
|
|||
|
||||
from nucypher.blockchain.eth.agents import CoordinatorAgent
|
||||
from nucypher.blockchain.eth.models import Coordinator
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.signers.software import InMemorySigner
|
||||
from nucypher.characters.lawful import Enrico, Ursula
|
||||
from nucypher.crypto.powers import RitualisticPower
|
||||
from nucypher.policy.conditions.lingo import ConditionLingo, ConditionType
|
||||
|
@ -123,7 +123,6 @@ def execute_round_2(ritual_id: int, cohort: List[Ursula]):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
@pytest.mark.parametrize("dkg_size, ritual_id, variant", PARAMS)
|
||||
@pytest_twisted.inlineCallbacks()
|
||||
def test_ursula_ritualist(
|
||||
|
@ -212,8 +211,7 @@ def test_ursula_ritualist(
|
|||
plaintext = PLAINTEXT.encode()
|
||||
|
||||
# create Enrico
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
enrico = Enrico(encrypting_key=encrypting_key, signer=signer)
|
||||
enrico = Enrico(encrypting_key=encrypting_key, signer=InMemorySigner())
|
||||
|
||||
# encrypt
|
||||
print(f"encrypting for DKG with key {bytes(encrypting_key).hex()}")
|
||||
|
|
|
@ -8,10 +8,7 @@ from twisted.internet.task import Clock
|
|||
|
||||
from nucypher.characters.lawful import Bob, Enrico
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN_NAME
|
||||
from tests.constants import (
|
||||
MOCK_ETH_PROVIDER_URI,
|
||||
NUMBER_OF_URSULAS_IN_DEVELOPMENT_DOMAIN,
|
||||
)
|
||||
from tests.constants import MOCK_ETH_PROVIDER_URI
|
||||
from tests.utils.middleware import MockRestMiddleware
|
||||
|
||||
|
||||
|
@ -38,7 +35,7 @@ def test_bob_full_retrieve_flow(
|
|||
assert b"Welcome to flippering number 0." == delivered_cleartexts[0]
|
||||
|
||||
|
||||
def test_bob_retrieves(alice, ursulas):
|
||||
def test_bob_retrieves(accounts, alice, ursulas):
|
||||
"""A test to show that Bob can retrieve data from Ursula"""
|
||||
|
||||
# Let's partition Ursulas in two parts
|
||||
|
@ -60,7 +57,7 @@ def test_bob_retrieves(alice, ursulas):
|
|||
|
||||
# Alice creates a policy granting access to Bob
|
||||
# Just for fun, let's assume she distributes KFrags among Ursulas unknown to Bob
|
||||
shares = NUMBER_OF_URSULAS_IN_DEVELOPMENT_DOMAIN - 2
|
||||
shares = accounts.NUMBER_OF_URSULAS_IN_TESTS - 2
|
||||
label = b'label://' + os.urandom(32)
|
||||
contract_end_datetime = maya.now() + datetime.timedelta(days=5)
|
||||
policy = alice.grant(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth import domains
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.signers.software import InMemorySigner
|
||||
from nucypher.characters.chaotic import (
|
||||
NiceGuyEddie,
|
||||
ThisBobAlwaysDecrypts,
|
||||
|
@ -19,8 +19,7 @@ from tests.constants import (
|
|||
def _attempt_decryption(BobClass, plaintext, testerchain):
|
||||
trinket = 80 # Doens't matter.
|
||||
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
enrico = NiceGuyEddie(encrypting_key=trinket, signer=signer)
|
||||
enrico = NiceGuyEddie(encrypting_key=trinket, signer=InMemorySigner())
|
||||
bob = BobClass(
|
||||
registry=MOCK_REGISTRY_FILEPATH,
|
||||
domain=domains.LYNX,
|
||||
|
@ -50,14 +49,12 @@ def _attempt_decryption(BobClass, plaintext, testerchain):
|
|||
return decrypted_cleartext
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
def test_user_controls_success(testerchain):
|
||||
plaintext = b"ever thus to deadbeats"
|
||||
result = _attempt_decryption(ThisBobAlwaysDecrypts, plaintext, testerchain)
|
||||
assert bytes(result) == bytes(plaintext)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
def test_user_controls_failure(testerchain):
|
||||
plaintext = b"ever thus to deadbeats"
|
||||
with pytest.raises(Ursula.NotEnoughUrsulas):
|
||||
|
|
|
@ -5,10 +5,7 @@ import pytest
|
|||
from eth_utils import is_checksum_address
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.signers import KeystoreSigner
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.cli.actions.select import select_client_account
|
||||
from nucypher.cli.literature import GENERIC_SELECT_ACCOUNT, NO_ACCOUNTS
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN_NAME
|
||||
|
@ -21,14 +18,17 @@ from tests.constants import (
|
|||
|
||||
@pytest.mark.parametrize("selection", range(NUMBER_OF_ETH_TEST_ACCOUNTS))
|
||||
def test_select_client_account(
|
||||
mock_stdin, test_emitter, testerchain, selection, capsys
|
||||
mock_stdin, test_emitter, testerchain, accounts, selection, capsys, mocker
|
||||
):
|
||||
"""Fine-grained assertions about the return value of interactive client account selection"""
|
||||
signer = mocker.Mock()
|
||||
signer.accounts = accounts.accounts_addresses
|
||||
|
||||
mock_stdin.line(str(selection))
|
||||
expected_account = testerchain.client.accounts[selection]
|
||||
expected_account = accounts.accounts_addresses[selection]
|
||||
selected_account = select_client_account(
|
||||
emitter=test_emitter,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
signer=signer,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
)
|
||||
|
@ -50,11 +50,13 @@ def test_select_client_account_with_no_accounts(
|
|||
testerchain,
|
||||
capsys,
|
||||
):
|
||||
mocker.patch.object(EthereumClient, "accounts", return_value=[])
|
||||
signer = mocker.Mock()
|
||||
signer.accounts = []
|
||||
|
||||
with pytest.raises(click.Abort):
|
||||
select_client_account(
|
||||
emitter=test_emitter,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
signer=signer,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
)
|
||||
|
@ -81,11 +83,13 @@ def test_select_client_account_ambiguous_source(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("selection", range(NUMBER_OF_ETH_TEST_ACCOUNTS))
|
||||
@pytest.mark.usefixtures("mock_registry_sources")
|
||||
def test_select_client_account_valid_sources(
|
||||
mocker,
|
||||
mock_stdin,
|
||||
test_emitter,
|
||||
testerchain,
|
||||
accounts,
|
||||
patch_keystore,
|
||||
mock_accounts,
|
||||
selection,
|
||||
|
@ -93,15 +97,19 @@ def test_select_client_account_valid_sources(
|
|||
):
|
||||
# From External Signer
|
||||
mock_stdin.line(str(selection))
|
||||
|
||||
signer = mocker.Mock()
|
||||
signer.accounts = accounts.accounts_addresses
|
||||
|
||||
mock_signer = mocker.patch.object(
|
||||
KeystoreSigner, "from_signer_uri", return_value=Web3Signer(testerchain.client)
|
||||
KeystoreSigner, "from_signer_uri", return_value=signer
|
||||
)
|
||||
selected_account = select_client_account(
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
emitter=test_emitter,
|
||||
signer_uri=MOCK_SIGNER_URI,
|
||||
)
|
||||
expected_account = testerchain.client.accounts[selection]
|
||||
expected_account = accounts.accounts_addresses[selection]
|
||||
assert selected_account == expected_account
|
||||
mock_signer.assert_called_once_with(uri=MOCK_SIGNER_URI, testnet=True)
|
||||
assert mock_stdin.empty()
|
||||
|
@ -113,49 +121,11 @@ def test_select_client_account_valid_sources(
|
|||
|
||||
# From Wallet
|
||||
mock_stdin.line(str(selection))
|
||||
expected_account = testerchain.client.accounts[selection]
|
||||
expected_account = accounts.accounts_addresses[selection]
|
||||
selected_account = select_client_account(
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
emitter=test_emitter,
|
||||
signer=Web3Signer(testerchain.client),
|
||||
)
|
||||
assert selected_account == expected_account
|
||||
assert mock_stdin.empty()
|
||||
captured = capsys.readouterr()
|
||||
assert (
|
||||
GENERIC_SELECT_ACCOUNT in captured.out
|
||||
and f"Selected {selection}" in captured.out
|
||||
)
|
||||
|
||||
# From pre-initialized Provider
|
||||
mock_stdin.line(str(selection))
|
||||
expected_account = testerchain.client.accounts[selection]
|
||||
selected_account = select_client_account(
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
emitter=test_emitter,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
)
|
||||
assert selected_account == expected_account
|
||||
assert mock_stdin.empty()
|
||||
captured = capsys.readouterr()
|
||||
assert (
|
||||
GENERIC_SELECT_ACCOUNT in captured.out
|
||||
and f"Selected {selection}" in captured.out
|
||||
)
|
||||
|
||||
# From uninitialized Provider
|
||||
mock_stdin.line(str(selection))
|
||||
mocker.patch.object(
|
||||
BlockchainInterfaceFactory, "is_interface_initialized", return_value=False
|
||||
)
|
||||
mocker.patch.object(BlockchainInterfaceFactory, "_interfaces", return_value={})
|
||||
mocker.patch.object(
|
||||
BlockchainInterfaceFactory, "get_interface", return_value=testerchain
|
||||
)
|
||||
selected_account = select_client_account(
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
emitter=test_emitter,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
signer=signer,
|
||||
)
|
||||
assert selected_account == expected_account
|
||||
assert mock_stdin.empty()
|
||||
|
@ -183,6 +153,7 @@ def test_select_client_account_with_balance_display(
|
|||
mock_stdin,
|
||||
test_emitter,
|
||||
testerchain,
|
||||
accounts,
|
||||
capsys,
|
||||
selection,
|
||||
show_matic,
|
||||
|
@ -199,7 +170,7 @@ def test_select_client_account_with_balance_display(
|
|||
)
|
||||
|
||||
# check for accurate selection consistency with client index
|
||||
assert selected_account == testerchain.client.accounts[selection]
|
||||
assert selected_account == accounts[selection]
|
||||
assert mock_stdin.empty()
|
||||
|
||||
# Display account info
|
||||
|
@ -212,7 +183,7 @@ def test_select_client_account_with_balance_display(
|
|||
for column_name in headers:
|
||||
assert column_name in captured.out, f'"{column_name}" column was not displayed'
|
||||
|
||||
for account in testerchain.client.accounts:
|
||||
for account in accounts:
|
||||
assert account in captured.out
|
||||
|
||||
if show_matic:
|
||||
|
|
|
@ -42,7 +42,7 @@ def test_destroy_with_no_configurations(click_runner, custom_filepath):
|
|||
|
||||
|
||||
def test_corrupted_configuration(
|
||||
click_runner, custom_filepath, testerchain, test_registry, mocker
|
||||
click_runner, custom_filepath, accounts, test_registry, mocker
|
||||
):
|
||||
#
|
||||
# Setup
|
||||
|
@ -53,7 +53,13 @@ def test_corrupted_configuration(
|
|||
shutil.rmtree(custom_filepath, ignore_errors=True)
|
||||
assert not custom_filepath.exists()
|
||||
|
||||
alice, ursula, another_ursula, staking_provider, *all_yall = testerchain.unassigned_accounts
|
||||
(
|
||||
alice,
|
||||
ursula,
|
||||
another_ursula,
|
||||
staking_provider,
|
||||
*all_yall,
|
||||
) = accounts.unassigned_accounts
|
||||
|
||||
#
|
||||
# Chaos
|
||||
|
|
|
@ -87,7 +87,6 @@ def test_ursula_startup_ip_checkup(click_runner, mocker):
|
|||
|
||||
def test_ursula_run_ip_checkup(
|
||||
testerchain,
|
||||
custom_filepath,
|
||||
click_runner,
|
||||
mocker,
|
||||
ursulas,
|
||||
|
@ -110,10 +109,10 @@ def test_ursula_run_ip_checkup(
|
|||
mocker.patch.object(Ursula, 'from_teacher_uri', return_value=teacher)
|
||||
|
||||
# Mock worker qualification
|
||||
staking_provider = ursulas[1]
|
||||
worker = ursulas[1]
|
||||
|
||||
def set_staking_provider_address(operator, *args, **kwargs):
|
||||
operator.checksum_address = staking_provider.checksum_address
|
||||
operator.checksum_address = worker.checksum_address
|
||||
return True
|
||||
|
||||
monkeypatch.setattr(Operator, "block_until_ready", set_staking_provider_address)
|
||||
|
@ -121,6 +120,7 @@ def test_ursula_run_ip_checkup(
|
|||
mocker.patch("nucypher.cli.commands.ursula.migrate", return_value=None)
|
||||
|
||||
ursula_test_config.rest_host = MOCK_IP_ADDRESS
|
||||
ursula_test_config.operator_address = worker.operator_address
|
||||
mocker.patch.object(
|
||||
UrsulaConfiguration, "from_configuration_file", return_value=ursula_test_config
|
||||
)
|
||||
|
|
|
@ -12,10 +12,10 @@ def mock_ursula_run(mocker, ursulas, monkeypatch, ursula_test_config, mock_prome
|
|||
ursula_test_config.rest_host = MOCK_IP_ADDRESS
|
||||
|
||||
# Mock worker qualification
|
||||
staking_provider = ursulas[1]
|
||||
worker = ursulas[1]
|
||||
|
||||
def set_staking_provider_address(operator):
|
||||
operator.checksum_address = staking_provider.checksum_address
|
||||
operator.checksum_address = worker.checksum_address
|
||||
return True
|
||||
|
||||
monkeypatch.setattr(Operator, "block_until_ready", set_staking_provider_address)
|
||||
|
@ -23,6 +23,8 @@ def mock_ursula_run(mocker, ursulas, monkeypatch, ursula_test_config, mock_prome
|
|||
# Mock migration
|
||||
mocker.patch("nucypher.cli.commands.ursula.migrate", return_value=None)
|
||||
|
||||
ursula_test_config.operator_address = worker.operator_address
|
||||
|
||||
# Mock Ursula configuration
|
||||
mocker.patch.object(
|
||||
UrsulaConfiguration, "from_configuration_file", return_value=ursula_test_config
|
||||
|
|
|
@ -11,7 +11,7 @@ from tests.constants import FAKE_PASSWORD_CONFIRMED, MOCK_IP_ADDRESS
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def v4_config_file(tempfile_path, ursula_test_config):
|
||||
def v4_config_file(tempfile_path, ursula_test_config, accounts):
|
||||
config_dictionary = {
|
||||
"federated_only": None,
|
||||
"checksum_address": None,
|
||||
|
@ -29,7 +29,7 @@ def v4_config_file(tempfile_path, ursula_test_config):
|
|||
"signer_uri": ursula_test_config.signer_uri,
|
||||
"gas_strategy": "fast",
|
||||
"max_gas_price": None,
|
||||
"operator_address": ursula_test_config.operator_address,
|
||||
"operator_address": accounts.ursulas_accounts[0],
|
||||
"rest_host": MOCK_IP_ADDRESS,
|
||||
"rest_port": ursula_test_config.rest_port,
|
||||
"db_filepath": "/root/.local/share/nucypher/ursula.db",
|
||||
|
@ -69,10 +69,10 @@ def test_ursula_run_specified_config_file(
|
|||
mocker.patch("nucypher.cli.utils.unlock_nucypher_keystore", return_value=True)
|
||||
|
||||
# Mock worker qualification
|
||||
staking_provider = ursulas[1]
|
||||
worker = ursulas[1]
|
||||
|
||||
def set_staking_provider_address(operator, *args, **kwargs):
|
||||
operator.checksum_address = staking_provider.checksum_address
|
||||
operator.checksum_address = worker.checksum_address
|
||||
return True
|
||||
|
||||
monkeypatch.setattr(Operator, "block_until_ready", set_staking_provider_address)
|
||||
|
|
|
@ -84,7 +84,7 @@ def test_interactive_initialize_ursula(click_runner, mocker, tmpdir):
|
|||
|
||||
|
||||
def test_initialize_custom_configuration_root(
|
||||
click_runner, custom_filepath: Path, testerchain
|
||||
click_runner, custom_filepath: Path, accounts
|
||||
):
|
||||
deploy_port = select_test_port()
|
||||
# Use a custom local filepath for configuration
|
||||
|
@ -104,7 +104,7 @@ def test_initialize_custom_configuration_root(
|
|||
"--polygon-endpoint",
|
||||
MOCK_ETH_PROVIDER_URI,
|
||||
"--operator-address",
|
||||
testerchain.ursulas_accounts[0],
|
||||
accounts.ursulas_accounts[0],
|
||||
)
|
||||
result = click_runner.invoke(
|
||||
nucypher_cli, init_args, input=FAKE_PASSWORD_CONFIRMED, catch_exceptions=False
|
||||
|
|
|
@ -41,18 +41,18 @@ all_configurations = tuple(
|
|||
)
|
||||
@pytest.mark.parametrize("character,configuration", characters_and_configurations)
|
||||
def test_development_character_configurations(
|
||||
character, configuration, mocker, testerchain
|
||||
character, configuration, mocker, accounts
|
||||
):
|
||||
params = dict(
|
||||
dev_mode=True,
|
||||
lonely=True,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
checksum_address=testerchain.unassigned_accounts[0],
|
||||
checksum_address=accounts.unassigned_accounts[0],
|
||||
eth_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
)
|
||||
if character is Ursula:
|
||||
params.update(dict(operator_address=testerchain.unassigned_accounts[0]))
|
||||
params.update(dict(operator_address=accounts.unassigned_accounts[0]))
|
||||
config = configuration(**params)
|
||||
|
||||
assert config.is_me is True
|
||||
|
@ -160,11 +160,11 @@ def test_default_character_configuration_preservation(
|
|||
expected_filepath.unlink()
|
||||
|
||||
|
||||
def test_ursula_development_configuration(testerchain):
|
||||
def test_ursula_development_configuration(accounts):
|
||||
config = UrsulaConfiguration(
|
||||
dev_mode=True,
|
||||
checksum_address=testerchain.unassigned_accounts[0],
|
||||
operator_address=testerchain.unassigned_accounts[1],
|
||||
checksum_address=accounts.unassigned_accounts[0],
|
||||
operator_address=accounts.unassigned_accounts[1],
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
eth_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
|
|
|
@ -10,7 +10,7 @@ from tests.constants import INSECURE_DEVELOPMENT_PASSWORD, MOCK_ETH_PROVIDER_URI
|
|||
from tests.utils.middleware import MockRestMiddleware
|
||||
|
||||
|
||||
def test_alices_powers_are_persistent(ursulas, temp_dir_path, testerchain):
|
||||
def test_alices_powers_are_persistent(ursulas, temp_dir_path, accounts):
|
||||
# Create a non-learning AliceConfiguration
|
||||
config_root = temp_dir_path / 'nucypher-custom-alice-config'
|
||||
alice_config = AliceConfiguration(
|
||||
|
@ -18,7 +18,7 @@ def test_alices_powers_are_persistent(ursulas, temp_dir_path, testerchain):
|
|||
config_root=config_root,
|
||||
network_middleware=MockRestMiddleware(eth_endpoint=MOCK_ETH_PROVIDER_URI),
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
checksum_address=testerchain.alice_account,
|
||||
checksum_address=accounts.alice_account,
|
||||
start_learning_now=False,
|
||||
save_metadata=False,
|
||||
reload_metadata=False,
|
||||
|
|
|
@ -10,7 +10,7 @@ from nucypher_core import (
|
|||
)
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.signers.software import InMemorySigner
|
||||
from nucypher.characters.lawful import Alice, Bob, Enrico, Ursula
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN_NAME
|
||||
from nucypher.crypto.ferveo.dkg import FerveoVariant
|
||||
|
@ -66,7 +66,7 @@ def test_generate_alice_keystore(temp_dir_path):
|
|||
|
||||
|
||||
@pytest.mark.usefixtures("mock_registry_sources")
|
||||
def test_characters_use_keystore(temp_dir_path, testerchain):
|
||||
def test_characters_use_keystore(temp_dir_path, testerchain, accounts):
|
||||
keystore = Keystore.generate(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
keystore_dir=temp_dir_path
|
||||
|
@ -83,7 +83,7 @@ def test_characters_use_keystore(temp_dir_path, testerchain):
|
|||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
eth_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
checksum_address=testerchain.alice_account,
|
||||
checksum_address=accounts.alice_account,
|
||||
pre_payment_method=pre_payment_method,
|
||||
)
|
||||
Bob(
|
||||
|
@ -92,6 +92,7 @@ def test_characters_use_keystore(temp_dir_path, testerchain):
|
|||
keystore=keystore,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
)
|
||||
ursula_account = accounts.ursulas_accounts[0]
|
||||
Ursula(
|
||||
eth_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
|
@ -101,8 +102,8 @@ def test_characters_use_keystore(temp_dir_path, testerchain):
|
|||
rest_port=12345,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
pre_payment_method=pre_payment_method,
|
||||
operator_address=testerchain.ursulas_accounts[0],
|
||||
signer=Web3Signer(testerchain.client),
|
||||
operator_address=ursula_account,
|
||||
signer=accounts.get_account_signer(ursula_account),
|
||||
condition_blockchain_endpoints={TESTERCHAIN_CHAIN_ID: MOCK_ETH_PROVIDER_URI},
|
||||
)
|
||||
alice.disenchant() # To stop Alice's publication threadpool. TODO: Maybe only start it at first enactment?
|
||||
|
@ -153,8 +154,7 @@ def test_tls_hosting_certificate_remains_the_same(temp_dir_path, mocker):
|
|||
recreated_ursula.disenchant()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_sign_message")
|
||||
def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
||||
def test_ritualist(temp_dir_path, testerchain, accounts, dkg_public_key):
|
||||
keystore = Keystore.generate(
|
||||
password=INSECURE_DEVELOPMENT_PASSWORD, keystore_dir=temp_dir_path
|
||||
)
|
||||
|
@ -164,6 +164,7 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
|||
blockchain_endpoint=MOCK_ETH_PROVIDER_URI, domain=TEMPORARY_DOMAIN_NAME
|
||||
)
|
||||
|
||||
ursula_account = accounts.ursulas_accounts[0]
|
||||
ursula = Ursula(
|
||||
start_learning_now=False,
|
||||
keystore=keystore,
|
||||
|
@ -171,8 +172,8 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
|||
rest_port=12345,
|
||||
domain=TEMPORARY_DOMAIN_NAME,
|
||||
pre_payment_method=pre_payment_method,
|
||||
operator_address=testerchain.ursulas_accounts[0],
|
||||
signer=Web3Signer(testerchain.client),
|
||||
operator_address=ursula_account,
|
||||
signer=accounts.get_account_signer(ursula_account),
|
||||
eth_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
polygon_endpoint=MOCK_ETH_PROVIDER_URI,
|
||||
condition_blockchain_endpoints={TESTERCHAIN_CHAIN_ID: MOCK_ETH_PROVIDER_URI},
|
||||
|
@ -192,8 +193,7 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
|||
}
|
||||
|
||||
# create enrico
|
||||
signer = Web3Signer(client=testerchain.client)
|
||||
enrico = Enrico(encrypting_key=dkg_public_key, signer=signer)
|
||||
enrico = Enrico(encrypting_key=dkg_public_key, signer=InMemorySigner())
|
||||
|
||||
# encrypt
|
||||
threshold_message_kit = enrico.encrypt_for_dkg(
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Optional
|
||||
|
||||
|
@ -22,7 +21,6 @@ from nucypher.blockchain.eth.registry import (
|
|||
ContractRegistry,
|
||||
)
|
||||
from nucypher.blockchain.eth.signers import KeystoreSigner
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.cli.types import ChecksumAddress
|
||||
from nucypher.config.characters import UrsulaConfiguration
|
||||
|
@ -49,13 +47,13 @@ def pytest_addhooks(pluginmanager):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def mock_sample_reservoir(testerchain, mock_contract_agency):
|
||||
def mock_sample_reservoir(accounts, mock_contract_agency):
|
||||
def mock_reservoir(
|
||||
without: Optional[Iterable[ChecksumAddress]] = None, *args, **kwargs
|
||||
):
|
||||
addresses = {
|
||||
address: 1
|
||||
for address in testerchain.stake_providers_accounts
|
||||
for address in accounts.staking_providers_accounts
|
||||
if address not in without
|
||||
}
|
||||
return StakingProvidersReservoir(addresses)
|
||||
|
@ -64,14 +62,6 @@ def mock_sample_reservoir(testerchain, mock_contract_agency):
|
|||
mock_agent.get_staking_provider_reservoir = mock_reservoir
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def mock_sign_message(mocker):
|
||||
mocked_sign_message = mocker.patch.object(
|
||||
Web3Signer, "sign_message", return_value=os.urandom(32)
|
||||
)
|
||||
return mocked_sign_message
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def mock_taco_application_agent(testerchain, mock_contract_agency):
|
||||
mock_agent = mock_contract_agency.get_agent(TACoApplicationAgent)
|
||||
|
@ -166,13 +156,13 @@ def agency(mock_contract_agency):
|
|||
|
||||
@pytest.fixture(scope="function")
|
||||
def mock_funding_and_bonding(
|
||||
testerchain, mocker, mock_taco_application_agent, mock_taco_child_application_agent
|
||||
accounts, mocker, mock_taco_application_agent, mock_taco_child_application_agent
|
||||
):
|
||||
# funding
|
||||
mocker.patch.object(EthereumClient, "get_balance", return_value=1)
|
||||
|
||||
# bonding
|
||||
staking_provider = testerchain.stake_providers_accounts[0]
|
||||
staking_provider = accounts.staking_providers_accounts[0]
|
||||
mock_taco_application_agent.get_staking_provider_from_operator.return_value = (
|
||||
staking_provider
|
||||
)
|
||||
|
@ -262,12 +252,14 @@ def real_operator_get_staking_provider_address():
|
|||
|
||||
@pytest.mark.usefixtures("monkeymodule")
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def staking_providers(real_operator_get_staking_provider_address, testerchain):
|
||||
def staking_providers(real_operator_get_staking_provider_address, accounts):
|
||||
def faked(self, *args, **kwargs):
|
||||
return testerchain.stake_providers_accounts[testerchain.ursulas_accounts.index(self.transacting_power.account)]
|
||||
return accounts.staking_providers_accounts[
|
||||
accounts.ursulas_accounts.index(self.transacting_power.account)
|
||||
]
|
||||
|
||||
Operator.get_staking_provider_address = faked
|
||||
return testerchain.stake_providers_accounts
|
||||
return accounts.staking_providers_accounts
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
|
|
@ -7,19 +7,18 @@ import requests
|
|||
from cryptography.hazmat.primitives import serialization
|
||||
from twisted.internet import threads
|
||||
|
||||
from tests.utils.ursula import make_ursulas
|
||||
from tests.utils.ursula import make_reserved_ursulas
|
||||
|
||||
|
||||
@pytest_twisted.inlineCallbacks
|
||||
def test_ursula_serves_statics(ursula_test_config, testerchain):
|
||||
def test_ursula_serves_statics(ursula_test_config, accounts):
|
||||
with tempfile.TemporaryDirectory() as STATICS_DIR:
|
||||
os.environ['NUCYPHER_STATIC_FILES_ROOT'] = str(STATICS_DIR)
|
||||
|
||||
node = make_ursulas(
|
||||
node = make_reserved_ursulas(
|
||||
accounts=accounts,
|
||||
ursula_config=ursula_test_config,
|
||||
quantity=1,
|
||||
staking_provider_addresses=testerchain.stake_providers_accounts,
|
||||
operator_addresses=testerchain.ursulas_accounts,
|
||||
).pop()
|
||||
node_deployer = node.get_deployer()
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Optional, Union
|
|||
from atxm.tx import FutureTx
|
||||
from hexbytes import HexBytes
|
||||
|
||||
from nucypher.blockchain.eth.clients import EthereumTesterClient
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
from tests.constants import MOCK_ETH_PROVIDER_URI, TESTERCHAIN_CHAIN_ID
|
||||
from tests.utils.blockchain import TesterBlockchain
|
||||
|
@ -62,10 +62,11 @@ class MockBlockchain(TesterBlockchain):
|
|||
|
||||
return future_tx
|
||||
|
||||
class MockEthereumClient(EthereumTesterClient):
|
||||
|
||||
class MockEthereumClient(EthereumClient):
|
||||
|
||||
def __init__(self, w3):
|
||||
super().__init__(w3=w3, node_technology=None, version=None, platform=None, backend=None)
|
||||
super().__init__(w3=w3)
|
||||
|
||||
def add_middleware(self, middleware):
|
||||
pass
|
||||
|
|
|
@ -8,7 +8,7 @@ from nucypher.blockchain.eth.registry import ContractRegistry
|
|||
from nucypher.crypto.ferveo import dkg
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.network.nodes import Teacher
|
||||
from tests.constants import TEMPORARY_DOMAIN
|
||||
from tests.constants import MOCK_ETH_PROVIDER_URI, TEMPORARY_DOMAIN
|
||||
from tests.mock.interfaces import MockBlockchain, MockEthereumClient
|
||||
from tests.utils.registry import MockRegistrySource, mock_registry_sources
|
||||
|
||||
|
@ -28,6 +28,8 @@ def test_registry(module_mocker):
|
|||
@pytest.fixture(scope='function')
|
||||
def mock_ethereum_client(mocker):
|
||||
web3_mock = mocker.Mock()
|
||||
web3_mock.provider = mocker.Mock()
|
||||
web3_mock.provider.endpoint_uri = MOCK_ETH_PROVIDER_URI
|
||||
mock_client = MockEthereumClient(w3=web3_mock)
|
||||
return mock_client
|
||||
|
||||
|
@ -74,14 +76,14 @@ def testerchain(mock_testerchain, module_mocker, clock) -> MockBlockchain:
|
|||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def staking_providers(testerchain, test_registry, monkeymodule):
|
||||
def staking_providers(accounts, test_registry, monkeymodule):
|
||||
def faked(self, *args, **kwargs):
|
||||
return testerchain.stake_providers_accounts[
|
||||
testerchain.ursulas_accounts.index(self.transacting_power.account)
|
||||
return accounts.staking_providers_accounts[
|
||||
accounts.ursulas_accounts.index(self.transacting_power.account)
|
||||
]
|
||||
|
||||
Operator.get_staking_provider_address = faked
|
||||
return testerchain.stake_providers_accounts
|
||||
return accounts.staking_providers_accounts
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
|
|
|
@ -10,9 +10,7 @@ CHAIN_ID = 23
|
|||
@pytest.mark.parametrize("chain_id_return_value", [hex(CHAIN_ID), CHAIN_ID])
|
||||
def test_cached_chain_id(mocker, chain_id_return_value):
|
||||
web3_mock = mocker.MagicMock()
|
||||
mock_client = EthereumClient(
|
||||
w3=web3_mock, node_technology=None, version=None, platform=None, backend=None
|
||||
)
|
||||
mock_client = EthereumClient(w3=web3_mock)
|
||||
|
||||
chain_id_property_mock = PropertyMock(return_value=chain_id_return_value)
|
||||
type(web3_mock.eth).chain_id = chain_id_property_mock
|
||||
|
@ -24,9 +22,7 @@ def test_cached_chain_id(mocker, chain_id_return_value):
|
|||
chain_id_property_mock.assert_called_once(), "not called again since cached"
|
||||
|
||||
# second instance of client, but uses the same w3 mock
|
||||
mock_client_2 = EthereumClient(
|
||||
w3=web3_mock, node_technology=None, version=None, platform=None, backend=None
|
||||
)
|
||||
mock_client_2 = EthereumClient(w3=web3_mock)
|
||||
assert mock_client_2.chain_id == CHAIN_ID
|
||||
assert (
|
||||
chain_id_property_mock.call_count == 2
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
from tests.constants import NUMBER_OF_ETH_TEST_ACCOUNTS
|
||||
from tests.utils.blockchain import ReservedTestAccountManager
|
||||
|
||||
|
||||
def test_account_organization():
|
||||
account_manager = ReservedTestAccountManager()
|
||||
|
||||
account_addresses = account_manager.accounts_addresses
|
||||
assert (
|
||||
len(set(account_addresses)) == NUMBER_OF_ETH_TEST_ACCOUNTS
|
||||
), "all unique addresses"
|
||||
|
||||
# special accounts
|
||||
assert account_manager.etherbase_account == account_addresses[0]
|
||||
assert account_manager.alice_account == account_addresses[1]
|
||||
assert account_manager.bob_account == account_addresses[2]
|
||||
|
||||
# staking provider addresses
|
||||
staking_providers = account_manager.staking_providers_accounts
|
||||
assert staking_providers == account_addresses[3:13]
|
||||
for i in range(len(staking_providers)):
|
||||
assert account_manager.staking_provider_account(i) == staking_providers[i]
|
||||
|
||||
# ursula addresses
|
||||
ursulas = account_manager.ursulas_accounts
|
||||
assert ursulas == account_addresses[13:23]
|
||||
for i in range(len(ursulas)):
|
||||
assert account_manager.ursula_account(i) == ursulas[i]
|
||||
|
||||
# unassigned addresses
|
||||
assert account_manager.unassigned_accounts == account_addresses[23:30]
|
|
@ -3,8 +3,7 @@ from atxm.exceptions import Fault, InsufficientFunds
|
|||
|
||||
from nucypher.blockchain.eth.agents import CoordinatorAgent
|
||||
from nucypher.blockchain.eth.models import PHASE1, PHASE2, Coordinator
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import RitualisticPower, TransactingPower
|
||||
from nucypher.crypto.powers import RitualisticPower
|
||||
from nucypher.types import PhaseId
|
||||
from tests.constants import MOCK_ETH_PROVIDER_URI
|
||||
from tests.mock.coordinator import MockCoordinatorAgent
|
||||
|
@ -39,10 +38,8 @@ def cohort(ursulas):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def transacting_power(testerchain, alice):
|
||||
return TransactingPower(
|
||||
account=alice.transacting_power.account, signer=Web3Signer(testerchain.client)
|
||||
)
|
||||
def transacting_power(alice):
|
||||
return alice.transacting_power
|
||||
|
||||
|
||||
def test_initiate_ritual(
|
||||
|
|
|
@ -1,64 +1,54 @@
|
|||
|
||||
|
||||
import datetime
|
||||
from unittest.mock import Mock, PropertyMock
|
||||
|
||||
import pytest
|
||||
from web3 import HTTPProvider, IPCProvider, WebsocketProvider
|
||||
from web3 import HTTPProvider
|
||||
|
||||
from nucypher.blockchain.eth.clients import (
|
||||
AlchemyClient,
|
||||
GanacheClient,
|
||||
GethClient,
|
||||
InfuraClient,
|
||||
ParityClient,
|
||||
)
|
||||
from nucypher.blockchain.eth.clients import EthereumClient
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
from nucypher.utilities.networking import LOOPBACK_ADDRESS
|
||||
|
||||
DEFAULT_GAS_PRICE = 42
|
||||
GAS_PRICE_FROM_STRATEGY = 1234
|
||||
CHAIN_ID = 11155111 # pretend to be sepolia
|
||||
|
||||
#
|
||||
# Mock Providers
|
||||
#
|
||||
|
||||
@pytest.mark.parametrize("chain_id_return_value", [hex(CHAIN_ID), CHAIN_ID])
|
||||
def test_cached_chain_id(mocker, chain_id_return_value):
|
||||
web3_mock = mocker.MagicMock()
|
||||
mock_client = EthereumClient(w3=web3_mock)
|
||||
|
||||
chain_id_property_mock = PropertyMock(return_value=chain_id_return_value)
|
||||
type(web3_mock.eth).chain_id = chain_id_property_mock
|
||||
|
||||
assert mock_client.chain_id == CHAIN_ID
|
||||
chain_id_property_mock.assert_called_once()
|
||||
|
||||
assert mock_client.chain_id == CHAIN_ID
|
||||
chain_id_property_mock.assert_called_once(), "not called again since cached"
|
||||
|
||||
# second instance of client, but uses the same w3 mock
|
||||
mock_client_2 = EthereumClient(
|
||||
w3=web3_mock,
|
||||
)
|
||||
assert mock_client_2.chain_id == CHAIN_ID
|
||||
assert (
|
||||
chain_id_property_mock.call_count == 2
|
||||
), "additional call since different client instance"
|
||||
|
||||
assert mock_client_2.chain_id == CHAIN_ID
|
||||
assert chain_id_property_mock.call_count == 2, "not called again since cached"
|
||||
|
||||
|
||||
class MockGethProvider:
|
||||
endpoint_uri = 'file:///ipc.geth'
|
||||
client_version = 'Geth/v1.4.11-stable-fed692f6/darwin/go1.7'
|
||||
|
||||
|
||||
class MockParityProvider:
|
||||
endpoint_uri = 'file:///ipc.parity'
|
||||
client_version = 'Parity-Ethereum/v2.5.1-beta-e0141f8-20190510/x86_64-linux-gnu/rustc1.34.1'
|
||||
|
||||
|
||||
class MockGanacheProvider:
|
||||
endpoint_uri = 'http://ganache:8445'
|
||||
client_version = 'EthereumJS TestRPC/v2.1.5/ethereum-js'
|
||||
|
||||
|
||||
class MockInfuraProvider:
|
||||
endpoint_uri = "wss://:@sepolia.infura.io/ws/v3/1234567890987654321abcdef"
|
||||
client_version = "Geth/v1.8.23-omnibus-2ad89aaa/linux-amd64/go1.11.1"
|
||||
|
||||
|
||||
class MockAlchemyProvider:
|
||||
endpoint_uri = 'https://eth-rinkeby.alchemyapi.io/v2/1234567890987654321abcdef'
|
||||
client_version = 'Geth/v1.9.20-stable-979fc968/linux-amd64/go1.15'
|
||||
|
||||
|
||||
class MockWebSocketProvider:
|
||||
endpoint_uri = f'ws://{LOOPBACK_ADDRESS}:8546'
|
||||
client_version = 'Geth/v1.8.23-omnibus-2ad89aaa/linux-amd64/go1.11.1'
|
||||
endpoint_uri = "http://192.168.9.0:8545"
|
||||
client_version = "Geth/v1.4.11-stable-fed692f6/darwin/go1.7"
|
||||
|
||||
|
||||
class SyncedMockW3Eth:
|
||||
|
||||
# Support older and newer versions of web3 py in-test
|
||||
version = 5
|
||||
chain_id = hex(5)
|
||||
chain_id = hex(CHAIN_ID)
|
||||
block_number = 5
|
||||
|
||||
def getBlock(self, blockNumber):
|
||||
|
@ -67,32 +57,6 @@ class SyncedMockW3Eth:
|
|||
}
|
||||
|
||||
|
||||
class MockedW3GethWithPeers:
|
||||
|
||||
@property
|
||||
def admin(self):
|
||||
|
||||
class GethAdmin:
|
||||
|
||||
def peers(self):
|
||||
return [1, 2, 3]
|
||||
|
||||
return GethAdmin()
|
||||
|
||||
|
||||
class MockedW3GethWithNoPeers:
|
||||
|
||||
@property
|
||||
def admin(self):
|
||||
|
||||
class GethAdmin:
|
||||
|
||||
def peers(self):
|
||||
return []
|
||||
|
||||
return GethAdmin()
|
||||
|
||||
|
||||
#
|
||||
# Mock Web3
|
||||
#
|
||||
|
@ -101,7 +65,6 @@ class SyncedMockWeb3:
|
|||
|
||||
net = SyncedMockW3Eth()
|
||||
eth = SyncedMockW3Eth()
|
||||
geth = MockedW3GethWithPeers()
|
||||
|
||||
def __init__(self, provider):
|
||||
self.provider = provider
|
||||
|
@ -153,36 +116,12 @@ class ProviderTypeTestClient(BlockchainInterfaceTestBase):
|
|||
super()._attach_blockchain_provider(provider=self.test_provider_to_attach)
|
||||
|
||||
|
||||
class InfuraTestClient(BlockchainInterfaceTestBase):
|
||||
|
||||
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
||||
super()._attach_blockchain_provider(provider=MockInfuraProvider())
|
||||
|
||||
|
||||
class AlchemyTestClient(BlockchainInterfaceTestBase):
|
||||
|
||||
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
||||
super()._attach_blockchain_provider(provider=MockAlchemyProvider())
|
||||
|
||||
|
||||
class GethClientTestBlockchain(BlockchainInterfaceTestBase):
|
||||
|
||||
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
||||
super()._attach_blockchain_provider(provider=MockGethProvider())
|
||||
|
||||
|
||||
class ParityClientTestInterface(BlockchainInterfaceTestBase):
|
||||
|
||||
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
||||
super()._attach_blockchain_provider(provider=MockParityProvider())
|
||||
|
||||
|
||||
class GanacheClientTestInterface(BlockchainInterfaceTestBase):
|
||||
|
||||
def _attach_blockchain_provider(self, *args, **kwargs) -> None:
|
||||
super()._attach_blockchain_provider(provider=MockGanacheProvider())
|
||||
|
||||
|
||||
def test_client_no_provider():
|
||||
with pytest.raises(BlockchainInterface.NoProvider):
|
||||
interface = BlockchainInterfaceTestBase()
|
||||
|
@ -190,138 +129,30 @@ def test_client_no_provider():
|
|||
|
||||
|
||||
def test_geth_web3_client():
|
||||
interface = GethClientTestBlockchain(endpoint="file:///ipc.geth")
|
||||
interface = GethClientTestBlockchain(endpoint="https://my.geth:8545")
|
||||
interface.connect()
|
||||
|
||||
assert isinstance(interface.client, GethClient)
|
||||
assert interface.client.node_technology == 'Geth'
|
||||
assert interface.client.node_version == 'v1.4.11-stable-fed692f6'
|
||||
assert interface.client.platform == 'darwin'
|
||||
assert interface.client.backend == 'go1.7'
|
||||
|
||||
assert interface.client.is_local is False
|
||||
assert interface.client.chain_id == 5 # Hardcoded above
|
||||
|
||||
|
||||
def test_autodetect_provider_type_file(tempfile_path):
|
||||
interface = ProviderTypeTestClient(
|
||||
endpoint=str(tempfile_path), # existing file for test
|
||||
expected_provider_class=IPCProvider,
|
||||
actual_provider_to_attach=MockGethProvider(),
|
||||
)
|
||||
interface.connect()
|
||||
assert isinstance(interface.client, GethClient)
|
||||
|
||||
|
||||
def test_autodetect_provider_type_file_none_existent():
|
||||
with pytest.raises(BlockchainInterface.UnsupportedProvider):
|
||||
interface = BlockchainInterfaceTestBase(endpoint="/none_existent.ipc.geth")
|
||||
interface.connect()
|
||||
|
||||
|
||||
def test_detect_provider_type_file():
|
||||
interface = ProviderTypeTestClient(
|
||||
endpoint="file:///ipc.geth",
|
||||
expected_provider_class=IPCProvider,
|
||||
actual_provider_to_attach=MockGethProvider(),
|
||||
)
|
||||
interface.connect()
|
||||
assert isinstance(interface.client, GethClient)
|
||||
|
||||
|
||||
def test_detect_provider_type_ipc():
|
||||
interface = ProviderTypeTestClient(
|
||||
endpoint="ipc:///ipc.geth",
|
||||
expected_provider_class=IPCProvider,
|
||||
actual_provider_to_attach=MockGethProvider(),
|
||||
)
|
||||
interface.connect()
|
||||
assert isinstance(interface.client, GethClient)
|
||||
assert isinstance(interface.client, EthereumClient)
|
||||
assert interface.client.chain_id == CHAIN_ID # Hardcoded above
|
||||
|
||||
|
||||
def test_detect_provider_type_http():
|
||||
interface = ProviderTypeTestClient(
|
||||
endpoint="http://ganache:8445",
|
||||
endpoint="http://mynode.com:8445",
|
||||
expected_provider_class=HTTPProvider,
|
||||
actual_provider_to_attach=MockGanacheProvider(),
|
||||
actual_provider_to_attach=MockGethProvider(),
|
||||
)
|
||||
interface.connect()
|
||||
assert isinstance(interface.client, GanacheClient)
|
||||
assert isinstance(interface.client, EthereumClient)
|
||||
|
||||
|
||||
def test_detect_provider_type_https():
|
||||
interface = ProviderTypeTestClient(
|
||||
endpoint="https://ganache:8445",
|
||||
endpoint="https://public-node.io:8445",
|
||||
expected_provider_class=HTTPProvider,
|
||||
actual_provider_to_attach=MockGanacheProvider(),
|
||||
actual_provider_to_attach=MockGethProvider,
|
||||
)
|
||||
interface.connect()
|
||||
assert isinstance(interface.client, GanacheClient)
|
||||
|
||||
|
||||
def test_detect_provider_type_ws():
|
||||
interface = ProviderTypeTestClient(
|
||||
endpoint=f"ws://{LOOPBACK_ADDRESS}:8546",
|
||||
expected_provider_class=WebsocketProvider,
|
||||
actual_provider_to_attach=MockWebSocketProvider(),
|
||||
)
|
||||
interface.connect()
|
||||
assert isinstance(interface.client, GethClient)
|
||||
|
||||
|
||||
def test_infura_web3_client():
|
||||
interface = InfuraTestClient(
|
||||
endpoint="wss://:@goerli.infura.io/ws/v3/1234567890987654321abcdef"
|
||||
)
|
||||
interface.connect()
|
||||
|
||||
assert isinstance(interface.client, InfuraClient)
|
||||
|
||||
assert interface.client.node_technology == 'Geth'
|
||||
assert interface.client.node_version == 'v1.8.23-omnibus-2ad89aaa'
|
||||
assert interface.client.platform == 'linux-amd64'
|
||||
assert interface.client.backend == 'go1.11.1'
|
||||
assert interface.client.is_local is False
|
||||
assert interface.client.chain_id == 5
|
||||
|
||||
assert interface.client.unlock_account('address', 'password') # Returns True on success
|
||||
|
||||
|
||||
def test_alchemy_web3_client():
|
||||
interface = AlchemyTestClient(
|
||||
endpoint="https://eth-rinkeby.alchemyapi.io/v2/1234567890987654321abcdef"
|
||||
)
|
||||
interface.connect()
|
||||
|
||||
assert isinstance(interface.client, AlchemyClient)
|
||||
|
||||
assert interface.client.node_technology == 'Geth'
|
||||
assert interface.client.node_version == 'v1.9.20-stable-979fc968'
|
||||
assert interface.client.platform == 'linux-amd64'
|
||||
assert interface.client.backend == 'go1.15'
|
||||
|
||||
|
||||
def test_parity_web3_client():
|
||||
interface = ParityClientTestInterface(endpoint="file:///ipc.parity")
|
||||
interface.connect()
|
||||
|
||||
assert isinstance(interface.client, ParityClient)
|
||||
assert interface.client.node_technology == 'Parity-Ethereum'
|
||||
assert interface.client.node_version == 'v2.5.1-beta-e0141f8-20190510'
|
||||
assert interface.client.platform == 'x86_64-linux-gnu'
|
||||
assert interface.client.backend == 'rustc1.34.1'
|
||||
|
||||
|
||||
def test_ganache_web3_client():
|
||||
interface = GanacheClientTestInterface(endpoint="http://ganache:8445")
|
||||
interface.connect()
|
||||
|
||||
assert isinstance(interface.client, GanacheClient)
|
||||
assert interface.client.node_technology == 'EthereumJS TestRPC'
|
||||
assert interface.client.node_version == 'v2.1.5'
|
||||
assert interface.client.platform is None
|
||||
assert interface.client.backend == 'ethereum-js'
|
||||
assert interface.client.is_local
|
||||
assert isinstance(interface.client, EthereumClient)
|
||||
|
||||
|
||||
def test_gas_prices(mocker, mock_ethereum_client):
|
||||
|
|
|
@ -1,48 +1,116 @@
|
|||
import os
|
||||
from functools import cached_property
|
||||
from typing import List, Union
|
||||
|
||||
import maya
|
||||
from ape.api import AccountAPI
|
||||
from ape.managers.accounts import TestAccountManager
|
||||
from eth_tester.exceptions import TransactionFailed
|
||||
from eth_typing import ChecksumAddress
|
||||
from hexbytes import HexBytes
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.blockchain.eth.interfaces import (
|
||||
BlockchainInterface,
|
||||
)
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.blockchain.eth.signers import Signer
|
||||
from nucypher.blockchain.eth.signers.software import InMemorySigner
|
||||
from nucypher.utilities.gas_strategies import EXPECTED_CONFIRMATION_TIME_IN_SECONDS
|
||||
from nucypher.utilities.logging import Logger
|
||||
from tests.constants import (
|
||||
DEVELOPMENT_ETH_AIRDROP_AMOUNT,
|
||||
INSECURE_DEVELOPMENT_PASSWORD,
|
||||
NUMBER_OF_ETH_TEST_ACCOUNTS,
|
||||
NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS,
|
||||
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS,
|
||||
TEST_ETH_PROVIDER_URI,
|
||||
)
|
||||
from tests.constants import NUMBER_OF_ETH_TEST_ACCOUNTS, TEST_ETH_PROVIDER_URI
|
||||
|
||||
|
||||
def token_airdrop(token_agent, amount: NU, transacting_power: TransactingPower, addresses: List[str]):
|
||||
"""Airdrops tokens from creator address to all other addresses!"""
|
||||
class ReservedTestAccountManager(TestAccountManager):
|
||||
|
||||
signer = Web3Signer(token_agent.blockchain.client)
|
||||
signer.unlock_account(account=transacting_power.account, password=INSECURE_DEVELOPMENT_PASSWORD)
|
||||
NUMBER_OF_URSULAS_IN_TESTS = 10
|
||||
NUMBER_OF_STAKING_PROVIDERS_IN_TESTS = NUMBER_OF_URSULAS_IN_TESTS
|
||||
|
||||
def txs():
|
||||
args = {'from': transacting_power.account, '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(contract_function=contract_function,
|
||||
transacting_power=transacting_power,
|
||||
payload=args)
|
||||
yield _receipt
|
||||
# Internal
|
||||
__STAKING_PROVIDERS_RANGE = range(NUMBER_OF_STAKING_PROVIDERS_IN_TESTS)
|
||||
__OPERATORS_RANGE = range(NUMBER_OF_URSULAS_IN_TESTS)
|
||||
|
||||
receipts = list()
|
||||
for receipt in txs(): # One at a time
|
||||
receipts.append(receipt)
|
||||
return receipts
|
||||
# Reserved addresses
|
||||
_ETHERBASE = 0
|
||||
_ALICE = 1
|
||||
_BOB = 2
|
||||
_FIRST_STAKING_PROVIDER = 3
|
||||
_FIRST_URSULA = _FIRST_STAKING_PROVIDER + NUMBER_OF_STAKING_PROVIDERS_IN_TESTS
|
||||
|
||||
# Unassigned
|
||||
_FIRST_UNASSIGNED = _FIRST_URSULA + NUMBER_OF_URSULAS_IN_TESTS
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.__ape_accounts = None
|
||||
|
||||
@cached_property
|
||||
def accounts_addresses(self) -> List[str]:
|
||||
test_accounts = [account.address for account in self.accounts]
|
||||
return test_accounts
|
||||
|
||||
@property
|
||||
def accounts(self) -> List[AccountAPI]:
|
||||
if self.__ape_accounts:
|
||||
return self.__ape_accounts
|
||||
|
||||
test_accounts = [test_account for test_account in list(super().accounts)]
|
||||
|
||||
# additional accounts only needed/applicable for unit/integration tests since acceptance
|
||||
# tests use a ape-config.yml to specify number of accounts.
|
||||
additional_required_accounts = NUMBER_OF_ETH_TEST_ACCOUNTS - len(test_accounts)
|
||||
if additional_required_accounts > 0:
|
||||
for i in range(additional_required_accounts):
|
||||
new_test_account = super(
|
||||
ReservedTestAccountManager, self
|
||||
).generate_test_account()
|
||||
test_accounts.append(new_test_account)
|
||||
|
||||
self.__ape_accounts = test_accounts
|
||||
return test_accounts
|
||||
|
||||
@property
|
||||
def etherbase_account(self) -> ChecksumAddress:
|
||||
return self[self._ETHERBASE].address
|
||||
|
||||
@property
|
||||
def alice_account(self) -> ChecksumAddress:
|
||||
return self[self._ALICE].address
|
||||
|
||||
@property
|
||||
def bob_account(self) -> ChecksumAddress:
|
||||
return self[self._BOB].address
|
||||
|
||||
def ursula_account(self, index):
|
||||
if index not in self.__OPERATORS_RANGE:
|
||||
raise ValueError(
|
||||
f"Ursula index must be lower than {self.NUMBER_OF_URSULAS_IN_TESTS}"
|
||||
)
|
||||
return self[index + self._FIRST_URSULA].address
|
||||
|
||||
@property
|
||||
def ursulas_accounts(self) -> List[ChecksumAddress]:
|
||||
return list(self.ursula_account(i) for i in self.__OPERATORS_RANGE)
|
||||
|
||||
def staking_provider_account(self, index) -> ChecksumAddress:
|
||||
if index not in self.__STAKING_PROVIDERS_RANGE:
|
||||
raise ValueError(
|
||||
f"Stake provider index must be lower than {self.NUMBER_OF_URSULAS_IN_TESTS}"
|
||||
)
|
||||
return self[index + self._FIRST_STAKING_PROVIDER].address
|
||||
|
||||
@property
|
||||
def staking_providers_accounts(self) -> List[ChecksumAddress]:
|
||||
return list(
|
||||
self.staking_provider_account(i) for i in self.__STAKING_PROVIDERS_RANGE
|
||||
)
|
||||
|
||||
@property
|
||||
def unassigned_accounts(self) -> List[ChecksumAddress]:
|
||||
unassigned = [
|
||||
account.address for account in self.accounts[self._FIRST_UNASSIGNED :]
|
||||
]
|
||||
return unassigned
|
||||
|
||||
def get_account_signer(self, account_address: str) -> Signer:
|
||||
return InMemorySigner(private_key=self[account_address].private_key)
|
||||
|
||||
|
||||
def free_gas_price_strategy(w3, transaction_params=None):
|
||||
|
@ -61,24 +129,10 @@ class TesterBlockchain(BlockchainInterface):
|
|||
ETH_PROVIDER_URI = TEST_ETH_PROVIDER_URI
|
||||
DEFAULT_GAS_STRATEGY = 'free'
|
||||
|
||||
# Reserved addresses
|
||||
_ETHERBASE = 0
|
||||
_ALICE = 1
|
||||
_BOB = 2
|
||||
_FIRST_STAKING_PROVIDER = 5
|
||||
_FIRST_URSULA = _FIRST_STAKING_PROVIDER + NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS
|
||||
|
||||
# Internal
|
||||
__STAKING_PROVIDERS_RANGE = range(NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS)
|
||||
__OPERATORS_RANGE = range(NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)
|
||||
__ACCOUNT_CACHE = list()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
test_accounts: int = NUMBER_OF_ETH_TEST_ACCOUNTS,
|
||||
poa: bool = True,
|
||||
light: bool = False,
|
||||
eth_airdrop: bool = False,
|
||||
*args,
|
||||
**kwargs,
|
||||
):
|
||||
|
@ -94,45 +148,9 @@ class TesterBlockchain(BlockchainInterface):
|
|||
self.log = Logger("test-blockchain")
|
||||
self.connect()
|
||||
|
||||
# Generate additional ethereum accounts for testing
|
||||
population = test_accounts
|
||||
enough_accounts = len(self.client.accounts) >= population
|
||||
if not enough_accounts:
|
||||
accounts_to_make = population - len(self.client.accounts)
|
||||
self.__generate_insecure_unlocked_accounts(quantity=accounts_to_make)
|
||||
assert test_accounts == len(self.w3.eth.accounts)
|
||||
|
||||
if eth_airdrop is True: # ETH for everyone!
|
||||
self.ether_airdrop(amount=DEVELOPMENT_ETH_AIRDROP_AMOUNT)
|
||||
|
||||
def attach_middleware(self):
|
||||
pass
|
||||
|
||||
def __generate_insecure_unlocked_accounts(self, quantity: int) -> List[str]:
|
||||
|
||||
addresses = list()
|
||||
for _ in range(quantity):
|
||||
address = self.provider.ethereum_tester.add_account('0x' + os.urandom(32).hex())
|
||||
addresses.append(address)
|
||||
self.__ACCOUNT_CACHE.append(address)
|
||||
self.log.info('Generated new insecure account {}'.format(address))
|
||||
return addresses
|
||||
|
||||
def ether_airdrop(self, amount: int) -> List[str]:
|
||||
"""Airdrops ether from creator address to all other addresses!"""
|
||||
coinbase, *addresses = self.client.accounts
|
||||
tx_hashes = list()
|
||||
for address in addresses:
|
||||
tx = {'to': address, 'from': coinbase, 'value': amount, 'gasPrice': self.w3.eth.generate_gas_price()}
|
||||
txhash = self.w3.eth.send_transaction(tx)
|
||||
|
||||
_receipt = self.wait_for_receipt(txhash)
|
||||
tx_hashes.append(txhash)
|
||||
eth_amount = Web3().from_wei(amount, 'ether')
|
||||
self.log.info("Airdropped {} ETH {} -> {}".format(eth_amount, tx['from'], tx['to']))
|
||||
|
||||
return tx_hashes
|
||||
|
||||
def time_travel(self,
|
||||
hours: int = None,
|
||||
seconds: int = None):
|
||||
|
@ -164,43 +182,6 @@ class TesterBlockchain(BlockchainInterface):
|
|||
self.log.info(f"Time traveled {delta} "
|
||||
f"| epoch {end_timestamp}")
|
||||
|
||||
@property
|
||||
def etherbase_account(self):
|
||||
return self.client.accounts[self._ETHERBASE]
|
||||
|
||||
@property
|
||||
def alice_account(self):
|
||||
return self.client.accounts[self._ALICE]
|
||||
|
||||
@property
|
||||
def bob_account(self):
|
||||
return self.client.accounts[self._BOB]
|
||||
|
||||
def ursula_account(self, index):
|
||||
if index not in self.__OPERATORS_RANGE:
|
||||
raise ValueError(f"Ursula index must be lower than {NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS}")
|
||||
return self.client.accounts[index + self._FIRST_URSULA]
|
||||
|
||||
def stake_provider_account(self, index):
|
||||
if index not in self.__STAKING_PROVIDERS_RANGE:
|
||||
raise ValueError(f"Stake provider index must be lower than {NUMBER_OF_STAKING_PROVIDERS_IN_BLOCKCHAIN_TESTS}")
|
||||
return self.client.accounts[index + self._FIRST_STAKING_PROVIDER]
|
||||
|
||||
@property
|
||||
def ursulas_accounts(self):
|
||||
return list(self.ursula_account(i) for i in self.__OPERATORS_RANGE)
|
||||
|
||||
@property
|
||||
def stake_providers_accounts(self):
|
||||
return list(self.stake_provider_account(i) for i in self.__STAKING_PROVIDERS_RANGE)
|
||||
|
||||
@property
|
||||
def unassigned_accounts(self):
|
||||
special_accounts = [self.etherbase_account, self.alice_account, self.bob_account]
|
||||
assigned_accounts = set(self.stake_providers_accounts + self.ursulas_accounts + special_accounts)
|
||||
accounts = set(self.client.accounts)
|
||||
return list(accounts.difference(assigned_accounts))
|
||||
|
||||
def wait_for_receipt(self, txhash: Union[bytes, str, HexBytes], timeout: int = None) -> dict:
|
||||
"""Wait for a transaction receipt and return it"""
|
||||
timeout = timeout or self.TIMEOUT
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from typing import List
|
||||
|
||||
from eth_typing import ChecksumAddress
|
||||
|
||||
from nucypher.blockchain.eth.registry import ContractRegistry
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.config.characters import (
|
||||
|
@ -45,7 +43,6 @@ def assemble(
|
|||
|
||||
|
||||
def make_ursula_test_configuration(
|
||||
operator_address: ChecksumAddress,
|
||||
rest_port: int = select_test_port(),
|
||||
polygon_endpoint: str = None,
|
||||
**assemble_kwargs
|
||||
|
@ -55,7 +52,6 @@ def make_ursula_test_configuration(
|
|||
**test_params,
|
||||
rest_port=rest_port,
|
||||
polygon_endpoint=polygon_endpoint,
|
||||
operator_address=operator_address,
|
||||
)
|
||||
return ursula_config
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import contextlib
|
||||
import os
|
||||
import socket
|
||||
from threading import Lock
|
||||
from typing import Iterable, List
|
||||
from typing import Iterable, List, Optional
|
||||
|
||||
from cryptography.x509 import Certificate
|
||||
from eth_utils import to_checksum_address
|
||||
from web3 import HTTPProvider
|
||||
|
||||
from nucypher.blockchain.eth.signers import InMemorySigner, Signer
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.config.characters import UrsulaConfiguration
|
||||
from nucypher.policy.conditions.evm import _CONDITION_CHAINS
|
||||
from tests.constants import (
|
||||
NUMBER_OF_URSULAS_IN_DEVELOPMENT_DOMAIN,
|
||||
TESTERCHAIN_CHAIN_ID,
|
||||
)
|
||||
from tests.constants import TESTERCHAIN_CHAIN_ID
|
||||
from tests.utils.blockchain import ReservedTestAccountManager
|
||||
|
||||
|
||||
class __ActivePortCache:
|
||||
|
@ -64,23 +65,64 @@ def select_test_port() -> int:
|
|||
return port
|
||||
|
||||
|
||||
def make_reserved_ursulas(
|
||||
accounts: ReservedTestAccountManager,
|
||||
ursula_config: UrsulaConfiguration,
|
||||
know_each_other: bool = True,
|
||||
quantity: Optional[int] = None,
|
||||
**ursula_overrides
|
||||
):
|
||||
num_values = quantity or accounts.NUMBER_OF_URSULAS_IN_TESTS
|
||||
|
||||
staking_providers = accounts.staking_providers_accounts[:num_values]
|
||||
operator_signers = [
|
||||
accounts.get_account_signer(operator_address)
|
||||
for operator_address in accounts.ursulas_accounts[:num_values]
|
||||
]
|
||||
return make_ursulas(
|
||||
ursula_config,
|
||||
staking_providers,
|
||||
operator_signers,
|
||||
know_each_other,
|
||||
**ursula_overrides
|
||||
)
|
||||
|
||||
|
||||
def make_random_ursulas(
|
||||
ursula_config: UrsulaConfiguration,
|
||||
quantity: int,
|
||||
know_each_other: bool = True,
|
||||
**ursula_overrides
|
||||
):
|
||||
staking_providers = [
|
||||
to_checksum_address("0x" + os.urandom(20).hex()) for _ in range(quantity)
|
||||
]
|
||||
operator_signers = [InMemorySigner() for _ in range(quantity)]
|
||||
return make_ursulas(
|
||||
ursula_config,
|
||||
staking_providers,
|
||||
operator_signers,
|
||||
know_each_other,
|
||||
**ursula_overrides
|
||||
)
|
||||
|
||||
|
||||
def make_ursulas(
|
||||
ursula_config: UrsulaConfiguration,
|
||||
staking_provider_addresses: Iterable[str],
|
||||
operator_addresses: Iterable[str],
|
||||
quantity: int = NUMBER_OF_URSULAS_IN_DEVELOPMENT_DOMAIN,
|
||||
operator_signers: Iterable[Signer],
|
||||
know_each_other: bool = True,
|
||||
**ursula_overrides
|
||||
) -> List[Ursula]:
|
||||
|
||||
providers_and_operators = list(zip(staking_provider_addresses, operator_addresses))[:quantity]
|
||||
):
|
||||
providers_and_operators = list(zip(staking_provider_addresses, operator_signers))
|
||||
ursulas = list()
|
||||
|
||||
for staking_provider_address, operator_address in providers_and_operators:
|
||||
for staking_provider_address, operator_signer in providers_and_operators:
|
||||
ursula = ursula_config.produce(
|
||||
checksum_address=staking_provider_address,
|
||||
operator_address=operator_address,
|
||||
operator_address=operator_signer.accounts[0],
|
||||
rest_port=select_test_port(),
|
||||
signer=operator_signer,
|
||||
**ursula_overrides
|
||||
)
|
||||
|
||||
|
@ -102,7 +144,6 @@ def make_ursulas(
|
|||
|
||||
return ursulas
|
||||
|
||||
|
||||
def start_pytest_ursula_services(ursula: Ursula) -> Certificate:
|
||||
"""
|
||||
Takes an ursula and starts its learning
|
||||
|
|
Loading…
Reference in New Issue