#include"Boss/Mod/ChannelCreationDecider.hpp" #include"Boss/Msg/AmountSettings.hpp" #include"Boss/Msg/ChannelFunds.hpp" #include"Boss/Msg/NeedsOnchainFunds.hpp" #include"Boss/Msg/OnchainFee.hpp" #include"Boss/Msg/OnchainFunds.hpp" #include"Boss/Msg/RequestChannelCreation.hpp" #include"Boss/concurrent.hpp" #include"Boss/log.hpp" #include"S/Bus.hpp" #include"Util/make_unique.hpp" #include"Util/stringify.hpp" #include namespace { /* How much onchain funds to add when emitting NeedsOnchainFunds. */ auto const needs_reserve = Ln::Amount::sat(30000); /* How much we are willing to emit as "needs onchain". */ auto const max_needs_onchain = Ln::Amount::btc(0.010); /* If our onchain funds are below this % of our total, hold off on * creating channels. */ auto const onchain_percent_min = double(10); /* If high fees, if onchain funds is more than this percent of * total funds, create channels. * If below this, wait for low-fee region. */ auto const onchain_percent_max = double(25); /* If onchain funds are above or equal to this amount, but in * onchain-percent terms is below onchain_percent_min, create * channels anyway --- the channels are going to be fairly * large still, so okay even if below onchain_percent_min. * This only comes into play for really rich nodes with * 1.6777215 BTC or more. */ auto const max_onchain_holdoff = Ln::Amount::sat(16777215); } namespace Boss { namespace Mod { class ChannelCreationDecider::Impl { private: S::Bus& bus; std::unique_ptr low_fee; std::unique_ptr channels; std::unique_ptr saved_onchain; Ln::Amount reserve; Ln::Amount min_amount; void start() { bus.subscribe< Msg::ChannelFunds >([this](Msg::ChannelFunds const& m) { if (!channels) channels = Util::make_unique(); *channels = m.total; return run(); }); bus.subscribe< Msg::OnchainFee >([this](Msg::OnchainFee const& m) { if (!low_fee) low_fee = Util::make_unique(); *low_fee = m.fees_low; return run(); }); bus.subscribe< Msg::OnchainFunds >([this](Msg::OnchainFunds const& m) { if (!saved_onchain) saved_onchain = Util::make_unique(); *saved_onchain = m.onchain; return run(); }); bus.subscribe([this](Msg::AmountSettings const& m) { reserve = m.reserve; min_amount = m.min_amount; return Ev::lift(); }); } Ev::Io run() { /* Do nothing if not complete information yet. */ if (!(low_fee && channels && saved_onchain)) return Ev::lift(); /* Get this trigger. */ auto onchain = std::move(saved_onchain); /* How much total money do we have anyway? */ auto total = *channels + *onchain; /* Is the amount the reserve? */ if (*onchain <= reserve) /* Do nothing and be silent. */ return decide_do_nothing_silently(); /* What is our minimum channeling amount? */ auto target = total * (onchain_percent_min / 100.0); if (target < min_amount) target = min_amount; if (target > max_onchain_holdoff) target = max_onchain_holdoff; /* Make sure to add the reserve! */ target += reserve; /* Below our minimum channeling amount? */ if (*onchain < target) { /* Add the needs_reserve here because fees. */ auto more = ( target + needs_reserve ) - *onchain ; auto more_btc = more.to_btc(); /* Totally not a "just send me more funds to * redeem your money" scam. */ return decide( std::string("Onchain amount too low, ") + "add " + Util::stringify(more_btc) + " or more before we create channels" , nullptr ).then([this, more]() { if (more > max_needs_onchain) { return Boss::log( bus, Debug , "ChannelCreationDecider: " "We need %s, which is " "greater than our limit of " "%s; will NOT move funds " "from channels to onchain." , std::string(more).c_str() , std::string(max_needs_onchain) .c_str() ); } /* And tell everyone about the * missingfunds. */ return bus.raise(Msg::NeedsOnchainFunds{ more }); }); } if (*low_fee) return decide("Low fees now", std::move(onchain)); auto onchain_percent = (*onchain / total) * 100; if (onchain_percent > onchain_percent_max) return decide( std::string("High fees, but ") + Util::stringify(onchain_percent) + "% of our funds are onchain, " "which is above our limit of " + Util::stringify(onchain_percent_max) + "%" , std::move(onchain) ); return decide("High fees now", nullptr); } Ev::Io decide( std::string comment , std::unique_ptr pfunding ) { if (!pfunding) return Boss::log( bus, Info , "ChannelCreationDecider: %s. " "Will not create channels yet." , comment.c_str() ); auto funding = *pfunding; auto actual = funding - reserve; return Boss::concurrent( bus.raise(Msg::RequestChannelCreation{actual}) ).then([ this , actual , comment ]() { return Boss::log( bus, Info , "ChannelCreationDecider: %s." "Will create channels worth %s " "(%s reserved for onchain actions)." , comment.c_str() , std::string(actual).c_str() , std::string(reserve).c_str() ); }); } Ev::Io decide_do_nothing_silently() { return Ev::lift(); } public: Impl() =delete; Impl(Impl const&) =delete; Impl(S::Bus& bus_) : bus(bus_) { start(); } }; ChannelCreationDecider::ChannelCreationDecider(S::Bus& bus) : pimpl(Util::make_unique(bus)) { } ChannelCreationDecider::ChannelCreationDecider(ChannelCreationDecider&& o) : pimpl(std::move(o.pimpl)) { } ChannelCreationDecider::~ChannelCreationDecider() { } }}