core-lightning/tests/test_xpay.py
Rusty Russell 75362d21c5 channeld_fakenet: fake channeld to simulate network given a gossmap_store.
Our gossmap_store uncompresser generates nodeids with well-known
privkeys, so we can decrypt and respond to HTLCs sent to such nodes.
By replacing channeld with a fake, we can connect a node to another
node, but then once the channel is established, allow payments to be
sent into the generated network, and respond appropriately.

This minimal version handles MPP timeouts, but doesn't insert any
delays or runtime capacity for channels.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-None: Testing only
2024-11-06 21:17:36 +10:30

135 lines
5.8 KiB
Python

from fixtures import * # noqa: F401,F403
from fixtures import TEST_NETWORK
from pyln.client import RpcError
from utils import (
TIMEOUT, first_scid, GenChannel, generate_gossip_store
)
import os
import pytest
import subprocess
from hashlib import sha256
def test_pay_fakenet(node_factory):
hash1 = sha256(bytes.fromhex('00' + '00' * 31)).hexdigest()
hash2 = sha256(bytes.fromhex('01' + '00' * 31)).hexdigest()
failhash = '00' * 32
# Create gossip map of channels from l2 (aka nodemap[0])
gsfile, nodemap = generate_gossip_store([GenChannel(0, 1, capacity_sats=100_000),
GenChannel(1, 2, capacity_sats=100_000),
GenChannel(2, 3, capacity_sats=200_000)],
nodemap={0: '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59'})
# l2 will warn l1 about its invalid gossip: ignore.
l1, l2 = node_factory.line_graph(2,
opts=[{'gossip_store_file': gsfile.name,
'subdaemon': 'channeld:../tests/plugins/channeld_fakenet',
'allow_warning': True}, {}])
# l1 needs to know l2's shaseed for the channel so it can make revocations
hsmfile = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")
# Needs peer node id and channel dbid (1, it's the first channel), prints out:
# "shaseed: xxxxxxx\n"
shaseed = subprocess.check_output(["tools/hsmtool", "dumpcommitments", l1.info['id'], "1", "0", hsmfile]).decode('utf-8').strip().partition(": ")[2]
l1.rpc.dev_peer_shachain(l2.info['id'], shaseed)
# Failure from final (unknown payment hash)
l1.rpc.sendpay(route=[{'id': l2.info['id'],
'channel': first_scid(l1, l2),
'delay': 18 + 6,
'amount_msat': 1000001},
{'id': nodemap[1],
'channel': '0x1x0',
'delay': 18,
'amount_msat': 100000}],
payment_hash=failhash)
with pytest.raises(RpcError, match="WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS") as err:
l1.rpc.waitsendpay(payment_hash=failhash, timeout=TIMEOUT)
assert err.value.error['data']['erring_node'] == nodemap[1]
# Success from final (known payment hash)
l1.rpc.sendpay(route=[{'id': l2.info['id'],
'channel': first_scid(l1, l2),
'delay': 18 + 6,
'amount_msat': 1000001},
{'id': nodemap[1],
'channel': '0x1x0',
'delay': 18,
'amount_msat': 100000}],
payment_hash=hash1)
l1.rpc.waitsendpay(payment_hash=hash1, timeout=TIMEOUT)
# Failure from node 2 (unknown scid)
l1.rpc.sendpay(route=[{'id': l2.info['id'],
'channel': first_scid(l1, l2),
'delay': 18 + 6 + 6,
'amount_msat': 1000002},
{'id': nodemap[1],
'channel': '0x1x0',
'delay': 18 + 6,
'amount_msat': 1000001},
{'id': nodemap[2],
'channel': '1x1x0',
'delay': 18,
'amount_msat': 1000000},
{'id': nodemap[3],
'channel': '0x1x0',
'delay': 18,
'amount_msat': 100000}],
payment_hash=failhash)
with pytest.raises(RpcError, match="WIRE_UNKNOWN_NEXT_PEER"):
l1.rpc.waitsendpay(payment_hash=failhash, timeout=TIMEOUT)
# MPP test
l1.rpc.sendpay(partid=1,
amount_msat=200000,
route=[{'id': l2.info['id'],
'channel': first_scid(l1, l2),
'delay': 18 + 6,
'amount_msat': 1000001},
{'id': nodemap[1],
'channel': '0x1x0',
'delay': 18,
'amount_msat': 100000}],
payment_hash=hash2,
payment_secret=hash2)
with pytest.raises(RpcError, match="WIRE_MPP_TIMEOUT"):
l1.rpc.waitsendpay(payment_hash=hash2, timeout=60 + TIMEOUT, partid=1)
# This one will actually work.
l1.rpc.sendpay(partid=2,
groupid=2,
amount_msat=200000,
route=[{'id': l2.info['id'],
'channel': first_scid(l1, l2),
'delay': 18 + 6,
'amount_msat': 1000001},
{'id': nodemap[1],
'channel': '0x1x0',
'delay': 18,
'amount_msat': 100000}],
payment_hash=hash2,
payment_secret=hash2)
l1.rpc.sendpay(partid=3,
groupid=2,
amount_msat=200000,
route=[{'id': l2.info['id'],
'channel': first_scid(l1, l2),
'delay': 18 + 6,
'amount_msat': 1000001},
{'id': nodemap[1],
'channel': '0x1x0',
'delay': 18,
'amount_msat': 100000}],
payment_hash=hash2,
payment_secret=hash2)
l1.rpc.waitsendpay(payment_hash=hash2, timeout=TIMEOUT, partid=2)
l1.rpc.waitsendpay(payment_hash=hash2, timeout=TIMEOUT, partid=3)