Skip to content

Commit 4170c09

Browse files
committed
add a test for racy forwarding requests
Changelog-None. Signed-off-by: Lagrang3 <[email protected]>
1 parent 495403d commit 4170c09

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed

tests/test_pay.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5996,3 +5996,148 @@ def test_enableoffer(node_factory):
59965996
# Can't enable unknown.
59975997
with pytest.raises(RpcError, match="Unknown offer"):
59985998
l1.rpc.enableoffer(offer_id=offer1['offer_id'])
5999+
6000+
6001+
def get_local_channel_by_id(node, chanid):
6002+
peerchannels = node.rpc.listpeerchannels()["channels"]
6003+
if not peerchannels:
6004+
return None
6005+
for c in peerchannels:
6006+
if c["channel_id"] == chanid:
6007+
return c
6008+
return None
6009+
6010+
6011+
def start_channels(connections):
6012+
"""Similar to join_nodes but with arbitrary connections."""
6013+
nodes = list()
6014+
for src, dst, fundamount in connections:
6015+
nodes.append(src)
6016+
nodes.append(dst)
6017+
src.rpc.connect(dst.info["id"], "localhost", dst.port)
6018+
6019+
bitcoind = nodes[0].bitcoin
6020+
# If we got here, we want to fund channels
6021+
for src, dst, fundamount in connections:
6022+
addr = src.rpc.newaddr()["bech32"]
6023+
bitcoind.rpc.sendtoaddress(addr, (fundamount + 1000000) / 10**8)
6024+
6025+
bitcoind.generate_block(1)
6026+
sync_blockheight(bitcoind, nodes)
6027+
txids = []
6028+
chan_ids = []
6029+
for src, dst, fundamount in connections:
6030+
reply = src.rpc.fundchannel(
6031+
dst.info["id"], fundamount, announce=True, minconf=0
6032+
)
6033+
txids.append(reply["txid"])
6034+
chan_ids.append(reply["channel_id"])
6035+
6036+
# Confirm all channels and wait for them to become usable
6037+
bitcoind.generate_block(1, wait_for_mempool=txids)
6038+
scids = []
6039+
for con, mychan_id in zip(connections, chan_ids):
6040+
src = con[0]
6041+
wait_for(
6042+
lambda: get_local_channel_by_id(src, mychan_id)["state"]
6043+
== "CHANNELD_NORMAL"
6044+
)
6045+
scids.append(get_local_channel_by_id(src, mychan_id)["short_channel_id"])
6046+
6047+
# Make sure they have all seen block so they don't complain about
6048+
# the coming gossip messages
6049+
sync_blockheight(bitcoind, nodes)
6050+
bitcoind.generate_block(5)
6051+
6052+
# Make sure everyone sees all channels, all other nodes
6053+
for n in nodes:
6054+
for scid in scids:
6055+
n.wait_channel_active(scid)
6056+
6057+
# Make sure we have all node announcements, too
6058+
for n in nodes:
6059+
for n2 in nodes:
6060+
wait_for(
6061+
lambda: "alias" in only_one(n.rpc.listnodes(n2.info["id"])["nodes"])
6062+
)
6063+
return chan_ids
6064+
6065+
6066+
def test_parallel_channels_reserve(node_factory):
6067+
"""Tests wether we are able to pay through parallel channels concurrently."""
6068+
def direction(node1, node2):
6069+
return 0 if node1.info["id"] < node2.info["id"] else 1
6070+
6071+
opts = {"disable-mpp": None, "fee-base": 0, "fee-per-satoshi": 0, "cltv-delta": 6}
6072+
l1, l2, l3 = node_factory.get_nodes(3, opts=opts)
6073+
6074+
chan_ids = start_channels(
6075+
[
6076+
(l1, l2, 100_000),
6077+
(l2, l3, 100_000),
6078+
(l1, l2, 200_000),
6079+
(l2, l3, 200_000),
6080+
(l1, l2, 300_000),
6081+
(l2, l3, 300_000),
6082+
(l1, l2, 400_000),
6083+
(l2, l3, 400_000),
6084+
]
6085+
)
6086+
6087+
# we should be able to send these four parts:
6088+
nparts = 4
6089+
route_amounts = ["75000sat", "175000sat", "275000sat", "375000sat"]
6090+
total_msat = sum([Millisatoshi(a) for a in route_amounts[:nparts]])
6091+
6092+
# Test succeeds if we are able to pay this invoice
6093+
inv = l3.rpc.call(
6094+
"invoice",
6095+
{"amount_msat": total_msat, "label": "inv", "description": "inv", "cltv": 10},
6096+
)
6097+
6098+
# Share data by every route we will construct: l1->l2->l3
6099+
route = [
6100+
{
6101+
"id": l2.info["id"],
6102+
"direction": direction(l1, l2),
6103+
"delay": 16,
6104+
"style": "tlv",
6105+
},
6106+
{
6107+
"id": l3.info["id"],
6108+
"direction": direction(l2, l3),
6109+
"delay": 10,
6110+
"style": "tlv",
6111+
},
6112+
]
6113+
6114+
# Send every part with sendpay
6115+
for part in range(nparts):
6116+
this_part_msat = Millisatoshi(route_amounts[part])
6117+
chan1 = get_local_channel_by_id(l1, chan_ids[part * 2])
6118+
chan2 = get_local_channel_by_id(l2, chan_ids[part * 2 + 1])
6119+
6120+
route[0]["channel"] = chan1["short_channel_id"]
6121+
route[1]["channel"] = chan2["short_channel_id"]
6122+
route[0]["amount_msat"] = route[1]["amount_msat"] = this_part_msat
6123+
6124+
assert chan1["spendable_msat"] >= this_part_msat
6125+
assert chan2["spendable_msat"] >= this_part_msat
6126+
6127+
l1.rpc.call(
6128+
"sendpay",
6129+
{
6130+
"route": route,
6131+
"payment_hash": inv["payment_hash"],
6132+
"payment_secret": inv["payment_secret"],
6133+
"amount_msat": total_msat,
6134+
"groupid": 1,
6135+
"partid": part + 1,
6136+
},
6137+
)
6138+
l1.wait_for_htlcs()
6139+
6140+
# Are we happy?
6141+
receipt = only_one(l3.rpc.listinvoices("inv")["invoices"])
6142+
assert receipt["status"] == "paid"
6143+
assert receipt["amount_received_msat"] == total_msat

0 commit comments

Comments
 (0)