SetConfigHandler: fail setconfig when the owner rejects the value

setconfig was acknowledged with unconditional success after
broadcasting Msg::Option, while owning modules reject bad values by
log-and-keep.  lightningd persists a setconfig value (configvar_save)
only on a success response, so the blanket ack recorded values clboss
never applied: listconfigs and config.setconfig diverged from the
running configuration, and a non-numeric value persisted for an
int-typed option fails lightningd's own option parse on the next
start -- lightningd refuses to boot.

Add a rejection back-channel to Msg::Option: SetConfigHandler
allocates a shared reject_reason (null on init-time raises, so
aggregate initialization at existing sites is unaffected), the owning
subscriber reports rejection via the new Msg::Option::reject() helper
(a no-op at init time, where quietly keeping the default is right),
and SetConfigHandler -- whose bus.raise() returns only after all
subscribers ran -- fails the command with invalid-params when a
reason was set.  All dynamic-option owners in this tree report their
rejections: RebalanceModeManager (unrecognized mode), FundsMover's
three numeric handlers, and AskreneUpdates' shared age/retain
handler.
This commit is contained in:
Ken Sedgwick 2026-07-02 12:21:59 -07:00
parent a2296177ba
commit c7fa9bd662
No known key found for this signature in database
GPG key ID: DBD2AF0849D711A9
6 changed files with 94 additions and 5 deletions

View file

@ -9,6 +9,7 @@
#include"Jsmn/Object.hpp"
#include"Json/Out.hpp"
#include"S/Bus.hpp"
#include<memory>
namespace {
@ -85,15 +86,36 @@ void SetConfigHandler::start() {
auto value = params.has("val")
? params["val"]
: Jsmn::Object();
/* Rejection back-channel: bus.raise() completes only
* after every subscriber has run, so the owner's verdict
* is in reject_reason by the time we acknowledge.
* Failing the command matters beyond cosmetics:
* lightningd persists the new value (configvar_save)
* only on a success response, so a blanket ack would
* record values clboss never applied -- and a
* non-numeric value persisted for an int-typed option
* even fails lightningd's own option parse on the next
* start. */
auto reject_reason = std::make_shared<std::string>();
return Boss::log( bus, Debug
, "SetConfigHandler: dispatching setconfig "
"'%s'"
, name.c_str()
)
+ bus.raise(Boss::Msg::Option{name, std::move(value)})
+ bus.raise(Boss::Msg::CommandResponse{
+ bus.raise(Boss::Msg::Option{
name, std::move(value), reject_reason
})
+ Ev::lift().then([this, id, reject_reason]() {
if (!reject_reason->empty())
return bus.raise(Boss::Msg::CommandFail{
id, RPC_INVALID_PARAMS,
"setconfig: " + *reject_reason,
Json::Out::empty_object()
});
return bus.raise(Boss::Msg::CommandResponse{
id, Json::Out::empty_object()
});
});
});
});
}