Skip to content
Open
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
2 changes: 1 addition & 1 deletion contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -16068,7 +16068,7 @@
"amount_msat": {
"type": "msat",
"description": [
"The amount for the first HTLC in millisatoshis. This is also the amount which will be forwarded to the first peer (if any) as we do not charge fees on our own payments. Note: this is shown in listsendpays as `amount_sent_msat`."
"The amount in millisatoshis this node would receive from a peer before forwarding the payment to the next."
]
},
"cltv_expiry": {
Expand Down
2 changes: 1 addition & 1 deletion doc/schemas/injectpaymentonion.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"amount_msat": {
"type": "msat",
"description": [
"The amount for the first HTLC in millisatoshis. This is also the amount which will be forwarded to the first peer (if any) as we do not charge fees on our own payments. Note: this is shown in listsendpays as `amount_sent_msat`."
"The amount in millisatoshis this node would receive from a peer before forwarding the payment to the next."
]
},
"cltv_expiry": {
Expand Down
22 changes: 11 additions & 11 deletions lightningd/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -1970,15 +1970,15 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
register_payment_and_waiter(cmd,
payment_hash,
*partid, *groupid,
*destination_msat, *msat, AMOUNT_MSAT(0),
*destination_msat, payload->amt_to_forward, AMOUNT_MSAT(0),
label, invstring, local_invreq_id,
&shared_secret,
destination);

/* Mark it pending now, though htlc_set_add might
* not resolve immediately */
fixme_ignore(command_still_pending(cmd));
htlc_set_add(cmd->ld, cmd->ld->log, *msat, *payload->total_msat,
htlc_set_add(cmd->ld, cmd->ld->log, payload->amt_to_forward, *payload->total_msat,
NULL, payment_hash, payload->payment_secret,
selfpay_mpp_fail, selfpay_mpp_succeeded,
selfpay);
Expand Down Expand Up @@ -2014,22 +2014,22 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
"Unknown peer %s",
fmt_node_id(tmpctx, &nid));

next = best_channel(cmd->ld, next_peer, *msat, NULL);
next = best_channel(cmd->ld, next_peer, payload->amt_to_forward, NULL);
if (!next)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"No available channel with peer %s",
fmt_node_id(tmpctx, &nid));
}

if (amount_msat_greater(*msat, next->htlc_maximum_msat)
|| amount_msat_less(*msat, next->htlc_minimum_msat)) {
if (amount_msat_greater(payload->amt_to_forward, next->htlc_maximum_msat)
|| amount_msat_less(payload->amt_to_forward, next->htlc_minimum_msat)) {
/* Are we in old-range grace-period? */
if (!timemono_before(time_mono(), next->old_feerate_timeout)
|| amount_msat_less(*msat, next->old_htlc_minimum_msat)
|| amount_msat_greater(*msat, next->old_htlc_maximum_msat)) {
|| amount_msat_less(payload->amt_to_forward, next->old_htlc_minimum_msat)
|| amount_msat_greater(payload->amt_to_forward, next->old_htlc_maximum_msat)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Amount %s not in htlc min/max range %s-%s",
fmt_amount_msat(tmpctx, *msat),
fmt_amount_msat(tmpctx, payload->amt_to_forward),
fmt_amount_msat(tmpctx, next->htlc_minimum_msat),
fmt_amount_msat(tmpctx, next->htlc_maximum_msat));
}
Expand Down Expand Up @@ -2082,11 +2082,11 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
if (command_check_only(cmd))
return command_check_done(cmd);

failmsg = send_htlc_out(tmpctx, next, *msat,
failmsg = send_htlc_out(tmpctx, next, payload->amt_to_forward,
*cltv,
/* If unknown, we set this equal (so accounting logs 0 fees) */
amount_msat_eq(*destination_msat, AMOUNT_MSAT(0))
? *msat : *destination_msat,
? payload->amt_to_forward : *destination_msat,
payment_hash,
next_path_key, NULL, *partid, *groupid,
serialize_onionpacket(tmpctx, rs->next),
Expand All @@ -2101,7 +2101,7 @@ static struct command_result *json_injectpaymentonion(struct command *cmd,
register_payment_and_waiter(cmd,
payment_hash,
*partid, *groupid,
*destination_msat, *msat, AMOUNT_MSAT(0),
*destination_msat, payload->amt_to_forward, AMOUNT_MSAT(0),
label, invstring, local_invreq_id,
&shared_secret,
destination);
Expand Down
12 changes: 10 additions & 2 deletions plugins/xpay/xpay.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ send_payment_req(struct command *aux_cmd,

/* For self-pay, we don't have hops. */
static struct amount_msat initial_sent(const struct attempt *attempt)
{
if (tal_count(attempt->hops) == 0)
return attempt->delivers;
return attempt->hops[0].amount_out;
}

static struct amount_msat inject_amount(const struct attempt *attempt)
{
if (tal_count(attempt->hops) == 0)
return attempt->delivers;
Expand Down Expand Up @@ -1143,7 +1150,7 @@ static struct command_result *do_inject(struct command *aux_cmd,
json_add_hex_talarr(req->js, "onion", onion);
json_add_sha256(req->js, "payment_hash", &attempt->payment->payment_hash);
/* If no route, its the same as delivery (self-pay) */
json_add_amount_msat(req->js, "amount_msat", initial_sent(attempt));
json_add_amount_msat(req->js, "amount_msat", inject_amount(attempt));
json_add_u32(req->js, "cltv_expiry", initial_cltv_delta(attempt) + effective_bheight);
json_add_u64(req->js, "partid", attempt->partid);
json_add_u64(req->js, "groupid", attempt->payment->group_id);
Expand Down Expand Up @@ -1458,7 +1465,8 @@ static struct command_result *getroutes_for(struct command *aux_cmd,
json_array_start(req->js, "layers");
/* Add local channels */
json_add_string(req->js, NULL, "auto.localchans");
/* We don't pay fees for ourselves */
/* For the MCF computation we must discard the cost of routing through
* our own channels because we don't pay fees for that. */
json_add_string(req->js, NULL, "auto.sourcefree");
/* Add xpay global channel */
json_add_string(req->js, NULL, "xpay");
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -6366,7 +6366,7 @@ def test_injectpaymentonion_selfpay(node_factory, executor):
'payload': serialize_payload_final_tlv(333, 18, 1000, blockheight, inv5['payment_secret']).hex()}]
onion1 = l1.rpc.createonion(hops=hops1, assocdata=inv5['payment_hash'])
hops2 = [{'pubkey': l1.info['id'],
'payload': serialize_payload_final_tlv(666, 18, 1000, blockheight, inv5['payment_secret']).hex()}]
'payload': serialize_payload_final_tlv(667, 18, 1000, blockheight, inv5['payment_secret']).hex()}]
onion2 = l1.rpc.createonion(hops=hops2, assocdata=inv5['payment_hash'])

fut1 = executor.submit(l1.rpc.injectpaymentonion,
Expand Down
31 changes: 31 additions & 0 deletions tests/test_xpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -1072,3 +1072,34 @@ def mock_getblockhash(req):
# Now let it catch up, and it will retry, and succeed.
l1.daemon.rpcproxy.mock_rpc('getblockhash')
fut.result(TIMEOUT)


def test_bolt12_fees(node_factory):
AMT_MSAT = 10000
FEES_MSAT = 5000
l1, l2 = node_factory.get_nodes(
2, opts={"may_reconnect": True, "fee-base": FEES_MSAT, "fee-per-satoshi": 0}
)
node_factory.join_nodes([l1, l2], wait_for_announce=True)

# BOLT 12, direct peer
offer = l2.rpc.offer("any")["bolt12"]
b12 = l1.rpc.fetchinvoice(offer, AMT_MSAT)["invoice"]

b12_decode = l1.rpc.decode(b12)
assert b12_decode["invoice_amount_msat"] == AMT_MSAT
assert len(b12_decode["invoice_paths"]) == 1
assert b12_decode["invoice_paths"][0]["first_node_id"] == l1.info["id"]
assert b12_decode["invoice_paths"][0]["payinfo"]["fee_base_msat"] == FEES_MSAT
assert b12_decode["invoice_paths"][0]["payinfo"]["fee_proportional_millionths"] == 0

ret = l1.rpc.xpay(invstring=b12)
assert ret["failed_parts"] == 0
assert ret["successful_parts"] == 1
assert ret["amount_msat"] == AMT_MSAT
assert ret["amount_sent_msat"] == AMT_MSAT

htlcs = l1.rpc.listhtlcs()["htlcs"]
assert len(htlcs) == 1
assert htlcs[0]["payment_hash"] == b12_decode["invoice_payment_hash"]
assert htlcs[0]["amount_msat"] == AMT_MSAT
Loading