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
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), )