Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->dev_hsmd_no_preapprove_check = false;
ld->dev_hsmd_fail_preapprove = false;
ld->dev_handshake_no_reply = false;
ld->dev_strict_forwarding = false;

/*~ We try to ensure enough fds for twice the number of channels
* we start with. We have a developer option to change that factor
Expand Down
4 changes: 4 additions & 0 deletions lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ struct lightningd {
/* Tell connectd not to talk after handshake */
bool dev_handshake_no_reply;

/* Remove the freedom to choose select between parallel channels to
* forward a payment. */
bool dev_strict_forwarding;

/* tor support */
struct wireaddr *proxyaddr;
bool always_use_proxy;
Expand Down
4 changes: 4 additions & 0 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,10 @@ static void dev_register_opts(struct lightningd *ld)
opt_set_bool,
&ld->dev_handshake_no_reply,
"Don't send or read init message after connection");
clnopt_noarg("--dev-strict-forwarding", OPT_DEV,
opt_set_bool,
&ld->dev_strict_forwarding,
"Forward HTLCs along the channel specified");
clnopt_noarg("--dev-throttle-gossip", OPT_DEV,
opt_set_bool,
&ld->dev_throttle_gossip,
Expand Down
6 changes: 5 additions & 1 deletion lightningd/peer_htlcs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,11 @@ static struct channel_id *calc_forwarding_channel(struct lightningd *ld,
c = NULL;
}

best = best_channel(ld, peer, p->amt_to_forward, c);
if (!ld->dev_strict_forwarding)
best = best_channel(ld, peer, p->amt_to_forward, c);
else
best = c;

if (!c) {
if (!best)
return NULL;
Expand Down
108 changes: 108 additions & 0 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -6586,3 +6586,111 @@ def test_injectpaymentonion_failures(node_factory, executor):
# PAY_INJECTPAYMENTONION_FAILED
assert err.value.error['code'] == 218
assert 'onionreply' in err.value.error['data']


def test_parallel_channels_reserve(node_factory, bitcoind):
"""Tests wether we are able to pay through parallel channels concurrently.
To do that we need to enable strict-forwarding."""

def direction(node1, node2):
return 0 if node1.info["id"] < node2.info["id"] else 1

def get_local_channel_by_id(node, chanid):
peerchannels = node.rpc.listpeerchannels()["channels"]
if not peerchannels:
return None
for c in peerchannels:
if c["channel_id"] == chanid:
return c
return None

opts = {
"fee-base": 0,
"fee-per-satoshi": 0,
"cltv-delta": 6,
"dev-strict-forwarding": None,
}
l1, l2, l3 = node_factory.get_nodes(3, opts=opts)

l1.fundwallet(10**7)
l2.fundwallet(10**7)

scids = []

l1.rpc.connect(l2.info["id"], "localhost", l2.port)
l2.rpc.connect(l3.info["id"], "localhost", l3.port)

c12 = l1.rpc.fundchannel(l2.info["id"], 3000_000, minconf=0)["channel_id"]

c23 = []
c23.append(l2.rpc.fundchannel(l3.info["id"], 1000_000, minconf=0)["channel_id"])
c23.append(l2.rpc.fundchannel(l3.info["id"], 2000_000, minconf=0)["channel_id"])

bitcoind.generate_block(6)
sync_blockheight(bitcoind, [l1, l2, l3])

scids.append(get_local_channel_by_id(l1, c12)["short_channel_id"])
scids.append(get_local_channel_by_id(l2, c23[0])["short_channel_id"])
scids.append(get_local_channel_by_id(l2, c23[1])["short_channel_id"])

for l in [l1, l2, l3]:
for c in scids:
l.wait_channel_active(c)

# we should be able to send these two parts:
nparts = 2
route_amounts = ["750000sat", "1750000sat"]
total_msat = sum([Millisatoshi(a) for a in route_amounts[:nparts]])

# Test succeeds if we are able to pay this invoice
inv = l3.rpc.call(
"invoice",
{"amount_msat": total_msat, "label": "inv", "description": "inv", "cltv": 10},
)

# Share data by every route we will construct: l1->l2->l3
route = [
{
"id": l2.info["id"],
"direction": direction(l1, l2),
"delay": 16,
"style": "tlv",
},
{
"id": l3.info["id"],
"direction": direction(l2, l3),
"delay": 10,
"style": "tlv",
},
]

# Send every part with sendpay
for part in range(nparts):
this_part_msat = Millisatoshi(route_amounts[part])
chan1 = get_local_channel_by_id(l1, c12)
chan2 = get_local_channel_by_id(l2, c23[part])

route[0]["channel"] = chan1["short_channel_id"]
route[1]["channel"] = chan2["short_channel_id"]
route[0]["amount_msat"] = route[1]["amount_msat"] = this_part_msat

assert chan1["spendable_msat"] >= this_part_msat
assert chan2["spendable_msat"] >= this_part_msat

l1.rpc.call(
"sendpay",
{
"route": route,
"payment_hash": inv["payment_hash"],
"payment_secret": inv["payment_secret"],
"amount_msat": total_msat,
"groupid": 1,
"partid": part + 1,
},
)
l1.wait_for_htlcs()

# Are we happy?
receipt = only_one(l3.rpc.listinvoices("inv")["invoices"])
assert receipt["status"] == "paid"
assert receipt["amount_received_msat"] == total_msat
Loading