def main( # pylint: disable-msg=too-many-arguments private_key: str, state_db: str, web3: Web3, contracts: Dict[str, Contract], start_block: BlockNumber, confirmations: int, host: str, service_fee: TokenAmount, operator: str, info_message: str, enable_debug: bool, ) -> int: """ The Pathfinding service for the Raiden Network. """ log.info("Starting Raiden Pathfinding Service") service = None api = None try: service = PathfindingService( web3=web3, contracts=contracts, sync_start_block=start_block, required_confirmations=confirmations, private_key=private_key, poll_interval=DEFAULT_POLL_INTERVALL, db_filename=state_db, ) api = ServiceApi( pathfinding_service=service, service_fee=service_fee, debug_mode=enable_debug, one_to_n_address=contracts[CONTRACT_ONE_TO_N].address, operator=operator, info_message=info_message, ) api.run(host=host) service.run() except (KeyboardInterrupt, SystemExit): print("Exiting...") finally: log.info("Stopping Pathfinding Service...") if api: api.stop() if service: service.stop() return 0
def pathfinding_service_web3_mock( web3: Web3, user_deposit_contract: Contract, get_private_key: Callable, create_service_account: Callable, ) -> Generator[PathfindingService, None, None]: pfs_address = to_canonical_address(create_service_account()) with patch("pathfinding_service.service.MatrixListener", new=Mock): pathfinding_service = PathfindingService( web3=web3, contracts={ CONTRACT_TOKEN_NETWORK_REGISTRY: Mock(address="0x" + "9" * 40), CONTRACT_USER_DEPOSIT: user_deposit_contract, }, private_key=get_private_key(pfs_address), db_filename=":memory:", required_confirmations=1, ) yield pathfinding_service
def test_pfs_min_calculation_with_capacity_updates( pathfinding_service_web3_mock: PathfindingService, ): token_network = setup_channel(pathfinding_service_web3_mock) view_to_partner, view_from_partner = token_network.get_channel_views_for_partner( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS) message1 = get_capacity_update_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=TA(90), other_capacity=TA(110), ) pathfinding_service_web3_mock.on_capacity_update(message1) # Now the channel capacities are set to 0, since only P1 sent an update assert view_to_partner.capacity == 0 assert view_from_partner.capacity == 0 # We need two Capacity Updates, one from each side to set the capacities due to min calculation message2 = get_capacity_update_message( updating_participant=PRIVATE_KEY_2_ADDRESS, other_participant=PRIVATE_KEY_1_ADDRESS, privkey_signer=PRIVATE_KEY_2, updating_capacity=TA(110), other_capacity=TA(90), ) pathfinding_service_web3_mock.on_capacity_update(message2) # Now after both participants have sent Capacity Updates, we have the correct capacities assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Now P1 sends the same update again, the capacities should not change (no need for nonces) pathfinding_service_web3_mock.on_capacity_update(message1) assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Now P1 tries to cheat and lies about his own capacity (10000) to mediate more message3 = get_capacity_update_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=TA(10000), other_capacity=TA(110), ) pathfinding_service_web3_mock.on_capacity_update(message3) # The capacities should be calculated out of the minimum of the two capacity updates, # so stay the same assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Now P1 tries to cheat and lies about his partner's capacity (0) to block him message4 = get_capacity_update_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=TA(90), other_capacity=TA(0), ) pathfinding_service_web3_mock.on_capacity_update(message4) # The capacities should be calculated out of the minimum of the two capacity updates, # he can block his partner assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 0 # Now P1 tries to cheat and lies about his partner's capacity (10000) for no obvious reason message4 = get_capacity_update_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=TA(90), other_capacity=TA(10000), ) pathfinding_service_web3_mock.on_capacity_update(message4) # The capacities should be calculated out of the minimum of the two capacity updates assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110
def test_pfs_with_mocked_client( # pylint: disable=too-many-arguments web3, token_network_registry_contract, channel_descriptions_case_1: List, get_accounts, wait_for_blocks, user_deposit_contract, token_network, custom_token, create_channel, get_private_key, ): # pylint: disable=too-many-locals """ Instantiates some MockClients and the PathfindingService. Mocks blockchain events to setup a token network with a given topology, specified in the channel_description fixture. Tests all PFS methods w.r.t. to that topology """ clients = get_accounts(7) token_network_address = decode_hex(token_network.address) with patch("pathfinding_service.service.MatrixListener", new=Mock): pfs = PathfindingService( web3=web3, contracts={ CONTRACT_TOKEN_NETWORK_REGISTRY: token_network_registry_contract, CONTRACT_USER_DEPOSIT: user_deposit_contract, }, required_confirmations=1, db_filename=":memory:", poll_interval=0.1, sync_start_block=BlockNumber(0), private_key="3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266", ) # greenlet needs to be started and context switched to pfs.start() wait_for_blocks(1) gevent.sleep(0.1) # there should be one token network registered assert len(pfs.token_networks) == 1 token_network_model = pfs.token_networks[token_network_address] graph = token_network_model.G channel_identifiers = [] for ( p1_index, p1_deposit, _p1_capacity, _p1_fee, _p1_reveal_timeout, _p1_reachability, p2_index, p2_deposit, _p2_capacity, _p2_fee, _p2_reveal_timeout, _p2_reachability, _settle_timeout, ) in channel_descriptions_case_1: # order is important here because we check order later channel_id = create_channel(clients[p1_index], clients[p2_index])[0] channel_identifiers.append(channel_id) for address, partner_address, amount in [ (clients[p1_index], clients[p2_index], p1_deposit), (clients[p2_index], clients[p1_index], p2_deposit), ]: custom_token.functions.mint(amount).transact({"from": address}) custom_token.functions.approve(token_network.address, amount).transact( {"from": address} ) token_network.functions.setTotalDeposit( channel_id, address, amount, partner_address ).transact({"from": address}) gevent.sleep() wait_for_blocks(1) gevent.sleep(0.1) # there should be as many open channels as described assert len(token_network_model.channel_id_to_addresses.keys()) == len( channel_descriptions_case_1 ) # check that deposits, settle_timeout and transfers got registered for ( index, ( _p1_index, p1_deposit, _p1_capacity, _p1_fee, _p1_reveal_timeout, _p1_reachability, _p2_index, p2_deposit, _p2_capacity, _p2_fee, _p2_reveal_timeout, _p2_reachability, _settle_timeout, ), ) in enumerate(channel_descriptions_case_1): channel_identifier = channel_identifiers[index] p1_address, p2_address = token_network_model.channel_id_to_addresses[channel_identifier] view1: ChannelView = graph[p1_address][p2_address]["view"] view2: ChannelView = graph[p2_address][p1_address]["view"] assert view1.deposit == p1_deposit assert view2.deposit == p2_deposit assert view1.settle_timeout == TEST_SETTLE_TIMEOUT_MIN assert view2.settle_timeout == TEST_SETTLE_TIMEOUT_MIN assert view1.reveal_timeout == DEFAULT_REVEAL_TIMEOUT assert view2.reveal_timeout == DEFAULT_REVEAL_TIMEOUT # now close all channels for ( index, ( p1_index, _p1_deposit, _p1_capacity, _p1_fee, _p1_reveal_timeout, _p1_reachability, p2_index, _p2_deposit, _p2_capacity, _p2_fee, _p2_reveal_timeout, _p2_reachability, _settle_timeout, ), ) in enumerate(channel_descriptions_case_1): channel_id = channel_identifiers[index] balance_proof = HashedBalanceProof( nonce=Nonce(1), transferred_amount=0, priv_key=get_private_key(clients[p2_index]), channel_identifier=channel_id, token_network_address=token_network.address, chain_id=ChainID(1), additional_hash="0x%064x" % 0, locked_amount=0, locksroot=encode_hex(LOCKSROOT_OF_NO_LOCKS), ) token_network.functions.closeChannel( channel_id, clients[p2_index], balance_proof.balance_hash, balance_proof.nonce, balance_proof.additional_hash, balance_proof.signature, ).transact({"from": clients[p1_index], "gas": 200_000}) wait_for_blocks(1) gevent.sleep(0.1) # there should be no channels assert len(token_network_model.channel_id_to_addresses.keys()) == 0 pfs.stop()
def test_pfs_min_calculation_with_capacity_updates( pathfinding_service_web3_mock: PathfindingService): token_network = setup_channel_with_deposits(pathfinding_service_web3_mock) view_to_partner, view_from_partner = token_network.get_channel_views_for_partner( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS) # Assert initial capacity with no Capacity Updates assert view_to_partner.capacity == 100 assert view_from_partner.capacity == 100 message1 = get_updatepfs_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=90, other_capacity=110, ) pathfinding_service_web3_mock.on_pfs_update(message1) # Now the channel capacities are set to 0, since only P1 sent an update assert view_to_partner.capacity == 0 assert view_from_partner.capacity == 0 # We need two Capacity Updates, one from each side to set the capacities due to min calculation message2 = get_updatepfs_message( updating_participant=PRIVATE_KEY_2_ADDRESS, other_participant=PRIVATE_KEY_1_ADDRESS, privkey_signer=PRIVATE_KEY_2, updating_capacity=110, other_capacity=90, ) pathfinding_service_web3_mock.on_pfs_update(message2) # Now after both participants have sent Capacity Updates, we have the correct capacities assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Now P1 sends the same update again, the capacities should not change (no need for nonces) pathfinding_service_web3_mock.on_pfs_update(message1) assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Now P1 tries to cheat and lies about his own capacity (10000) to mediate more message3 = get_updatepfs_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=10000, other_capacity=110, ) pathfinding_service_web3_mock.on_pfs_update(message3) # The capacities should be calculated out of the minimum of the two capacity updates, # so stay the same assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Now P1 tries to cheat and lies about his partner's capacity (0) to block him message4 = get_updatepfs_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=90, other_capacity=0, ) pathfinding_service_web3_mock.on_pfs_update(message4) # The capacities should be calculated out of the minimum of the two capacity updates, # he can block his partner assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 0 # Now P1 tries to cheat and lies about his partner's capacity (10000) for no obvious reason message4 = get_updatepfs_message( updating_participant=PRIVATE_KEY_1_ADDRESS, other_participant=PRIVATE_KEY_2_ADDRESS, privkey_signer=PRIVATE_KEY_1, updating_capacity=90, other_capacity=10000, ) pathfinding_service_web3_mock.on_pfs_update(message4) # The capacities should be calculated out of the minimum of the two capacity updates assert view_to_partner.capacity == 90 assert view_from_partner.capacity == 110 # Test if new deposits work as expected, so P1 deposits 100 new tokens # (total deposit 200, but capacity must be 190) token_network.handle_channel_new_deposit_event( channel_identifier=DEFAULT_CHANNEL_ID, receiver=PRIVATE_KEY_1_ADDRESS, total_deposit=TokenAmount(200), ) assert view_to_partner.capacity == 190 assert view_from_partner.capacity == 110
def main( # pylint: disable=too-many-arguments,too-many-locals private_key: PrivateKey, state_db: str, web3: Web3, contracts: Dict[str, Contract], start_block: BlockNumber, confirmations: BlockTimeout, host: str, port: int, service_fee: TokenAmount, operator: str, info_message: str, enable_debug: bool, matrix_server: List[str], accept_disclaimer: bool, enable_tracing: bool, tracing_sampler: str, tracing_param: str, ) -> int: """The Pathfinding service for the Raiden Network.""" log.info("Starting Raiden Pathfinding Service") click.secho(PFS_DISCLAIMER, fg="yellow") if not accept_disclaimer: click.confirm(CONFIRMATION_OF_UNDERSTANDING, abort=True) if not confirmations: chain_id = ChainID(web3.eth.chain_id) confirmations = (BlockTimeout(0) if "arbitrum" in ID_TO_CHAINNAME.get( chain_id, "") else DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS) log.info("Setting number of confirmation blocks", confirmations=confirmations) log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3)) hex_addresses = { name: to_checksum_address(contract.address) for name, contract in contracts.items() } log.info("Contract information", addresses=hex_addresses, start_block=start_block) if enable_tracing: tracing_config = Config( config={ "sampler": { "type": tracing_sampler, "param": tracing_param }, "logging": True }, service_name="pfs", scope_manager=GeventScopeManager(), validate=True, ) # Tracer is stored in `opentracing.tracer` tracing_config.initialize_tracer() assert isinstance(web3.provider, HTTPProvider), MYPY_ANNOTATION assert web3.provider.endpoint_uri is not None, MYPY_ANNOTATION # Set `Web3` requests Session to use `SessionTracing` cache_session( web3.provider.endpoint_uri, SessionTracing(propagate=False, span_tags={"target": "ethnode"}), ) service = None api = None try: service = PathfindingService( web3=web3, contracts=contracts, sync_start_block=start_block, required_confirmations=confirmations, private_key=private_key, poll_interval=DEFAULT_POLL_INTERVALL, db_filename=state_db, matrix_servers=matrix_server, enable_tracing=enable_tracing, ) service.start() log.debug("Waiting for service to start before accepting API requests") try: service.startup_finished.get(timeout=PFS_START_TIMEOUT) except gevent.Timeout: raise Exception("PFS did not start within time.") log.debug("Starting API") api = PFSApi( pathfinding_service=service, service_fee=service_fee, debug_mode=enable_debug, one_to_n_address=to_canonical_address( contracts[CONTRACT_ONE_TO_N].address), operator=operator, info_message=info_message, enable_tracing=enable_tracing, ) api.run(host=host, port=port) service.get() except (KeyboardInterrupt, SystemExit): print("Exiting...") finally: log.info("Stopping Pathfinding Service...") if api: api.stop() if service: service.stop() return 0
def test_pfs_with_mocked_client( web3, ethereum_tester, contracts_manager: ContractManager, token_network_registry_contract, channel_descriptions_case_1: List, generate_raiden_clients, wait_for_blocks, user_deposit_contract, ): """ Instantiates some MockClients and the PathfindingService. Mocks blockchain events to setup a token network with a given topology, specified in the channel_description fixture. Tests all PFS methods w.r.t. to that topology """ clients = generate_raiden_clients(7) token_network_address = clients[0].contract.address with patch("pathfinding_service.service.MatrixListener", new=Mock): pfs = PathfindingService( web3=web3, contracts={ CONTRACT_TOKEN_NETWORK_REGISTRY: token_network_registry_contract, CONTRACT_USER_DEPOSIT: user_deposit_contract, }, required_confirmations=1, db_filename=":memory:", poll_interval=0.1, sync_start_block=BlockNumber(0), private_key= "3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266", ) # greenlet needs to be started and context switched to pfs.start() wait_for_blocks(1) gevent.sleep(0.1) # there should be one token network registered assert len(pfs.token_networks) == 1 token_network = pfs.token_networks[token_network_address] graph = token_network.G channel_identifiers = [] for ( p1_index, p1_deposit, _p1_capacity, _p1_fee, _p1_reveal_timeout, p2_index, p2_deposit, _p2_capacity, _p2_fee, _p2_reveal_timeout, _settle_timeout, ) in channel_descriptions_case_1: # order is important here because we check order later channel_identifier = clients[p1_index].open_channel( clients[p2_index].address) channel_identifiers.append(channel_identifier) clients[p1_index].deposit_to_channel(clients[p2_index].address, p1_deposit) clients[p2_index].deposit_to_channel(clients[p1_index].address, p2_deposit) gevent.sleep() wait_for_blocks(1) gevent.sleep(0.1) # there should be as many open channels as described assert len(token_network.channel_id_to_addresses.keys()) == len( channel_descriptions_case_1) # check that deposits, settle_timeout and transfers got registered for ( index, ( _p1_index, p1_deposit, _p1_capacity, _p1_fee, _p1_reveal_timeout, _p2_index, p2_deposit, _p2_capacity, _p2_fee, _p2_reveal_timeout, _settle_timeout, ), ) in enumerate(channel_descriptions_case_1): channel_identifier = channel_identifiers[index] p1_address, p2_address = token_network.channel_id_to_addresses[ channel_identifier] view1: ChannelView = graph[p1_address][p2_address]["view"] view2: ChannelView = graph[p2_address][p1_address]["view"] assert view1.deposit == p1_deposit assert view2.deposit == p2_deposit assert view1.settle_timeout == 15 assert view2.settle_timeout == 15 assert view1.reveal_timeout == DEFAULT_REVEAL_TIMEOUT assert view2.reveal_timeout == DEFAULT_REVEAL_TIMEOUT # now close all channels for ( p1_index, _p1_deposit, _p1_capacity, _p1_fee, _p1_reveal_timeout, p2_index, _p2_deposit, _p2_capacity, _p2_fee, _p2_reveal_timeout, _settle_timeout, ) in channel_descriptions_case_1: balance_proof = clients[p2_index].get_balance_proof( clients[p1_index].address, nonce=1, transferred_amount=0, locked_amount=0, locksroot="0x%064x" % 0, additional_hash="0x%064x" % 1, ) clients[p1_index].close_channel(clients[p2_index].address, balance_proof) wait_for_blocks(1) gevent.sleep(0.1) # there should be no channels assert len(token_network.channel_id_to_addresses.keys()) == 0 pfs.stop()
def main( # pylint: disable=too-many-arguments,too-many-locals private_key: PrivateKey, state_db: str, web3: Web3, contracts: Dict[str, Contract], start_block: BlockNumber, confirmations: BlockTimeout, host: str, port: int, service_fee: TokenAmount, operator: str, info_message: str, enable_debug: bool, matrix_server: List[str], accept_disclaimer: bool, ) -> int: """The Pathfinding service for the Raiden Network.""" log.info("Starting Raiden Pathfinding Service") click.secho(PFS_DISCLAIMER, fg="yellow") if not accept_disclaimer: click.confirm(CONFIRMATION_OF_UNDERSTANDING, abort=True) log.info("Using RPC endpoint", rpc_url=get_web3_provider_info(web3)) hex_addresses = { name: to_checksum_address(contract.address) for name, contract in contracts.items() } log.info("Contract information", addresses=hex_addresses, start_block=start_block) service = None api = None try: service = PathfindingService( web3=web3, contracts=contracts, sync_start_block=start_block, required_confirmations=confirmations, private_key=private_key, poll_interval=DEFAULT_POLL_INTERVALL, db_filename=state_db, matrix_servers=matrix_server, ) service.start() log.debug("Waiting for service to start before accepting API requests") try: service.startup_finished.get(timeout=PFS_START_TIMEOUT) except gevent.Timeout: raise Exception("PFS did not start within time.") log.debug("Starting API") api = PFSApi( pathfinding_service=service, service_fee=service_fee, debug_mode=enable_debug, one_to_n_address=to_canonical_address( contracts[CONTRACT_ONE_TO_N].address), operator=operator, info_message=info_message, ) api.run(host=host, port=port) service.get() except (KeyboardInterrupt, SystemExit): print("Exiting...") finally: log.info("Stopping Pathfinding Service...") if api: api.stop() if service: service.stop() return 0