mirror of
https://github.com/ZmnSCPxj/clboss.git
synced 2026-08-13 12:33:20 +02:00
CLN v26.06 deprecates getroute in favor of askrene's getroutes. Replace the Dowser's getroute+listchannels loop (up to ~40 RPC round-trips) with a single getroutes call and let askrene's min-cost-flow solver enumerate multi-path capacity directly. Response parsing follows the getroutes schema; 205/206 (no route / too expensive) maps to zero capacity. Size the probe to the caller's threshold. The Dowser is a flow estimator: callers compare the dowsed flow against a channel size (the janitor and preinvestigator min-channel acceptance test, the channel creator's sizing). A fixed probe amount caps the estimate, so any threshold at or above that cap rejects every candidate regardless of real capacity. RequestDowser now carries a min_amount; callers that test against a channel size set it, and the probe runs at min_amount / reserve_factor so a full-flow result clears the threshold for any configured channel size. The channel creator dowses at max-channel so new channels size toward the operator ceiling instead of being pinned at the min-channel floor. The manual clboss-dowser command keeps the fixed default probe.
176 lines
4.3 KiB
C++
176 lines
4.3 KiB
C++
#include"Boss/Mod/ChannelCandidatePreinvestigator.hpp"
|
|
#include"Boss/ModG/ReqResp.hpp"
|
|
#include"Boss/Msg/AmountSettings.hpp"
|
|
#include"Boss/Msg/PreinvestigateChannelCandidates.hpp"
|
|
#include"Boss/Msg/ProposeChannelCandidates.hpp"
|
|
#include"Boss/Msg/RequestConnect.hpp"
|
|
#include"Boss/Msg/RequestDowser.hpp"
|
|
#include"Boss/Msg/ResponseConnect.hpp"
|
|
#include"Boss/Msg/ResponseDowser.hpp"
|
|
#include"Boss/log.hpp"
|
|
#include"Ev/Io.hpp"
|
|
#include"Ln/Amount.hpp"
|
|
#include"S/Bus.hpp"
|
|
#include"Util/make_unique.hpp"
|
|
#include<functional>
|
|
#include<map>
|
|
#include<queue>
|
|
|
|
namespace Boss { namespace Mod {
|
|
|
|
class ChannelCandidatePreinvestigator::Impl {
|
|
private:
|
|
S::Bus& bus;
|
|
ModG::ReqResp<Msg::RequestDowser, Msg::ResponseDowser> dowser;
|
|
Ln::Amount min_channel;
|
|
|
|
void start() {
|
|
using std::placeholders::_1;
|
|
bus.subscribe<Msg::PreinvestigateChannelCandidates
|
|
>(std::bind(&Impl::on_preinv, this, _1));
|
|
bus.subscribe<Msg::ResponseConnect
|
|
>(std::bind(&Impl::on_connect, this, _1));
|
|
bus.subscribe<Msg::AmountSettings
|
|
>([this](Msg::AmountSettings const& m) {
|
|
min_channel = m.min_channel;
|
|
return Ev::lift();
|
|
});
|
|
}
|
|
|
|
/* A sequence of candidates that are being preinvestigated. */
|
|
class Case : public std::enable_shared_from_this<Case> {
|
|
private:
|
|
S::Bus& bus;
|
|
Impl& impl;
|
|
std::queue<Msg::ProposeChannelCandidates> candidates;
|
|
std::size_t remaining;
|
|
Ln::Amount min_channel;
|
|
|
|
Case( S::Bus& bus_
|
|
, Impl& impl_
|
|
, Msg::PreinvestigateChannelCandidates const& p
|
|
, Ln::Amount min_channel_
|
|
) : bus(bus_)
|
|
, impl(impl_)
|
|
, min_channel(min_channel_)
|
|
{
|
|
remaining = p.max_candidates;
|
|
for (auto const& c : p.candidates)
|
|
candidates.emplace(c);
|
|
}
|
|
|
|
public:
|
|
static
|
|
std::shared_ptr<Case>
|
|
create( S::Bus& bus
|
|
, Impl& impl
|
|
, Msg::PreinvestigateChannelCandidates const& p
|
|
, Ln::Amount min_channel
|
|
) {
|
|
/* Private constructor, cannot use
|
|
* std::make_shared. */
|
|
return std::shared_ptr<Case>(
|
|
new Case(bus, impl, p, min_channel)
|
|
);
|
|
}
|
|
|
|
static
|
|
Ev::Io<void> run(std::shared_ptr<Case> self) {
|
|
return self->loop().then([self]() {
|
|
return Ev::lift();
|
|
});
|
|
}
|
|
private:
|
|
Ev::Io<void> loop() {
|
|
/* If no more, end of preinvestigation. */
|
|
if (candidates.empty())
|
|
return Ev::lift();
|
|
if (remaining == 0)
|
|
return Ev::lift();
|
|
|
|
auto& curr = candidates.front();
|
|
auto& node = curr.proposal;
|
|
auto node_s = std::string(node);
|
|
|
|
/* Add to cases being tracked. */
|
|
impl.add_case(node_s, shared_from_this());
|
|
|
|
/* Trigger connect. */
|
|
return bus.raise(Msg::RequestConnect{
|
|
std::move(node_s)
|
|
});
|
|
}
|
|
public:
|
|
Ev::Io<void> on_connect_response(bool success) {
|
|
auto curr = std::move(candidates.front());
|
|
candidates.pop();
|
|
if (success) {
|
|
--remaining;
|
|
return on_success(curr);
|
|
} else
|
|
return loop();
|
|
}
|
|
private:
|
|
Ev::Io<void>
|
|
on_success(Msg::ProposeChannelCandidates const& p) {
|
|
auto self = shared_from_this();
|
|
return Boss::log( bus, Info
|
|
, "ChannelCandidatesPreinvestigator: "
|
|
"Proposing %s (patron %s)"
|
|
, std::string(p.proposal).c_str()
|
|
, std::string(p.patron).c_str()
|
|
).then([self, p]() {
|
|
return self->bus.raise(p);
|
|
}).then([self]() {
|
|
return self->loop();
|
|
});
|
|
}
|
|
};
|
|
|
|
std::map<std::string, std::shared_ptr<Case>> cases;
|
|
void add_case( std::string const& node
|
|
, std::shared_ptr<Case> c
|
|
) {
|
|
cases[node] = std::move(c);
|
|
}
|
|
|
|
Ev::Io<void> on_preinv(Msg::PreinvestigateChannelCandidates const& p) {
|
|
auto c = Case::create(bus, *this, p, min_channel);
|
|
return Case::run(c);
|
|
}
|
|
Ev::Io<void> on_connect(Msg::ResponseConnect const& r) {
|
|
/* Look it up. */
|
|
auto it = cases.find(r.node);
|
|
if (it == cases.end())
|
|
return Ev::lift();
|
|
|
|
/* Remove it from cases. */
|
|
auto c = std::move(it->second);
|
|
cases.erase(it);
|
|
|
|
/* Execute it. */
|
|
return c->on_connect_response(r.success).then([c]() {
|
|
return Ev::lift();
|
|
});
|
|
}
|
|
|
|
public:
|
|
Impl() =delete;
|
|
Impl(S::Bus& bus_
|
|
) : bus(bus_)
|
|
, dowser(bus_)
|
|
{ start(); }
|
|
};
|
|
|
|
|
|
ChannelCandidatePreinvestigator::ChannelCandidatePreinvestigator(S::Bus& bus)
|
|
: pimpl(Util::make_unique<Impl>(bus))
|
|
{ }
|
|
ChannelCandidatePreinvestigator::ChannelCandidatePreinvestigator
|
|
(ChannelCandidatePreinvestigator&& o)
|
|
: pimpl(std::move(o.pimpl))
|
|
{ }
|
|
|
|
ChannelCandidatePreinvestigator::~ChannelCandidatePreinvestigator() { }
|
|
|
|
}}
|