def _add_token(self, token_address: TokenAddress,
                   additional_arguments: Dict) -> Address:
        if not is_binary_address(token_address):
            raise InvalidAddress("Expected binary address format for token")

        token_proxy = Token(
            jsonrpc_client=self.client,
            token_address=token_address,
            contract_manager=self.contract_manager,
        )

        if token_proxy.total_supply() == "":
            raise InvalidToken(
                "Given token address does not follow the ERC20 standard (missing totalSupply()"
            )

        log_details = {
            "node": pex(self.node_address),
            "token_address": pex(token_address),
            "registry_address": pex(self.address),
        }
        log.debug("createERC20TokenNetwork called", **log_details)

        checking_block = self.client.get_checking_block()
        error_prefix = "Call to createERC20TokenNetwork will fail"

        kwarguments = {"_token_address": token_address}
        kwarguments.update(additional_arguments)
        gas_limit = self.proxy.estimate_gas(checking_block,
                                            "createERC20TokenNetwork",
                                            **kwarguments)

        if gas_limit:
            error_prefix = "Call to createERC20TokenNetwork failed"
            transaction_hash = self.proxy.transact(
                "createERC20TokenNetwork",
                safe_gas_limit(gas_limit,
                               GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK),
                **kwarguments,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            if transaction_executed:
                block = receipt_or_none["blockNumber"]
            else:
                block = checking_block

            required_gas = gas_limit if gas_limit else GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name="createERC20TokenNetwork",
                transaction_executed=transaction_executed,
                required_gas=required_gas,
                block_identifier=block,
            )

            if self.get_token_network(token_address, block):
                error_msg = f"{error_prefix}. Token already registered"
                log.warning(error_msg, **log_details)
                raise RaidenRecoverableError(error_msg)

            error_msg = f"{error_prefix}"
            log.critical(error_msg, **log_details)
            raise RaidenUnrecoverableError(error_msg)

        token_network_address = self.get_token_network(token_address, "latest")
        if token_network_address is None:
            msg = "createERC20TokenNetwork succeeded but token network address is Null"
            log.critical(msg, **log_details)
            raise RuntimeError(msg)

        log.info(
            "createERC20TokenNetwork successful",
            token_network_address=pex(token_network_address),
            **log_details,
        )

        return token_network_address
Exemple #2
0
    def _add_token(
        self,
        token_address: TokenAddress,
        additional_arguments: Dict,
    ) -> Address:
        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token')

        token_proxy = Token(
            jsonrpc_client=self.client,
            token_address=token_address,
            contract_manager=self.contract_manager,
        )

        if token_proxy.total_supply() == '':
            raise InvalidToken(
                'Given token address does not follow the ERC20 standard (missing totalSupply()',
            )

        log_details = {
            'node': pex(self.node_address),
            'token_address': pex(token_address),
            'registry_address': pex(self.address),
        }
        log.debug('createERC20TokenNetwork called', **log_details)

        checking_block = self.client.get_checking_block()
        error_prefix = 'Call to createERC20TokenNetwork will fail'

        kwarguments = {'_token_address': token_address}
        kwarguments.update(additional_arguments)
        gas_limit = self.proxy.estimate_gas(
            checking_block,
            'createERC20TokenNetwork',
            **kwarguments,
        )

        if gas_limit:
            error_prefix = 'Call to createERC20TokenNetwork failed'
            transaction_hash = self.proxy.transact(
                'createERC20TokenNetwork',
                safe_gas_limit(gas_limit,
                               GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK),
                **kwarguments,
            )

            self.client.poll(transaction_hash)
            receipt_or_none = check_transaction_threw(self.client,
                                                      transaction_hash)

        transaction_executed = gas_limit is not None
        if not transaction_executed or receipt_or_none:
            error_type = RaidenUnrecoverableError
            if transaction_executed:
                block = receipt_or_none['blockNumber']
            else:
                block = checking_block

            required_gas = gas_limit if gas_limit else GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK
            self.proxy.jsonrpc_client.check_for_insufficient_eth(
                transaction_name='createERC20TokenNetwork',
                transaction_executed=transaction_executed,
                required_gas=required_gas,
                block_identifier=block,
            )

            msg = ''
            if self.get_token_network(token_address, block):
                msg = 'Token already registered'
                error_type = RaidenRecoverableError

            error_msg = f'{error_prefix}. {msg}'
            if error_type == RaidenRecoverableError:
                log.warning(error_msg, **log_details)
            else:
                log.critical(error_msg, **log_details)
            raise error_type(error_msg)

        token_network_address = self.get_token_network(token_address, 'latest')
        if token_network_address is None:
            msg = 'createERC20TokenNetwork succeeded but token network address is Null'
            log.critical(msg, **log_details)
            raise RuntimeError(msg)

        log.info(
            'createERC20TokenNetwork successful',
            token_network_address=pex(token_network_address),
            **log_details,
        )

        return token_network_address
    def _add_token(
        self,
        token_address: TokenAddress,
        channel_participant_deposit_limit: TokenAmount,
        token_network_deposit_limit: TokenAmount,
        log_details: Dict[Any, Any],
    ) -> Tuple[TransactionHash, TokenNetworkAddress]:
        token_network_address = None

        kwargs = {
            "_token_address": token_address,
            "_channel_participant_deposit_limit":
            channel_participant_deposit_limit,
            "_token_network_deposit_limit": token_network_deposit_limit,
        }
        estimated_transaction = self.rpc_client.estimate_gas(
            self.proxy, "createERC20TokenNetwork", log_details, **kwargs)

        if estimated_transaction is not None:
            estimated_transaction.estimated_gas = safe_gas_limit(
                estimated_transaction.estimated_gas,
                self.gas_measurements[
                    "TokenNetworkRegistry createERC20TokenNetwork"],
            )

            transaction_sent = self.rpc_client.transact(estimated_transaction)
            transaction_mined = self.rpc_client.poll_transaction(
                transaction_sent)
            receipt = transaction_mined.receipt

            if not was_transaction_successfully_mined(transaction_mined):
                failed_at_blocknumber = BlockNumber(receipt["blockNumber"])

                max_token_networks = self.get_max_token_networks(
                    block_identifier=failed_at_blocknumber)
                token_networks_created = self.get_token_network_created(
                    block_identifier=failed_at_blocknumber)
                already_registered = self.get_token_network(
                    token_address=token_address,
                    block_identifier=failed_at_blocknumber)
                deprecation_executor = self.get_deprecation_executor(
                    block_identifier=failed_at_blocknumber)
                settlement_timeout_min = self.settlement_timeout_min(
                    block_identifier=failed_at_blocknumber)
                settlement_timeout_max = self.settlement_timeout_max(
                    block_identifier=failed_at_blocknumber)
                chain_id = self.get_chain_id(
                    block_identifier=failed_at_blocknumber)
                secret_registry_address = self.get_secret_registry_address(
                    block_identifier=failed_at_blocknumber)

                try:
                    # Creating a new instance to run the constructor checks.
                    token_proxy = Token(
                        jsonrpc_client=self.rpc_client,
                        token_address=token_address,
                        contract_manager=self.proxy_manager.contract_manager,
                        block_identifier=failed_at_blocknumber,
                    )
                except AddressWithoutCode:
                    # This cannot be an unrecoverable error, since the ERC20
                    # code is external.
                    raise RaidenRecoverableError(
                        "Token disappeared! The address "
                        f"{to_checksum_address(token_address)} did have code at "
                        f"block {log_details['given_block_identifier']}, however "
                        f"at block {failed_at_blocknumber} when the registration "
                        "transaction was mined the address didn't have code "
                        "anymore.")

                check_transaction_failure(transaction_mined, self.rpc_client)

                check_address_has_code_handle_pruned_block(
                    client=self.rpc_client,
                    address=Address(secret_registry_address),
                    contract_name=CONTRACT_SECRET_REGISTRY,
                    expected_code=decode_hex(
                        self.proxy_manager.contract_manager.
                        get_runtime_hexcode(CONTRACT_SECRET_REGISTRY)),
                    given_block_identifier=failed_at_blocknumber,
                )

                if token_networks_created >= max_token_networks:
                    raise RaidenRecoverableError(
                        "The number of existing token networks reached the maximum allowed"
                    )

                if already_registered:
                    # Race condition lost, the token network was created in a different
                    # transaction which got mined first.
                    raise RaidenRecoverableError(
                        "The token was already registered in the TokenNetworkRegistry."
                    )

                if deprecation_executor == NULL_ADDRESS_BYTES:
                    raise RaidenUnrecoverableError(
                        "The deprecation executor property for the "
                        "TokenNetworkRegistry is invalid.")

                if chain_id == 0:
                    raise RaidenUnrecoverableError(
                        "The chain ID property for the TokenNetworkRegistry is invalid."
                    )

                if chain_id != self.rpc_client.chain_id:
                    raise RaidenUnrecoverableError(
                        f"The provided chain ID {chain_id} does not match the "
                        f"network Raiden is running on: {self.rpc_client.chain_id}."
                    )

                if secret_registry_address == NULL_ADDRESS_BYTES:
                    raise RaidenUnrecoverableError(
                        "The secret registry address for the token network is invalid."
                    )

                if settlement_timeout_min == 0:
                    raise RaidenUnrecoverableError(
                        "The minimum settlement timeout for the token network "
                        "should be larger than zero.")

                if settlement_timeout_max <= settlement_timeout_min:
                    raise RaidenUnrecoverableError(
                        "The maximum settlement timeout for the token network "
                        "should be larger than the minimum settlement timeout."
                    )

                total_supply = token_proxy.total_supply(
                    block_identifier=failed_at_blocknumber)
                if not total_supply or total_supply <= 0:
                    raise RaidenRecoverableError(
                        f"The given token address is not a valid ERC20 token, "
                        f"total_supply() returned an invalid value {total_supply}."
                    )

                # At this point, the TokenNetworkRegistry fails to instantiate
                # a new TokenNetwork.
                raise RaidenUnrecoverableError(
                    "createERC20TokenNetwork failed for an unknown reason, even "
                    "though the gas estimation succeeded.")

            succeeded_at_blockhash = receipt["blockHash"]
            token_network_address = self.get_token_network(
                token_address, succeeded_at_blockhash)
            if token_network_address is None:
                msg = "createERC20TokenNetwork succeeded but token network address is Null"
                raise RaidenUnrecoverableError(msg)
        else:  # `estimated_transaction` is None
            # The latest block can not be used reliably because of reorgs,
            # therefore every call using this block has to handle pruned data.
            failed_at_block = self.rpc_client.get_block(BLOCK_ID_LATEST)
            failed_at_blockhash = failed_at_block["hash"].hex()
            failed_at_blocknumber = failed_at_block["number"]

            max_token_networks = self.get_max_token_networks(
                block_identifier=failed_at_blocknumber)
            token_networks_created = self.get_token_network_created(
                block_identifier=failed_at_blocknumber)

            already_registered = self.get_token_network(
                token_address=token_address,
                block_identifier=failed_at_blocknumber)
            deprecation_executor = self.get_deprecation_executor(
                block_identifier=failed_at_blocknumber)
            settlement_timeout_min = self.settlement_timeout_min(
                block_identifier=failed_at_blocknumber)
            settlement_timeout_max = self.settlement_timeout_max(
                block_identifier=failed_at_blocknumber)
            chain_id = self.get_chain_id(
                block_identifier=failed_at_blocknumber)
            secret_registry_address = self.get_secret_registry_address(
                block_identifier=failed_at_blocknumber)

            try:
                # Creating a new instance to run the constructor checks.
                token_proxy = Token(
                    jsonrpc_client=self.rpc_client,
                    token_address=token_address,
                    contract_manager=self.proxy_manager.contract_manager,
                    block_identifier=failed_at_blocknumber,
                )
            except AddressWithoutCode:
                # This cannot be an unrecoverable error, since the ERC20
                # code is external.
                raise RaidenRecoverableError(
                    "Token disappeared! The address "
                    "{to_checksum_address(token_address)} did have code at "
                    "block {log_details['given_block_identifier']}, however "
                    "at block {failed_at_blocknumber} when the registration "
                    "transaction was mined the address didn't have code "
                    "anymore.")

            check_address_has_code_handle_pruned_block(
                client=self.rpc_client,
                address=Address(secret_registry_address),
                contract_name=CONTRACT_SECRET_REGISTRY,
                expected_code=decode_hex(
                    self.proxy_manager.contract_manager.get_runtime_hexcode(
                        CONTRACT_SECRET_REGISTRY)),
                given_block_identifier=failed_at_blocknumber,
            )

            required_gas = self.gas_measurements[
                "TokenNetworkRegistry createERC20TokenNetwork"]

            self.rpc_client.check_for_insufficient_eth(
                transaction_name="createERC20TokenNetwork",
                transaction_executed=False,
                required_gas=required_gas,
                block_identifier=failed_at_blocknumber,
            )

            if token_networks_created >= max_token_networks:
                raise RaidenRecoverableError(
                    "The number of existing token networks reached the maximum allowed"
                )

            if already_registered:
                # Race condition lost, the token network was created in a different
                # transaction which got mined first.
                raise RaidenRecoverableError(
                    "The token was already registered in the TokenNetworkRegistry."
                )

            if deprecation_executor == NULL_ADDRESS_BYTES:
                raise RaidenUnrecoverableError(
                    "The deprecation executor property for the TokenNetworkRegistry is invalid."
                )

            if chain_id == 0:
                raise RaidenUnrecoverableError(
                    "The chain ID property for the TokenNetworkRegistry is invalid."
                )

            if chain_id != self.rpc_client.chain_id:
                raise RaidenUnrecoverableError(
                    f"The provided chain ID {chain_id} does not match the "
                    f"network Raiden is running on: {self.rpc_client.chain_id}."
                )

            if secret_registry_address == NULL_ADDRESS_BYTES:
                raise RaidenUnrecoverableError(
                    "The secret registry address for the token network is invalid."
                )

            if settlement_timeout_min <= 0:
                raise RaidenUnrecoverableError(
                    "The minimum settlement timeout for the token network "
                    "should be larger than zero.")

            if settlement_timeout_max <= settlement_timeout_min:
                raise RaidenUnrecoverableError(
                    "The maximum settlement timeout for the token network "
                    "should be larger than the minimum settlement timeout.")

            total_supply = token_proxy.total_supply(
                block_identifier=failed_at_blocknumber)
            if not total_supply or total_supply <= 0:
                raise RaidenRecoverableError(
                    f"The given token address is not a valid ERC20 token, "
                    f"total_supply() returned an invalid value {total_supply}."
                )

            # At this point, the TokenNetworkRegistry fails to instantiate
            # a new TokenNetwork.
            raise RaidenUnrecoverableError(
                f"createERC20TokenNetwork gas estimation failed for an unknown "
                f"reason. Reference block {failed_at_blockhash} "
                f"{failed_at_blocknumber}.")
        return (
            TransactionHash(transaction_mined.transaction_hash),
            TokenNetworkAddress(token_network_address),
        )