mirror of
https://github.com/ZmnSCPxj/clboss.git
synced 2026-08-15 12:50:42 +02:00
Move the askrene-inform-channel and askrene-disable-node helpers from Boss::Mod::FundsMover::Attempter::Impl (where they were private member functions) into a new shared module Boss::Mod::AskreneLayer. Lift the "clboss" layer name from a FundsMover-local constant into a shared constant. Pure refactor, no behavioral change. Existing tests still pass (79/79). Done now because PR4 (ActiveProber feeding the same layer) will be the second producer; sharing avoids duplicating the JSON construction, the layer-name string, and the silent-RpcError-catch logic. The new module exposes three free functions: - inform_channel_constrained(rpc, layer, scid, dir, amount): same shape and semantics as Attempter's prior private method. - inform_channel_succeeded(rpc, layer, scid, dir, amount): new variant emitting inform=succeeded instead of inform=constrained. Not used by FundsMover (sendpay failures only carry negative signals). Added in this commit because the implementation is one line over the shared internal helper, and PR4's open-question may have ActiveProber call it on probe-success to record positive lower-bound observations on probed channels. - disable_node(rpc, layer, node_id): same shape and semantics as Attempter's prior private method. All three functions silently swallow RpcError, matching the prior behavior in Attempter. The rationale: if the layer is unavailable (e.g. CLN < v24.11 where askrene layers do not exist), the call gracefully degrades to no-op rather than crashing the caller. Better degraded learning than crashed plugin. The layer name "clboss" follows the xpay convention: persistent shared-knowledge layer = owning plugin name. All CLBOSS-internal writers (FundsMover today, ActiveProber in PR4, possibly others later) target the same single layer so the accumulated knowledge benefits every downstream getroutes call.
106 lines
2.9 KiB
C++
106 lines
2.9 KiB
C++
#include"Boss/Mod/AskreneLayer.hpp"
|
|
#include"Boss/Mod/Rpc.hpp"
|
|
#include"Ev/Io.hpp"
|
|
#include"Jsmn/Object.hpp"
|
|
#include"Json/Out.hpp"
|
|
#include"Util/stringify.hpp"
|
|
#include<assert.h>
|
|
|
|
namespace Boss { namespace Mod { namespace AskreneLayer {
|
|
|
|
std::string const clboss_layer_name = "clboss";
|
|
|
|
namespace {
|
|
|
|
/* Common machinery for the two inform_channel variants. askrene
|
|
* accepts inform=succeeded / constrained / unconstrained as the
|
|
* only difference between them; everything else (scid_dir,
|
|
* amount_msat, layer) is identical.
|
|
*/
|
|
Ev::Io<void>
|
|
inform_channel( Boss::Mod::Rpc& rpc
|
|
, std::string const& layer
|
|
, Ln::Scid scid
|
|
, std::uint32_t direction
|
|
, Ln::Amount amount
|
|
, char const* inform
|
|
) {
|
|
/* askrene only accepts direction 0 or 1 in
|
|
* short_channel_id_dir. All callers feed values from
|
|
* CLN's getroutes/sendpay responses, which are
|
|
* guaranteed to be 0/1, but guard explicitly: a bad
|
|
* direction would produce a syntactically valid but
|
|
* semantically wrong RPC param that askrene rejects, and
|
|
* the silent-swallow RpcError handler below would drop
|
|
* the learning update without a trace.
|
|
*/
|
|
assert(direction <= 1);
|
|
if (direction > 1)
|
|
return Ev::lift();
|
|
auto sdir = std::string(scid) + "/" + Util::stringify(direction);
|
|
auto parms = Json::Out()
|
|
.start_object()
|
|
.field("layer", layer)
|
|
.field("short_channel_id_dir", sdir)
|
|
.field("amount_msat", amount.to_msat())
|
|
.field("inform", std::string(inform))
|
|
.end_object()
|
|
;
|
|
return rpc.command( "askrene-inform-channel"
|
|
, std::move(parms)
|
|
).then([](Jsmn::Object _) {
|
|
return Ev::lift();
|
|
}).catching<RpcError>([](RpcError const&) {
|
|
/* Non-fatal -- if the layer is missing (e.g. layer-
|
|
* create failed at startup on CLN < v24.11),
|
|
* subsequent getroutes calls simply will not benefit
|
|
* from the constraint. Better to degrade learning
|
|
* than to crash the caller.
|
|
*/
|
|
return Ev::lift();
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
Ev::Io<void>
|
|
inform_channel_constrained( Boss::Mod::Rpc& rpc
|
|
, std::string const& layer
|
|
, Ln::Scid scid
|
|
, std::uint32_t direction
|
|
, Ln::Amount amount
|
|
) {
|
|
return inform_channel(rpc, layer, scid, direction, amount, "constrained");
|
|
}
|
|
|
|
Ev::Io<void>
|
|
inform_channel_unconstrained( Boss::Mod::Rpc& rpc
|
|
, std::string const& layer
|
|
, Ln::Scid scid
|
|
, std::uint32_t direction
|
|
, Ln::Amount amount
|
|
) {
|
|
return inform_channel(rpc, layer, scid, direction, amount, "unconstrained");
|
|
}
|
|
|
|
Ev::Io<void>
|
|
disable_node( Boss::Mod::Rpc& rpc
|
|
, std::string const& layer
|
|
, Ln::NodeId node
|
|
) {
|
|
auto parms = Json::Out()
|
|
.start_object()
|
|
.field("layer", layer)
|
|
.field("node", std::string(node))
|
|
.end_object()
|
|
;
|
|
return rpc.command( "askrene-disable-node"
|
|
, std::move(parms)
|
|
).then([](Jsmn::Object _) {
|
|
return Ev::lift();
|
|
}).catching<RpcError>([](RpcError const&) {
|
|
return Ev::lift();
|
|
});
|
|
}
|
|
|
|
}}}
|