Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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']
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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}"
            )
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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]
Ejemplo n.º 8
0
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]
Ejemplo n.º 9
0
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