mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-21 19:18:22 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1aae015656 | ||
|
|
92e98a3ec9 | ||
|
|
f1e56f0166 |
No files matched your search
@@ -156,6 +156,7 @@ test.protocol > xrpld.core
|
||||
test.protocol > xrpl.json
|
||||
test.protocol > xrpl.protocol
|
||||
test.rpc > test.jtx
|
||||
test.rpc > test.unit_test
|
||||
test.rpc > xrpl.basics
|
||||
test.rpc > xrpl.config
|
||||
test.rpc > xrpl.core
|
||||
|
||||
@@ -32,6 +32,7 @@ Version 3.5.0 is not yet released.
|
||||
|
||||
### Additions in 3.5.0
|
||||
|
||||
- `config_reload`: New admin method. It reads the config file again and applies the sections in the required `sections` field, without a restart. Only `transaction_queue` can be reloaded now. If `sections` names a section that requires a restart, the method returns `invalidParams`. If the file cannot be read or is not valid, the method returns an error. In both cases, the server applies nothing. The `applied` field lists the sections that were applied. Command line: `config_reload <section> [<section> ...]`. ([#8630](https://github.com/XRPLF/rippled/pull/8630))
|
||||
- `subscribe`, `unsubscribe`: Added an optional `mpt_issuances` request field, an array of MPT issuance IDs (hex strings). Subscribers receive the same `transaction` message as the `transactions` stream for each validated transaction whose metadata affects a subscribed issuance. MPT issuance subscriptions count toward the per-connection subscription limit. An empty array, a non-array value, or an invalid ID returns `invalidParams`. ([#5671](https://github.com/XRPLF/rippled/pull/5671))
|
||||
- `ledger_entry`: Add full support for checks, NFT offers, payment channels, and signer lists. ([#6319](https://github.com/XRPLF/rippled/pull/6319))
|
||||
|
||||
|
||||
@@ -537,6 +537,7 @@ JSS(search_depth); // in: RipplePathFind
|
||||
JSS(searched_all); // out: Tx
|
||||
JSS(secret); // in: TransactionSign, ValidationCreate, ValidationSeed,
|
||||
// channel_authorize
|
||||
JSS(sections); // in: ConfigReload
|
||||
JSS(seed); //
|
||||
JSS(seed_hex); // in: WalletPropose, TransactionSign
|
||||
JSS(send_currencies); // out: AccountCurrencies
|
||||
|
||||
@@ -615,6 +615,42 @@ public:
|
||||
checkMetrics(*this, env, 0, 16, 6, 8);
|
||||
}
|
||||
|
||||
void
|
||||
testReconfigure()
|
||||
{
|
||||
testcase("Reconfigure");
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this, makeConfig({{Keys::kMinimumTxnInLedgerStandalone, "3"}}));
|
||||
auto& txq = env.app().getTxQ();
|
||||
checkMetrics(*this, env, 0, std::nullopt, 0, 3);
|
||||
|
||||
// The maximum queue size is not set before the first ledger closes.
|
||||
auto setup = setupTxQ(env.app().config());
|
||||
setup.minimumTxnInLedgerSA = 4;
|
||||
txq.reconfigure(setup);
|
||||
checkMetrics(*this, env, 0, std::nullopt, 0, 4);
|
||||
|
||||
env.close();
|
||||
checkMetrics(*this, env, 0, 8, 0, 4);
|
||||
|
||||
// New limits apply before the next ledger closes.
|
||||
setup.minimumTxnInLedgerSA = 5;
|
||||
setup.ledgersInQueue = 3;
|
||||
txq.reconfigure(setup);
|
||||
checkMetrics(*this, env, 0, 15, 0, 5);
|
||||
|
||||
// A lower minimum keeps the current expected count.
|
||||
setup.minimumTxnInLedgerSA = 2;
|
||||
txq.reconfigure(setup);
|
||||
checkMetrics(*this, env, 0, 15, 0, 5);
|
||||
|
||||
// The minimum queue size still applies.
|
||||
setup.queueSizeMin = 20;
|
||||
txq.reconfigure(setup);
|
||||
checkMetrics(*this, env, 0, 20, 0, 5);
|
||||
}
|
||||
|
||||
void
|
||||
testTecResult()
|
||||
{
|
||||
@@ -4740,6 +4776,7 @@ public:
|
||||
testQueueSeq();
|
||||
testQueueTicket();
|
||||
testTecResult();
|
||||
testReconfigure();
|
||||
testLocalTxRetry();
|
||||
testLastLedgerSeq();
|
||||
testZeroFeeTxn();
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/envconfig.h>
|
||||
#include <test/unit_test/FileDirGuard.h>
|
||||
|
||||
#include <xrpld/app/misc/TxQ.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class ConfigReload_test : public beast::unit_test::Suite
|
||||
{
|
||||
static constexpr auto kIpsMessage =
|
||||
"Section 'ips' in 'sections' cannot be reloaded without a restart. Sections that can be "
|
||||
"reloaded: transaction_queue.";
|
||||
|
||||
static std::string
|
||||
configText(std::string const& txqLines)
|
||||
{
|
||||
return "[transaction_queue]\n" + txqLines +
|
||||
"ledgers_in_queue = 2\n"
|
||||
"minimum_queue_size = 2\n"
|
||||
"\n"
|
||||
"[ips]\n"
|
||||
"r.ripple.com 51235\n";
|
||||
}
|
||||
|
||||
static void
|
||||
writeFile(std::filesystem::path const& path, std::string const& contents)
|
||||
{
|
||||
std::ofstream o(path.string(), std::ios::trunc);
|
||||
o << contents;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Config>
|
||||
configFromFile(std::filesystem::path const& path)
|
||||
{
|
||||
auto cfg = std::make_unique<Config>();
|
||||
cfg->setup(path.string(), true, true, true);
|
||||
test::setupConfigForUnitTests(*cfg);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
void
|
||||
testParams()
|
||||
{
|
||||
testcase("Params");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this};
|
||||
auto const expectInvalid = [&](std::string const& params, std::string const& message) {
|
||||
auto const result = env.rpc("json", "config_reload", params)[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_message] == message);
|
||||
};
|
||||
expectInvalid("{}", "Missing field 'sections'.");
|
||||
expectInvalid(R"({"sections": "transaction_queue"})", "Invalid field 'sections'.");
|
||||
expectInvalid(R"({"sections": []})", "Invalid field 'sections'.");
|
||||
expectInvalid(R"({"sections": [1]})", "Invalid field 'sections'.");
|
||||
expectInvalid(R"({"sections": ["transaction_queue", "ips"]})", kIpsMessage);
|
||||
|
||||
// Valid params, but the server did not load a config file.
|
||||
auto const result = env.rpc("config_reload", "transaction_queue")[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "notSupported");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcNotSupported);
|
||||
}
|
||||
|
||||
void
|
||||
testReload()
|
||||
{
|
||||
testcase("Reload");
|
||||
using namespace test::jtx;
|
||||
|
||||
detail::FileDirGuard const file(
|
||||
*this,
|
||||
"test_config_reload",
|
||||
"xrpld.cfg",
|
||||
configText("minimum_txn_in_ledger_standalone = 3\n"));
|
||||
|
||||
Env env{*this, configFromFile(file.file())};
|
||||
auto const perLedger = [&] {
|
||||
return env.app().getTxQ().getMetrics(*env.current()).txPerLedger;
|
||||
};
|
||||
BEAST_EXPECT(perLedger() == 3);
|
||||
|
||||
writeFile(file.file(), configText("minimum_txn_in_ledger_standalone = 7\n"));
|
||||
|
||||
// A request that includes a section that needs a restart applies
|
||||
// nothing.
|
||||
{
|
||||
auto const result = env.rpc("config_reload", "transaction_queue", "ips")[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "invalidParams");
|
||||
BEAST_EXPECT(result[jss::error_code] == RpcInvalidParams);
|
||||
BEAST_EXPECT(result[jss::error_message] == kIpsMessage);
|
||||
BEAST_EXPECT(perLedger() == 3);
|
||||
}
|
||||
|
||||
{
|
||||
auto const result = env.rpc("config_reload", "transaction_queue")[jss::result];
|
||||
BEAST_EXPECT(result[jss::status] == "success");
|
||||
BEAST_EXPECT(result[jss::applied].size() == 1);
|
||||
BEAST_EXPECT(result[jss::applied][0u] == "transaction_queue");
|
||||
BEAST_EXPECT(perLedger() == 7);
|
||||
}
|
||||
|
||||
// An invalid file changes nothing.
|
||||
writeFile(
|
||||
file.file(),
|
||||
configText(
|
||||
"minimum_txn_in_ledger_standalone = 9\n"
|
||||
"maximum_txn_in_ledger = 1\n"));
|
||||
{
|
||||
auto const result = env.rpc("config_reload", "transaction_queue")[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "internal");
|
||||
BEAST_EXPECT(
|
||||
result[jss::error_message].asString().starts_with("Invalid config file: "));
|
||||
BEAST_EXPECT(perLedger() == 7);
|
||||
}
|
||||
|
||||
// A missing file changes nothing.
|
||||
std::filesystem::remove(file.file());
|
||||
{
|
||||
auto const result = env.rpc("config_reload", "transaction_queue")[jss::result];
|
||||
BEAST_EXPECT(result[jss::error] == "internal");
|
||||
BEAST_EXPECT(perLedger() == 7);
|
||||
}
|
||||
// Put a file back so that the guard can remove it.
|
||||
writeFile(file.file(), "");
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testParams();
|
||||
testReload();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ConfigReload, rpc, xrpl);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -2578,6 +2578,57 @@ static RPCCallTestData const kRpcCallTestArray[] = {
|
||||
})",
|
||||
},
|
||||
|
||||
// config_reload
|
||||
// -------------------------------------------------------------------
|
||||
{
|
||||
"config_reload: one section.",
|
||||
__LINE__,
|
||||
{"config_reload", "transaction_queue"},
|
||||
RPCCallTestData::Exception::NoException,
|
||||
R"({
|
||||
"method" : "config_reload",
|
||||
"params" : [
|
||||
{
|
||||
"api_version" : %API_VER%,
|
||||
"sections" : [ "transaction_queue" ]
|
||||
}
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"config_reload: two sections.",
|
||||
__LINE__,
|
||||
{"config_reload", "transaction_queue", "ips"},
|
||||
RPCCallTestData::Exception::NoException,
|
||||
R"({
|
||||
"method" : "config_reload",
|
||||
"params" : [
|
||||
{
|
||||
"api_version" : %API_VER%,
|
||||
"sections" : [ "transaction_queue", "ips" ]
|
||||
}
|
||||
]
|
||||
})",
|
||||
},
|
||||
{
|
||||
"config_reload: too few arguments.",
|
||||
__LINE__,
|
||||
{
|
||||
"config_reload",
|
||||
},
|
||||
RPCCallTestData::Exception::NoException,
|
||||
R"({
|
||||
"method" : "config_reload",
|
||||
"params" : [
|
||||
{
|
||||
"error" : "badSyntax",
|
||||
"error_code" : 1,
|
||||
"error_message" : "Syntax error."
|
||||
}
|
||||
]
|
||||
})",
|
||||
},
|
||||
|
||||
// connect
|
||||
// ---------------------------------------------------------------------
|
||||
{
|
||||
|
||||
@@ -147,6 +147,7 @@ printHelp(po::options_description const& desc)
|
||||
" can_delete [<ledgerid>|<ledgerhash>|now|always|never]\n"
|
||||
" channel_authorize <private_key> <channel_id> <drops>\n"
|
||||
" channel_verify <public_key> <channel_id> <drops> <signature>\n"
|
||||
" config_reload <section> [<section> ...]\n"
|
||||
" connect <ip> [<port>]\n"
|
||||
" consensus_info\n"
|
||||
" deposit_authorized <source_account> <destination_account> "
|
||||
|
||||
+24
-18
@@ -366,6 +366,15 @@ public:
|
||||
void
|
||||
processClosedLedger(Application& app, ReadView const& view, bool timeLeap);
|
||||
|
||||
/**
|
||||
* Replace the setup parameters while the server runs.
|
||||
*
|
||||
* @note The new limits apply at once. Transactions that are already in
|
||||
* the queue stay there, even if the new limits are lower.
|
||||
*/
|
||||
void
|
||||
reconfigure(Setup const& setup);
|
||||
|
||||
/**
|
||||
* Return the next sequence that would go in the TxQ for an account.
|
||||
*/
|
||||
@@ -441,22 +450,22 @@ private:
|
||||
/**
|
||||
* Minimum value of txnsExpected.
|
||||
*/
|
||||
std::size_t const minimumTxnCount_;
|
||||
std::size_t minimumTxnCount_ = 0;
|
||||
/**
|
||||
* Number of transactions per ledger that fee escalation "works
|
||||
* towards".
|
||||
*/
|
||||
std::size_t const targetTxnCount_;
|
||||
std::size_t targetTxnCount_ = 0;
|
||||
/**
|
||||
* Maximum value of txnsExpected
|
||||
*/
|
||||
std::optional<std::size_t> const maximumTxnCount_;
|
||||
std::optional<std::size_t> maximumTxnCount_;
|
||||
/**
|
||||
* Number of transactions expected per ledger.
|
||||
* One more than this value will be accepted
|
||||
* before escalation kicks in.
|
||||
*/
|
||||
std::size_t txnsExpected_;
|
||||
std::size_t txnsExpected_{0};
|
||||
/**
|
||||
* Recent history of transaction counts that
|
||||
* exceed the targetTxnCount_
|
||||
@@ -477,24 +486,21 @@ private:
|
||||
* Constructor
|
||||
*/
|
||||
FeeMetrics(Setup const& setup, beast::Journal j)
|
||||
: minimumTxnCount_(
|
||||
setup.standAlone ? setup.minimumTxnInLedgerSA : setup.minimumTxnInLedger)
|
||||
, targetTxnCount_(
|
||||
setup.targetTxnInLedger < minimumTxnCount_ ? minimumTxnCount_
|
||||
: setup.targetTxnInLedger)
|
||||
, maximumTxnCount_([&] -> std::optional<std::size_t> {
|
||||
if (!setup.maximumTxnInLedger)
|
||||
return std::nullopt;
|
||||
return *setup.maximumTxnInLedger < targetTxnCount_ ? targetTxnCount_
|
||||
: *setup.maximumTxnInLedger;
|
||||
}())
|
||||
, txnsExpected_(minimumTxnCount_)
|
||||
, recentTxnCounts_(setup.ledgersInQueue)
|
||||
: recentTxnCounts_(setup.ledgersInQueue)
|
||||
, escalationMultiplier_(setup.minimumEscalationMultiplier)
|
||||
, j_(j)
|
||||
{
|
||||
// txnsExpected_ starts at 0, so this sets it to the minimum.
|
||||
reconfigure(setup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply new setup parameters. Keeps the current number of expected
|
||||
* transactions, but moves it into the new limits.
|
||||
*/
|
||||
void
|
||||
reconfigure(Setup const& setup);
|
||||
|
||||
/**
|
||||
* Updates fee metrics based on the transactions in the ReadView
|
||||
* for use in fee escalation calculations.
|
||||
@@ -885,7 +891,7 @@ private:
|
||||
/**
|
||||
* Setup parameters used to control the behavior of the queue
|
||||
*/
|
||||
Setup const setup_;
|
||||
Setup setup_;
|
||||
/**
|
||||
* Journal
|
||||
*/
|
||||
|
||||
@@ -814,6 +814,23 @@ TxQApplyImpl::addTxToQueue()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
TxQ::FeeMetrics::reconfigure(Setup const& setup)
|
||||
{
|
||||
minimumTxnCount_ = setup.standAlone ? setup.minimumTxnInLedgerSA : setup.minimumTxnInLedger;
|
||||
targetTxnCount_ = std::max<std::size_t>(setup.targetTxnInLedger, minimumTxnCount_);
|
||||
maximumTxnCount_.reset();
|
||||
if (setup.maximumTxnInLedger)
|
||||
maximumTxnCount_ = std::max<std::size_t>(*setup.maximumTxnInLedger, targetTxnCount_);
|
||||
|
||||
txnsExpected_ = std::max(txnsExpected_, minimumTxnCount_);
|
||||
if (maximumTxnCount_)
|
||||
txnsExpected_ = std::min(txnsExpected_, *maximumTxnCount_);
|
||||
// Keep the most recent counts if the history gets shorter.
|
||||
recentTxnCounts_.rset_capacity(setup.ledgersInQueue);
|
||||
escalationMultiplier_ = std::max(escalationMultiplier_, setup.minimumEscalationMultiplier);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
TxQ::FeeMetrics::update(
|
||||
Application& app,
|
||||
@@ -1693,6 +1710,21 @@ TxQ::processClosedLedger(Application& app, ReadView const& view, bool timeLeap)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TxQ::reconfigure(Setup const& setup)
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
|
||||
setup_ = setup;
|
||||
feeMetrics_.reconfigure(setup_);
|
||||
// maxSize_ is not set until the first ledger closes.
|
||||
if (maxSize_)
|
||||
{
|
||||
maxSize_ = std::max(
|
||||
feeMetrics_.getSnapshot().txnsExpected * setup_.ledgersInQueue, setup_.queueSizeMin);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TxQ::rebuildQueue(OpenView& view)
|
||||
{
|
||||
|
||||
@@ -416,6 +416,15 @@ public:
|
||||
{
|
||||
return j_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the config file. Empty if no file was loaded.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path const&
|
||||
configFile() const
|
||||
{
|
||||
return configFile_;
|
||||
}
|
||||
};
|
||||
|
||||
FeeSetup
|
||||
|
||||
@@ -31,6 +31,7 @@ inline constexpr std::string_view kBookOffers{"book_offers"};
|
||||
inline constexpr std::string_view kCanDelete{"can_delete"};
|
||||
inline constexpr std::string_view kChannelAuthorize{"channel_authorize"};
|
||||
inline constexpr std::string_view kChannelVerify{"channel_verify"};
|
||||
inline constexpr std::string_view kConfigReload{"config_reload"};
|
||||
inline constexpr std::string_view kConnect{"connect"};
|
||||
inline constexpr std::string_view kConsensusInfo{"consensus_info"};
|
||||
inline constexpr std::string_view kDepositAuthorized{"deposit_authorized"};
|
||||
|
||||
@@ -213,6 +213,12 @@ constexpr auto kHandlerArray = std::to_array<Handler>({
|
||||
.role = Role::USER,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
{
|
||||
.name = method::kConfigReload,
|
||||
.valueMethod = Method::of<&byRef<&doConfigReload>>(),
|
||||
.role = Role::ADMIN,
|
||||
.condition = Condition::NoCondition,
|
||||
},
|
||||
{
|
||||
.name = method::kConnect,
|
||||
.valueMethod = Method::of<&byRef<&doConnect>>(),
|
||||
|
||||
@@ -477,6 +477,18 @@ private:
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
// config_reload <section> [<section> ...]
|
||||
json::Value
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
parseConfigReload(json::Value const& jvParams)
|
||||
{
|
||||
json::Value jvRequest(json::ValueType::Object);
|
||||
auto& sections = (jvRequest[jss::sections] = json::ValueType::Array);
|
||||
for (json::UInt i = 0; i < jvParams.size(); ++i)
|
||||
sections.append(jvParams[i].asString());
|
||||
return jvRequest;
|
||||
}
|
||||
|
||||
// connect <ip[:port]> [port]
|
||||
json::Value
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
@@ -1412,6 +1424,11 @@ private:
|
||||
.minParams = 4,
|
||||
.maxParams = 4,
|
||||
},
|
||||
{
|
||||
.name = rpc::method::kConfigReload,
|
||||
.parse = Parse::of<&RPCParser::parseConfigReload>(),
|
||||
.minParams = 1,
|
||||
},
|
||||
{
|
||||
.name = rpc::method::kConnect,
|
||||
.parse = Parse::of<&RPCParser::parseConnect>(),
|
||||
|
||||
@@ -23,6 +23,8 @@ doChannelAuthorize(rpc::JsonContext&);
|
||||
json::Value
|
||||
doChannelVerify(rpc::JsonContext&);
|
||||
json::Value
|
||||
doConfigReload(rpc::JsonContext&);
|
||||
json::Value
|
||||
doConnect(rpc::JsonContext&);
|
||||
json::Value
|
||||
doConsensusInfo(rpc::JsonContext&);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/app/misc/TxQ.h>
|
||||
#include <xrpld/core/Config.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
|
||||
#include <xrpl/basics/FileUtilities.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <exception>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Reads the config file again and applies the requested sections. Each
|
||||
// requested section must be one that can change while the server runs.
|
||||
json::Value
|
||||
doConfigReload(rpc::JsonContext& context)
|
||||
{
|
||||
auto const& params = context.params;
|
||||
if (!params.isMember(jss::sections))
|
||||
return rpc::missingFieldError(jss::sections);
|
||||
|
||||
auto const& requested = params[jss::sections];
|
||||
if (!requested.isArray() || requested.size() == 0)
|
||||
return rpc::invalidFieldError(jss::sections);
|
||||
|
||||
std::set<std::string> sections;
|
||||
for (auto const& section : requested)
|
||||
{
|
||||
if (!section.isString())
|
||||
return rpc::invalidFieldError(jss::sections);
|
||||
auto const name = section.asString();
|
||||
if (name != Sections::kTransactionQueue)
|
||||
{
|
||||
return rpc::makeParamError(
|
||||
"Section '" + name +
|
||||
"' in 'sections' cannot be reloaded without a restart. Sections that can be "
|
||||
"reloaded: " +
|
||||
Sections::kTransactionQueue + ".");
|
||||
}
|
||||
sections.insert(name);
|
||||
}
|
||||
|
||||
Config const& running = context.app.config();
|
||||
if (running.configFile().empty())
|
||||
return rpc::makeError(RpcNotSupported, "The server did not load a config file.");
|
||||
|
||||
std::error_code ec;
|
||||
auto const contents = getFileContents(ec, running.configFile());
|
||||
if (ec)
|
||||
return rpc::makeError(RpcInternal, "Unable to read the config file: " + ec.message());
|
||||
|
||||
// Check the full file before anything is applied.
|
||||
Config updated;
|
||||
updated.configDir = running.configDir;
|
||||
TxQ::Setup txQSetup;
|
||||
try
|
||||
{
|
||||
updated.loadFromString(contents);
|
||||
txQSetup = setupTxQ(updated);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
return rpc::makeError(RpcInternal, std::string("Invalid config file: ") + e.what());
|
||||
}
|
||||
txQSetup.standAlone = running.standalone();
|
||||
|
||||
context.app.getTxQ().reconfigure(txQSetup);
|
||||
|
||||
json::Value result(json::ValueType::Object);
|
||||
auto& applied = (result[jss::applied] = json::ValueType::Array);
|
||||
for (auto const& name : sections)
|
||||
applied.append(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
Reference in new issue
Block a user