Remove clboss-xrebalance-size-factor and its per-cycle lo:hi random sweep. The matched-pool cycle now requests the full matched volume that cleared the floor. Both purposes in the option's help text no longer apply: the per-channel band-edge caps in the request lists bound what each channel absorbs, and per-request askrene layers removed the accumulated route state the size sweep kept from staling. The focused cycle was already sized flat (target deficit). The clboss-xrebalance-view emulation drops --size-factor to match. Configs that set the option must drop it before starting this build (an unknown plugin option fails lightningd startup). |
||
|---|---|---|
| .. | ||
| BoltzSwapper | ||
| ChannelCandidateInvestigator | ||
| ChannelCreator | ||
| FundsMover | ||
| PeerCompetitorFeeMonitor | ||
| PeerComplaintsDesk | ||
| PeerJudge | ||
| XMoveFunds | ||
| ActiveProber.cpp | ||
| ActiveProber.hpp | ||
| all.cpp | ||
| all.hpp | ||
| AmountSettingsHandler.cpp | ||
| AmountSettingsHandler.hpp | ||
| AskreneLayer.cpp | ||
| AskreneLayer.hpp | ||
| AskreneUpdates.cpp | ||
| AskreneUpdates.hpp | ||
| AutoDisconnector.cpp | ||
| AutoDisconnector.hpp | ||
| AvailableRpcCommandsAnnouncer.cpp | ||
| AvailableRpcCommandsAnnouncer.hpp | ||
| BlockTracker.cpp | ||
| BlockTracker.hpp | ||
| ChannelCandidateMatchmaker.cpp | ||
| ChannelCandidateMatchmaker.hpp | ||
| ChannelCandidatePreinvestigator.cpp | ||
| ChannelCandidatePreinvestigator.hpp | ||
| ChannelCreateDestroyMonitor.cpp | ||
| ChannelCreateDestroyMonitor.hpp | ||
| ChannelCreationDecider.cpp | ||
| ChannelCreationDecider.hpp | ||
| ChannelFeeManager.cpp | ||
| ChannelFeeManager.hpp | ||
| ChannelFeeSetter.cpp | ||
| ChannelFeeSetter.hpp | ||
| ChannelFinderByDistance.cpp | ||
| ChannelFinderByDistance.hpp | ||
| ChannelFinderByEarnedFee.cpp | ||
| ChannelFinderByEarnedFee.hpp | ||
| ChannelFinderByListpays.cpp | ||
| ChannelFinderByListpays.hpp | ||
| ChannelFinderByPopularity.cpp | ||
| ChannelFinderByPopularity.hpp | ||
| ChannelFundsComputer.cpp | ||
| ChannelFundsComputer.hpp | ||
| CommandReceiver.cpp | ||
| CommandReceiver.hpp | ||
| ComplainerByLowConnectRate.cpp | ||
| ComplainerByLowConnectRate.hpp | ||
| ComplainerByLowSuccessPerDay.cpp | ||
| ComplainerByLowSuccessPerDay.hpp | ||
| ConnectFinderByDns.cpp | ||
| ConnectFinderByDns.hpp | ||
| ConnectFinderByHardcode.cpp | ||
| ConnectFinderByHardcode.hpp | ||
| Connector.cpp | ||
| Connector.hpp | ||
| ConstructedListpeers.cpp | ||
| ConstructedListpeers.hpp | ||
| Dowser.cpp | ||
| Dowser.hpp | ||
| EarningsRebalancer.cpp | ||
| EarningsRebalancer.hpp | ||
| EarningsTracker.cpp | ||
| EarningsTracker.hpp | ||
| FeeModderByBalance.cpp | ||
| FeeModderByBalance.hpp | ||
| FeeModderByPriceTheory.cpp | ||
| FeeModderByPriceTheory.hpp | ||
| FeeModderBySize.cpp | ||
| FeeModderBySize.hpp | ||
| FeeMonitor.cpp | ||
| FeeMonitor.hpp | ||
| ForwardFeeMonitor.cpp | ||
| ForwardFeeMonitor.hpp | ||
| HtlcAcceptor.cpp | ||
| HtlcAcceptor.hpp | ||
| InitialConnect.cpp | ||
| InitialConnect.hpp | ||
| InitialRebalancer.cpp | ||
| InitialRebalancer.hpp | ||
| Initiator.cpp | ||
| Initiator.hpp | ||
| InternetConnectionMonitor.cpp | ||
| InternetConnectionMonitor.hpp | ||
| InvoicePayer.cpp | ||
| InvoicePayer.hpp | ||
| JitRebalancer.cpp | ||
| JitRebalancer.hpp | ||
| JsonOutputter.cpp | ||
| JsonOutputter.hpp | ||
| ListfundsAnalyzer.cpp | ||
| ListfundsAnalyzer.hpp | ||
| ListfundsAnnouncer.cpp | ||
| ListfundsAnnouncer.hpp | ||
| ListpaysHandler.cpp | ||
| ListpaysHandler.hpp | ||
| ListpeersAnalyzer.cpp | ||
| ListpeersAnalyzer.hpp | ||
| ListpeersAnnouncer.cpp | ||
| ListpeersAnnouncer.hpp | ||
| Manifester.cpp | ||
| Manifester.hpp | ||
| MoveFundsCommand.cpp | ||
| MoveFundsCommand.hpp | ||
| NeedsConnectSolicitor.cpp | ||
| NeedsConnectSolicitor.hpp | ||
| NeedsOnchainFundsSwapper.cpp | ||
| NeedsOnchainFundsSwapper.hpp | ||
| NewaddrHandler.cpp | ||
| NewaddrHandler.hpp | ||
| NodeBalanceSwapper.cpp | ||
| NodeBalanceSwapper.hpp | ||
| OnchainFeeMonitor.cpp | ||
| OnchainFeeMonitor.hpp | ||
| OnchainFundsAnnouncer.cpp | ||
| OnchainFundsAnnouncer.hpp | ||
| OnchainFundsIgnorer.cpp | ||
| OnchainFundsIgnorer.hpp | ||
| PaymentDeleter.cpp | ||
| PaymentDeleter.hpp | ||
| PeerFromScidMapper.cpp | ||
| PeerFromScidMapper.hpp | ||
| PeerMetrician.cpp | ||
| PeerMetrician.hpp | ||
| PeerStatistician.cpp | ||
| PeerStatistician.hpp | ||
| PeerTrackRecord.cpp | ||
| PeerTrackRecord.hpp | ||
| README.md | ||
| RebalanceModeManager.cpp | ||
| RebalanceModeManager.hpp | ||
| RebalanceUnmanager.cpp | ||
| RebalanceUnmanager.hpp | ||
| Reconnector.cpp | ||
| Reconnector.hpp | ||
| RegularActiveProbe.cpp | ||
| RegularActiveProbe.hpp | ||
| Rpc.cpp | ||
| Rpc.hpp | ||
| RpcWrapper.cpp | ||
| RpcWrapper.hpp | ||
| SelfUptimeMonitor.cpp | ||
| SelfUptimeMonitor.hpp | ||
| SendpayResultMonitor.cpp | ||
| SendpayResultMonitor.hpp | ||
| SetConfigHandler.cpp | ||
| SetConfigHandler.hpp | ||
| StatusCommand.cpp | ||
| StatusCommand.hpp | ||
| SwapManager.cpp | ||
| SwapManager.hpp | ||
| SwapReporter.cpp | ||
| SwapReporter.hpp | ||
| Timers.cpp | ||
| Timers.hpp | ||
| TimerTwiceDailyAnnouncer.cpp | ||
| TimerTwiceDailyAnnouncer.hpp | ||
| UnmanagedManager.cpp | ||
| UnmanagedManager.hpp | ||
| Waiter.cpp | ||
| Waiter.hpp | ||
| XRebalanceHistory.cpp | ||
| XRebalanceHistory.hpp | ||
| XRebalancePartMonitor.cpp | ||
| XRebalancePartMonitor.hpp | ||
| XRebalancePredict.cpp | ||
| XRebalancePredict.hpp | ||
| XRebalancePredictor.cpp | ||
| XRebalancePredictor.hpp | ||
| XRebalancer.cpp | ||
| XRebalancer.hpp | ||
Boss Modules
This directory contains modules of CLBOSS.
CLBOSS (Boss::Main) is constructed as a bunch of modules centered
around a common bus, the S::Bus.
Each module is given access to the S::Bus, and can subscribe to
any messages on the bus.
It can also raise any messages on the bus.
A module is just a generic object. The only requirement is that it be constructible and destructible.
Ideally, modules should not have references or pointers to other
modules; instead, it interacts with other modules by raise-ing
messages, and responding to subscribed messages.
There are a few modules which violate this rule, mostly ones from
the early days of CLBOSS when I was much less confident of this
design, but those few --- mostly Boss::Mod::Rpc and
Boss::Mod::Waiter --- should eventually be migrated to using
S::Bus fully.
To create a new module, use the new-mod script in the top
level:
./new-mod NewModule
Module Header
Below is a sketch of what a new module would have in its header:
#ifndef BOSS_MOD_NEWMODULE_HPP
#define BOSS_MOD_NEWMODULE_HPP
#include<memory>
namespace S { class Bus; }
namespace Boss { namespace Mod {
/** class Boss::Mod::NewModule
*
* @brief this is a simple example of a new module.
*/
class NewModule {
private:
class Impl;
std::unique_ptr<Impl> pimpl;
public:
/* We need to be constructed explicitly with a
* S::Bus! */
NewModule() =delete;
/* These will be defined in the source file using
* `default`.
*/
NewModule(NewModule&&);
~NewModule();
/* The constructor. */
explicit
NewModule(S::Bus&);
};
}}
#endif /* !defined(BOSS_MOD_NEWMODULE_HPP) */
Yes, it is really that simple.
There is no need to define anything in the header other than the
fact that the module exists and can be moved and destructed and
can be constructed with a S::Bus.
The meat of the module will be defined in the source file, in the
Boss::Mod::NewModule::Impl implementation class.
(The few modules which do not use this pattern were from earlier days before I realized this pattern worked best.)
Module Source
A minimal new module has this code in the corresponding source file:
#include"Boss/Mod/NewModule.hpp"
#include"Ev/Io.hpp"
#include"S/Bus.hpp"
#include"Util/make_unique.hpp"
namespace Boss { namespace Mod {
/* This contains all the data and code that the new module
* actually needs.
*/
class NewModule::Impl {
private:
S::Bus& bus;
int your_data_here;
void start() {
your_data_here = default_your_data_value;
bus.subacribe<Boss::Msg::Whateverness
>([this](Boss::Msg::Whateverness const& msg) {
return do_whatever(msg);
});
// ...
}
Ev::Io<void> do_whatever(Boss::Msg::Whateverness const& msg) {
// ...
return Ev::lift();
}
public:
/* The below are not really necessary (this source file is
* the only one that will see the `Impl` class anyway) but I
* feel they are necessary. */
Impl() =delete;
Impl(Impl&&) =delete;
explicit
Impl(S::Bus& bus_) : bus(bus_) { start(); }
};
/* The C++-generated defaults should work fine. We just need to
* let the compiler know about the `Impl` class first before the
* compiler can generate the `default`, which is why the `Impl`
* class definition above comes first.
*/
NewModule::NewModule(NewModule&&) =default;
NewModule::~NewModule() =default;
/* Just construct the implementation. */
NewModule::NewModule(S::Bus& bus)
: pimpl(Util::make_unique<Impl>(bus)) { }
}}
So, as mentioned, the Impl class contains all that your module
is.
It declares the data and the code of your module.
From within your module, you can subscribe to and raise messages.
Those are the ways by which your module can affect the rest of the
modules.
Module Registration
Module code is nice, but they do need to be instantiated in the initial object graph before the module can start interacting with the rest of CLBOSS.
In order to do that, you need to add it to Boss/Mod/all.cpp.
Just #include the header file, then add an install decleration:
all->install<NewModule>(bus);
The order in which modules are installed into the instantiated CLBOSS should not matter. However, some old modules may be sensitive to order (due to being designed with dependencies, but "modern" CLBOSS should have modules depend only on a common interface, the bus, i.e. Dependency Inversion), so avoid reordering existing modules (but feel free to insert new modules wherever, do note that the modules are roughly grouped so try to locate the most logical group for the module and add it there).
Shutting Down
When the lightningd shuts down, it closes the stdin of clboss.
This causes Boss::JsonInput::run (the main loop) to return, which
then causes the special message Boss::Shutdown to be emitted on
the bus.
Some blocking interfaces are connected to objects that also monitor
this message on the S::Bus.
Below is not an exhaustive list but probably has all the important
ones you would use.
Boss::Mod::Rpc::commandBoss::Mod::Waiter::waitBoss::ModG::ReqResp<MsgType>::execute
When the Boss::Shutdown message is broadcasted on the bus, the
above interfaces will throw an Ev::Io-level exception, of the
Boss::Shutdown type (yes, the same as the message --- it is just
some type, and that is all C++ cares about).
The proper thing to do is to simply not handle Boss::Shutdown.
This causes the Ev::Io-level greenthread your module code is
running in, to terminate with an uncaught exception, which allows
CLBOSS to exit cleanly once all greenthreads have terminated.
There exists a Boss::concurrent function that you should use for
launching Ev::Io-level greenthreads.
This simply catches any uncaught Boss::Shutdown in the spawned
greenthread and exits the greenthread when this is caught.
Otherwise, the user will get a uncaught exception scare-message
on stderr, which is benign (since the exception only exists to
unblock blocking interfaces).
S::Bus::raise, Ev::map, Ev::foreach, which do not use
Boss::concurrent to launch new Ev::Io-level greenthreads,
are fine since any Ev::Io-level exception --- including a thrown
Boss::Shutdown from the above blocking interfaces --- from any
sub-greenthread they launch will propagate it to the calling
thread as well, which should eventually reach out to the
Boss::concurrent that launched whatever greenthread your module
started in.
Thus, for the most part, shutting down "just works" and you can ignore it, usually.
However, if you have code that has to wait for something (and
that something is not one of the above blocking interfaces),
then take note that you should have a long-lived object that
monitors for Boss::Shutdown and forces your code to stop
waiting and throws Boss::Shutdown to the caller.
For example, it could set a flag somewhere in your object, and
your waiting code should also ccheck this flag (meaning your
waiting code has to poll this flag as well, and throw
Boss::Shutdown if the flag is set).
Lifetime Management
Managing the lifetimes of objects is fairly important in C++.
Modules registered in the Boss::Mod::all function are
persistent; they will be alive until after Boss::Shutdown
is broadcast on the bus and the above blocking interfaces have
thrown the exception at the Ev::Io level.
Of course, if you use objects in your own code, you do need to
be responsible for managing their lifetimes, usually by use of
standard std::unique_ptr and std::shared_ptr.
Typically it means you need to have a container in your module
that contains std::unique_ptrs to objects that have less than
persistent lifetime.
Then when you know that the object lifetime is over, you can
remove the object from the container.
Sometimes, you might want to launch a background Ev::Io
greenthread with its own object, and have the object
automatically be reaped when the greenthread exits.
For that case, you can take advantage of C++ lambda captures:
Ev::Io<void> do_something_in_foreground() {
auto temp_object = std::make_shared<TempObject>();
return Ev::lift().then([temp_object]() {
return temp_object->do_something();
}).then([temp_object]() {
/* This function only exists to keep the
* `temp_object` alive until the above
* function completes.
*/
return Ev::lift();
});
}
Ev::Io<void> do_something() {
return Boss::concurrent(do_something_in_foreground());
}
The above do_something function will then launch the task in
some background greenthread.
The TempObject remains alive until its do_something member
function returns.
The nice thing is that even if the TempObject::do_something
throws an uncaught exception (like the aforementioned
Boss::Shutdown), the temp_object will also be destructed
correctly.
When using the above pattern, be careful with all uses of
Boss::concurrent from within the temporary objects.
If the Boss::concurrent outlasts the temporary object, then
it might refer to objects that the temporary object is
keeping alive, and which might be dead at that point.
See commit f01a730e for an example of this bug.
An important thing to note is that only persistent objects
can subscribe to the S::Bus.
Temporary objects cannot subscribe to any messages.
If a temporary object like the above TempObject needs to
listen for messages, your module (being persistent) should
listen on behalf of those objects, then either forward it to
any TempObject, or allow TempObject to somehow access the
contents of messages.
If you have a temporary object subscribe on the bus, then
when the object dies and is destructed, the bus will still
forward any raised messages to its function, which would, with
high probability, attempt to access its previous memory area
and lead to use-after-free bugs.
This is a limitation of the current S::Bus; in principle it
should be possible to add a sort of "leased subscribe" which
would return an object that your temporary object will keep
alive, whose destruction would unsubscribe the corresponding
function.
Boss::ModG::ReqResp
Often, a module keeps track of some information about the
universe, or may serve as wrappers around some functionality
that can be replaced (most likely for mocking, so that we can
write module-level tests by instantiating just the module
under test and adding code to subscribe and raise messages on
the test harness.
See the tests in tests/boss).
Obviously, other modules then need to somehow "call" into the module, to query the data or to trigger the functionality.
In a typical object graph this is done by having the caller object somehow get access to the interface of the callee object.
However, with the S::Bus, the caller can instead just be
given the very generic S::Bus interface to trigger code on
the callee object, and get a response.
To facilitate this, the Boss::ModG::ReqResp templated class
exists.
Simply instantiate this in your module --- it has to be a
persistent object with the same lifetime as your module,
since it subscribes to messages --- and call its execute
member function.
This function will send the Req message (the first argument
of the template) and will wait for the corresponding Resp
message, and will block the calling greenthread until the
corresponding response is received.
It will also handle Boss::Shutdown correctly --- any blocked
greenthread will experience Boss::Shutdown being thrown to
unblock the greenthread.
Now, Boss::ModG::ReqResp is not magic.
Any Req message and its corresponding Resp has to have a
void* requester member.
The callee object that handles this Req and raises the
Resp has to propagate the void* requester from the Req
to the Resp that contains the response to the given Req.
For example:
/* Request message. */
struct RequestAddOne {
void* requester;
int x;
};
/* Response message. */
struct ResponseAddOne {
void* requester;
int x_plus_1;
};
/* Callee module implementation. */
class AddOneHandler::Impl {
private:
S::Bus& bus;
void start() {
bus.subscribe< RequestAddOne
>([this](RequestAddOne const& m) {
auto x_plus_1 = m.x + 1;
return bus.raise(ResponseAddOne{
/* Propagate the requester. */
m.requester,
x_plus_1
});
});
}
public:
Impl() =delete;
Impl(S::Bus& bus_) : bus(bus_) { start(); }
};
/* Caller module implementation. */
class AddOneCaller::Impl {
private:
S::Bus& bus;
Boss::ModG::ReqResp< RequestAddOne
, ResponseAddOne
> add_one;
void start() {
bus.subscribe< Boss::Msg::Init
>([this](Boss::Msg::Init const& _) {
return Ev::lift().then([this]() {
/* Call into the AddOneHandler. */
return add_one.execute(RequestAddOne{
/* ReqResp will fill in the
* requester field appropriately. */
nullptr,
/* x. */
42
});
}).then([](ResponseAddOne rsp) {
assert(resp.x_plus_1 == 43);
return Ev::lift();
});
});
}
public:
Impl() =delete;
Impl( S::Bus& bus_
) : bus(bus_)
, add_one( bus_
, [](RequestAddOne& m, void* p) {
m.requester = p;
}
, [](ResponseAddOne& m) {
return m.requester;
}
)
{ start(); }
};
In principle, the functions passed into the constructor of
Boss::ModG::ReqResp should really be in a traits class, this
should be modified at some point in the future.
A major advantage of this system is that it provides some
amount of cross-module introspection.
Any module can "tap into" the Req and/or Resp of another
module, and keep track of various statistics about how often
those bits of functionality get called.
For example, a module could be written that just subscribes
to RequestMoveFunds and ResponseMoveFunds, which then
keeps statistics on how often and how much fund movements can
actually succeed.
Then that module can expose this information.
Indeed, the other blocking interfaces, Boss::Mod::Rpc and
Boss::Mod::Waiter, do not use the Boss::ModG::ReqResp
framework only because they predate it.
At some point, those interfaces should be migrated to using
Boss::ModG::ReqResp for a single implementation of handling
the Boss::Shutdown message.
On-disk Data
If your module keeps statistics or other information on various
events, you probably want to persist that data across restarts
of lightningd.
This is done by using an SQLITE3 database.
See the Sqlite3/ directory for the internal API.
CLBOSS uses a single database, named data.clboss.
An Sqlite3::Db is broadcasted at initialization via the
Boss::Msg::DbResource message, which represents an
opened handle to this database.
Your module should subscribe to this message, and copy the
handle to its own variable.
Your module should then create a table in this database to hold
all the data that needs to persist.
Table names you create should be prefixed with the module name,
for example NewModule_nodes, to ensure that tables of other
modules do not conflict.
You should probably use CREATE TABLE IF NOT EXISTS in order
to create tables if and only if they do not exist yet.
Note that you cannot easily change the table schema --- if
your module has been released before, then users already
have the previous table schema and you cannot easily modify it.
Adding columns is possible with SQLITE3 via ALTER TABLE, but
changing columns or deleting them is not possible, so make sure
to think deeply about the table design before writing code for
it.