mirror of
https://github.com/ZmnSCPxj/clboss.git
synced 2026-08-14 12:43:19 +02:00
The third v26.06 deprecation hitting CLBOSS. pay was deprecated in v26.06 with last supported version v27.03 (tighter than getroute's v27.06 window) and is replaced by xpay, which has been available since v24.11 -- so this is a straight migration with no compat bridge required on any CLN version we care about. The sole producer of Msg::PayInvoice is SwapManager paying a Boltz swap-out invoice (Boss/Mod/SwapManager.cpp:541): when CLBOSS does an off-chain to on-chain swap, Boltz issues a bolt11 invoice for the off-chain side and CLBOSS pays it; Boltz then sends the equivalent on-chain. - pay -> xpay - request field bolt11 -> invstring - replace maxfeepercent (a percentage) with maxfee (an absolute msat amount) computed from the decoded invoice amount; the fraction 0.005 preserves the legacy non-MPP 0.5% cap. xpay's own default maxfee is 1% (5000msat floor), which would have doubled our historical fee budget on swap-outs. Because this code path is exclusively swap-out invoice payment -- real money on prod -- we set an explicit absolute maxfee instead of accepting xpay's default. Worst-case extra fee per swap was bounded at 0.5% of the swap amount under the default; this keeps it at the historical 0.5%. - The MPP feature-bit detection (hex-parsing the bolt11 features string and checking bits 16/17) is removed. Its sole purpose was to pick between two maxfeepercent values for pre-xpay pay, whose pre-v0.10 MPP fee-budget management was poor and required a generous 5% cap to succeed. xpay's askrene-driven multi-path routing manages its own per-part fee budget, so we always pass a single absolute maxfee and let xpay split or not as needed. - The Util::Str::HexParseFailure catch path is removed (no longer reachable -- we no longer parse hex features). - The Util/Str.hpp include is dropped. - The nonmpp_maxfeepercent and mpp_maxfeepercent constants are replaced by a single maxfee_fraction. - The decode round-trip and validation that the input is a valid bolt11 invoice. Boltz swap-out invoices are always bolt11, so this stays useful as a sanity check. The decode is now also used to extract amount_msat for the maxfee computation; if the invoice is amountless (which Boltz never emits) the parser throws Jsmn::TypeError and the existing "Unexpected decode result" log path catches it. - retry_for = 1000 (matches historical behavior). - The RpcError / Jsmn::TypeError catching pattern; response parsing is unchanged (we still consume the response as Jsmn::Object _ without reading fields).
122 lines
3.4 KiB
C++
122 lines
3.4 KiB
C++
#include"Boss/Mod/InvoicePayer.hpp"
|
|
#include"Boss/Mod/Rpc.hpp"
|
|
#include"Boss/Msg/Init.hpp"
|
|
#include"Boss/Msg/PayInvoice.hpp"
|
|
#include"Boss/concurrent.hpp"
|
|
#include"Boss/log.hpp"
|
|
#include"Ev/Io.hpp"
|
|
#include"Ev/foreach.hpp"
|
|
#include"Jsmn/Object.hpp"
|
|
#include"Json/Out.hpp"
|
|
#include"Ln/Amount.hpp"
|
|
#include"S/Bus.hpp"
|
|
#include<memory>
|
|
|
|
namespace {
|
|
|
|
/* Maximum routing fee we are willing to pay, expressed as a
|
|
* divisor of the invoice amount. 200 = 0.5% cap, preserving the
|
|
* historical pre-xpay `pay` non-MPP behaviour (xpay's own default
|
|
* would be 1%). Integer math: maxfee_msat = amount_msat / 200,
|
|
* avoiding the rounding drift a 0.005 floating-point multiplication
|
|
* would introduce on larger invoices. The only producer of
|
|
* Msg::PayInvoice is SwapManager paying a Boltz swap-out invoice,
|
|
* so tight fee discipline matters here.
|
|
*/
|
|
auto constexpr maxfee_divisor = std::uint64_t(200);
|
|
|
|
}
|
|
|
|
namespace Boss { namespace Mod {
|
|
|
|
void InvoicePayer::start() {
|
|
bus.subscribe<Msg::Init
|
|
>([this](Msg::Init const& init) {
|
|
rpc = &init.rpc;
|
|
/* Pay pending invoices. */
|
|
auto f = [this](std::string invoice) {
|
|
return pay(std::move(invoice));
|
|
};
|
|
return Boss::concurrent(
|
|
Ev::foreach(f, std::move(pending_invoices))
|
|
);
|
|
});
|
|
|
|
bus.subscribe<Msg::PayInvoice
|
|
>([this](Msg::PayInvoice const& p) {
|
|
if (!rpc) {
|
|
/* Not yet ready, add to pending. */
|
|
pending_invoices.push_back(p.invoice);
|
|
return Ev::lift();
|
|
}
|
|
return Boss::concurrent(pay(p.invoice));
|
|
});
|
|
}
|
|
|
|
Ev::Io<void> InvoicePayer::pay(std::string n_invoice) {
|
|
auto inv = std::make_shared<std::string>(std::move(n_invoice));
|
|
return Ev::lift().then([this, inv]() {
|
|
return Boss::log( bus, Debug
|
|
, "InvoicePayer: Initiating: %s"
|
|
, inv->c_str()
|
|
);
|
|
}).then([this, inv]() {
|
|
auto parms = Json::Out()
|
|
.start_object()
|
|
.field("string", *inv)
|
|
.end_object()
|
|
;
|
|
return rpc->command("decode", std::move(parms));
|
|
}).then([this, inv](Jsmn::Object res) {
|
|
if (!res.has("type")
|
|
|| std::string(res["type"]) != "bolt11 invoice"
|
|
|| !res.has("valid")
|
|
|| !res["valid"].is_boolean()
|
|
|| !bool(res["valid"])
|
|
|| !res.has("amount_msat")
|
|
) {
|
|
throw Jsmn::TypeError();
|
|
}
|
|
|
|
/* Compute an absolute maxfee from the invoice amount,
|
|
* preserving the historical 0.5% cap via integer
|
|
* division. xpay's MPP handling makes the legacy
|
|
* feature-bit-driven MPP branch unnecessary: askrene
|
|
* + xpay manage the fee budget across parts internally.
|
|
*/
|
|
auto amount = Ln::Amount::object(res["amount_msat"]);
|
|
auto maxfee_msat = amount.to_msat() / maxfee_divisor;
|
|
|
|
/* TODO: Get created_at and expiry, add them, then determine
|
|
* current time and subtract, to get retry_for.
|
|
*/
|
|
auto retry_for = 1000;
|
|
|
|
auto parms = Json::Out()
|
|
.start_object()
|
|
.field("invstring", *inv)
|
|
.field("retry_for", retry_for)
|
|
.field("maxfee", maxfee_msat)
|
|
.end_object()
|
|
;
|
|
return rpc->command("xpay", std::move(parms));
|
|
}).then([this, inv](Jsmn::Object _) {
|
|
return Boss::log( bus, Debug
|
|
, "InvoicePayer: Paid: %s"
|
|
, inv->c_str()
|
|
);
|
|
}).catching<RpcError>([this, inv](RpcError const& _) {
|
|
return Boss::log( bus, Debug
|
|
, "InvoicePayer: Failed to pay: %s"
|
|
, inv->c_str()
|
|
);
|
|
}).catching<Jsmn::TypeError>([this, inv](Jsmn::TypeError const& _) {
|
|
return Boss::log( bus, Error
|
|
, "InvoicePayer: "
|
|
"Unexpected decode result for invoice: %s"
|
|
, inv->c_str()
|
|
);
|
|
});
|
|
}
|
|
|
|
}}
|