def test_routing_simple(token_networks: List[TokenNetwork],
                        populate_token_networks_case_1: None,
                        addresses: List[Address]):
    token_network = token_networks[0]

    view01: ChannelView = token_network.G[addresses[0]][addresses[1]]['view']
    view10: ChannelView = token_network.G[addresses[1]][addresses[0]]['view']

    assert view01.deposit == 100
    assert view01.transferred_amount == 20
    assert view01.locked_amount == 0
    assert isclose(view01._relative_fee, 10)
    assert view01.capacity == 90
    assert view10.capacity == 60

    # 0->1->4->3 is as short as 0->1->2->3 but the shortcut 1->4 is a lot more expensive.
    # 0->2->3 would be shorter but 0->2 is degraded.
    paths = token_network.get_paths(addresses[0],
                                    addresses[3],
                                    value=10,
                                    k=1,
                                    hop_bias=0)
    assert len(paths) == 1
    assert paths[0] == {
        'path': [addresses[0], addresses[1], addresses[2], addresses[3]],
        'estimated_fee': 25
    }

    # Bottleneck should be 0->1 and 2->3 with a capacity of 90.
    with pytest.raises(NetworkXNoPath):
        token_network.get_paths(addresses[0], addresses[3], value=100, k=1)

    # Not connected.
    with pytest.raises(NetworkXNoPath):
        token_network.get_paths(addresses[0], addresses[5], value=10, k=1)
def test_routing_hop_fee_balance(token_networks: List[TokenNetwork],
                                 populate_token_networks_case_1: None,
                                 addresses: List[Address]):
    token_network = token_networks[0]

    # 1->4 has an extremely high fee, so 1->2->3->4 would be cheaper but slower.
    # Prefer cheap over fast.
    paths = token_network.get_paths(addresses[1],
                                    addresses[4],
                                    value=10,
                                    k=1,
                                    hop_bias=0)
    assert paths[0] == {
        'path': [addresses[1], addresses[2], addresses[3], addresses[4]],
        'estimated_fee': 26
    }
    # Prefer fast over cheap.
    paths = token_network.get_paths(addresses[1],
                                    addresses[4],
                                    value=10,
                                    k=1,
                                    hop_bias=1)
    assert paths[0] == {
        'path': [addresses[1], addresses[4]],
        'estimated_fee': 100
    }
def test_routing_benchmark(token_networks: List[TokenNetwork],
                           populate_token_networks_random: None):
    value = 100
    G = token_networks[0].G
    token_network = token_networks[0]
    times = []
    start = time.time()
    for i in range(100):
        tic = time.time()
        source, target = random.sample(G.nodes, 2)
        paths = token_network.get_paths(source,
                                        target,
                                        value=value,
                                        k=5,
                                        bias=0.0)
        toc = time.time()
        times.append(toc - tic)
    end = time.time()
    for path_object in paths:
        path = path_object['path']
        fees = path_object['estimated_fee']
        for node1, node2 in zip(path[:-1], path[1:]):
            view: ChannelView = G[node1][node2]['view']
            print('fee = ', view.relative_fee, 'capacity = ', view.capacity)
        print('fee sum = ', fees)
    print(paths)
    print(np.mean(np.array(times)), np.min(np.array(times)),
          np.max(np.array(times)))
    print("total_runtime = {}".format(end - start))
def test_routing_disjoint_case2(token_networks: List[TokenNetwork],
                                populate_token_networks_case_2: None,
                                addresses: List[Address],
                                monkeypatch: MonkeyPatch):
    token_network = token_networks[0]

    # test default diversity penalty
    paths = token_network.get_paths(addresses[0], addresses[4], value=10, k=3)
    assert len(paths) == 3
    assert paths[0]['path'] == [
        addresses[0], addresses[2], addresses[5], addresses[4]
    ]
    assert paths[0]['estimated_fee'] == 3000

    assert paths[1]['path'] == [
        addresses[0], addresses[2], addresses[3], addresses[4]
    ]
    assert paths[1]['estimated_fee'] == 3500

    assert paths[2]['path'] == [addresses[0], addresses[1], addresses[4]]
    assert paths[2]['estimated_fee'] == 5000

    # set diversity penalty higher
    monkeypatch.setattr(pathfinder.model.token_network,
                        'DIVERSITY_PEN_DEFAULT', 10000)
    paths = token_network.get_paths(addresses[0], addresses[4], value=10, k=3)
    assert len(paths) == 3
    assert paths[0]['path'] == [
        addresses[0], addresses[2], addresses[5], addresses[4]
    ]
    assert paths[0]['estimated_fee'] == 3000

    assert paths[1]['path'] == [addresses[0], addresses[1], addresses[4]]
    assert paths[1]['estimated_fee'] == 5000

    assert paths[2]['path'] == [
        addresses[0], addresses[2], addresses[3], addresses[4]
    ]
    assert paths[2]['estimated_fee'] == 3500
def test_routing_disjoint_case1(token_networks: List[TokenNetwork],
                                populate_token_networks_case_1: None,
                                addresses: List[Address],
                                monkeypatch: MonkeyPatch):
    token_network = token_networks[0]

    # Paths should be "as disjoint as possible". There are only 2 different paths though.
    monkeypatch.setattr(pathfinder.model.token_network,
                        'DIVERSITY_PEN_DEFAULT', 1)
    paths = token_network.get_paths(addresses[0], addresses[2], value=10, k=3)
    assert len(paths) == 2
    assert paths[0] == {
        'path': [addresses[0], addresses[1], addresses[2]],
        'estimated_fee': 18,
    }
    assert paths[1] == {
        'path':
        [addresses[0], addresses[1], addresses[4], addresses[3], addresses[2]],
        'estimated_fee':
        131
    }