Exemple #1
0
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()
Exemple #5
0
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
Exemple #6
0
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()
Exemple #8
0
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