Simplify solidity compiler module with a more stateless and functional approach; Use the standard compiler method.

pull/2439/head
Kieran R. Prasch 2020-05-27 02:56:44 -07:00 committed by vzotova
parent 066255d22a
commit 11a807c096
7 changed files with 304 additions and 194 deletions

View File

@ -55,7 +55,7 @@ from nucypher.blockchain.eth.providers import (
_get_websocket_provider
)
from nucypher.blockchain.eth.registry import BaseContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.blockchain.eth.sol.compile import compile_nucypher
from nucypher.blockchain.eth.utils import get_transaction_name, prettify_eth_amount
from nucypher.characters.control.emitters import JSONRPCStdoutEmitter, StdoutEmitter
from nucypher.utilities.ethereum import encode_constructor_arguments
@ -806,28 +806,27 @@ class BlockchainDeployerInterface(BlockchainInterface):
pass
def __init__(self,
compiler: SolidityCompiler = None,
ignore_solidity_check: bool = False,
dry_run: bool = False,
*args, **kwargs):
super().__init__(*args, **kwargs)
self.dry_run = dry_run
self.compiler = compiler or SolidityCompiler(ignore_solidity_check=ignore_solidity_check)
self.ignore_solidity_check = ignore_solidity_check
def connect(self):
def connect(self, test_contracts: bool = False) -> bool:
super().connect()
self._setup_solidity(compiler=self.compiler)
self._setup_solidity(compile_now=True, test_contracts=test_contracts)
return self.is_connected
def _setup_solidity(self, compiler: SolidityCompiler = None) -> None:
def _setup_solidity(self, compile_now: bool, test_contracts: bool) -> None:
if self.dry_run:
self.log.info("Dry run is active, skipping solidity compile steps.")
return
if compiler:
if compile_now:
# Execute the compilation if we're recompiling
# Otherwise read compiled contract data from the registry.
_raw_contract_cache = compiler.compile()
_raw_contract_cache = compile_nucypher(ignore_version_check=self.ignore_solidity_check, test_contracts=test_contracts)
else:
_raw_contract_cache = NO_COMPILATION_PERFORMED
self._raw_contract_cache = _raw_contract_cache
@ -945,7 +944,7 @@ class BlockchainDeployerInterface(BlockchainInterface):
"""Retrieve compiled interface data from the cache and return web3 contract"""
version, interface = self.find_raw_contract_data(contract_name, version)
contract = self.client.w3.eth.contract(abi=interface['abi'],
bytecode=interface['bin'],
bytecode=interface['evm']['bytecode']['object'],
version=version,
address=address,
ContractFactoryClass=self._contract_factory)

View File

@ -16,156 +16,301 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
from os.path import abspath, dirname
import itertools
import os
import re
from typing import List, NamedTuple, Optional, Set
from pathlib import Path
from typing import Dict, Iterator, List, Optional, Pattern, Tuple, TypeVar, Union
from nucypher.blockchain.eth.sol import SOLIDITY_COMPILER_VERSION
from nucypher.blockchain.eth.sol import SOLIDITY_COMPILER_VERSION as SOURCE_VERSION
from nucypher.exceptions import DevelopmentInstallationRequired
from nucypher.utilities.logging import Logger
class SourceDirs(NamedTuple):
root_source_dir: str
other_source_dirs: Optional[Set[str]] = None
LOG: Logger = Logger('solidity-compiler')
class SolidityCompiler:
#
# Types
#
__default_contract_version = 'v0.0.0'
__default_contract_dir = os.path.join(dirname(abspath(__file__)), 'source')
# TODO: Is there a better way to specify complex dictionaries with variable keys?
CompiledContracts = TypeVar('CompiledContracts', bound=Dict[str, Dict[str, List[Dict[str, Union[str, List[Dict[str, str]]]]]]])
__compiled_contracts_dir = 'contracts'
__zeppelin_library_dir = 'zeppelin'
__aragon_library_dir = 'aragon'
optimization_runs = 200
class CompilerConfiguration(Dict):
language: str
sources: Dict[str, Dict[str, str]]
settings: Dict
class CompilerError(Exception):
pass
class VersionError(Exception):
pass
#
# Exceptions
#
@classmethod
def default_contract_dir(cls):
return cls.__default_contract_dir
class CompilationError(RuntimeError):
"""
Raised when there is a problem compiling nucypher contracts
or with the expected compiler configuration.
"""
def __init__(self,
source_dirs: List[SourceDirs] = None,
ignore_solidity_check: bool = False
) -> None:
# Allow for optional installation
from solcx.install import get_executable
#
# Compiler Constants
#
self.log = Logger('solidity-compiler')
version = SOLIDITY_COMPILER_VERSION if not ignore_solidity_check else None
self.__sol_binary_path = get_executable(version=version)
DEFAULT_CONTRACT_VERSION: str = 'v0.0.0'
SOURCE_ROOT: Path = Path(__file__).parent / 'source'
if source_dirs is None or len(source_dirs) == 0:
self.source_dirs = [SourceDirs(root_source_dir=self.__default_contract_dir)]
else:
self.source_dirs = source_dirs
CONTRACTS: str = 'contracts'
NUCYPHER_CONTRACTS_DIR: Path = SOURCE_ROOT / CONTRACTS
def compile(self) -> dict:
interfaces = dict()
for root_source_dir, other_source_dirs in self.source_dirs:
if root_source_dir is None:
self.log.warn("One of the root directories is None")
continue
# Third Party Contracts
ZEPPELIN: str = 'zeppelin'
ARAGON: str = 'aragon'
ZEPPELIN_DIR: Path = SOURCE_ROOT / ZEPPELIN
ARAGON_DIR: Path = SOURCE_ROOT / ARAGON
raw_interfaces = self._compile(root_source_dir, other_source_dirs)
for name, data in raw_interfaces.items():
# Extract contract version from docs
version_search = re.search(r"""
\"details\": # @dev tag in contract docs
\".*? # Skip any data in the beginning of details
\| # Beginning of version definition |
(v # Capture version starting from symbol v
\d+ # At least one digit of major version
\. # Digits splitter
\d+ # At least one digit of minor version
\. # Digits splitter
\d+ # At least one digit of patch
) # End of capturing
\| # End of version definition |
.*?\" # Skip any data in the end of details
""", data['devdoc'], re.VERBOSE)
version = version_search.group(1) if version_search else self.__default_contract_version
try:
existence_data = interfaces[name]
except KeyError:
existence_data = dict()
interfaces.update({name: existence_data})
if version not in existence_data:
existence_data.update({version: data})
return interfaces
def _compile(self, root_source_dir: str, other_source_dirs: [str]) -> dict:
"""Executes the compiler with parameters specified in the json config"""
SOURCES: List[str] = [
str(NUCYPHER_CONTRACTS_DIR.resolve(strict=True))
]
# Allow for optional installation
from solcx import compile_files
from solcx.exceptions import SolcError
ALLOWED_PATHS: List[str] = [
str(SOURCE_ROOT.resolve(strict=True))
]
self.log.info("Using solidity compiler binary at {}".format(self.__sol_binary_path))
contracts_dir = os.path.join(root_source_dir, self.__compiled_contracts_dir)
self.log.info("Compiling solidity source files at {}".format(contracts_dir))
IGNORE_CONTRACT_PREFIXES: Tuple[str, ...] = (
'Abstract',
'Interface'
)
source_paths = set()
source_walker = os.walk(top=contracts_dir, topdown=True)
if other_source_dirs is not None:
for source_dir in other_source_dirs:
other_source_walker = os.walk(top=source_dir, topdown=True)
source_walker = itertools.chain(source_walker, other_source_walker)
# RE pattern for matching solidity source compile version specification in devdoc details.
VERSION_PATTERN: Pattern = re.compile(r"""
^\| # Anchored pipe literal at beginning of version definition
(v # Capture version starting from symbol v
\d+ # At least one digit of major version
\. # Digits splitter
\d+ # At least one digit of minor version
\. # Digits splitter
\d+ # At least one digit of patch
) # End of capturing
\|$ # Anchored end of version definition |
""", re.VERBOSE)
#
# Standard "JSON I/O" Compiler Config Reference:
# https://solidity.readthedocs.io/en/latest/using-the-compiler.html#input-description
#
#
# WARNING: Do not change these values unless you know what you are doing.
#
LANGUAGE: str = 'Solidity'
EVM_VERSION: str = 'berlin'
FILE_OUTPUTS: List[str] = []
CONTRACT_OUTPUTS: List[str] = [
'abi', # ABI
'devdoc', # Developer documentation (natspec)
'userdoc', # User documentation (natspec)
'evm.bytecode.object', # Bytecode object
]
IMPORT_REMAPPINGS: List[str] = [
f"{CONTRACTS}={NUCYPHER_CONTRACTS_DIR.resolve()}",
f"{ZEPPELIN}={ZEPPELIN_DIR.resolve()}",
f"{ARAGON}={ARAGON_DIR.resolve()}",
]
OPTIMIZER: bool = True
OPTIMIZATION_RUNS: int = 200
OPTIMIZER_SETTINGS = dict(
enabled=True,
runs=200
)
COMPILER_SETTINGS: Dict = dict(
remappings=IMPORT_REMAPPINGS,
optimizer=OPTIMIZER_SETTINGS,
evmVersion=EVM_VERSION,
outputSelection={"*": {"*": CONTRACT_OUTPUTS, "": FILE_OUTPUTS}} # all contacts(*), all files("")
)
COMPILER_CONFIG = CompilerConfiguration(
language=LANGUAGE,
sources=SOURCES,
settings=COMPILER_SETTINGS
)
#
# Collection
#
def __source_filter(filename: str) -> bool:
"""Helper function for filtering out contracts not intended for compilation"""
contains_ignored_prefix: bool = any(prefix in filename for prefix in IGNORE_CONTRACT_PREFIXES)
is_solidity_file: bool = filename.endswith('.sol')
return is_solidity_file and not contains_ignored_prefix
def __collect_sources(test_contracts: bool,
source_dirs: Optional[Path] = None
) -> Dict[str, Dict[str, List[str]]]:
# Default
if not source_dirs:
source_dirs: List[Path] = [SOURCE_ROOT / CONTRACTS]
# Add test contracts to sources
if test_contracts:
from tests.constants import TEST_CONTRACTS_DIR
source_dirs.append(TEST_CONTRACTS_DIR)
# Collect all source directories
source_paths: Dict[str, Dict[str, List[str]]] = dict()
for source_dir in source_dirs:
source_walker: Iterator = os.walk(top=str(source_dir), topdown=True)
# Collect single directory
for root, dirs, files in source_walker:
for filename in files:
if filename.endswith('.sol'):
path = os.path.join(root, filename)
source_paths.add(path)
self.log.debug("Collecting solidity source {}".format(path))
# Collect files in source dir
for filename in filter(__source_filter, files):
path = Path(root) / filename
source_paths[filename] = dict(urls=[str(path.resolve(strict=True))])
LOG.debug(f"Collecting solidity source {path}")
LOG.info(f"Collected {len(source_paths)} solidity source files at {source_dir}")
return source_paths
# Compile with remappings: https://github.com/ethereum/py-solc
zeppelin_dir = os.path.join(root_source_dir, self.__zeppelin_library_dir)
aragon_dir = os.path.join(root_source_dir, self.__aragon_library_dir)
remappings = ("contracts={}".format(contracts_dir),
"zeppelin={}".format(zeppelin_dir),
"aragon={}".format(aragon_dir),
)
def __compile(source_dirs: Tuple[Path, ...] = None,
include_ast: bool = True,
ignore_version_check: bool = False,
test_contracts: bool = False) -> dict:
"""Executes the compiler"""
self.log.info("Compiling with import remappings {}".format(", ".join(remappings)))
try:
# Lazy import to allow for optional installation of solcx
from solcx.install import get_executable
from solcx.main import compile_standard
except ImportError:
raise DevelopmentInstallationRequired(importable_name='solcx')
optimization_runs = self.optimization_runs
# Solidity Compiler Binary
compiler_version: str = SOURCE_VERSION if not ignore_version_check else None
solc_binary_path: str = get_executable(version=compiler_version)
try:
compiled_sol = compile_files(source_files=source_paths,
solc_binary=self.__sol_binary_path,
import_remappings=remappings,
allow_paths=root_source_dir,
optimize=True,
optimize_runs=optimization_runs)
# Extra compiler options
if include_ast:
FILE_OUTPUTS.append('ast')
self.log.info("Successfully compiled {} contracts with {} optimization runs".format(len(compiled_sol),
optimization_runs))
# Handle allowed paths
if test_contracts:
from tests.constants import TEST_CONTRACTS_DIR
ALLOWED_PATHS.append(str(TEST_CONTRACTS_DIR.resolve(True)))
if source_dirs:
for source in source_dirs:
ALLOWED_PATHS.append(str(source.resolve(strict=True)))
except FileNotFoundError:
raise RuntimeError("The solidity compiler is not at the specified path. "
"Check that the file exists and is executable.")
except PermissionError:
raise RuntimeError("The solidity compiler binary at {} is not executable. "
"Check the file's permissions.".format(self.__sol_binary_path))
# Resolve Sources
allowed_paths: str = ','.join(ALLOWED_PATHS)
sources = __collect_sources(source_dirs=source_dirs, test_contracts=test_contracts)
COMPILER_CONFIG.update(dict(sources=sources))
except SolcError:
raise
LOG.info(f"Compiling with import remappings {' '.join(IMPORT_REMAPPINGS)}")
try:
compiled_sol: CompiledContracts = compile_standard(input_data=COMPILER_CONFIG, allow_paths=allowed_paths)
except FileNotFoundError:
raise RuntimeError("The solidity compiler is not at the specified path. "
"Check that the file exists and is executable.")
except PermissionError:
raise RuntimeError(f"The solidity compiler binary at {solc_binary_path} is not executable. "
"Check the file's permissions.")
LOG.info(f"Successfully compiled {len(compiled_sol)} contracts with {OPTIMIZATION_RUNS} optimization runs")
return compiled_sol
# Cleanup the compiled data keys
interfaces = {name.split(':')[-1]: compiled_sol[name] for name in compiled_sol}
return interfaces
def __extract_version(contract_name: str, contract_data: dict) -> str:
# Parse
try:
devdoc: Dict[str, str] = contract_data['devdoc']
except KeyError:
raise CompilationError(f'Solidity compiler did not export devdoc for {contract_name}.')
try:
devdoc_details: str = devdoc['details']
except KeyError:
LOG.warn(f'No solidity source version specified for {contract_name}')
return DEFAULT_CONTRACT_VERSION
# RE
raw_matches = VERSION_PATTERN.fullmatch(devdoc_details)
# Positive match(es)
if raw_matches:
matches = raw_matches.groups()
if len(matches) != 1: # sanity check
raise CompilationError(f"Multiple version matches in devdoc")
version = matches[0] # good match
return version
# Negative match: Devdoc included without a version
else:
LOG.warn(f'Compiler version not included in devdoc details for {contract_name}')
return DEFAULT_CONTRACT_VERSION
def __handle_contract(contract_data: dict,
ast: bool,
source_data,
interfaces: dict,
exported_name: str,
) -> None:
if ast:
# TODO: Sort AST by contract
ast = source_data['ast']
contract_data['ast'] = ast
try:
existence_data = interfaces[exported_name]
except KeyError:
existence_data = dict()
interfaces.update({exported_name: existence_data})
version = __extract_version(contract_name=exported_name, contract_data=contract_data)
if version not in existence_data:
existence_data.update({version: contract_data})
def compile_nucypher(source_dirs: Optional[Tuple[Path, ...]] = None,
include_ast: bool = False,
ignore_version_check: bool = False,
test_contracts: bool = False
) -> CompiledContracts:
"""Compile nucypher contracts"""
# Compile
compile_result = __compile(include_ast=include_ast,
ignore_version_check=ignore_version_check,
test_contracts=test_contracts,
source_dirs=source_dirs)
# Aggregate
interfaces = dict()
compiled_contracts, compiled_sources = compile_result['contracts'].items(), compile_result['sources'].items()
for (source_path, source_data), (contract_path, compiled_contract) in zip(compiled_sources, compiled_contracts):
for exported_name, contract_data in compiled_contract.items():
__handle_contract(ast=include_ast,
contract_data=contract_data,
source_data=source_data,
interfaces=interfaces,
exported_name=exported_name)
return interfaces
# Control export values
__all__ = (
compile_nucypher,
DEFAULT_CONTRACT_VERSION
)

View File

@ -14,33 +14,30 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import types
from os.path import abspath, dirname
import os
from unittest.mock import PropertyMock
from pathlib import Path
import maya
import pytest
from hexbytes import HexBytes
from nucypher.blockchain.eth.clients import EthereumClient
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler, SourceDirs
from nucypher.blockchain.eth.sol.compile import _compile
from nucypher.crypto.powers import TransactingPower
from tests.constants import (
DEVELOPMENT_ETH_AIRDROP_AMOUNT,
INSECURE_DEVELOPMENT_PASSWORD,
NUMBER_OF_ETH_TEST_ACCOUNTS,
NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS,
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS
)
# Prevents TesterBlockchain to be picked up by py.test as a test class
from tests.fixtures import _make_testerchain
from tests.mock.interfaces import MockBlockchain
from tests.utils.blockchain import TesterBlockchain as _TesterBlockchain, free_gas_price_strategy
from tests.constants import (DEVELOPMENT_ETH_AIRDROP_AMOUNT, INSECURE_DEVELOPMENT_PASSWORD,
NUMBER_OF_ETH_TEST_ACCOUNTS, NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS,
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)
@pytest.fixture()
def another_testerchain(solidity_compiler):
testerchain = _TesterBlockchain(eth_airdrop=True, free_transactions=True, light=True, compiler=solidity_compiler)
def another_testerchain():
testerchain = _TesterBlockchain(eth_airdrop=True, free_transactions=True, light=True)
testerchain.deployer_address = testerchain.etherbase_account
assert testerchain.is_light
yield testerchain
@ -96,17 +93,14 @@ def test_testerchain_creation(testerchain, another_testerchain):
def test_multiversion_contract():
# Prepare compiler
base_dir = os.path.join(dirname(abspath(__file__)), "contracts", "multiversion")
v1_dir = os.path.join(base_dir, "v1")
v2_dir = os.path.join(base_dir, "v2")
root_dir = SolidityCompiler.default_contract_dir()
solidity_compiler = SolidityCompiler(source_dirs=[SourceDirs(root_dir, {v2_dir}),
SourceDirs(root_dir, {v1_dir})])
base_dir = Path(__file__).parent / 'contracts' / 'multiversion'
v1_dir, v2_dir = base_dir / 'v1', base_dir / 'v2'
_compile(source_dirs=(v1_dir, v2_dir))
# Prepare chain
blockchain_interface = BlockchainDeployerInterface(provider_uri='tester://pyevm/2',
compiler=solidity_compiler,
gas_strategy=free_gas_price_strategy)
blockchain_interface.connect()
origin = blockchain_interface.client.accounts[0]

View File

@ -14,12 +14,11 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import os
from os.path import abspath, dirname
from pathlib import Path
from nucypher.blockchain.eth.deployers import NucypherTokenDeployer
from nucypher.blockchain.eth.sol.compile import SolidityCompiler, SourceDirs
from tests.constants import TEST_CONTRACTS_DIR
from nucypher.blockchain.eth.sol.compile import compile_nucypher
def test_nucypher_contract_compiled(testerchain, test_registry):
@ -34,34 +33,19 @@ def test_nucypher_contract_compiled(testerchain, test_registry):
def test_multi_source_compilation(testerchain):
solidity_compiler = SolidityCompiler(source_dirs=[
(SolidityCompiler.default_contract_dir(), None),
(SolidityCompiler.default_contract_dir(), {TEST_CONTRACTS_DIR})
])
interfaces = solidity_compiler.compile()
# Remove AST because id in tree node depends on compilation scope
for contract_name, contract_data in interfaces.items():
for version, data in contract_data.items():
data.pop("ast")
# TODO: Remove AST because id in tree node depends on compilation scope <<< Still relevant?
interfaces = compile_nucypher(test_contracts=True)
raw_cache = testerchain._raw_contract_cache.copy()
for contract_name, contract_data in raw_cache.items():
for version, data in contract_data.items():
data.pop("ast")
assert interfaces == raw_cache
def test_multi_versions():
base_dir = os.path.join(dirname(abspath(__file__)), "contracts", "multiversion")
v1_dir = os.path.join(base_dir, "v1")
v2_dir = os.path.join(base_dir, "v2")
root_dir = SolidityCompiler.default_contract_dir()
solidity_compiler = SolidityCompiler(source_dirs=[SourceDirs(root_dir, {v1_dir}),
SourceDirs(root_dir, {v2_dir})])
interfaces = solidity_compiler.compile()
assert "VersionTest" in interfaces
contract_data = interfaces["VersionTest"]
base_dir = Path(__file__).parent / "contracts" / "multiversion"
v1_dir, v2_dir = base_dir / "v1", base_dir / "v2"
interfaces = compile_nucypher(source_dirs=(v1_dir, v2_dir))
assert "VersionTest.sol" in interfaces['contracts']
contract_data = interfaces['contracts']["VersionTest.sol"]
assert len(contract_data) == 2
assert "v1.2.3" in contract_data
assert "v1.1.4" in contract_data
assert contract_data["v1.2.3"]["devdoc"] != contract_data["v1.1.4"]["devdoc"]
assert contract_data["v1.2.3"]["devdoc"]['details'] != contract_data["v1.1.4"]["devdoc"]['details']

View File

@ -25,7 +25,6 @@ from nucypher.blockchain.eth.deployers import AdjudicatorDeployer, BaseContractD
PolicyManagerDeployer, StakingEscrowDeployer
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler, SourceDirs
from nucypher.crypto.powers import TransactingPower
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD
from tests.utils.blockchain import free_gas_price_strategy
@ -94,17 +93,15 @@ def deploy_earliest_contract(blockchain_interface: BlockchainDeployerInterface,
pass # Skip errors related to initialization
def test_upgradeability(temp_dir_path):
@pytest.mark.skip('GH 403') # FIXME
def test_upgradeability(temp_dir_path, token_economics):
# Prepare remote source for compilation
download_github_dir(GITHUB_SOURCE_LINK, temp_dir_path)
solidity_compiler = SolidityCompiler(source_dirs=[SourceDirs(SolidityCompiler.default_contract_dir()),
SourceDirs(temp_dir_path)])
# Prepare the blockchain
provider_uri = 'tester://pyevm/2'
try:
blockchain_interface = BlockchainDeployerInterface(provider_uri=provider_uri,
compiler=solidity_compiler,
gas_strategy=free_gas_price_strategy)
blockchain_interface.connect()
origin = blockchain_interface.client.accounts[0]

View File

@ -52,7 +52,6 @@ from nucypher.blockchain.eth.deployers import (
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, LocalContractRegistry
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.blockchain.eth.token import NU
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.characters.lawful import Bob, Enrico
@ -458,13 +457,6 @@ def token_economics(testerchain):
return make_token_economics(blockchain=testerchain)
@pytest.fixture(scope='session')
def solidity_compiler():
"""Doing this more than once per session will result in slower test run times."""
compiler = SolidityCompiler()
yield compiler
@pytest.fixture(scope='module')
def test_registry():
registry = InMemoryContractRegistry()

View File

@ -28,7 +28,6 @@ from nucypher.blockchain.economics import BaseEconomics, StandardTokenEconomics
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
from nucypher.blockchain.eth.token import NU
from nucypher.blockchain.eth.utils import epoch_to_period
from nucypher.crypto.powers import TransactingPower
@ -81,7 +80,6 @@ class TesterBlockchain(BlockchainDeployerInterface):
DEFAULT_GAS_STRATEGY = 'free'
_PROVIDER_URI = 'tester://pyevm'
_compiler = SolidityCompiler(source_dirs=[(SolidityCompiler.default_contract_dir(), {TEST_CONTRACTS_DIR})])
_test_account_cache = list()
_default_test_accounts = NUMBER_OF_ETH_TEST_ACCOUNTS
@ -103,7 +101,6 @@ class TesterBlockchain(BlockchainDeployerInterface):
light=False,
eth_airdrop=False,
free_transactions=False,
compiler: SolidityCompiler = None,
mock_backend: bool = False,
*args, **kwargs):
@ -120,7 +117,6 @@ class TesterBlockchain(BlockchainDeployerInterface):
provider_process=None,
poa=poa,
light=light,
compiler=self._compiler,
dry_run=mock_backend,
*args, **kwargs)
@ -138,6 +134,9 @@ class TesterBlockchain(BlockchainDeployerInterface):
if eth_airdrop is True: # ETH for everyone!
self.ether_airdrop(amount=DEVELOPMENT_ETH_AIRDROP_AMOUNT)
def connect(self):
return super().connect(test_contracts=True)
def attach_middleware(self):
if self.free_transactions:
self.w3.eth.setGasPriceStrategy(free_gas_price_strategy)
@ -219,7 +218,7 @@ class TesterBlockchain(BlockchainDeployerInterface):
"""For use with metric testing scripts"""
registry = InMemoryContractRegistry()
testerchain = cls(compiler=SolidityCompiler())
testerchain = cls()
BlockchainInterfaceFactory.register_interface(testerchain)
power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)