2020-09-30 13:20:24 +08:00
|
|
|
#include"Boss/Mod/PeerCompetitorFeeMonitor/Main.hpp"
|
|
|
|
|
#include"Boss/Mod/PeerCompetitorFeeMonitor/Surveyor.hpp"
|
2022-04-26 12:19:28 +08:00
|
|
|
#include"Boss/Msg/AvailableRpcCommands.hpp"
|
2020-09-30 13:20:24 +08:00
|
|
|
#include"Boss/Msg/Init.hpp"
|
|
|
|
|
#include"Boss/Msg/ListpeersAnalyzedResult.hpp"
|
|
|
|
|
#include"Boss/Msg/PeerMedianChannelFee.hpp"
|
|
|
|
|
#include"Boss/Msg/TimerRandomHourly.hpp"
|
|
|
|
|
#include"Boss/concurrent.hpp"
|
|
|
|
|
#include"Boss/log.hpp"
|
|
|
|
|
#include"Ev/Io.hpp"
|
|
|
|
|
#include"Ev/foreach.hpp"
|
|
|
|
|
#include"Ev/map.hpp"
|
|
|
|
|
#include"S/Bus.hpp"
|
|
|
|
|
#include"Util/make_unique.hpp"
|
|
|
|
|
#include<algorithm>
|
|
|
|
|
#include<iterator>
|
|
|
|
|
#include<sstream>
|
|
|
|
|
|
|
|
|
|
namespace Boss { namespace Mod { namespace PeerCompetitorFeeMonitor {
|
|
|
|
|
|
|
|
|
|
class Main::Impl {
|
|
|
|
|
private:
|
|
|
|
|
S::Bus& bus;
|
|
|
|
|
Boss::Mod::Rpc* rpc;
|
|
|
|
|
|
2022-04-26 12:19:28 +08:00
|
|
|
bool have_listchannels_destination;
|
|
|
|
|
bool got_available_commands;
|
|
|
|
|
|
2020-09-30 13:20:24 +08:00
|
|
|
Ln::NodeId self_id;
|
|
|
|
|
|
|
|
|
|
/* Current known peers with channels. */
|
|
|
|
|
std::vector<Ln::NodeId> channels;
|
|
|
|
|
/* Whether the above vector is valid. */
|
|
|
|
|
bool have_channels;
|
|
|
|
|
/* Whether we have fired the initial channel fees. */
|
|
|
|
|
bool fired_init;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Impl(S::Bus& bus_
|
|
|
|
|
) : bus(bus_)
|
|
|
|
|
, rpc(nullptr)
|
2022-04-26 12:19:28 +08:00
|
|
|
, got_available_commands(false)
|
2020-09-30 13:20:24 +08:00
|
|
|
, have_channels(false)
|
2020-11-02 14:54:02 +08:00
|
|
|
, fired_init(false)
|
2020-09-30 13:20:24 +08:00
|
|
|
{ start(); }
|
|
|
|
|
private:
|
|
|
|
|
void start() {
|
|
|
|
|
bus.subscribe<Msg::Init
|
|
|
|
|
>([this](Msg::Init const& init) {
|
|
|
|
|
rpc = &init.rpc;
|
|
|
|
|
self_id = init.self_id;
|
|
|
|
|
if (have_channels && !fired_init) {
|
|
|
|
|
fired_init = true;
|
|
|
|
|
return Boss::concurrent(on_periodic());
|
|
|
|
|
}
|
|
|
|
|
return Ev::lift();
|
|
|
|
|
});
|
|
|
|
|
bus.subscribe<Msg::ListpeersAnalyzedResult
|
|
|
|
|
>([this](Msg::ListpeersAnalyzedResult const& r) {
|
|
|
|
|
/* Copy into channels vector. */
|
|
|
|
|
channels.clear();
|
|
|
|
|
std::copy( r.connected_channeled.begin()
|
|
|
|
|
, r.connected_channeled.end()
|
|
|
|
|
, std::back_inserter(channels)
|
|
|
|
|
);
|
|
|
|
|
std::copy( r.disconnected_channeled.begin()
|
|
|
|
|
, r.disconnected_channeled.end()
|
|
|
|
|
, std::back_inserter(channels)
|
|
|
|
|
);
|
|
|
|
|
have_channels = true;
|
|
|
|
|
|
|
|
|
|
if (rpc && !fired_init) {
|
|
|
|
|
fired_init = true;
|
|
|
|
|
return Boss::concurrent(on_periodic());
|
|
|
|
|
}
|
|
|
|
|
return Ev::lift();
|
|
|
|
|
});
|
|
|
|
|
bus.subscribe<Msg::TimerRandomHourly
|
|
|
|
|
>([this](Msg::TimerRandomHourly const&) {
|
|
|
|
|
if (!rpc || !have_channels)
|
|
|
|
|
return Ev::lift();
|
|
|
|
|
return on_periodic();
|
|
|
|
|
});
|
2022-04-26 12:19:28 +08:00
|
|
|
bus.subscribe<Msg::AvailableRpcCommands
|
|
|
|
|
>([this](Msg::AvailableRpcCommands const& m) {
|
|
|
|
|
got_available_commands = true;
|
|
|
|
|
auto it = m.commands.find("listchannels");
|
|
|
|
|
if (it == m.commands.end()) {
|
|
|
|
|
/* No `listchannels` command...? */
|
|
|
|
|
have_listchannels_destination = false;
|
|
|
|
|
return Ev::lift();
|
|
|
|
|
}
|
|
|
|
|
auto& desc = it->second;
|
|
|
|
|
auto& usage = desc.usage;
|
|
|
|
|
|
|
|
|
|
/* Look for [destination] in usage. */
|
|
|
|
|
auto static const needle = std::string("[destination]");
|
|
|
|
|
auto result = std::search( usage.begin(), usage.end()
|
|
|
|
|
, needle.begin(), needle.end()
|
|
|
|
|
);
|
|
|
|
|
have_listchannels_destination = (result != usage.end());
|
|
|
|
|
|
|
|
|
|
return Boss::log( bus, Debug
|
|
|
|
|
, "PeerCompetitorFeeMonitor: "
|
|
|
|
|
"have_listchannels_destination = %s"
|
|
|
|
|
, have_listchannels_destination ? "true" : "false"
|
|
|
|
|
);
|
|
|
|
|
});
|
2020-09-30 13:20:24 +08:00
|
|
|
}
|
|
|
|
|
|
2022-04-26 12:19:28 +08:00
|
|
|
Ev::Io<void> wait_available_commands() {
|
2020-09-30 13:20:24 +08:00
|
|
|
return Ev::lift().then([this]() {
|
2022-04-26 12:19:28 +08:00
|
|
|
if (got_available_commands)
|
|
|
|
|
return Ev::lift();
|
|
|
|
|
return Ev::yield() + wait_available_commands();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ev::Io<void> on_periodic() {
|
|
|
|
|
return wait_available_commands().then([this]() {
|
2020-09-30 13:20:24 +08:00
|
|
|
auto f = [this](Ln::NodeId nid) {
|
|
|
|
|
auto surveyor = Surveyor::create
|
|
|
|
|
( bus
|
|
|
|
|
, *rpc
|
|
|
|
|
, self_id
|
|
|
|
|
, std::move(nid)
|
2022-04-26 12:19:28 +08:00
|
|
|
, have_listchannels_destination
|
2020-09-30 13:20:24 +08:00
|
|
|
);
|
|
|
|
|
return surveyor->run();
|
|
|
|
|
};
|
|
|
|
|
/* Do not std::move channels --- we need to retain
|
|
|
|
|
* it. */
|
|
|
|
|
return Ev::map(f, channels);
|
|
|
|
|
}).then([this](std::vector<std::unique_ptr<Surveyor::Result>> results) {
|
|
|
|
|
/* Build report. */
|
|
|
|
|
auto os = std::ostringstream();
|
|
|
|
|
auto first = true;
|
|
|
|
|
for (auto const& r : results) {
|
|
|
|
|
if (!r)
|
|
|
|
|
continue;
|
|
|
|
|
if (first)
|
|
|
|
|
first = false;
|
|
|
|
|
else
|
|
|
|
|
os << ", ";
|
|
|
|
|
os << r->peer_id << "("
|
|
|
|
|
<< "b = " << r->median_base << ", "
|
|
|
|
|
<< "p = " << r->median_proportional
|
|
|
|
|
<< ")"
|
|
|
|
|
;
|
|
|
|
|
}
|
2020-09-30 17:49:28 +08:00
|
|
|
auto act = Boss::log( bus, Debug
|
|
|
|
|
, "PeerCompetitorFeeMonitor: "
|
|
|
|
|
"Weighted median fees: %s"
|
|
|
|
|
, os.str().c_str()
|
|
|
|
|
);
|
2020-09-30 13:20:24 +08:00
|
|
|
|
|
|
|
|
auto f = [this](std::unique_ptr<Surveyor::Result> r) {
|
|
|
|
|
if (!r)
|
|
|
|
|
return Ev::lift();
|
|
|
|
|
return bus.raise(Msg::PeerMedianChannelFee{
|
|
|
|
|
std::move(r->peer_id),
|
|
|
|
|
r->median_base,
|
|
|
|
|
r->median_proportional
|
|
|
|
|
});
|
|
|
|
|
};
|
2020-09-30 17:49:28 +08:00
|
|
|
act += Ev::foreach(f, std::move(results));
|
2020-09-30 13:20:24 +08:00
|
|
|
|
|
|
|
|
return act;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Main::Main(Main&&) =default;
|
|
|
|
|
Main& Main::operator=(Main&&) =default;
|
|
|
|
|
Main::~Main() =default;
|
|
|
|
|
|
|
|
|
|
Main::Main(S::Bus& bus) : pimpl(Util::make_unique<Impl>(bus)) { }
|
|
|
|
|
|
|
|
|
|
}}}
|