TokenNetworkAddress, ) from raiden_contracts.constants import CONTRACT_TOKEN_NETWORK_REGISTRY, CONTRACT_USER_DEPOSIT from raiden_contracts.tests.utils import get_random_privkey, to_canonical_address from raiden_libs.events import ( ReceiveChannelClosedEvent, ReceiveChannelOpenedEvent, ReceiveTokenNetworkCreatedEvent, UpdatedHeadBlockEvent, ) from raiden_libs.logging import format_to_hex from ..libs.mocks.web3 import ContractMock, Web3Mock PARTICIPANT1_PRIVKEY, PARTICIPANT1 = make_privkey_address() PARTICIPANT2 = Address(bytes([2] * 20)) def test_save_and_load_token_networks(pathfinding_service_mock_empty): pfs = pathfinding_service_mock_empty token_address = Address(bytes([1] * 20)) token_network_address = TokenNetworkAddress(bytes([2] * 20)) channel_id = ChannelID(1) p1 = Address(bytes([3] * 20)) p2 = Address(bytes([4] * 20)) events = [ ReceiveTokenNetworkCreatedEvent( token_address=token_address, token_network_address=token_network_address, block_number=BlockNumber(1),
def make_address() -> Address: return Address(make_20bytes())
def deploy_solidity_contract( self, # pylint: disable=too-many-locals contract_name, all_contracts, libraries=None, constructor_parameters=None, contract_path=None, ): """ Deploy a solidity contract. Args: sender (address): the sender address contract_name (str): the name of the contract to compile all_contracts (dict): the json dictionary containing the result of compiling a file libraries (list): A list of libraries to use in deployment constructor_parameters (tuple): A tuple of arguments to pass to the constructor contract_path (str): If we are dealing with solc >= v0.4.9 then the path to the contract is a required argument to extract the contract data from the `all_contracts` dict. """ if libraries is None: libraries = dict() if constructor_parameters is None: constructor_parameters = [] all_contracts = copy.deepcopy(all_contracts) if contract_name in all_contracts: contract_key = contract_name elif contract_path is not None: contract_key = os.path.basename( contract_path) + ':' + contract_name if contract_key not in all_contracts: raise ValueError('Unknown contract {}'.format(contract_name)) else: raise ValueError( 'Unknown contract {} and no contract_path given'.format( contract_name), ) libraries = dict(libraries) contract = all_contracts[contract_key] contract_interface = contract['abi'] symbols = solidity_unresolved_symbols(contract['bin']) if symbols: available_symbols = list( map(solidity_library_symbol, all_contracts.keys())) unknown_symbols = set(symbols) - set(available_symbols) if unknown_symbols: msg = 'Cannot deploy contract, known symbols {}, unresolved symbols {}.'.format( available_symbols, unknown_symbols, ) raise Exception(msg) dependencies = deploy_dependencies_symbols(all_contracts) deployment_order = dependencies_order_of_build( contract_key, dependencies) deployment_order.pop() # remove `contract_name` from the list log.debug('Deploying dependencies: {}'.format( str(deployment_order))) for deploy_contract in deployment_order: dependency_contract = all_contracts[deploy_contract] hex_bytecode = solidity_resolve_symbols( dependency_contract['bin'], libraries) bytecode = unhexlify(hex_bytecode) dependency_contract['bin'] = bytecode transaction_hash_hex = self.send_transaction( to=Address(b''), data=bytecode, ) transaction_hash = unhexlify(transaction_hash_hex) self.poll(transaction_hash) receipt = self.get_transaction_receipt(transaction_hash) contract_address = receipt['contractAddress'] # remove the hexadecimal prefix 0x from the address contract_address = contract_address[2:] libraries[deploy_contract] = contract_address deployed_code = self.web3.eth.getCode( to_checksum_address(contract_address)) if not deployed_code: raise RuntimeError( 'Contract address has no code, check gas usage.') hex_bytecode = solidity_resolve_symbols(contract['bin'], libraries) bytecode = unhexlify(hex_bytecode) contract['bin'] = bytecode if isinstance(contract['bin'], str): contract['bin'] = unhexlify(contract['bin']) if not constructor_parameters: constructor_parameters = () contract = self.web3.eth.contract(abi=contract['abi'], bytecode=contract['bin']) bytecode = contract.constructor( *constructor_parameters).buildTransaction()['data'] transaction_hash_hex = self.send_transaction( to=Address(b''), data=bytecode, ) transaction_hash = unhexlify(transaction_hash_hex) self.poll(transaction_hash) receipt = self.get_transaction_receipt(transaction_hash) contract_address = receipt['contractAddress'] deployed_code = self.web3.eth.getCode( to_checksum_address(contract_address)) if not deployed_code: raise RuntimeError( 'Deployment of {} failed. Contract address has no code, check gas usage.' .format(contract_name, ), ) return self.new_contract_proxy( contract_interface, contract_address, )
TODEVICE = "toDevice" # supports sending and receiving toDevice messages on Matrix class ServerListType(Enum): ACTIVE_SERVERS = "active_servers" ALL_SERVERS = "all_servers" # Set at 64 since parity's default is 64 and Geth's default is 128 # TODO: Make this configurable. Since in parity this is also a configurable value STATE_PRUNING_AFTER_BLOCKS = 64 STATE_PRUNING_SAFETY_MARGIN = 8 NO_STATE_QUERY_AFTER_BLOCKS = STATE_PRUNING_AFTER_BLOCKS - STATE_PRUNING_SAFETY_MARGIN NULL_ADDRESS_BYTES = bytes(20) NULL_ADDRESS_HEX = to_hex_address(Address(NULL_ADDRESS_BYTES)) NULL_ADDRESS_CHECKSUM = to_checksum_address(Address(NULL_ADDRESS_BYTES)) EMPTY_HASH = BlockHash(bytes(32)) EMPTY_BALANCE_HASH = BalanceHash(bytes(32)) EMPTY_MESSAGE_HASH = AdditionalHash(bytes(32)) EMPTY_SIGNATURE = Signature(bytes(65)) EMPTY_SECRET = Secret(bytes(32)) EMPTY_SECRETHASH = SecretHash(bytes(32)) EMPTY_SECRET_SHA256 = sha256_secrethash(EMPTY_SECRET) LOCKSROOT_OF_NO_LOCKS = Locksroot(keccak(b"")) EMPTY_LOCKSROOT = Locksroot(bytes(32)) ZERO_TOKENS = TokenAmount(0) ABSENT_SECRET = Secret(b"")
def deploy_solidity_contract( self, # pylint: disable=too-many-locals contract_name: str, all_contracts: Dict[str, ABI], libraries: Dict[str, str] = None, constructor_parameters: Tuple[Any] = None, contract_path: str = None, ): """ Deploy a solidity contract. Args: contract_name: The name of the contract to compile. all_contracts: The json dictionary containing the result of compiling a file. libraries: A list of libraries to use in deployment. constructor_parameters: A tuple of arguments to pass to the constructor. contract_path: If we are dealing with solc >= v0.4.9 then the path to the contract is a required argument to extract the contract data from the `all_contracts` dict. """ if libraries: libraries = dict(libraries) else: libraries = dict() ctor_parameters = constructor_parameters or () all_contracts = copy.deepcopy(all_contracts) if contract_name in all_contracts: contract_key = contract_name elif contract_path is not None: contract_key = os.path.basename( contract_path) + ":" + contract_name if contract_key not in all_contracts: raise ValueError("Unknown contract {}".format(contract_name)) else: raise ValueError( "Unknown contract {} and no contract_path given".format( contract_name)) contract = all_contracts[contract_key] contract_interface = contract["abi"] symbols = solidity_unresolved_symbols(contract["bin"]) if symbols: available_symbols = list( map(solidity_library_symbol, all_contracts.keys())) unknown_symbols = set(symbols) - set(available_symbols) if unknown_symbols: msg = "Cannot deploy contract, known symbols {}, unresolved symbols {}.".format( available_symbols, unknown_symbols) raise Exception(msg) dependencies = deploy_dependencies_symbols(all_contracts) deployment_order = dependencies_order_of_build( contract_key, dependencies) deployment_order.pop() # remove `contract_name` from the list log.debug("Deploying dependencies: {}".format( str(deployment_order)), node=pex(self.address)) for deploy_contract in deployment_order: dependency_contract = all_contracts[deploy_contract] hex_bytecode = solidity_resolve_symbols( dependency_contract["bin"], libraries) bytecode = decode_hex(hex_bytecode) dependency_contract["bin"] = bytecode gas_limit = self.web3.eth.getBlock( "latest")["gasLimit"] * 8 // 10 transaction_hash = self.send_transaction(to=Address(b""), startgas=gas_limit, data=bytecode) self.poll(transaction_hash) receipt = self.get_transaction_receipt(transaction_hash) contract_address = receipt["contractAddress"] # remove the hexadecimal prefix 0x from the address contract_address = remove_0x_prefix(contract_address) libraries[deploy_contract] = contract_address deployed_code = self.web3.eth.getCode( to_checksum_address(contract_address)) if not deployed_code: raise RuntimeError( "Contract address has no code, check gas usage.") hex_bytecode = solidity_resolve_symbols(contract["bin"], libraries) bytecode = decode_hex(hex_bytecode) contract["bin"] = bytecode if isinstance(contract["bin"], str): contract["bin"] = decode_hex(contract["bin"]) contract_object = self.web3.eth.contract(abi=contract["abi"], bytecode=contract["bin"]) contract_transaction = contract_object.constructor( *ctor_parameters).buildTransaction() transaction_hash = self.send_transaction( to=Address(b""), data=contract_transaction["data"], startgas=self._gas_estimate_correction( contract_transaction["gas"]), ) self.poll(transaction_hash) receipt = self.get_transaction_receipt(transaction_hash) contract_address = receipt["contractAddress"] deployed_code = self.web3.eth.getCode( to_checksum_address(contract_address)) if not deployed_code: raise RuntimeError( "Deployment of {} failed. Contract address has no code, check gas usage." .format(contract_name)) return self.new_contract_proxy(contract_interface, contract_address), receipt
def services_bundle_from_contracts_deployment( config: RaidenConfig, proxy_manager: ProxyManager, routing_mode: RoutingMode, deployed_addresses: DeploymentAddresses, pathfinding_service_address: str, enable_monitoring: bool, ) -> ServicesBundle: """ Initialize and setup the contract proxies. Depending on the provided contract addresses via the CLI, the routing mode, the environment type and the network id try to initialize the proxies. Returns the initialized proxies or exits the application with an error if there is a problem. Also depending on the given arguments populate config with PFS related settings """ node_network_id = config.chain_id environment_type = config.environment_type user_deposit_address = deployed_addresses.user_deposit_address service_registry_address = deployed_addresses.service_registry_address token_network_registry_address = deployed_addresses.token_network_registry_address contractname_address: List[Tuple[str, Address, Callable]] = [ ("user_deposit", Address(user_deposit_address), proxy_manager.user_deposit) ] if routing_mode == RoutingMode.PFS: contractname_address.append( ("service_registry", Address(service_registry_address), proxy_manager.service_registry) ) if enable_monitoring or routing_mode == RoutingMode.PFS: contractname_address.append( ( "monitoring_service", Address(deployed_addresses.monitoring_service_address), proxy_manager.monitoring_service, ) ) contractname_address.append( ("one_to_n", Address(deployed_addresses.one_to_n_address), proxy_manager.one_to_n) ) proxies = dict() for contractname, address, constructor in contractname_address: try: proxy = constructor(address) except ContractCodeMismatch as e: handle_contract_code_mismatch(e) except AddressWithoutCode: handle_contract_no_code(contractname, address) except AddressWrongContract: handle_contract_wrong_address(contractname, address) proxies[contractname] = proxy if routing_mode == RoutingMode.PFS: check_pfs_configuration(pathfinding_service_address=pathfinding_service_address) pfs_info = configure_pfs_or_exit( pfs_url=pathfinding_service_address, routing_mode=routing_mode, service_registry=proxies["service_registry"], node_network_id=node_network_id, token_network_registry_address=TokenNetworkRegistryAddress( token_network_registry_address ), pathfinding_max_fee=config.services.pathfinding_max_fee, ) msg = "Eth address of selected pathfinding service is unknown." assert pfs_info.payment_address is not None, msg # Only check that PFS is registered in production mode if environment_type == Environment.PRODUCTION: check_pfs_for_production( service_registry=proxies["service_registry"], pfs_info=pfs_info ) config.pfs_config = PFSConfig( info=pfs_info, maximum_fee=config.services.pathfinding_max_fee, iou_timeout=config.services.pathfinding_iou_timeout, max_paths=config.services.pathfinding_max_paths, ) else: config.pfs_config = None return ServicesBundle( user_deposit=cast(UserDeposit, proxies.get("user_deposit")), service_registry=cast(ServiceRegistry, proxies.get("service_registry")), monitoring_service=cast(MonitoringService, proxies.get("monitoring_service")), one_to_n=cast(OneToN, proxies.get("one_to_n")), )
def start_mediated_transfer_with_secret( self, token_network_identifier: TokenNetworkID, amount: PaymentAmount, target: TargetAddress, identifier: PaymentID, secret: Secret, secret_hash: SecretHash = None, ) -> PaymentStatus: if secret_hash is None: secret_hash = sha3(secret) # LEFTODO: Supply a proper block id secret_registered = self.default_secret_registry.check_registered( secrethash=secret_hash, block_identifier='latest', ) if secret_registered: raise RaidenUnrecoverableError( f'Attempted to initiate a locked transfer with secrethash {pex(secret_hash)}.' f' That secret is already registered onchain.', ) self.start_health_check_for(Address(target)) if identifier is None: identifier = create_default_identifier() with self.payment_identifier_lock: payment_status = self.targets_to_identifiers_to_statuses[target].get(identifier) if payment_status: payment_status_matches = payment_status.matches( token_network_identifier, amount, ) if not payment_status_matches: raise PaymentConflict( 'Another payment with the same id is in flight', ) return payment_status payment_status = PaymentStatus( payment_identifier=identifier, amount=amount, token_network_identifier=token_network_identifier, payment_done=AsyncResult(), secret=secret, secret_hash=secret_hash, ) self.targets_to_identifiers_to_statuses[target][identifier] = payment_status init_initiator_statechange = initiator_init( raiden=self, transfer_identifier=identifier, transfer_amount=amount, transfer_secret=secret, token_network_identifier=token_network_identifier, target_address=target, ) # Dispatch the state change even if there are no routes to create the # wal entry. self.handle_state_change(init_initiator_statechange) return payment_status
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), )
def get_deprecation_executor(self, block_identifier: BlockIdentifier) -> Address: return Address( to_canonical_address( self.proxy.functions.deprecation_executor().call( block_identifier=block_identifier)))
def handle_message_lockedtransfer( raiden: "RaidenService", message: LockedTransfer # pylint: disable=unused-argument ) -> List[StateChange]: secrethash = message.lock.secrethash # We must check if the secret was registered against the latest block, # even if the block is forked away and the transaction that registers # the secret is removed from the blockchain. The rationale here is that # someone else does know the secret, regardless of the chain state, so # the node must not use it to start a payment. # # For this particular case, it's preferable to use `latest` instead of # having a specific block_hash, because it's preferable to know if the secret # was ever known, rather than having a consistent view of the blockchain. registered = raiden.default_secret_registry.is_secret_registered( secrethash=secrethash, block_identifier=BLOCK_ID_LATEST ) if registered: log.warning( f"Ignoring received locked transfer with secrethash {to_hex(secrethash)} " f"since it is already registered in the secret registry" ) return [] assert message.sender, "Invalid message dispatched, it should be signed" if message.target == TargetAddress(raiden.address): raiden.immediate_health_check_for(Address(message.initiator)) from_transfer = lockedtransfersigned_from_message(message) from_hop = HopState( node_address=message.sender, # pylint: disable=E1101 channel_identifier=from_transfer.balance_proof.channel_identifier, ) init_target_statechange = ActionInitTarget( from_hop=from_hop, transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) return [init_target_statechange] else: from_transfer = lockedtransfersigned_from_message(message) from_hop = HopState( message.sender, from_transfer.balance_proof.channel_identifier, # pylint: disable=E1101 ) token_network_address = ( from_transfer.balance_proof.token_network_address # pylint: disable=E1101 ) route_states = routing.resolve_routes( routes=message.metadata.routes, token_network_address=token_network_address, chain_state=views.state_from_raiden(raiden), ) init_mediator_statechange = ActionInitMediator( from_hop=from_hop, route_states=route_states, from_transfer=from_transfer, balance_proof=from_transfer.balance_proof, sender=from_transfer.balance_proof.sender, # pylint: disable=no-member ) return [init_mediator_statechange]
from raiden.tests.utils import factories from raiden.tests.utils.tests import fixture_all_combinations from raiden.transfer.mediated_transfer.mediation_fee import FeeScheduleState from raiden.transfer.mediated_transfer.state_change import ( ReceiveLockExpired, ReceiveSecretRequest, ReceiveSecretReveal, ) from raiden.transfer.state_change import ReceiveDelivered, ReceiveProcessed, ReceiveUnlock from raiden.utils import sha3 from raiden.utils.packing import pack_balance_proof, pack_reward_proof, pack_signed_balance_proof from raiden.utils.signer import LocalSigner, recover from raiden.utils.typing import Address, TokenAmount from raiden_contracts.constants import MessageTypeId MSC_ADDRESS = Address(bytes([1] * 20)) PARTNER_PRIVKEY, PARTNER_ADDRESS = factories.make_privkey_address() PRIVKEY, ADDRESS = factories.make_privkey_address() signer = LocalSigner(PRIVKEY) def test_signature(): ping = Ping(nonce=0, current_protocol_version=0, signature=EMPTY_SIGNATURE) ping.sign(signer) assert ping.sender == ADDRESS def test_request_monitoring() -> None: properties = factories.BalanceProofSignedStateProperties(pkey=PARTNER_PRIVKEY) balance_proof = factories.create(properties) partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state(
def public_key_to_address(public_key: PublicKey) -> Address: """Converts a public key to an Ethereum address.""" key_bytes = public_key.format(compressed=False) return Address(keccak(key_bytes[1:])[-20:])
TokenAmount, TokenNetworkRegistryAddress, ) from raiden_contracts.constants import ( CONTRACT_SECRET_REGISTRY, CONTRACT_SERVICE_REGISTRY, CONTRACT_TOKEN_NETWORK_REGISTRY, CONTRACT_USER_DEPOSIT, ) from raiden_contracts.utils.type_aliases import ChainID token_network_registry_address_test_default = TokenNetworkRegistryAddress( to_canonical_address("0xB9633dd9a9a71F22C933bF121d7a22008f66B908") ) user_deposit_address_test_default = Address( to_canonical_address("0x8888888888888888888888888888888888888888") ) pfs_payment_address_default = to_canonical_address("0xB9633dd9a9a71F22C933bF121d7a22008f66B907") PFS_INFO = PFSInfo( url="my-pfs", price=TokenAmount(12), chain_id=ChainID(5), token_network_registry_address=token_network_registry_address_test_default, user_deposit_address=user_deposit_address_test_default, payment_address=pfs_payment_address_default, confirmed_block_number=BlockNumber(1), message="This is your favorite pathfinding service", operator="John Doe", version="0.0.3",
def test_crash(tmpdir, mockchain): # pylint: disable=too-many-locals """ Process blocks and compare results with/without crash A somewhat meaninful crash handling is simulated by not including the UpdatedHeadBlockEvent in every block. """ token_address = Address(bytes([1] * 20)) token_network_address = TokenNetworkAddress(bytes([2] * 20)) channel_id = ChannelID(1) p1 = Address(bytes([3] * 20)) p2 = Address(bytes([4] * 20)) events = [ [ ReceiveTokenNetworkCreatedEvent( token_address=token_address, token_network_address=token_network_address, block_number=BlockNumber(1), ) ], [UpdatedHeadBlockEvent(BlockNumber(2))], [ ReceiveChannelOpenedEvent( token_network_address=token_network_address, channel_identifier=channel_id, participant1=p1, participant2=p2, settle_timeout=1000, block_number=BlockNumber(3), ) ], [UpdatedHeadBlockEvent(BlockNumber(4))], ] mockchain(events) server_private_key = get_random_privkey() contracts = { CONTRACT_TOKEN_NETWORK_REGISTRY: ContractMock(), CONTRACT_USER_DEPOSIT: ContractMock(), } def new_service(filename): service = PathfindingService( web3=Web3Mock(), private_key=server_private_key, contracts=contracts, db_filename=os.path.join(tmpdir, filename), ) return service # initialize stable service stable_service = new_service("stable.db") # process each block and compare results between crashy and stable service for to_block in range(len(events)): crashy_service = new_service( "crashy.db") # new instance to simulate crash result_state: List[dict] = [] for service in [stable_service, crashy_service]: service._process_new_blocks(BlockNumber(to_block)) # pylint: disable=protected-access result_state.append( dict(db_dump=list(service.database.conn.iterdump()))) # both instances should have the same state after processing for stable_state, crashy_state in zip(result_state[0].values(), result_state[1].values()): # do asserts for each key separately to get better error messages assert stable_state == crashy_state
def test_register_secret_happy_path( secret_registry_proxy: SecretRegistry, contract_manager: ContractManager ) -> None: """Test happy path of SecretRegistry with a single secret. Test that `register_secret` changes the smart contract state by registering the secret, this can be verified by the block height and the existence of the SecretRegistered event. """ secret = make_secret() secrethash = sha256_secrethash(secret) secret_unregistered = make_secret() secrethash_unregistered = sha256_secrethash(secret_unregistered) assert not secret_registry_proxy.is_secret_registered( secrethash=secrethash, block_identifier=BLOCK_ID_LATEST ), "Test setup is invalid, secret must be unknown" assert not secret_registry_proxy.is_secret_registered( secrethash=secrethash_unregistered, block_identifier=BLOCK_ID_LATEST ), "Test setup is invalid, secret must be unknown" secret_registry_proxy.client.wait_until_block(BlockNumber(STATE_PRUNING_AFTER_BLOCKS + 1)) with pytest.raises(NoStateForBlockIdentifier): secret_registry_proxy.is_secret_registered( secrethash=secrethash_unregistered, block_identifier=BlockNumber(0) ) secret_registry_proxy.register_secret(secret=secret) proxy_manager = ProxyManager( rpc_client=secret_registry_proxy.client, contract_manager=contract_manager, metadata=ProxyManagerMetadata( token_network_registry_deployed_at=GENESIS_BLOCK_NUMBER, filters_start_at=GENESIS_BLOCK_NUMBER, ), ) logs = get_contract_events( proxy_manager=proxy_manager, abi=proxy_manager.contract_manager.get_contract_abi(CONTRACT_SECRET_REGISTRY), contract_address=Address(secret_registry_proxy.address), ) secret_registered = must_have_event( logs, {"event": "SecretRevealed", "args": {"secrethash": secrethash}} ) msg = "SecretRegistry.register_secret returned but the SecretRevealed event was not emitted." assert secret_registered, msg registered_block = secret_registry_proxy.get_secret_registration_block_by_secrethash( secrethash=secrethash, block_identifier=BLOCK_ID_LATEST ) msg = ( "Block height returned by the SecretRegistry.get_secret_registration_block_by_secrethash " "does not match the block from the SecretRevealed event." ) assert secret_registered["block_number"] == registered_block, msg block = secret_registry_proxy.get_secret_registration_block_by_secrethash( secrethash=secrethash_unregistered, block_identifier=BLOCK_ID_LATEST ) assert block is None, "The secret that was not registered must not change block height!"
def test_payment_statuses_are_restored( # pylint: disable=unused-argument raiden_network: List[App], token_addresses: List[TokenAddress], network_wait: float): """ Test that when the Raiden is restarted, the dictionary of `targets_to_identifiers_to_statuses` is populated before the transport is started. This should happen because if a client gets restarted during a transfer cycle, once restarted, the client will proceed with the cycle until the transfer is successfully sent. However, the dictionary `targets_to_identifiers_to_statuses` will not contain the payment identifiers that were originally registered when the previous client started the transfers. Related issue: https://github.com/raiden-network/raiden/issues/3432 """ app0, app1 = raiden_network token_address = token_addresses[0] chain_state = views.state_from_app(app0) token_network_registry_address = app0.raiden.default_registry.address token_network_address = views.get_token_network_address_by_token_address( chain_state, token_network_registry_address, token_address) assert token_network_address target_address = TargetAddress(app1.raiden.address) # make a few transfers from app0 to app1 amount = PaymentAmount(1) spent_amount = TokenAmount(7) for identifier in range(spent_amount): # Make sure the transfer is not completed secret = make_secret(identifier) assert isinstance(app0.raiden.raiden_event_handler, HoldRaidenEventHandler) # for mypy app0.raiden.raiden_event_handler.hold(SendSecretReveal, {"secret": secret}) identifier = identifier + 1 payment_status = app0.raiden.mediated_transfer_async( token_network_address=token_network_address, amount=amount, target=target_address, identifier=PaymentID(identifier), secret=secret, ) assert payment_status.payment_identifier == identifier app0_restart = App( config=app0.config, rpc_client=app0.raiden.rpc_client, proxy_manager=app0.raiden.proxy_manager, query_start_block=BlockNumber(0), default_registry=app0.raiden.default_registry, default_secret_registry=app0.raiden.default_secret_registry, default_service_registry=app0.raiden.default_service_registry, default_one_to_n_address=app0.raiden.default_one_to_n_address, default_msc_address=app0.raiden.default_msc_address, transport=MatrixTransport( config=app0.raiden.config.transport, environment=app0.raiden.config.environment_type), raiden_event_handler=RaidenEventHandler(), message_handler=MessageHandler(), routing_mode=RoutingMode.PRIVATE, ) app0.stop() del app0 # from here on the app0_restart should be used # stop app1 to make sure that we don't complete the transfers before our checks app1.stop() app0_restart.start() # Check that the payment statuses were restored properly after restart for identifier in range(spent_amount): identifier = PaymentID(identifier + 1) mapping = app0_restart.raiden.targets_to_identifiers_to_statuses status = mapping[target_address][identifier] assert status.amount == 1 assert status.payment_identifier == identifier assert status.token_network_address == token_network_address app1.start() # now that our checks are done start app1 again with watch_for_unlock_failures(*raiden_network): waiting.wait_for_healthy(app0_restart.raiden, app1.raiden.address, network_wait) waiting.wait_for_payment_balance( raiden=app1.raiden, token_network_registry_address=token_network_registry_address, token_address=token_address, partner_address=app0_restart.raiden.address, target_address=Address(target_address), target_balance=spent_amount, retry_timeout=network_wait, ) # Check that payments are completed after both nodes come online after restart for identifier in range(spent_amount): assert raiden_events_search_for_item( app0_restart.raiden, EventPaymentSentSuccess, { "identifier": identifier + 1, "amount": 1 }, )
def get_best_routes( chain_state: ChainState, token_network_id: TokenNetworkID, one_to_n_address: Optional[Address], from_address: InitiatorAddress, to_address: TargetAddress, amount: PaymentAmount, previous_address: Optional[Address], config: Dict[str, Any], privkey: bytes, ) -> Tuple[List[RouteState], Optional[UUID]]: services_config = config.get("services", None) # the pfs should not be requested when the target is linked via a direct channel if to_address in views.all_neighbour_nodes(chain_state): neighbours = get_best_routes_internal( chain_state=chain_state, token_network_id=token_network_id, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, ) channel_state = views.get_channelstate_by_token_network_and_partner( chain_state=chain_state, token_network_id=token_network_id, partner_address=Address(to_address), ) for route_state in neighbours: if to_address == route_state.node_address and ( channel_state # other conditions about e.g. channel state are checked in best routes internal and channel.get_distributable( sender=channel_state.our_state, receiver=channel_state.partner_state) >= amount): return [route_state], None if (services_config and services_config["pathfinding_service_address"] is not None and one_to_n_address is not None): pfs_answer_ok, pfs_routes, pfs_feedback_token = get_best_routes_pfs( chain_state=chain_state, token_network_id=token_network_id, one_to_n_address=one_to_n_address, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, config=services_config, privkey=privkey, ) if pfs_answer_ok: log.info("Received route(s) from PFS", routes=pfs_routes, feedback_token=pfs_feedback_token) return pfs_routes, pfs_feedback_token else: log.warning("Request to Pathfinding Service was not successful, " "falling back to internal routing.") return ( get_best_routes_internal( chain_state=chain_state, token_network_id=token_network_id, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, ), None, )
def deploy_smoketest_contracts( client: JSONRPCClient, chain_id: ChainID, contract_manager: ContractManager, token_address: AddressHex, ) -> Dict[str, Address]: if client.eth_node is EthClient.GETH: client.web3.geth.personal.unlockAccount(client.web3.eth.accounts[0], DEFAULT_PASSPHRASE) elif client.eth_node is EthClient.PARITY: client.web3.parity.personal.unlockAccount(client.web3.eth.accounts[0], DEFAULT_PASSPHRASE) contract_proxy, _ = client.deploy_single_contract( contract_name=CONTRACT_SECRET_REGISTRY, contract=contract_manager.get_contract(CONTRACT_SECRET_REGISTRY), constructor_parameters=None, ) secret_registry_address = Address( to_canonical_address(contract_proxy.address)) secret_registry_constructor_arguments = ( to_checksum_address(secret_registry_address), chain_id, TEST_SETTLE_TIMEOUT_MIN, TEST_SETTLE_TIMEOUT_MAX, UINT256_MAX, ) contract_proxy, _ = client.deploy_single_contract( contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY, contract=contract_manager.get_contract( CONTRACT_TOKEN_NETWORK_REGISTRY), constructor_parameters=secret_registry_constructor_arguments, ) token_network_registry_address = Address( to_canonical_address(contract_proxy.address)) service_registry_constructor_arguments = ( token_address, EMPTY_ADDRESS, int(500e18), 6, 5, 180 * SECONDS_PER_DAY, 1000, 200 * SECONDS_PER_DAY, ) service_registry_contract, _ = client.deploy_single_contract( contract_name=CONTRACT_SERVICE_REGISTRY, contract=contract_manager.get_contract(CONTRACT_SERVICE_REGISTRY), constructor_parameters=service_registry_constructor_arguments, ) service_registry_address = Address( to_canonical_address(service_registry_contract.address)) user_deposit_contract, _ = client.deploy_single_contract( contract_name=CONTRACT_USER_DEPOSIT, contract=contract_manager.get_contract(CONTRACT_USER_DEPOSIT), constructor_parameters=(token_address, UINT256_MAX), ) user_deposit_address = Address( to_canonical_address(user_deposit_contract.address)) monitoring_service_contract, _ = client.deploy_single_contract( contract_name=CONTRACT_MONITORING_SERVICE, contract=contract_manager.get_contract(CONTRACT_MONITORING_SERVICE), constructor_parameters=( token_address, service_registry_address, user_deposit_address, token_network_registry_address, ), ) monitoring_service_address = Address( to_canonical_address(monitoring_service_contract.address)) one_to_n_contract, _ = client.deploy_single_contract( contract_name=CONTRACT_ONE_TO_N, contract=contract_manager.get_contract(CONTRACT_ONE_TO_N), constructor_parameters=(user_deposit_address, chain_id, service_registry_address), ) one_to_n_address = Address(to_canonical_address(one_to_n_contract.address)) proxy_manager = ProxyManager( rpc_client=client, contract_manager=contract_manager, metadata=ProxyManagerMetadata( token_network_registry_deployed_at=GENESIS_BLOCK_NUMBER, filters_start_at=GENESIS_BLOCK_NUMBER, ), ) user_deposit_proxy = UserDeposit( jsonrpc_client=client, user_deposit_address=UserDepositAddress( to_canonical_address(user_deposit_contract.address)), contract_manager=contract_manager, proxy_manager=proxy_manager, block_identifier=BLOCK_ID_LATEST, ) transaction_hash = user_deposit_proxy.init( monitoring_service_address=MonitoringServiceAddress( monitoring_service_address), one_to_n_address=OneToNAddress(one_to_n_address), given_block_identifier=BLOCK_ID_LATEST, ) assert is_tx_hash_bytes(transaction_hash) addresses = { CONTRACT_SECRET_REGISTRY: secret_registry_address, CONTRACT_TOKEN_NETWORK_REGISTRY: token_network_registry_address, CONTRACT_SERVICE_REGISTRY: service_registry_address, CONTRACT_USER_DEPOSIT: user_deposit_address, CONTRACT_MONITORING_SERVICE: monitoring_service_address, CONTRACT_ONE_TO_N: one_to_n_address, } return addresses
from raiden.utils.typing import Address KEYSTORE_FILE_NAME = "keystore.txt" KEYSTORE_PASSWORD = "******" TEST_MSC_ADDRESS = Address(b"9" * 20)
def run_smoketest(print_step: StepPrinter, setup: RaidenTestSetup) -> None: print_step("Starting Raiden") app = None try: app = run_raiden_service(**setup.args) raiden_api = app.raiden_api assert raiden_api is not None # for mypy partner_address = Address(b"1" * 20) block = BlockNumber(app.get_block_number() + DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS) # Proxies now use the confirmed block hash to query the chain for # prerequisite checks. Wait a bit here to make sure that the confirmed # block hash contains the deployed token network or else things break wait_for_block(raiden=app, block_number=block, retry_timeout=1.0) raiden_api.channel_open( registry_address=TokenNetworkRegistryAddress( setup.contract_addresses[CONTRACT_TOKEN_NETWORK_REGISTRY]), token_address=TokenAddress( to_canonical_address(setup.token.address)), partner_address=partner_address, ) raiden_api.set_total_channel_deposit( registry_address=TokenNetworkRegistryAddress( setup.contract_addresses[CONTRACT_TOKEN_NETWORK_REGISTRY]), token_address=TokenAddress( to_canonical_address(setup.token.address)), partner_address=partner_address, total_deposit=TEST_DEPOSIT_AMOUNT, ) token_addresses = [to_checksum_address(setup.token.address) ] # type: ignore print_step("Running smoketest") raiden_service = app token_network_added_events = raiden_service.default_registry.filter_token_added_events( ) events_token_addresses = [ event["args"]["token_address"] for event in token_network_added_events ] assert events_token_addresses == token_addresses token_networks = views.get_token_identifiers( views.state_from_raiden(raiden_service), raiden_service.default_registry.address) assert len(token_networks) == 1 channel_state = views.get_channelstate_for( chain_state=views.state_from_raiden(raiden_service), token_network_registry_address=raiden_service.default_registry. address, token_address=token_networks[0], partner_address=partner_address, ) assert channel_state distributable = channel.get_distributable(channel_state.our_state, channel_state.partner_state) assert distributable == TEST_DEPOSIT_AMOUNT assert Balance( distributable) == channel_state.our_state.contract_balance assert channel.get_status(channel_state) == ChannelState.STATE_OPENED port_number = raiden_service.config.rest_api.port response = requests.get( f"http://localhost:{port_number}/api/v1/channels") assert response.status_code == HTTPStatus.OK response_json = json.loads(response.content) assert response_json[0]["partner_address"] == to_checksum_address( partner_address) assert response_json[0]["state"] == "opened" assert int(response_json[0]["balance"]) > 0 finally: if app is not None: app.stop() app.greenlet.get()
def get_best_routes( chain_state: ChainState, token_network_address: TokenNetworkAddress, one_to_n_address: Optional[OneToNAddress], from_address: InitiatorAddress, to_address: TargetAddress, amount: PaymentAmount, previous_address: Optional[Address], pfs_config: Optional[PFSConfig], privkey: PrivateKey, ) -> Tuple[Optional[str], List[RouteState], Optional[UUID]]: token_network = views.get_token_network_by_address(chain_state, token_network_address) assert token_network, "The token network must be validated and exist." # Always use a direct channel if available: # - There are no race conditions and the capacity is guaranteed to be # available. # - There will be no mediation fees # - The transfer will be faster if Address( to_address ) in token_network.partneraddresses_to_channelidentifiers.keys(): for channel_id in token_network.partneraddresses_to_channelidentifiers[ Address(to_address)]: channel_state = token_network.channelidentifiers_to_channels[ channel_id] # direct channels don't have fees payment_with_fee_amount = PaymentWithFeeAmount(amount) is_usable = channel.is_channel_usable_for_new_transfer( channel_state, payment_with_fee_amount, None) if is_usable is channel.ChannelUsability.USABLE: direct_route = RouteState( route=[Address(from_address), Address(to_address)], estimated_fee=FeeAmount(0)) return None, [direct_route], None if pfs_config is None or one_to_n_address is None: log.warning("Pathfinding Service could not be used.") return "Pathfinding Service could not be used.", list(), None # Does any channel have sufficient capacity for the payment? channels = [ token_network.channelidentifiers_to_channels[channel_id] for channels_to_partner in token_network.partneraddresses_to_channelidentifiers.values() for channel_id in channels_to_partner ] for channel_state in channels: payment_with_fee_amount = PaymentWithFeeAmount(amount) is_usable = channel.is_channel_usable_for_new_transfer( channel_state, payment_with_fee_amount, None) if is_usable is channel.ChannelUsability.USABLE: break else: return ("You have no suitable channel to initiate this payment.", list(), None) # Make sure that the PFS knows about the last channel we opened latest_channel_opened_at = 0 for channel_state in token_network.channelidentifiers_to_channels.values(): latest_channel_opened_at = max( latest_channel_opened_at, channel_state.open_transaction.finished_block_number) pfs_error_msg, pfs_routes, pfs_feedback_token = get_best_routes_pfs( chain_state=chain_state, token_network_address=token_network_address, one_to_n_address=one_to_n_address, from_address=from_address, to_address=to_address, amount=amount, previous_address=previous_address, pfs_config=pfs_config, privkey=privkey, pfs_wait_for_block=BlockNumber(latest_channel_opened_at), ) if pfs_error_msg: log.warning( "Request to Pathfinding Service was not successful. " "No routes to the target were found.", pfs_message=pfs_error_msg, ) return pfs_error_msg, list(), None if not pfs_routes: # As of version 0.5 it is possible for the PFS to return an empty # list of routes without an error message. return "PFS could not find any routes", list(), None log.info("Received route(s) from PFS", routes=pfs_routes, feedback_token=pfs_feedback_token) return pfs_error_msg, pfs_routes, pfs_feedback_token
def make_receive_transfer_mediated( channel_state: NettingChannelState, privkey: bytes, nonce: Nonce, transferred_amount: TokenAmount, lock: HashTimeLockState, pending_locks: PendingLocksState = None, locked_amount: Optional[PaymentWithFeeAmount] = None, chain_id: Optional[ChainID] = None, ) -> LockedTransferSignedState: typecheck(lock, HashTimeLockState) signer = LocalSigner(privkey) address = signer.address if address not in (channel_state.our_state.address, channel_state.partner_state.address): raise ValueError("Private key does not match any of the participants.") if pending_locks is None: locks = make_empty_pending_locks_state() locks.locks.append(lock.encoded) else: assert bytes(lock.encoded) in pending_locks.locks locks = pending_locks if locked_amount is None: locked_amount = lock.amount assert locked_amount >= lock.amount locksroot = compute_locksroot(locks) payment_identifier = PaymentID(nonce) transfer_target = make_target_address() transfer_initiator = make_initiator_address() chain_id = chain_id or channel_state.chain_id transfer_metadata = Metadata(routes=[ RouteMetadata( route=[channel_state.our_state.address, Address(transfer_target)]) ]) mediated_transfer_msg = LockedTransfer( chain_id=chain_id, message_identifier=make_message_identifier(), payment_identifier=payment_identifier, nonce=nonce, token_network_address=channel_state.token_network_address, token=channel_state.token_address, channel_identifier=channel_state.identifier, transferred_amount=transferred_amount, locked_amount=TokenAmount(locked_amount), recipient=channel_state.partner_state.address, locksroot=locksroot, lock=Lock(amount=lock.amount, expiration=lock.expiration, secrethash=lock.secrethash), target=transfer_target, initiator=transfer_initiator, signature=EMPTY_SIGNATURE, fee=0, metadata=transfer_metadata, ) mediated_transfer_msg.sign(signer) receive_lockedtransfer = LockedTransferSignedState( payment_identifier=payment_identifier, token=channel_state.token_address, lock=lock, initiator=transfer_initiator, target=transfer_target, message_identifier=make_message_identifier(), balance_proof=balanceproof_from_envelope(mediated_transfer_msg), routes=[ route_metadata.route for route_metadata in transfer_metadata.routes ], ) return receive_lockedtransfer
def mock_matrix( monkeypatch, mock_raiden_service, retry_interval_initial, retry_interval_max, retries_before_backoff, ): from raiden.network.transport.matrix.client import GMatrixClient from raiden.network.transport.matrix.utils import UserPresence from raiden.network.transport.matrix import transport as transport_module def make_client_monkey(handle_messages_callback, handle_member_join_callback, servers, *args, **kwargs): # pylint: disable=unused-argument return GMatrixClient( handle_messages_callback=handle_messages_callback, handle_member_join_callback=handle_member_join_callback, base_url=servers[0], ) monkeypatch.setattr(User, "get_display_name", lambda _: "random_display_name") monkeypatch.setattr(transport_module, "make_client", make_client_monkey) def mock_get_room_ids_for_address( # pylint: disable=unused-argument klass, address: Address) -> List[str]: return ["!roomID:server"] def mock_set_room_id_for_address( # pylint: disable=unused-argument self, address: Address, room_id: Optional[str]): pass def mock_on_messages(messages): # pylint: disable=unused-argument for message in messages: assert message assert message.sender def mock_get_user_presence(self, user_id: str): # pylint: disable=unused-argument return UserPresence.ONLINE config = MatrixTransportConfig( broadcast_rooms=[], retries_before_backoff=retries_before_backoff, retry_interval_initial=retry_interval_initial, retry_interval_max=retry_interval_max, server="http://none", available_servers=[], ) def mock_join_room(self, room_id_or_alias): raise MatrixRequestError(code=404, content={ "errcode": "M_UNKNOWN", "error": "No known servers" }) transport = MatrixTransport(config=config, environment=Environment.DEVELOPMENT) transport._raiden_service = mock_raiden_service transport._stop_event.clear() transport._address_mgr.add_userid_for_address(Address(factories.HOP1), USERID1) transport._client.user_id = USERID0 monkeypatch.setattr(MatrixTransport, "_get_room_ids_for_address", mock_get_room_ids_for_address) monkeypatch.setattr(MatrixTransport, "_set_room_id_for_address", mock_set_room_id_for_address) monkeypatch.setattr(transport._raiden_service, "on_messages", mock_on_messages) monkeypatch.setattr(GMatrixClient, "get_user_presence", mock_get_user_presence) monkeypatch.setattr(GMatrixClient, "join_room", mock_join_room) monkeypatch.setattr(transport._client.api, "leave_room", lambda room_id: None) monkeypatch.setattr(transport._client, "sync_token", "already_synced") return transport
def _deposit_check_result( self, transaction_sent: Optional[TransactionSent], token: Token, beneficiary: Address, total_deposit: TokenAmount, amount_to_deposit: TokenAmount, ) -> None: if transaction_sent is None: failed_at = self.client.get_block(BLOCK_ID_LATEST) failed_at_blocknumber = failed_at["number"] self.client.check_for_insufficient_eth( transaction_name="deposit", transaction_executed=False, required_gas=self.gas_measurements["UserDeposit.deposit"], block_identifier=failed_at_blocknumber, ) latest_deposit = self.get_total_deposit( address=beneficiary, block_identifier=failed_at_blocknumber ) amount_to_deposit = TokenAmount(total_deposit - latest_deposit) allowance = token.allowance( owner=self.node_address, spender=Address(self.address), block_identifier=failed_at_blocknumber, ) whole_balance = self.whole_balance(block_identifier=failed_at_blocknumber) whole_balance_limit = self.whole_balance_limit(block_identifier=failed_at_blocknumber) if allowance < amount_to_deposit: msg = ( "The allowance is insufficient. Check concurrent deposits " "for the same user deposit but different proxies." ) raise RaidenRecoverableError(msg) if token.balance_of(self.node_address, failed_at_blocknumber) < amount_to_deposit: msg = "The address doesnt have enough tokens" raise RaidenRecoverableError(msg) if latest_deposit < total_deposit: msg = "Deposit amount did not increase after deposit transaction" raise RaidenRecoverableError(msg) if whole_balance + amount_to_deposit > UINT256_MAX: msg = ( f"Current whole balance is {whole_balance}. " f"The new deposit of {amount_to_deposit} would lead to an overflow." ) raise RaidenRecoverableError(msg) if whole_balance + amount_to_deposit > whole_balance_limit: msg = ( f"Current whole balance is {whole_balance}. " f"With the new deposit of {amount_to_deposit}, the deposit " f"limit of {whole_balance_limit} would be exceeded." ) raise RaidenRecoverableError(msg) raise RaidenRecoverableError("Deposit failed of unknown reason") else: transaction_mined = self.client.poll_transaction(transaction_sent) if not was_transaction_successfully_mined(transaction_mined): failed_at_blocknumber = BlockNumber(transaction_mined.receipt["blockNumber"]) latest_deposit = self.get_total_deposit( address=beneficiary, block_identifier=failed_at_blocknumber ) amount_to_deposit = TokenAmount(total_deposit - latest_deposit) allowance = token.allowance( owner=self.node_address, spender=Address(self.address), block_identifier=failed_at_blocknumber, ) whole_balance = self.whole_balance(block_identifier=failed_at_blocknumber) whole_balance_limit = self.whole_balance_limit( block_identifier=failed_at_blocknumber ) if latest_deposit >= total_deposit: msg = "Deposit amount already increased after another transaction" raise RaidenRecoverableError(msg) if allowance < amount_to_deposit: msg = ( "The allowance is insufficient. Check concurrent deposits " "for the same token network but different proxies." ) raise RaidenRecoverableError(msg) # Because we acquired the lock for the token, and the gas estimation succeeded, # We know that the account had enough balance for the deposit transaction. if token.balance_of(self.node_address, failed_at_blocknumber) < amount_to_deposit: msg = ( "Transaction failed and balance decreased unexpectedly. " "This could be a bug in Raiden or a mallicious " "ERC20 Token." ) raise RaidenRecoverableError(msg) if whole_balance + amount_to_deposit > UINT256_MAX: msg = ( f"Current whole balance is {whole_balance}. " f"The new deposit of {amount_to_deposit} caused an overflow." ) raise RaidenRecoverableError(msg) if whole_balance + amount_to_deposit > whole_balance_limit: msg = ( f"Current whole balance is {whole_balance}. " f"With the new deposit of {amount_to_deposit}, the deposit " f"limit of {whole_balance_limit} was exceeded." ) raise RaidenRecoverableError(msg) if latest_deposit < total_deposit: msg = "Deposit amount did not increase after deposit transaction" raise RaidenRecoverableError(msg) raise RaidenRecoverableError("Deposit failed of unknown reason")
def token_minting_proxy(client: JSONRPCClient, address: TokenAddress) -> ContractProxy: return client.new_contract_proxy(abi=_MINT_ABI, contract_address=Address(address))
def handle_secretrequest( initiator_state: InitiatorTransferState, state_change: ReceiveSecretRequest, channel_state: NettingChannelState, pseudo_random_generator: random.Random, ) -> TransitionResult[InitiatorTransferState]: is_message_from_target = ( state_change.sender == initiator_state.transfer_description.target and state_change.secrethash == initiator_state.transfer_description.secrethash and state_change.payment_identifier == initiator_state.transfer_description.payment_identifier) lock = channel.get_lock(channel_state.our_state, initiator_state.transfer_description.secrethash) # This should not ever happen. This task clears itself when the lock is # removed. assert lock is not None, "channel is does not have the transfer's lock" already_received_secret_request = initiator_state.received_secret_request # transfer_description.amount is the actual payment amount without fees. # For the transfer to be valid and the unlock allowed the target must # receive at least that amount. is_valid_secretrequest = ( state_change.amount >= initiator_state.transfer_description.amount and state_change.expiration == lock.expiration and initiator_state.transfer_description.secret != ABSENT_SECRET) if already_received_secret_request and is_message_from_target: # A secret request was received earlier, all subsequent are ignored # as it might be an attack iteration = TransitionResult(initiator_state, list()) elif is_valid_secretrequest and is_message_from_target: # Reveal the secret to the target node and wait for its confirmation. # At this point the transfer is not cancellable anymore as either the lock # timeouts or a secret reveal is received. # # Note: The target might be the first hop # message_identifier = message_identifier_from_prng( pseudo_random_generator) transfer_description = initiator_state.transfer_description recipient = transfer_description.target revealsecret = SendSecretReveal( recipient=Address(recipient), message_identifier=message_identifier, secret=transfer_description.secret, canonical_identifier=CANONICAL_IDENTIFIER_GLOBAL_QUEUE, ) initiator_state.transfer_state = "transfer_secret_revealed" initiator_state.received_secret_request = True iteration = TransitionResult(initiator_state, [revealsecret]) elif not is_valid_secretrequest and is_message_from_target: initiator_state.received_secret_request = True invalid_request = EventInvalidSecretRequest( payment_identifier=state_change.payment_identifier, intended_amount=initiator_state.transfer_description.amount, actual_amount=state_change.amount, ) iteration = TransitionResult(initiator_state, [invalid_request]) else: iteration = TransitionResult(initiator_state, list()) return iteration
def handle_inittarget( state_change: ActionInitTarget, channel_state: NettingChannelState, pseudo_random_generator: random.Random, block_number: BlockNumber, ) -> TransitionResult[Optional[TargetTransferState]]: """ Handles an ActionInitTarget state change. """ transfer = state_change.transfer route = state_change.route assert channel_state.identifier == transfer.balance_proof.channel_identifier is_valid, channel_events, errormsg = channel.handle_receive_lockedtransfer( channel_state, transfer, ) if is_valid: # A valid balance proof does not mean the payment itself is still valid. # e.g. the lock may be near expiration or have expired. This is fine. The # message with an unusable lock must be handled to properly synchronize the # local view of the partner's channel state, allowing the next balance # proofs to be handled. This however, must only be done once, which is # enforced by the nonce increasing sequentially, which is verified by # the handler handle_receive_lockedtransfer. target_state = TargetTransferState(route, transfer) safe_to_wait, _ = is_safe_to_wait( transfer.lock.expiration, channel_state.reveal_timeout, block_number, ) # If there is not enough time to safely unlock the lock on-chain # silently let the transfer expire. The target task must be created to # handle the ReceiveLockExpired state change, which will clear the # expired lock. if safe_to_wait: message_identifier = message_identifier_from_prng(pseudo_random_generator) recipient = transfer.initiator secret_request = SendSecretRequest( recipient=Address(recipient), channel_identifier=CHANNEL_IDENTIFIER_GLOBAL_QUEUE, message_identifier=message_identifier, payment_identifier=transfer.payment_identifier, amount=transfer.lock.amount, expiration=transfer.lock.expiration, secrethash=transfer.lock.secrethash, ) channel_events.append(secret_request) iteration = TransitionResult(target_state, channel_events) else: # If the balance proof is not valid, do *not* create a task. Otherwise it's # possible for an attacker to send multiple invalid transfers, and increase # the memory usage of this Node. unlock_failed = EventUnlockClaimFailed( identifier=transfer.payment_identifier, secrethash=transfer.lock.secrethash, reason=errormsg, ) channel_events.append(unlock_failed) iteration = TransitionResult(None, channel_events) return iteration
def setup_proxies_or_exit( config: Dict[str, Any], tokennetwork_registry_contract_address: Address, secret_registry_contract_address: Address, endpoint_registry_contract_address: Address, user_deposit_contract_address: Address, service_registry_contract_address: Address, blockchain_service: BlockChainService, contracts: Dict[str, Any], routing_mode: RoutingMode, pathfinding_service_address: str, ) -> Proxies: """ Initialize and setup the contract proxies. Depending on the provided contract addresses via the CLI, the routing mode, the environment type and the network id try to initialize the proxies. Returns the initialized proxies or exits the application with an error if there is a problem. Also depending on the given arguments populate config with PFS related settings """ node_network_id = config["chain_id"] environment_type = config["environment_type"] check_smart_contract_addresses( environment_type, node_network_id, tokennetwork_registry_contract_address, secret_registry_contract_address, endpoint_registry_contract_address, contracts, ) try: registered_address: Address if tokennetwork_registry_contract_address is not None: registered_address = Address( tokennetwork_registry_contract_address) else: registered_address = to_canonical_address( contracts[CONTRACT_TOKEN_NETWORK_REGISTRY]["address"]) token_network_registry = blockchain_service.token_network_registry( registered_address) except ContractVersionMismatch as e: handle_contract_version_mismatch(e) except AddressWithoutCode: handle_contract_no_code("token network registry", tokennetwork_registry_contract_address) except AddressWrongContract: handle_contract_wrong_address("token network registry", tokennetwork_registry_contract_address) try: secret_registry = blockchain_service.secret_registry( secret_registry_contract_address or to_canonical_address( contracts[CONTRACT_SECRET_REGISTRY]["address"])) except ContractVersionMismatch as e: handle_contract_version_mismatch(e) except AddressWithoutCode: handle_contract_no_code("secret registry", secret_registry_contract_address) except AddressWrongContract: handle_contract_wrong_address("secret registry", secret_registry_contract_address) # If services contracts are provided via the CLI use them instead if user_deposit_contract_address is not None: contracts[CONTRACT_USER_DEPOSIT] = user_deposit_contract_address if service_registry_contract_address is not None: contracts[ CONTRACT_SERVICE_REGISTRY] = service_registry_contract_address user_deposit = None should_use_user_deposit = ( environment_type == Environment.DEVELOPMENT and ID_TO_NETWORKNAME.get(node_network_id) != "smoketest" and CONTRACT_USER_DEPOSIT in contracts) if should_use_user_deposit: try: user_deposit = blockchain_service.user_deposit( user_deposit_contract_address or to_canonical_address( contracts[CONTRACT_USER_DEPOSIT]["address"])) except ContractVersionMismatch as e: handle_contract_version_mismatch(e) except AddressWithoutCode: handle_contract_no_code("user deposit", user_deposit_contract_address) except AddressWrongContract: handle_contract_wrong_address("user_deposit", user_deposit_contract_address) service_registry = None if CONTRACT_SERVICE_REGISTRY in contracts or service_registry_contract_address: try: service_registry = blockchain_service.service_registry( service_registry_contract_address or to_canonical_address( contracts[CONTRACT_SERVICE_REGISTRY]["address"])) except ContractVersionMismatch as e: handle_contract_version_mismatch(e) except AddressWithoutCode: handle_contract_no_code("service registry", service_registry_contract_address) except AddressWrongContract: handle_contract_wrong_address("secret registry", service_registry_contract_address) if routing_mode == RoutingMode.PFS: check_pfs_configuration(routing_mode, environment_type, service_registry, pathfinding_service_address) pfs_config = configure_pfs_or_exit( pfs_address=pathfinding_service_address, routing_mode=routing_mode, service_registry=service_registry, ) msg = "Eth address of selected pathfinding service is unknown." assert pfs_config.eth_address is not None, msg config["services"]["pathfinding_service_address"] = pfs_config.url config["services"]["pathfinding_eth_address"] = pfs_config.eth_address config["services"]["pathfinding_fee"] = pfs_config.fee else: config["services"]["pathfinding_service_address"] = None config["services"]["pathfinding_eth_address"] = None proxies = Proxies( token_network_registry=token_network_registry, secret_registry=secret_registry, user_deposit=user_deposit, service_registry=service_registry, ) return proxies
"presence": presence.value }, } self._presence_callback(event, next(self._presence_update_ids)) class NonValidatingUserAddressManager(UserAddressManager): @staticmethod def _validate_userid_signature(user: User) -> Optional[Address]: match = USERID_RE.match(user.user_id) if not match: return None return to_canonical_address(match.group(1)) ADDR1 = Address(b"\x11" * 20) ADDR2 = Address(b'""""""""""""""""""""') INVALID_USER_ID = "bla:bla" USER0_ID = "@0x0000000000000000000000000000000000000000:server1" USER1_S1_ID = "@0x1111111111111111111111111111111111111111:server1" USER1_S2_ID = "@0x1111111111111111111111111111111111111111:server2" USER2_S1_ID = "@0x2222222222222222222222222222222222222222:server1" USER2_S2_ID = "@0x2222222222222222222222222222222222222222:server2" USER1_S1 = User(api=None, user_id=USER1_S1_ID) USER1_S2 = User(api=None, user_id=USER1_S2_ID) USER2_S1 = User(api=None, user_id=USER2_S1_ID) USER2_S2 = User(api=None, user_id=USER2_S2_ID) @pytest.fixture def user_directory_content():
def set_total_channel_deposit( self, registry_address: TokenNetworkRegistryAddress, token_address: TokenAddress, partner_address: Address, total_deposit: TokenAmount, retry_timeout: NetworkTimeout = DEFAULT_RETRY_TIMEOUT, ) -> None: """ Set the `total_deposit` in the channel with the peer at `partner_address` and the given `token_address` in order to be able to do transfers. Raises: InvalidBinaryAddress: If either token_address or partner_address is not 20 bytes long. RaidenRecoverableError: May happen for multiple reasons: - If the token approval fails, e.g. the token may validate if account has enough balance for the allowance. - The deposit failed, e.g. the allowance did not set the token aside for use and the user spent it before deposit was called. - The channel was closed/settled between the allowance call and the deposit call. AddressWithoutCode: The channel was settled during the deposit execution. DepositOverLimit: The total deposit amount is higher than the limit. UnexpectedChannelState: The channel is no longer in an open state. """ chain_state = views.state_from_raiden(self.raiden) token_addresses = views.get_token_identifiers(chain_state, registry_address) channel_state = views.get_channelstate_for( chain_state=chain_state, token_network_registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) if not is_binary_address(token_address): raise InvalidBinaryAddress( "Expected binary address format for token in channel deposit") if not is_binary_address(partner_address): raise InvalidBinaryAddress( "Expected binary address format for partner in channel deposit" ) if token_address not in token_addresses: raise UnknownTokenAddress("Unknown token address") if channel_state is None: raise NonexistingChannel( "No channel with partner_address for the given token") confirmed_block_identifier = chain_state.block_hash token = self.raiden.proxy_manager.token( token_address, block_identifier=confirmed_block_identifier) token_network_registry = self.raiden.proxy_manager.token_network_registry( registry_address, block_identifier=confirmed_block_identifier) token_network_address = token_network_registry.get_token_network( token_address=token_address, block_identifier=confirmed_block_identifier) if token_network_address is None: raise UnknownTokenAddress( f"Token {to_checksum_address(token_address)} is not registered " f"with the network {to_checksum_address(registry_address)}.") token_network_proxy = self.raiden.proxy_manager.token_network( address=token_network_address, block_identifier=confirmed_block_identifier) channel_proxy = self.raiden.proxy_manager.payment_channel( channel_state=channel_state, block_identifier=confirmed_block_identifier) blockhash = chain_state.block_hash token_network_proxy = channel_proxy.token_network safety_deprecation_switch = token_network_proxy.safety_deprecation_switch( block_identifier=blockhash) balance = token.balance_of(self.raiden.address, block_identifier=blockhash) network_balance = token.balance_of( address=Address(token_network_address), block_identifier=blockhash) token_network_deposit_limit = token_network_proxy.token_network_deposit_limit( block_identifier=blockhash) addendum = total_deposit - channel_state.our_state.contract_balance channel_participant_deposit_limit = token_network_proxy.channel_participant_deposit_limit( block_identifier=blockhash) total_channel_deposit = total_deposit + channel_state.partner_state.contract_balance is_channel_open = channel.get_status( channel_state) == ChannelState.STATE_OPENED if not is_channel_open: raise UnexpectedChannelState("Channel is not in an open state.") if safety_deprecation_switch: msg = ("This token_network has been deprecated. " "All channels in this network should be closed and " "the usage of the newly deployed token network contract " "is highly encouraged.") raise TokenNetworkDeprecated(msg) if total_deposit <= channel_state.our_state.contract_balance: raise DepositMismatch("Total deposit did not increase.") # If this check succeeds it does not imply the `deposit` will # succeed, since the `deposit` transaction may race with another # transaction. if not (balance >= addendum): msg = "Not enough balance to deposit. {} Available={} Needed={}".format( to_checksum_address(token_address), balance, addendum) raise InsufficientFunds(msg) if network_balance + addendum > token_network_deposit_limit: msg = f"Deposit of {addendum} would have exceeded the token network deposit limit." raise DepositOverLimit(msg) if total_deposit > channel_participant_deposit_limit: msg = (f"Deposit of {total_deposit} is larger than the " f"channel participant deposit limit") raise DepositOverLimit(msg) if total_channel_deposit >= UINT256_MAX: raise DepositOverLimit("Deposit overflow") try: channel_proxy.approve_and_set_total_deposit( total_deposit=total_deposit, block_identifier=blockhash) except RaidenRecoverableError as e: log.info(f"Deposit failed. {str(e)}") target_address = self.raiden.address waiting.wait_for_participant_deposit( raiden=self.raiden, token_network_registry_address=registry_address, token_address=token_address, partner_address=partner_address, target_address=target_address, target_balance=total_deposit, retry_timeout=retry_timeout, )