def test_multi_source_compilation(testerchain): bundles = [ SourceBundle(base_path=SOLIDITY_SOURCE_ROOT), SourceBundle(base_path=SOLIDITY_SOURCE_ROOT, other_paths=(TEST_SOLIDITY_SOURCE_ROOT, )) ] interfaces = multiversion_compile(source_bundles=bundles) raw_cache = testerchain._raw_contract_cache.copy() assert interfaces == raw_cache
def test_multi_versions(): base_dir = TEST_MULTIVERSION_CONTRACTS v1_dir, v2_dir = base_dir / "v1", base_dir / "v2" bundles = [SourceBundle(base_path=v1_dir), SourceBundle(base_path=v2_dir)] interfaces = multiversion_compile(source_bundles=bundles) assert "VersionTest" in interfaces contract_data = interfaces["VersionTest"] 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"]['details'] != contract_data[ "v1.1.4"]["devdoc"]['details']
def generate_doc() -> None: """Compile solidity contracts, extract json docs and generate rst files from them""" GlobalLoggerSettings.start_console_logging() base_dir = Path(__file__).parent.parent.parent.resolve() solidity_source_root = base_dir / 'nucypher' / 'blockchain' / 'eth' / 'sol' / 'source' bundle = SourceBundle(base_path=solidity_source_root) contracts = multiversion_compile(source_bundles=[bundle]) # Prepare folders base_path = base_dir / 'docs' / 'source' / 'contracts_api' base_path.mkdir(exist_ok=True) for dir in CONTRACTS.keys(): category_path = base_path / dir category_path.mkdir(exist_ok=True) contract_names = { contract for contracts in CONTRACTS.values() for contract in contracts } patch() for contract, data in contracts.items(): if contract not in contract_names: continue # Merge, update and generate resulting rst no_version = next(iter(data.values())) docs = merge_and_update(no_version["userdoc"], dict()) docs = merge_and_update(no_version["devdoc"], docs) rst = schema2rst(docs, "kind,version,title", contract) # Find proper category and write file category_path = base_path for category, contracts in CONTRACTS.items(): if contract in contracts: category_path /= category with open(category_path / f"{contract}.rst", 'w') as file: file.write(rst)
class BlockchainDeployerInterface(BlockchainInterface): TIMEOUT = 600 # seconds _CONTRACT_FACTORY = VersionedContract # TODO: Make more func - use as a parameter # Source directories to (recursively) compile SOURCES: List[SourceBundle] = [ SourceBundle(base_path=SOLIDITY_SOURCE_ROOT), ] _raw_contract_cache = NO_COMPILATION_PERFORMED class NoDeployerAddress(RuntimeError): pass class DeploymentFailed(RuntimeError): pass def connect(self, compile_now: bool = True, ignore_solidity_check: bool = False) -> bool: super().connect() if compile_now: # Execute the compilation if we're recompiling # Otherwise read compiled contract data from the registry. check = not ignore_solidity_check compiled_contracts = multiversion_compile( source_bundles=self.SOURCES, compiler_version_check=check) self._raw_contract_cache = compiled_contracts return self.is_connected @validate_checksum_address def deploy_contract( self, deployer_address: str, registry: BaseContractRegistry, contract_name: str, *constructor_args, enroll: bool = True, gas_limit: int = None, confirmations: int = 0, contract_version: str = 'latest', **constructor_kwargs) -> Tuple[VersionedContract, TxReceipt]: """ Retrieve compiled interface data from the cache and return an instantiated deployed contract """ # # Build the deployment transaction # # deploy_transaction = dict() if gas_limit: deploy_transaction.update({'gas': gas_limit}) pprint_args = ', '.join( list(map(str, constructor_args)) + list(f"{k}={v}" for k, v in constructor_kwargs.items())) contract_factory = self.get_contract_factory( contract_name=contract_name, version=contract_version) self.log.info( f"Deploying contract {contract_name}:{contract_factory.version} with " f"deployer address {deployer_address} " f"and parameters {pprint_args}") constructor_function = contract_factory.constructor( *constructor_args, **constructor_kwargs) constructor_calldata = encode_constructor_arguments( self.client.w3, constructor_function, *constructor_args, **constructor_kwargs) if constructor_calldata: self.log.info(f"Constructor calldata: {constructor_calldata}") # # Transmit the deployment tx # # receipt = self.send_transaction(contract_function=constructor_function, sender_address=deployer_address, payload=deploy_transaction, confirmations=confirmations) # Success address = receipt['contractAddress'] self.log.info( f"Confirmed {contract_name}:{contract_factory.version} deployment: new address {address}" ) # # Instantiate & Enroll contract # contract = self.client.w3.eth.contract( address=address, abi=contract_factory.abi, version=contract_factory.version, ContractFactoryClass=self._CONTRACT_FACTORY) if enroll is True: registry.enroll(contract_name=contract_name, contract_address=contract.address, contract_abi=contract.abi, contract_version=contract.version) return contract, receipt # receipt def find_raw_contract_data( self, contract_name: str, requested_version: str = 'latest') -> Tuple[str, dict]: try: contract_data = self._raw_contract_cache[contract_name] except KeyError: raise self.UnknownContract( '{} is not a locally compiled contract.'.format(contract_name)) except TypeError: if self._raw_contract_cache is NO_COMPILATION_PERFORMED: message = "The local contract compiler cache is empty because no compilation was performed." raise self.InterfaceError(message) raise try: return requested_version, contract_data[requested_version] except KeyError: if requested_version != 'latest' and requested_version != 'earliest': available = ', '.join(contract_data.keys()) raise self.UnknownContract( f'Version {contract_name} of contract {contract_name} is not a locally compiled. ' f'Available versions: {available}') if len(contract_data.keys()) == 1: return next(iter(contract_data.items())) # Get the latest or the earliest versions current_version_parsed = (-1, -1, -1) current_version = None current_data = None for version, data in contract_data.items(): major, minor, patch = [int(v) for v in version[1:].split(".", 3)] if current_version_parsed[0] == -1 or \ requested_version == 'latest' and (major, minor, patch) > current_version_parsed or \ requested_version == 'earliest' and (major, minor, patch) < current_version_parsed: current_version_parsed = (major, minor, patch) current_data = data current_version = version return current_version, current_data def __get_contract_interface( self, contract_name: str, version: str = 'latest', address: ChecksumAddress = None) -> VersionedContract: """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['evm']['bytecode']['object'], version=version, address=address, ContractFactoryClass=self._CONTRACT_FACTORY) return contract def get_contract_instance(self, address: ChecksumAddress, contract_name: str, version: str = 'latest') -> VersionedContract: """Retrieve compiled contract data from the cache and return web3 contract instantiated for some address""" contract_instance = self.__get_contract_interface( address=address, contract_name=contract_name, version=version) return contract_instance def get_contract_factory(self, contract_name: str, version: str = 'latest') -> VersionedContract: """Retrieve compiled contract data from the cache and return web3 contract factory""" contract_factory = self.__get_contract_interface( contract_name=contract_name, version=version) return contract_factory def _wrap_contract( self, wrapper_contract: VersionedContract, target_contract: VersionedContract) -> VersionedContract: """ Used for upgradeable contracts; Returns a new contract object assembled with its own address but the abi of the other. """ # Wrap the contract wrapped_contract = self.client.w3.eth.contract( abi=target_contract.abi, address=wrapper_contract.address, version=target_contract.version, ContractFactoryClass=self._CONTRACT_FACTORY) return wrapped_contract @validate_checksum_address def get_proxy_contract(self, registry: BaseContractRegistry, target_address: str, proxy_name: str) -> VersionedContract: # Lookup proxies; Search for a registered proxy that targets this contract record records = registry.search(contract_name=proxy_name) dispatchers = list() for name, version, address, abi in records: proxy_contract = self.client.w3.eth.contract( abi=abi, address=address, version=version, ContractFactoryClass=self._CONTRACT_FACTORY) # Read this dispatchers target address from the blockchain proxy_live_target_address = proxy_contract.functions.target().call( ) if proxy_live_target_address == target_address: dispatchers.append(proxy_contract) if len(dispatchers) > 1: message = f"Multiple Dispatcher deployments are targeting {target_address}" raise self.InterfaceError(message) try: return dispatchers[0] except IndexError: raise self.UnknownContract( f"No registered Dispatcher deployments target {target_address}" )
def test_multiversion_contract(): # Prepare compiler base_dir = TEST_MULTIVERSION_CONTRACTS v1_dir, v2_dir = base_dir / 'v1', base_dir / 'v2' bundles = [ SourceBundle(base_path=SOLIDITY_SOURCE_ROOT, other_paths=(v1_dir, )), SourceBundle(base_path=SOLIDITY_SOURCE_ROOT, other_paths=(v2_dir, )) ] compiled_contracts = multiversion_compile(source_bundles=bundles) # Prepare chain BlockchainDeployerInterface.GAS_STRATEGIES = { **BlockchainDeployerInterface.GAS_STRATEGIES, 'free': free_gas_price_strategy } blockchain_interface = BlockchainDeployerInterface( provider_uri='tester://pyevm/2', gas_strategy='free') blockchain_interface.connect(compile_now=False) blockchain_interface._raw_contract_cache = compiled_contracts origin = blockchain_interface.client.accounts[0] blockchain_interface.transacting_power = TransactingPower( password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(blockchain_interface.client), account=origin) blockchain_interface.transacting_power.activate() # Searching both contract through raw data contract_name = "VersionTest" requested_version = "v1.2.3" version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version=requested_version) assert version == requested_version version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version="latest") assert version == requested_version requested_version = "v1.1.4" version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version=requested_version) assert version == requested_version version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version="earliest") assert version == requested_version # Deploy different contracts and check their versions registry = InMemoryContractRegistry() contract, receipt = blockchain_interface.deploy_contract( deployer_address=origin, registry=registry, contract_name=contract_name, contract_version="v1.1.4") assert contract.version == "v1.1.4" assert contract.functions.VERSION().call() == 1 contract, receipt = blockchain_interface.deploy_contract( deployer_address=origin, registry=registry, contract_name=contract_name, contract_version="earliest") assert contract.version == "v1.1.4" assert contract.functions.VERSION().call() == 1 contract, receipt = blockchain_interface.deploy_contract( deployer_address=origin, registry=registry, contract_name=contract_name, contract_version="v1.2.3") assert contract.version == "v1.2.3" assert contract.functions.VERSION().call() == 2 contract, receipt = blockchain_interface.deploy_contract( deployer_address=origin, registry=registry, contract_name=contract_name, contract_version="latest") assert contract.version == "v1.2.3" assert contract.functions.VERSION().call() == 2 contract, receipt = blockchain_interface.deploy_contract( deployer_address=origin, registry=registry, contract_name=contract_name) assert contract.version == "v1.2.3" assert contract.functions.VERSION().call() == 2
class TesterBlockchain(BlockchainDeployerInterface): """ Blockchain subclass with additional test utility methods and options. """ __test__ = False # prohibit pytest from collecting this object as a test # Solidity SOURCES: List[SourceBundle] = [ SourceBundle(base_path=SOLIDITY_SOURCE_ROOT, other_paths=(TEST_SOLIDITY_SOURCE_ROOT, )) ] # Web3 GAS_STRATEGIES = { **BlockchainDeployerInterface.GAS_STRATEGIES, 'free': free_gas_price_strategy } PROVIDER_URI = PYEVM_DEV_URI DEFAULT_GAS_STRATEGY = 'free' # Reserved addresses _ETHERBASE = 0 _ALICE = 1 _BOB = 2 _FIRST_STAKER = 5 _FIRST_URSULA = _FIRST_STAKER + NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS # Internal __STAKERS_RANGE = range(NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS) __WORKERS_RANGE = range(NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS) __ACCOUNT_CACHE = list() # Defaults DEFAULT_ECONOMICS = StandardTokenEconomics() def __init__(self, test_accounts: int = NUMBER_OF_ETH_TEST_ACCOUNTS, poa: bool = True, light: bool = False, eth_airdrop: bool = False, free_transactions: bool = False, compile_now: bool = True, *args, **kwargs): self.free_transactions = free_transactions EXPECTED_CONFIRMATION_TIME_IN_SECONDS[ 'free'] = 5 # Just some upper-limit super().__init__(provider_uri=self.PROVIDER_URI, poa=poa, light=light, *args, **kwargs) self.log = Logger("test-blockchain") self.connect(compile_now=compile_now) # 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): if self.free_transactions: self.w3.eth.setGasPriceStrategy(free_gas_price_strategy) def __generate_insecure_unlocked_accounts(self, quantity: int) -> List[str]: # # Sanity Check - Only PyEVM can be used. # # Detect provider platform client_version = self.w3.clientVersion if 'Geth' in client_version: raise RuntimeError("WARNING: Geth providers are not implemented.") elif "Parity" in client_version: raise RuntimeError( "WARNING: Parity providers are not implemented.") 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.w3.eth.accounts tx_hashes = list() for address in addresses: tx = {'to': address, 'from': coinbase, 'value': amount} txhash = self.w3.eth.sendTransaction(tx) _receipt = self.wait_for_receipt(txhash) tx_hashes.append(txhash) eth_amount = Web3().fromWei(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, periods: int = None): """ Wait the specified number of wait_hours by comparing block timestamps and mines a single block. """ more_than_one_arg = sum(map(bool, (hours, seconds, periods))) > 1 if more_than_one_arg: raise ValueError( "Specify hours, seconds, or periods, not a combination") if periods: duration = self.DEFAULT_ECONOMICS.seconds_per_period * periods base = self.DEFAULT_ECONOMICS.seconds_per_period elif hours: duration = hours * (60 * 60) base = 60 * 60 elif seconds: duration = seconds base = 1 else: raise ValueError("Specify either hours, seconds, or periods.") now = self.w3.eth.getBlock('latest').timestamp end_timestamp = ((now + duration) // base) * base self.w3.eth.web3.testing.timeTravel(timestamp=end_timestamp) self.w3.eth.web3.testing.mine(1) delta = maya.timedelta(seconds=end_timestamp - now) self.log.info( f"Time traveled {delta} " f"| period {epoch_to_period(epoch=end_timestamp, seconds_per_period=self.DEFAULT_ECONOMICS.seconds_per_period)} " f"| epoch {end_timestamp}") @classmethod def bootstrap_network( cls, registry: Optional[BaseContractRegistry] = None, economics: BaseEconomics = None ) -> Tuple['TesterBlockchain', 'InMemoryContractRegistry']: """For use with metric testing scripts""" if registry is None: registry = InMemoryContractRegistry() testerchain = cls() if not BlockchainInterfaceFactory.is_interface_initialized( provider_uri=testerchain.provider_uri): BlockchainInterfaceFactory.register_interface( interface=testerchain) origin = testerchain.client.etherbase admin = ContractAdministrator(deployer_address=origin, registry=registry, signer=Web3Signer(testerchain.client), economics=economics or cls.DEFAULT_ECONOMICS) gas_limit = None # TODO: Gas management - #842 for deployer_class in admin.primary_deployer_classes: if deployer_class is StakingEscrowDeployer: admin.deploy_contract( contract_name=deployer_class.contract_name, gas_limit=gas_limit, deployment_mode=INIT) else: admin.deploy_contract( contract_name=deployer_class.contract_name, gas_limit=gas_limit) admin.deploy_contract( contract_name=StakingEscrowDeployer.contract_name, gas_limit=gas_limit) return testerchain, registry @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.__WORKERS_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 staker_account(self, index): if index not in self.__STAKERS_RANGE: raise ValueError( f"Staker index must be lower than {NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS}" ) return self.client.accounts[index + self._FIRST_STAKER] @property def ursulas_accounts(self): return list(self.ursula_account(i) for i in self.__WORKERS_RANGE) @property def stakers_accounts(self): return list(self.staker_account(i) for i in self.__STAKERS_RANGE) @property def unassigned_accounts(self): special_accounts = [ self.etherbase_account, self.alice_account, self.bob_account ] assigned_accounts = set(self.stakers_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 result = self.client.wait_for_receipt(transaction_hash=txhash, timeout=timeout) if result.status == 0: raise TransactionFailed() return result def get_block_number(self) -> int: return self.client.block_number def read_storage_slot(self, address, slot): # https://github.com/ethereum/web3.py/issues/1490 address = to_canonical_address(address) return self.client.w3.provider.ethereum_tester.backend.chain.get_vm( ).state.get_storage(address, slot)
def test_upgradeability(temp_dir_path): # Prepare remote source for compilation download_github_dir(GITHUB_SOURCE_LINK, temp_dir_path) # Prepare the blockchain TesterBlockchain.SOURCES = [ SourceBundle(base_path=SOLIDITY_SOURCE_ROOT, other_paths=(TEST_SOLIDITY_SOURCE_ROOT, )), SourceBundle(base_path=Path(temp_dir_path)) ] eth_provider_uri = 'tester://pyevm/2' # TODO: Testerchain caching Issues try: blockchain_interface = TesterBlockchain(gas_strategy='free') blockchain_interface.eth_provider_uri = eth_provider_uri blockchain_interface.connect() origin = blockchain_interface.client.accounts[0] BlockchainInterfaceFactory.register_interface( interface=blockchain_interface) transacting_power = TransactingPower( password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(blockchain_interface.client), account=origin) economics = make_token_economics(blockchain_interface) # Check contracts with multiple versions contract_name = StakingEscrowDeployer.contract_name skip_staking_escrow_test = skip_test(blockchain_interface, contract_name) if skip_staking_escrow_test: return # Prepare master version of contracts and upgrade to the latest registry = InMemoryContractRegistry() token_deployer = NucypherTokenDeployer(registry=registry, economics=economics) token_deployer.deploy(transacting_power=transacting_power) staking_escrow_deployer = StakingEscrowDeployer(registry=registry, economics=economics) staking_escrow_deployer.deploy(deployment_mode=constants.INIT, transacting_power=transacting_power) if not skip_staking_escrow_test: economics.worklock_supply = economics.maximum_allowed_locked worklock_deployer = WorklockDeployer(registry=registry, economics=economics) worklock_deployer.deploy(transacting_power=transacting_power) staking_escrow_deployer = StakingEscrowDeployer(registry=registry, economics=economics) deploy_base_contract(blockchain_interface, staking_escrow_deployer, transacting_power=transacting_power, skipt_test=skip_staking_escrow_test) if not skip_staking_escrow_test: prepare_staker(blockchain_interface=blockchain_interface, deployer=staking_escrow_deployer, transacting_power=transacting_power) staking_escrow_deployer.upgrade( transacting_power=transacting_power, contract_version="latest", confirmations=0) finally: # Unregister interface # TODO: Move to method? with contextlib.suppress(KeyError): del BlockchainInterfaceFactory._interfaces[eth_provider_uri]
def test_upgradeability(temp_dir_path): # Prepare remote source for compilation download_github_dir(GITHUB_SOURCE_LINK, temp_dir_path) # Prepare the blockchain BlockchainDeployerInterface.SOURCES = [ SourceBundle(base_path=SOLIDITY_SOURCE_ROOT), SourceBundle(base_path=Path(temp_dir_path)) ] provider_uri = 'tester://pyevm/2' # TODO: Testerchain caching Issues try: blockchain_interface = BlockchainDeployerInterface( provider_uri=provider_uri, gas_strategy='free') blockchain_interface.connect() origin = blockchain_interface.client.accounts[0] BlockchainInterfaceFactory.register_interface( interface=blockchain_interface) blockchain_interface.transacting_power = TransactingPower( password=INSECURE_DEVELOPMENT_PASSWORD, account=origin) blockchain_interface.transacting_power.activate() economics = make_token_economics(blockchain_interface) # Check contracts with multiple versions contract_name = AdjudicatorDeployer.contract_name skip_adjudicator_test = skip_test(blockchain_interface, contract_name) contract_name = StakingEscrowDeployer.contract_name skip_staking_escrow_test = skip_test(blockchain_interface, contract_name) contract_name = PolicyManagerDeployer.contract_name skip_policy_manager_test = skip_test(blockchain_interface, contract_name) if not skip_adjudicator_test and not skip_staking_escrow_test and not skip_policy_manager_test: return # Prepare master version of contracts and upgrade to the latest registry = InMemoryContractRegistry() token_deployer = NucypherTokenDeployer(registry=registry, deployer_address=origin, economics=economics) token_deployer.deploy() staking_escrow_deployer = StakingEscrowDeployer( registry=registry, deployer_address=origin, economics=economics) staking_escrow_deployer.deploy(deployment_mode=constants.INIT) policy_manager_deployer = PolicyManagerDeployer( registry=registry, deployer_address=origin, economics=economics) deploy_base_contract(blockchain_interface, policy_manager_deployer, skipt_test=skip_policy_manager_test) adjudicator_deployer = AdjudicatorDeployer(registry=registry, deployer_address=origin, economics=economics) deploy_base_contract(blockchain_interface, adjudicator_deployer, skipt_test=skip_adjudicator_test) if skip_staking_escrow_test: worklock_deployer = WorklockDeployer(registry=registry, deployer_address=origin, economics=economics) worklock_deployer.deploy() staking_escrow_deployer = StakingEscrowDeployer( registry=registry, deployer_address=origin, economics=economics) deploy_base_contract(blockchain_interface, staking_escrow_deployer, skipt_test=skip_staking_escrow_test) if not skip_staking_escrow_test: # TODO prepare at least one staker before calling upgrade staking_escrow_deployer.upgrade(contract_version="latest", confirmations=0) if not skip_policy_manager_test: policy_manager_deployer.upgrade(contract_version="latest", confirmations=0) if not skip_adjudicator_test: adjudicator_deployer.upgrade(contract_version="latest", confirmations=0) finally: # Unregister interface # TODO: Move to method? with contextlib.suppress(KeyError): del BlockchainInterfaceFactory._interfaces[provider_uri]
def test_deployer_interface_multiversion_contract(): # Prepare compiler base_dir = TEST_MULTIVERSION_CONTRACTS v1_dir, v2_dir = base_dir / 'v1', base_dir / 'v2' # TODO: Check type of sources # I am a contract administrator and I an compiling a new updated version of an existing contract... # Represents "Manually hardcoding" a new source directory on BlockchainDeployerInterface.SOURCES. BlockchainDeployerInterface.SOURCES = (SourceBundle(base_path=v1_dir), SourceBundle(base_path=v2_dir)) # Prepare chain BlockchainInterfaceFactory._interfaces.clear() blockchain_interface = BlockchainDeployerInterface( provider_uri='tester://pyevm', gas_strategy='free') blockchain_interface.connect() BlockchainInterfaceFactory.register_interface( interface=blockchain_interface) # Lets this test run in isolation origin = blockchain_interface.client.accounts[0] transacting_power = TransactingPower( password=INSECURE_DEVELOPMENT_PASSWORD, signer=Web3Signer(blockchain_interface.client), account=origin) # Searching both contract through raw data contract_name = "VersionTest" requested_version = "v1.2.3" version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version=requested_version) assert version == requested_version version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version="latest") assert version == requested_version requested_version = "v1.1.4" version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version=requested_version) assert version == requested_version version, _data = blockchain_interface.find_raw_contract_data( contract_name=contract_name, requested_version="earliest") assert version == requested_version # Deploy different contracts and check their versions registry = InMemoryContractRegistry() contract, receipt = blockchain_interface.deploy_contract( transacting_power=transacting_power, registry=registry, contract_name=contract_name, contract_version="v1.1.4") assert contract.version == "v1.1.4" assert contract.functions.VERSION().call() == 1 contract, receipt = blockchain_interface.deploy_contract( transacting_power=transacting_power, registry=registry, contract_name=contract_name, contract_version="earliest") assert contract.version == "v1.1.4" assert contract.functions.VERSION().call() == 1 contract, receipt = blockchain_interface.deploy_contract( transacting_power=transacting_power, registry=registry, contract_name=contract_name, contract_version="v1.2.3") assert contract.version == "v1.2.3" assert contract.functions.VERSION().call() == 2 contract, receipt = blockchain_interface.deploy_contract( transacting_power=transacting_power, registry=registry, contract_name=contract_name, contract_version="latest") assert contract.version == "v1.2.3" assert contract.functions.VERSION().call() == 2 contract, receipt = blockchain_interface.deploy_contract( transacting_power=transacting_power, registry=registry, contract_name=contract_name) assert contract.version == "v1.2.3" assert contract.functions.VERSION().call() == 2