#include"Boss/Mod/SwapReporter.hpp" #include"Boss/Msg/CommandRequest.hpp" #include"Boss/Msg/CommandResponse.hpp" #include"Boss/Msg/DbResource.hpp" #include"Boss/Msg/ManifestCommand.hpp" #include"Boss/Msg/Manifestation.hpp" #include"Boss/Msg/ProvideStatus.hpp" #include"Boss/Msg/SolicitStatus.hpp" #include"Boss/Msg/SwapCompleted.hpp" #include"Ev/Io.hpp" #include"Ev/now.hpp" #include"Json/Out.hpp" #include"S/Bus.hpp" #include"Sqlite3.hpp" #include"Util/date.hpp" #include"Util/make_unique.hpp" namespace Boss { namespace Mod { class SwapReporter::Impl { private: S::Bus& bus; Sqlite3::Db db; void start() { bus.subscribe([this](Msg::DbResource const& m) { db = m.db; return db.transact().then([](Sqlite3::Tx tx) { tx.query_execute(R"QRY( CREATE TABLE IF NOT EXISTS "SwapReporter" ( id INTEGER PRIMARY KEY , time REAL NOT NULL , amount_sent INTEGER NOT NULL , amount_received INTEGER NOT NULL , provider_name TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS "SwapReporter_time_idx" ON "SwapReporter"(time) ; )QRY"); tx.commit(); return Ev::lift(); }); }); bus.subscribe([](Msg::SwapCompleted const& m) { /* Record it in our own table, using the transaction * that SwapManager is keeping open. */ m.dbtx->query(R"QRY( INSERT INTO "SwapReporter" VALUES( NULL -- id, autogenerated , :time , :amount_sent , :amount_received , :provider_name ); )QRY") .bind(":time", Ev::now()) .bind( ":amount_sent" , m.amount_sent.to_msat() ) .bind( ":amount_received" , m.amount_received.to_msat() ) .bind( ":provider_name" , m.provider_name ) .execute() ; return Ev::lift(); }); /* `clboss-status` */ bus.subscribe([this](Msg::SolicitStatus const& _) { return db.transact().then([this](Sqlite3::Tx tx) { auto report = make_report(tx); tx.commit(); return bus.raise(Msg::ProvideStatus{ "swap_report", std::move(report) }); }); }); /* `clboss-swaps` */ bus.subscribe([this](Msg::Manifestation const& _) { return bus.raise(Msg::ManifestCommand{ "clboss-swaps", "", "Report about offchain-to-onchain swaps", false }); }); bus.subscribe([this](Msg::CommandRequest const& r) { if (r.command != "clboss-swaps") return Ev::lift(); auto id = r.id; if (!db) return bus.raise(Msg::CommandResponse{ id, Json::Out() .start_object() .field("error", "db not yet available") .end_object() }); return db.transact().then([this, id](Sqlite3::Tx tx) { auto report = make_report(tx); tx.commit(); return bus.raise(Msg::CommandResponse{ id, report }); }); }); } Json::Out make_report(Sqlite3::Tx& tx) { auto total_amount_sent = Ln::Amount::sat(0); auto total_amount_received = Ln::Amount::sat(0); auto delta = Ln::Amount::sat(0); auto percent = double(); auto rv = Json::Out(); auto obj = rv.start_object(); auto scan = tx.query(R"QRY( SELECT time, amount_sent, amount_received, provider_name FROM "SwapReporter" ORDER BY time ASC; )QRY").execute(); auto arr = obj.start_array("swaps"); for (auto& r : scan) { auto time = r.get(0); auto amount_sent = Ln::Amount::msat( r.get(1) ); auto amount_received = Ln::Amount::msat( r.get(2) ); auto amount_lost = amount_sent - amount_received; auto provider_name = r.get(3); total_amount_sent += amount_sent; total_amount_received += amount_received; arr .start_object() .field("time", time) .field("time_human", Util::date(time)) .field( "amount_sent" , std::string(amount_sent) ) .field( "amount_received" , std::string(amount_received) ) .field( "amount_lost" , std::string(amount_lost) ) .field("provider", provider_name) .end_object() ; } arr.end_array(); delta = total_amount_sent - total_amount_received; if (total_amount_sent > Ln::Amount::sat(0)) percent = 100.0 * (delta / total_amount_sent); obj .field( "total_amount_sent" , std::string(total_amount_sent) ) .field( "total_amount_received" , std::string(total_amount_received) ) .field( "loss" , std::string(delta) ) .field( "percent_loss" , percent ) ; obj.end_object(); return rv; } public: Impl() =delete; Impl(Impl&&) =delete; explicit Impl(S::Bus& bus_) : bus(bus_) { start(); } }; SwapReporter::SwapReporter(SwapReporter&&) =default; SwapReporter::~SwapReporter() =default; SwapReporter::SwapReporter(S::Bus& bus) : pimpl(Util::make_unique(bus)) { } }}