Skip to content

Commit

Permalink
lightningd: add option dev-strict-forwarding
Browse files Browse the repository at this point in the history
Changelog-Add: add option dev-strict-forwarding
  • Loading branch information
Lagrang3 committed Aug 25, 2024
1 parent 9d88ce3 commit d0ca1d7
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
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 @@ -1212,7 +1212,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 @@ -5996,3 +5996,111 @@ def test_enableoffer(node_factory):
# Can't enable unknown.
with pytest.raises(RpcError, match="Unknown offer"):
l1.rpc.enableoffer(offer_id=offer1['offer_id'])


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

0 comments on commit d0ca1d7

Please sign in to comment.