mirror of
https://github.com/ZmnSCPxj/clboss.git
synced 2026-08-13 12:33:20 +02:00
ChannelCreator: fund candidates in track-record tier order
After the size rearranger and the IP-binning reprioritizer, partition the proposal list by track-record verdict: keepers first, then candidates with no record, then underperformers. Within a tier the earlier stages' order is preserved. Because the Planner consumes proposals in order until funds run out, placing a tier last implements 'only used if no better candidate can absorb the funds' without an outright veto. The partition runs last so the earlier perturbation stages cannot promote a candidate across a tier boundary. Logs one Info line per creation request with the per-tier membership and each judged candidate's TRAL and observed days.
This commit is contained in:
parent
a83489ab03
commit
0aecb8bcc3
3 changed files with 133 additions and 3 deletions
|
|
@ -22,6 +22,7 @@
|
|||
#include"Util/make_unique.hpp"
|
||||
#include<algorithm>
|
||||
#include<assert.h>
|
||||
#include<cmath>
|
||||
#include<sstream>
|
||||
|
||||
namespace {
|
||||
|
|
@ -161,6 +162,19 @@ Manager::on_request_channel_creation(Ln::Amount amt) {
|
|||
* nodes with similar locations.
|
||||
*/
|
||||
return reprioritize(std::move(proposals));
|
||||
}).then([this](std::vector<std::pair<Ln::NodeId, Ln::NodeId>> proposals) {
|
||||
/* Finally, partition by earnings track record, so
|
||||
* proven earners are funded first and known
|
||||
* underperformers only when nothing else can absorb
|
||||
* the funds. This runs after the rearranger and
|
||||
* reprioritizer on purpose: those two only perturb
|
||||
* the order, and must not promote a candidate across
|
||||
* a track-record tier boundary. The Planner consumes
|
||||
* proposals in order until funds run out, so placing
|
||||
* a tier last implements "only if there are no
|
||||
* others" without an outright veto.
|
||||
*/
|
||||
return prioritize_by_track_record(std::move(proposals));
|
||||
}).then([ num_chans
|
||||
, amt
|
||||
, dowser_func
|
||||
|
|
@ -307,5 +321,103 @@ Manager::reprioritize(std::vector<std::pair<Ln::NodeId, Ln::NodeId>> proposals_v
|
|||
return Ev::lift(std::move(*proposals));
|
||||
});
|
||||
}
|
||||
Ev::Io<std::vector<std::pair<Ln::NodeId, Ln::NodeId>>>
|
||||
Manager::prioritize_by_track_record(std::vector<std::pair<Ln::NodeId, Ln::NodeId>> proposals_v) {
|
||||
typedef std::vector<std::pair<Ln::NodeId, Ln::NodeId>> Proposals;
|
||||
|
||||
if (proposals_v.empty())
|
||||
return Ev::lift(std::move(proposals_v));
|
||||
|
||||
auto nodes = std::vector<Ln::NodeId>();
|
||||
for (auto const& p : proposals_v)
|
||||
nodes.push_back(p.first);
|
||||
auto proposals = std::make_shared<Proposals>(std::move(proposals_v));
|
||||
|
||||
return track_record.execute(Msg::RequestPeerTrackRecord{
|
||||
nullptr, std::move(nodes)
|
||||
}).then([ this
|
||||
, proposals
|
||||
](Msg::ResponsePeerTrackRecord resp) {
|
||||
auto keepers = Proposals();
|
||||
auto no_records = Proposals();
|
||||
auto underperformers = Proposals();
|
||||
|
||||
/* Per-tier report text; nodes within a tier keep their
|
||||
* relative order from the earlier stages. */
|
||||
auto keepers_s = std::string();
|
||||
auto no_records_s = std::string();
|
||||
auto underperformers_s = std::string();
|
||||
auto append = []( std::string& s
|
||||
, std::string const& entry
|
||||
) {
|
||||
if (!s.empty())
|
||||
s += ", ";
|
||||
s += entry;
|
||||
};
|
||||
|
||||
for (auto const& p : *proposals) {
|
||||
auto rec = Msg::TrackRecord{
|
||||
Msg::TrackRecordVerdict::NoRecord,
|
||||
0.0, 0.0, 0
|
||||
};
|
||||
auto it = resp.records.find(p.first);
|
||||
if (it != resp.records.end())
|
||||
rec = it->second;
|
||||
|
||||
auto os = std::ostringstream();
|
||||
os << p.first;
|
||||
if (rec.verdict != Msg::TrackRecordVerdict::NoRecord)
|
||||
os << "(" << std::showpos
|
||||
<< (long long) std::llround(rec.tral_bps)
|
||||
<< std::noshowpos << "bps/"
|
||||
<< (long long) std::llround(rec.op_days)
|
||||
<< "d)"
|
||||
;
|
||||
|
||||
switch (rec.verdict) {
|
||||
case Msg::TrackRecordVerdict::Keeper:
|
||||
keepers.push_back(p);
|
||||
append(keepers_s, os.str());
|
||||
break;
|
||||
case Msg::TrackRecordVerdict::NoRecord:
|
||||
no_records.push_back(p);
|
||||
append(no_records_s, os.str());
|
||||
break;
|
||||
case Msg::TrackRecordVerdict::Underperformer:
|
||||
underperformers.push_back(p);
|
||||
append(underperformers_s, os.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto report = std::string();
|
||||
if (!keepers_s.empty())
|
||||
report += "keepers: " + keepers_s + "; ";
|
||||
if (!no_records_s.empty())
|
||||
report += "no record: " + no_records_s + "; ";
|
||||
if (!underperformers_s.empty())
|
||||
report += "underperformers: "
|
||||
+ underperformers_s + "; "
|
||||
;
|
||||
/* Trim the trailing "; ". */
|
||||
report.erase(report.size() - 2);
|
||||
|
||||
*proposals = std::move(keepers);
|
||||
proposals->insert( proposals->end()
|
||||
, no_records.begin(), no_records.end()
|
||||
);
|
||||
proposals->insert( proposals->end()
|
||||
, underperformers.begin()
|
||||
, underperformers.end()
|
||||
);
|
||||
|
||||
return Boss::log( bus, Info
|
||||
, "ChannelCreator: Track records: %s"
|
||||
, report.c_str()
|
||||
).then([proposals]() {
|
||||
return Ev::lift(std::move(*proposals));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}}}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
#include"Boss/Mod/ChannelCreator/Reprioritizer.hpp"
|
||||
#include"Boss/ModG/ReqResp.hpp"
|
||||
#include"Boss/Msg/RequestDowser.hpp"
|
||||
#include"Boss/Msg/RequestPeerTrackRecord.hpp"
|
||||
#include"Boss/Msg/ResponseDowser.hpp"
|
||||
#include"Boss/Msg/ResponsePeerTrackRecord.hpp"
|
||||
#include"Ln/NodeId.hpp"
|
||||
#include<memory>
|
||||
#include<utility>
|
||||
|
|
@ -40,6 +42,9 @@ private:
|
|||
Ln::NodeId self;
|
||||
|
||||
ModG::ReqResp<Msg::RequestDowser, Msg::ResponseDowser> dowser;
|
||||
ModG::ReqResp< Msg::RequestPeerTrackRecord
|
||||
, Msg::ResponsePeerTrackRecord
|
||||
> track_record;
|
||||
|
||||
std::unique_ptr<Boss::Mod::ChannelCreator::Reprioritizer> reprioritizer;
|
||||
|
||||
|
|
@ -56,6 +61,9 @@ private:
|
|||
/* Perform reprioritization and log it. */
|
||||
Ev::Io<std::vector<std::pair<Ln::NodeId, Ln::NodeId>>>
|
||||
reprioritize(std::vector<std::pair<Ln::NodeId, Ln::NodeId>>);
|
||||
/* Partition proposals by earnings track record and log it. */
|
||||
Ev::Io<std::vector<std::pair<Ln::NodeId, Ln::NodeId>>>
|
||||
prioritize_by_track_record(std::vector<std::pair<Ln::NodeId, Ln::NodeId>>);
|
||||
|
||||
public:
|
||||
Manager() =delete;
|
||||
|
|
@ -72,6 +80,7 @@ public:
|
|||
, carpenter(carpenter_)
|
||||
, self()
|
||||
, dowser(bus)
|
||||
, track_record(bus)
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# This script produces a summary of channel forwarding stats
|
||||
#
|
||||
# - Displays PeerID, SCID, and Alias for each channel
|
||||
# - Displays SCID and Alias (or the full PeerID with --ids) for each channel
|
||||
# - Uses `clboss-recent-earnings` to limit the history considered
|
||||
#
|
||||
# The channels at the top of the list are good, the ones at the bottom are bad.
|
||||
|
|
@ -222,6 +222,11 @@ def main():
|
|||
"--sort", choices=["fral", "tral"], default="fral",
|
||||
help="Column to sort by (default: fral).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ids", action="store_true",
|
||||
help="Show full peer node ids instead of aliases "
|
||||
"(paste-ready for commands that take a nodeid).",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
|
@ -389,7 +394,10 @@ def main():
|
|||
tralstr = f"{tral:.3f}" if tral is not None else "--"
|
||||
fwded = peer.get("in_forwarded", 0) + peer.get("out_forwarded", 0)
|
||||
netearnings = peer_net_earnings(peer)
|
||||
alias = pad_string(peer["alias"], max_alias_length)
|
||||
if args.ids:
|
||||
alias = peer_id
|
||||
else:
|
||||
alias = pad_string(peer["alias"], max_alias_length)
|
||||
opener = "L" if channels[short_channel_id]["opener"] == "local" else "R"
|
||||
agestr = str(int(peer_age_days(peer)))
|
||||
opdaysstr = f"{op_days:.1f}"
|
||||
|
|
@ -414,9 +422,10 @@ def main():
|
|||
)
|
||||
|
||||
# Print the table without grid
|
||||
id_col = "PeerID" if args.ids else "Alias"
|
||||
table_str = tabulate(
|
||||
table_data,
|
||||
headers=["Alias", "SCID", "O", "curr_to_us", "avg_to_us", "Age", "OpDays", "Forwarded", "NetEarn", "FRAL**", "TRAL*"],
|
||||
headers=[id_col, "SCID", "O", "curr_to_us", "avg_to_us", "Age", "OpDays", "Forwarded", "NetEarn", "FRAL**", "TRAL*"],
|
||||
tablefmt="plain",
|
||||
stralign="left",
|
||||
numalign="right",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue