mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-23 07:10:53 +00:00
refactor: Use std::format instead of boost::format where it fits (#7996)
Co-authored-by: Timur Yalymov <36795566+tyalymov@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Co-authored-by: Ayaz Salikhov <mathbunnyru@users.noreply.github.com>
This commit is contained in:
@@ -18,12 +18,11 @@
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -111,7 +110,7 @@ STLedgerEntry::getSType() const
|
||||
std::string
|
||||
STLedgerEntry::getText() const
|
||||
{
|
||||
return str(boost::format("{ %s, %s }") % to_string(key_) % STObject::getText());
|
||||
return std::format("{{ {}, {} }}", to_string(key_), STObject::getText());
|
||||
}
|
||||
|
||||
json::Value
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <boost/container/flat_set.hpp>
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <expected>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -399,16 +399,21 @@ STTx::getMetaSQL(
|
||||
TxnSql status,
|
||||
std::string const& escapedMetaData) const
|
||||
{
|
||||
static boost::format const kBfTrans("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)");
|
||||
std::string rTxn = sqlBlobLiteral(rawTxn.peekData());
|
||||
|
||||
auto format = TxFormats::getInstance().findByType(txType_);
|
||||
XRPL_ASSERT(format, "xrpl::STTx::getMetaSQL : non-null type format");
|
||||
|
||||
return str(
|
||||
boost::format(kBfTrans) % to_string(getTransactionID()) % format->getName() %
|
||||
toBase58(getAccountID(sfAccount)) % getFieldU32(sfSequence) % inLedger %
|
||||
safeCast<char>(status) % rTxn % escapedMetaData);
|
||||
return std::format(
|
||||
"('{}', '{}', '{}', '{}', '{}', '{}', {}, {})",
|
||||
to_string(getTransactionID()),
|
||||
format->getName(),
|
||||
toBase58(getAccountID(sfAccount)),
|
||||
getFieldU32(sfSequence),
|
||||
inLedger,
|
||||
safeCast<char>(status),
|
||||
rTxn,
|
||||
escapedMetaData);
|
||||
}
|
||||
|
||||
static std::expected<void, std::string>
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -141,10 +140,15 @@ STXChainBridge::getJson(JsonOptions jo) const
|
||||
std::string
|
||||
STXChainBridge::getText() const
|
||||
{
|
||||
return str(
|
||||
boost::format("{ %s = %s, %s = %s, %s = %s, %s = %s }") % sfLockingChainDoor.getName() %
|
||||
lockingChainDoor_.getText() % sfLockingChainIssue.getName() % lockingChainIssue_.getText() %
|
||||
sfIssuingChainDoor.getName() % issuingChainDoor_.getText() % sfIssuingChainIssue.getName() %
|
||||
return std::format(
|
||||
"{{ {} = {}, {} = {}, {} = {}, {} = {} }}",
|
||||
sfLockingChainDoor.getName(),
|
||||
lockingChainDoor_.getText(),
|
||||
sfLockingChainIssue.getName(),
|
||||
lockingChainIssue_.getText(),
|
||||
sfIssuingChainDoor.getName(),
|
||||
issuingChainDoor_.getText(),
|
||||
sfIssuingChainIssue.getName(),
|
||||
issuingChainIssue_.getText());
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <xrpl/rdb/DBInit.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
|
||||
#include <boost/format.hpp> // IWYU pragma: keep
|
||||
|
||||
#include <soci/into.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -40,7 +38,7 @@ doVacuumDB(DatabaseCon::Setup const& setup, beast::Journal j)
|
||||
// Only the most trivial databases will fit in memory on typical
|
||||
// (recommended) hardware. Force temp files to be written to disk
|
||||
// regardless of the config settings.
|
||||
session << boost::format(kCommonDbPragmaTemp) % "file";
|
||||
session << commonDbPragmaTemp("file");
|
||||
session << "PRAGMA page_size;", soci::into(pageSize);
|
||||
|
||||
std::cout << "VACUUM beginning. page_size: " << pageSize << std::endl;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
#include <xrpl/server/Manifest.h>
|
||||
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
#include <boost/optional/optional.hpp> // IWYU pragma: keep
|
||||
|
||||
#include <soci/blob-exchange.h> // IWYU pragma: keep
|
||||
@@ -30,6 +29,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -172,11 +172,10 @@ getNodeIdentity(soci::session& session)
|
||||
// If a valid identity wasn't found, we randomly generate a new one:
|
||||
auto [newpublicKey, newsecretKey] = randomKeyPair(KeyType::Secp256k1);
|
||||
|
||||
session << str(
|
||||
boost::format(
|
||||
"INSERT INTO NodeIdentity (PublicKey,PrivateKey) "
|
||||
"VALUES ('%s','%s');") %
|
||||
toBase58(TokenType::NodePublic, newpublicKey) %
|
||||
session << std::format(
|
||||
"INSERT INTO NodeIdentity (PublicKey,PrivateKey) "
|
||||
"VALUES ('{}','{}');",
|
||||
toBase58(TokenType::NodePublic, newpublicKey),
|
||||
toBase58(TokenType::NodePrivate, newsecretKey));
|
||||
|
||||
return {newpublicKey, newsecretKey};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -188,8 +189,7 @@ class AMMCalc_test : public beast::unit_test::Suite
|
||||
static std::string
|
||||
toString(STAmount const& a)
|
||||
{
|
||||
return (boost::format("%s/%s") % a.getText() % ::xrpl::to_string(a.get<Issue>().currency))
|
||||
.str();
|
||||
return std::format("{}/{}", a.getText(), ::xrpl::to_string(a.get<Issue>().currency));
|
||||
}
|
||||
|
||||
static STAmount
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
#include <xrpl/protocol/SystemParameters.h> // IWYU pragma: keep
|
||||
#include <xrpl/server/Port.h>
|
||||
|
||||
#include <boost/format.hpp> // IWYU pragma: keep
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
#include <boost/lexical_cast/bad_lexical_cast.hpp>
|
||||
|
||||
#include <array>
|
||||
@@ -20,6 +18,7 @@
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
@@ -36,7 +35,7 @@ namespace detail {
|
||||
std::string
|
||||
configContents(std::string const& dbPath, std::string const& validatorsFile)
|
||||
{
|
||||
static boost::format kConfigContentsTemplate(R"xrpldConfig(
|
||||
static constexpr char const* kConfigContentsTemplate = R"xrpldConfig(
|
||||
[server]
|
||||
port_rpc
|
||||
port_peer
|
||||
@@ -83,9 +82,9 @@ cache_mb=256
|
||||
file_size_mb=8
|
||||
file_size_mult=2
|
||||
|
||||
%1%
|
||||
{}
|
||||
|
||||
%2%
|
||||
{}
|
||||
|
||||
# This needs to be an absolute directory reference, not a relative one.
|
||||
# Modify this value as required.
|
||||
@@ -106,7 +105,7 @@ r.ripple.com 51235
|
||||
# Turn down default logging to save disk space in the long run.
|
||||
# Valid values here are trace, debug, info, warning, error, and fatal
|
||||
[rpc_startup]
|
||||
{ "command": "log_level", "severity": "warning" }
|
||||
{{ "command": "log_level", "severity": "warning" }}
|
||||
|
||||
# Defaults to 1 ("yes") so that certificates will be validated. To allow the use
|
||||
# of self-signed certificates for development or internal use, set to 0 ("no").
|
||||
@@ -115,12 +114,12 @@ r.ripple.com 51235
|
||||
|
||||
[sqdb]
|
||||
backend=sqlite
|
||||
)xrpldConfig");
|
||||
)xrpldConfig";
|
||||
|
||||
std::string dbPathSection = dbPath.empty() ? "" : "[database_path]\n" + dbPath;
|
||||
std::string valFileSection =
|
||||
validatorsFile.empty() ? "" : "[validators_file]\n" + validatorsFile;
|
||||
return boost::str(kConfigContentsTemplate % dbPathSection % valFileSection);
|
||||
return std::format(kConfigContentsTemplate, dbPathSection, valFileSection);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,7 +426,7 @@ port_wss_admin
|
||||
|
||||
using namespace std::filesystem;
|
||||
{
|
||||
boost::format cc("[database_path]\n%1%\n");
|
||||
constexpr char const* cc = "[database_path]\n{}\n";
|
||||
|
||||
auto const cwd = current_path();
|
||||
path const dataDirRel("test_data_dir");
|
||||
@@ -435,13 +434,13 @@ port_wss_admin
|
||||
{
|
||||
// Dummy test - do we get back what we put in
|
||||
Config c;
|
||||
c.loadFromString(boost::str(cc % dataDirAbs.string()));
|
||||
c.loadFromString(std::format(cc, dataDirAbs.string()));
|
||||
BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == dataDirAbs.string());
|
||||
}
|
||||
{
|
||||
// Rel paths should convert to abs paths
|
||||
Config c;
|
||||
c.loadFromString(boost::str(cc % dataDirRel.string()));
|
||||
c.loadFromString(std::format(cc, dataDirRel.string()));
|
||||
BEAST_EXPECT(c.legacy(Sections::kDatabasePath) == dataDirAbs.string());
|
||||
}
|
||||
{
|
||||
@@ -508,20 +507,20 @@ port_wss_admin
|
||||
|
||||
{
|
||||
Config c;
|
||||
static boost::format kConfigTemplate(R"xrpldConfig(
|
||||
static constexpr char const* kConfigTemplate = R"xrpldConfig(
|
||||
[validation_seed]
|
||||
%1%
|
||||
{}
|
||||
|
||||
[validator_token]
|
||||
%2%
|
||||
)xrpldConfig");
|
||||
{}
|
||||
)xrpldConfig";
|
||||
std::string error;
|
||||
auto const expectedError =
|
||||
"Cannot have both [validation_seed] "
|
||||
"and [validator_token] config sections";
|
||||
try
|
||||
{
|
||||
c.loadFromString(boost::str(kConfigTemplate % validationSeed % token));
|
||||
c.loadFromString(std::format(kConfigTemplate, validationSeed, token));
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
@@ -604,7 +603,7 @@ main
|
||||
using namespace std::filesystem;
|
||||
{
|
||||
// load should throw for missing specified validators file
|
||||
boost::format cc("[validators_file]\n%1%\n");
|
||||
constexpr char const* cc = "[validators_file]\n{}\n";
|
||||
std::string error;
|
||||
std::string const missingPath = "/no/way/this/path/exists";
|
||||
auto const expectedError =
|
||||
@@ -612,7 +611,7 @@ main
|
||||
try
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString(boost::str(cc % missingPath));
|
||||
c.loadFromString(std::format(cc, missingPath));
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
@@ -624,14 +623,14 @@ main
|
||||
// load should throw for invalid [validators_file]
|
||||
detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
|
||||
path const invalidFile = current_path() / vtg.subdir();
|
||||
boost::format cc("[validators_file]\n%1%\n");
|
||||
constexpr char const* cc = "[validators_file]\n{}\n";
|
||||
std::string error;
|
||||
auto const expectedError =
|
||||
"Invalid file specified in [validators_file]: " + invalidFile.string();
|
||||
try
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString(boost::str(cc % invalidFile.string()));
|
||||
c.loadFromString(std::format(cc, invalidFile.string()));
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
@@ -829,8 +828,8 @@ trust-these-validators.gov
|
||||
detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
|
||||
BEAST_EXPECT(vtg.validatorsFileExists());
|
||||
Config c;
|
||||
boost::format cc("[validators_file]\n%1%\n");
|
||||
c.loadFromString(boost::str(cc % vtg.validatorsFile()));
|
||||
constexpr char const* cc = "[validators_file]\n{}\n";
|
||||
c.loadFromString(std::format(cc, vtg.validatorsFile()));
|
||||
BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == vtg.validatorsFile());
|
||||
BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 8);
|
||||
BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 2);
|
||||
@@ -909,9 +908,9 @@ trust-these-validators.gov
|
||||
|
||||
{
|
||||
// load validators from both config and validators file
|
||||
boost::format cc(R"xrpldConfig(
|
||||
constexpr char const* cc = R"xrpldConfig(
|
||||
[validators_file]
|
||||
%1%
|
||||
{}
|
||||
|
||||
[validators]
|
||||
n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
|
||||
@@ -930,11 +929,11 @@ trust-these-validators.gov
|
||||
|
||||
[validator_list_keys]
|
||||
021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
|
||||
)xrpldConfig");
|
||||
)xrpldConfig";
|
||||
detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
|
||||
BEAST_EXPECT(vtg.validatorsFileExists());
|
||||
Config c;
|
||||
c.loadFromString(boost::str(cc % vtg.validatorsFile()));
|
||||
c.loadFromString(std::format(cc, vtg.validatorsFile()));
|
||||
BEAST_EXPECT(c.legacy(Sections::kValidatorsFile) == vtg.validatorsFile());
|
||||
BEAST_EXPECT(c.section(Sections::kValidators).values().size() == 15);
|
||||
BEAST_EXPECT(c.section(Sections::kValidatorListSites).values().size() == 4);
|
||||
@@ -945,13 +944,13 @@ trust-these-validators.gov
|
||||
{
|
||||
// load should throw if [validator_list_threshold] is present both
|
||||
// in xrpld.cfg and validators file
|
||||
boost::format cc(R"xrpldConfig(
|
||||
constexpr char const* cc = R"xrpldConfig(
|
||||
[validators_file]
|
||||
%1%
|
||||
{}
|
||||
|
||||
[validator_list_threshold]
|
||||
1
|
||||
)xrpldConfig");
|
||||
)xrpldConfig";
|
||||
std::string error;
|
||||
detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
|
||||
BEAST_EXPECT(vtg.validatorsFileExists());
|
||||
@@ -961,7 +960,7 @@ trust-these-validators.gov
|
||||
try
|
||||
{
|
||||
Config c;
|
||||
c.loadFromString(boost::str(cc % vtg.validatorsFile()));
|
||||
c.loadFromString(std::format(cc, vtg.validatorsFile()));
|
||||
fail();
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
@@ -975,7 +974,7 @@ trust-these-validators.gov
|
||||
// [validator_list_keys] are missing from xrpld.cfg and
|
||||
// validators file
|
||||
Config const c;
|
||||
boost::format cc("[validators_file]\n%1%\n");
|
||||
constexpr char const* cc = "[validators_file]\n{}\n";
|
||||
std::string error;
|
||||
detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
|
||||
BEAST_EXPECT(vtg.validatorsFileExists());
|
||||
@@ -988,7 +987,7 @@ trust-these-validators.gov
|
||||
try
|
||||
{
|
||||
Config c2;
|
||||
c2.loadFromString(boost::str(cc % vtg.validatorsFile()));
|
||||
c2.loadFromString(std::format(cc, vtg.validatorsFile()));
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
#include <xrpl/protocol/jss.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::test {
|
||||
@@ -36,12 +35,13 @@ public:
|
||||
makeValidatorConfig()
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
boost::format toLoad(R"xrpldConfig(
|
||||
auto const toLoad = std::format(
|
||||
R"xrpldConfig(
|
||||
[validator_token]
|
||||
%1%
|
||||
{}
|
||||
|
||||
[validators]
|
||||
%2%
|
||||
{}
|
||||
|
||||
[port_grpc]
|
||||
ip = 0.0.0.0
|
||||
@@ -52,9 +52,11 @@ ip = 0.0.0.0
|
||||
port = 50052
|
||||
protocol = wss2
|
||||
admin = 127.0.0.1
|
||||
)xrpldConfig");
|
||||
)xrpldConfig",
|
||||
validator_data::kToken,
|
||||
validator_data::kPublicKey);
|
||||
|
||||
p->loadFromString(boost::str(toLoad % validator_data::kToken % validator_data::kPublicKey));
|
||||
p->loadFromString(toLoad);
|
||||
|
||||
setupConfigForUnitTests(*p);
|
||||
|
||||
|
||||
60
src/tests/libxrpl/protocol/STXChainBridge.cpp
Normal file
60
src/tests/libxrpl/protocol/STXChainBridge.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Issue.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
using namespace xrpl;
|
||||
|
||||
namespace {
|
||||
|
||||
// Built from raw bytes rather than base58 so the test does not depend on
|
||||
// hand-computed checksums.
|
||||
AccountID
|
||||
account(std::string_view hex)
|
||||
{
|
||||
AccountID id;
|
||||
EXPECT_TRUE(id.parseHex(hex));
|
||||
return id;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// getText() builds its string from eight substitutions of the same type, so a
|
||||
// transposed pair would still compile and still type check. Pin the output so
|
||||
// the field/value pairing is actually verified.
|
||||
TEST(STXChainBridge, getTextPairsEachFieldWithItsValue)
|
||||
{
|
||||
auto const lockingDoor = account("0102030405060708090A0B0C0D0E0F1011121314");
|
||||
auto const issuingDoor = account("14131211100F0E0D0C0B0A090807060504030201");
|
||||
|
||||
auto const lockingIssue = xrpIssue();
|
||||
Issue const issuingIssue{toCurrency("USD"), issuingDoor};
|
||||
|
||||
STXChainBridge const bridge{lockingDoor, lockingIssue, issuingDoor, issuingIssue};
|
||||
|
||||
std::string const expected = "{ LockingChainDoor = " + toBase58(lockingDoor) +
|
||||
", LockingChainIssue = " + lockingIssue.getText() +
|
||||
", IssuingChainDoor = " + toBase58(issuingDoor) +
|
||||
", IssuingChainIssue = " + issuingIssue.getText() + " }";
|
||||
|
||||
EXPECT_EQ(bridge.getText(), expected);
|
||||
}
|
||||
|
||||
TEST(STXChainBridge, getTextOnADefaultBridge)
|
||||
{
|
||||
STXChainBridge const bridge;
|
||||
auto const text = bridge.getText();
|
||||
|
||||
// The outer braces are literal, and the four field names appear in
|
||||
// declaration order regardless of the values.
|
||||
EXPECT_TRUE(text.starts_with("{ LockingChainDoor = "));
|
||||
EXPECT_TRUE(text.ends_with(" }"));
|
||||
EXPECT_LT(text.find("LockingChainIssue"), text.find("IssuingChainDoor"));
|
||||
EXPECT_LT(text.find("IssuingChainDoor"), text.find("IssuingChainIssue"));
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
#include <xrpl/protocol/TxSearched.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
|
||||
// boost::optional (not std::optional) appears in the declarations below,
|
||||
// because SOCI's into()/use() bindings only support boost::optional.
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/asio/ssl/stream_base.hpp>
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -38,7 +38,7 @@ WorkSSL::WorkSSL(
|
||||
{
|
||||
auto ec = context_.preConnectVerify(stream_, host_);
|
||||
if (ec)
|
||||
Throw<std::runtime_error>(boost::str(boost::format("preConnectVerify: %s") % ec.message()));
|
||||
Throw<std::runtime_error>(std::format("preConnectVerify: {}", ec.message()));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <xrpl/net/HTTPClientSSLContext.h>
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <xrpl/rdb/RelationalDatabase.h>
|
||||
#include <xrpl/rdb/SociDB.h>
|
||||
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
#include <boost/optional/optional.hpp> // IWYU pragma: keep
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
|
||||
@@ -58,6 +57,7 @@
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
@@ -109,18 +109,16 @@ makeLedgerDBs(
|
||||
// ledger database
|
||||
auto lgr{std::make_unique<DatabaseCon>(
|
||||
setup, kLgrDbName, setup.lgrPragma, kLgrDbInit, checkpointerSetup, j)};
|
||||
lgr->getSession() << boost::str(
|
||||
boost::format("PRAGMA cache_size=-%d;") %
|
||||
kilobytes(config.getValueFor(SizedItem::LgrDbCache)));
|
||||
lgr->getSession() << std::format(
|
||||
"PRAGMA cache_size=-{};", kilobytes(config.getValueFor(SizedItem::LgrDbCache)));
|
||||
|
||||
if (config.useTxTables())
|
||||
{
|
||||
// transaction database
|
||||
auto tx{std::make_unique<DatabaseCon>(
|
||||
setup, kTxDbName, setup.txPragma, kTxDbInit, checkpointerSetup, j)};
|
||||
tx->getSession() << boost::str(
|
||||
boost::format("PRAGMA cache_size=-%d;") %
|
||||
kilobytes(config.getValueFor(SizedItem::TxnDbCache)));
|
||||
tx->getSession() << std::format(
|
||||
"PRAGMA cache_size=-{};", kilobytes(config.getValueFor(SizedItem::TxnDbCache)));
|
||||
|
||||
if (!setup.standAlone || setup.startUp == StartUpType::Load ||
|
||||
setup.startUp == StartUpType::LoadFile || setup.startUp == StartUpType::Replay)
|
||||
@@ -280,15 +278,17 @@ saveValidatedLedger(
|
||||
}
|
||||
|
||||
{
|
||||
static boost::format kDeleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %u;");
|
||||
static boost::format kDeleteTranS1("DELETE FROM Transactions WHERE LedgerSeq = %u;");
|
||||
static boost::format kDeleteTranS2("DELETE FROM AccountTransactions WHERE LedgerSeq = %u;");
|
||||
static boost::format kDeleteAcctTrans(
|
||||
"DELETE FROM AccountTransactions WHERE TransID = '%s';");
|
||||
static constexpr char const* kDeleteLedger = "DELETE FROM Ledgers WHERE LedgerSeq = {};";
|
||||
static constexpr char const* kDeleteTranS1 =
|
||||
"DELETE FROM Transactions WHERE LedgerSeq = {};";
|
||||
static constexpr char const* kDeleteTranS2 =
|
||||
"DELETE FROM AccountTransactions WHERE LedgerSeq = {};";
|
||||
static constexpr char const* kDeleteAcctTrans =
|
||||
"DELETE FROM AccountTransactions WHERE TransID = '{}';";
|
||||
|
||||
{
|
||||
auto db = ldgDB.checkoutDb();
|
||||
*db << boost::str(kDeleteLedger % seq);
|
||||
*db << std::format(kDeleteLedger, seq);
|
||||
}
|
||||
|
||||
if (app.config().useTxTables())
|
||||
@@ -305,19 +305,19 @@ saveValidatedLedger(
|
||||
|
||||
soci::transaction tr(*db);
|
||||
|
||||
*db << boost::str(kDeleteTranS1 % seq);
|
||||
*db << boost::str(kDeleteTranS2 % seq);
|
||||
*db << std::format(kDeleteTranS1, seq);
|
||||
*db << std::format(kDeleteTranS2, seq);
|
||||
|
||||
std::string const ledgerSeq(std::to_string(seq));
|
||||
|
||||
for (auto const& acceptedLedgerTx : *aLedger)
|
||||
{
|
||||
uint256 transactionID = acceptedLedgerTx->getTransactionID();
|
||||
uint256 const transactionID = acceptedLedgerTx->getTransactionID();
|
||||
|
||||
std::string const txnId(to_string(transactionID));
|
||||
std::string const txnSeq(std::to_string(acceptedLedgerTx->getTxnSeq()));
|
||||
|
||||
*db << boost::str(kDeleteAcctTrans % transactionID);
|
||||
*db << std::format(kDeleteAcctTrans, txnId);
|
||||
|
||||
auto const& accts = acceptedLedgerTx->getAffected();
|
||||
|
||||
@@ -629,11 +629,11 @@ getHashesByIndex(soci::session& session, LedgerIndex minSeq, LedgerIndex maxSeq,
|
||||
std::pair<std::vector<std::shared_ptr<Transaction>>, int>
|
||||
getTxHistory(soci::session& session, Application& app, LedgerIndex startIndex, int quantity)
|
||||
{
|
||||
std::string const sql = boost::str(
|
||||
boost::format(
|
||||
"SELECT LedgerSeq, Status, RawTxn "
|
||||
"FROM Transactions ORDER BY LedgerSeq DESC LIMIT %u,%u;") %
|
||||
startIndex % quantity);
|
||||
std::string const sql = std::format(
|
||||
"SELECT LedgerSeq, Status, RawTxn "
|
||||
"FROM Transactions ORDER BY LedgerSeq DESC LIMIT {},{};",
|
||||
startIndex,
|
||||
quantity);
|
||||
|
||||
std::vector<std::shared_ptr<Transaction>> txs;
|
||||
int total = 0;
|
||||
@@ -730,41 +730,50 @@ transactionsSQL(
|
||||
|
||||
if (options.ledgerRange.max != 0u)
|
||||
{
|
||||
maxClause = boost::str(
|
||||
boost::format("AND AccountTransactions.LedgerSeq <= '%u'") % options.ledgerRange.max);
|
||||
maxClause =
|
||||
std::format("AND AccountTransactions.LedgerSeq <= '{}'", options.ledgerRange.max);
|
||||
}
|
||||
|
||||
if (options.ledgerRange.min != 0u)
|
||||
{
|
||||
minClause = boost::str(
|
||||
boost::format("AND AccountTransactions.LedgerSeq >= '%u'") % options.ledgerRange.min);
|
||||
minClause =
|
||||
std::format("AND AccountTransactions.LedgerSeq >= '{}'", options.ledgerRange.min);
|
||||
}
|
||||
|
||||
std::string sql;
|
||||
|
||||
if (count)
|
||||
{
|
||||
sql = boost::str(
|
||||
boost::format(
|
||||
"SELECT %s FROM AccountTransactions "
|
||||
"WHERE Account = '%s' %s %s LIMIT %u, %u;") %
|
||||
selection % toBase58(options.account) % maxClause % minClause % options.offset %
|
||||
sql = std::format(
|
||||
"SELECT {} FROM AccountTransactions "
|
||||
"WHERE Account = '{}' {} {} LIMIT {}, {};",
|
||||
selection,
|
||||
toBase58(options.account),
|
||||
maxClause,
|
||||
minClause,
|
||||
options.offset,
|
||||
numberOfResults);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql = boost::str(
|
||||
boost::format(
|
||||
"SELECT %s FROM "
|
||||
"AccountTransactions INNER JOIN Transactions "
|
||||
"ON Transactions.TransID = AccountTransactions.TransID "
|
||||
"WHERE Account = '%s' %s %s "
|
||||
"ORDER BY AccountTransactions.LedgerSeq %s, "
|
||||
"AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s "
|
||||
"LIMIT %u, %u;") %
|
||||
selection % toBase58(options.account) % maxClause % minClause %
|
||||
(descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") %
|
||||
(descending ? "DESC" : "ASC") % options.offset % numberOfResults);
|
||||
char const* const order = descending ? "DESC" : "ASC";
|
||||
sql = std::format(
|
||||
"SELECT {} FROM "
|
||||
"AccountTransactions INNER JOIN Transactions "
|
||||
"ON Transactions.TransID = AccountTransactions.TransID "
|
||||
"WHERE Account = '{}' {} {} "
|
||||
"ORDER BY AccountTransactions.LedgerSeq {}, "
|
||||
"AccountTransactions.TxnSeq {}, AccountTransactions.TransID {} "
|
||||
"LIMIT {}, {};",
|
||||
selection,
|
||||
toBase58(options.account),
|
||||
maxClause,
|
||||
minClause,
|
||||
order,
|
||||
order,
|
||||
order,
|
||||
options.offset,
|
||||
numberOfResults);
|
||||
}
|
||||
JLOG(j.trace()) << "txSQL query: " << sql;
|
||||
return sql;
|
||||
@@ -1105,14 +1114,6 @@ accountTxPage(
|
||||
|
||||
std::optional<RelationalDatabase::AccountTxMarker> newmarker;
|
||||
|
||||
static std::string const kPrefix(
|
||||
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
|
||||
Status,RawTxn,TxnMeta
|
||||
FROM AccountTransactions INNER JOIN Transactions
|
||||
ON Transactions.TransID = AccountTransactions.TransID
|
||||
AND AccountTransactions.Account = '%s' WHERE
|
||||
)");
|
||||
|
||||
std::string sql;
|
||||
|
||||
// SQL's BETWEEN uses a closed interval ([a,b])
|
||||
@@ -1121,13 +1122,22 @@ accountTxPage(
|
||||
|
||||
if (findLedger == 0)
|
||||
{
|
||||
sql = boost::str(
|
||||
boost::format(kPrefix + R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u
|
||||
ORDER BY AccountTransactions.LedgerSeq %s,
|
||||
AccountTransactions.TxnSeq %s
|
||||
LIMIT %u;)") %
|
||||
toBase58(options.account) % options.ledgerRange.min % options.ledgerRange.max % order %
|
||||
order % queryLimit);
|
||||
sql = std::format(
|
||||
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
|
||||
Status,RawTxn,TxnMeta
|
||||
FROM AccountTransactions INNER JOIN Transactions
|
||||
ON Transactions.TransID = AccountTransactions.TransID
|
||||
AND AccountTransactions.Account = '{}' WHERE
|
||||
AccountTransactions.LedgerSeq BETWEEN {} AND {}
|
||||
ORDER BY AccountTransactions.LedgerSeq {},
|
||||
AccountTransactions.TxnSeq {}
|
||||
LIMIT {};)",
|
||||
toBase58(options.account),
|
||||
options.ledgerRange.min,
|
||||
options.ledgerRange.max,
|
||||
order,
|
||||
order,
|
||||
queryLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1136,27 +1146,34 @@ accountTxPage(
|
||||
std::uint32_t const maxLedger = forward ? options.ledgerRange.max : findLedger - 1;
|
||||
|
||||
auto b58acct = toBase58(options.account);
|
||||
sql = boost::str(
|
||||
boost::format(
|
||||
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
|
||||
sql = std::format(
|
||||
R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,
|
||||
Status,RawTxn,TxnMeta
|
||||
FROM AccountTransactions, Transactions WHERE
|
||||
(AccountTransactions.TransID = Transactions.TransID AND
|
||||
AccountTransactions.Account = '%s' AND
|
||||
AccountTransactions.LedgerSeq BETWEEN %u AND %u)
|
||||
AccountTransactions.Account = '{}' AND
|
||||
AccountTransactions.LedgerSeq BETWEEN {} AND {})
|
||||
UNION
|
||||
SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta
|
||||
FROM AccountTransactions, Transactions WHERE
|
||||
(AccountTransactions.TransID = Transactions.TransID AND
|
||||
AccountTransactions.Account = '%s' AND
|
||||
AccountTransactions.LedgerSeq = %u AND
|
||||
AccountTransactions.TxnSeq %s %u)
|
||||
ORDER BY AccountTransactions.LedgerSeq %s,
|
||||
AccountTransactions.TxnSeq %s
|
||||
LIMIT %u;
|
||||
)") %
|
||||
b58acct % minLedger % maxLedger % b58acct % findLedger % compare % findSeq % order %
|
||||
order % queryLimit);
|
||||
AccountTransactions.Account = '{}' AND
|
||||
AccountTransactions.LedgerSeq = {} AND
|
||||
AccountTransactions.TxnSeq {} {})
|
||||
ORDER BY AccountTransactions.LedgerSeq {},
|
||||
AccountTransactions.TxnSeq {}
|
||||
LIMIT {};
|
||||
)",
|
||||
b58acct,
|
||||
minLedger,
|
||||
maxLedger,
|
||||
b58acct,
|
||||
findLedger,
|
||||
compare,
|
||||
findSeq,
|
||||
order,
|
||||
order,
|
||||
queryLimit);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
#include <boost/multiprecision/detail/endian.hpp>
|
||||
#include <boost/predef.h>
|
||||
#include <boost/regex.hpp> // IWYU pragma: keep
|
||||
@@ -34,6 +33,7 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
@@ -400,7 +400,7 @@ Config::setup(std::string const& strConf, bool bQuiet, bool bSilent, bool bStand
|
||||
std::filesystem::create_directories(dataDir, ec);
|
||||
|
||||
if (ec)
|
||||
Throw<std::runtime_error>(boost::str(boost::format("Can not create %s") % dataDir));
|
||||
Throw<std::runtime_error>(std::format("Can not create {}", dataDir.string()));
|
||||
|
||||
legacy(Sections::kDatabasePath, std::filesystem::absolute(dataDir).string());
|
||||
}
|
||||
@@ -1315,8 +1315,7 @@ setupDatabaseCon(Config const& c, std::optional<beast::Journal> j)
|
||||
boost::iequals(journalMode, "truncate") || boost::iequals(journalMode, "persist") ||
|
||||
boost::iequals(journalMode, "wal"))
|
||||
{
|
||||
result->emplace_back(
|
||||
boost::str(boost::format(kCommonDbPragmaJournal) % journalMode));
|
||||
result->emplace_back(commonDbPragmaJournal(journalMode));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1337,7 +1336,7 @@ setupDatabaseCon(Config const& c, std::optional<beast::Journal> j)
|
||||
if (higherRisk || boost::iequals(synchronous, "normal") ||
|
||||
boost::iequals(synchronous, "full") || boost::iequals(synchronous, "extra"))
|
||||
{
|
||||
result->emplace_back(boost::str(boost::format(kCommonDbPragmaSync) % synchronous));
|
||||
result->emplace_back(commonDbPragmaSync(synchronous));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1358,7 +1357,7 @@ setupDatabaseCon(Config const& c, std::optional<beast::Journal> j)
|
||||
if (higherRisk || boost::iequals(tempStore, "default") ||
|
||||
boost::iequals(tempStore, "file"))
|
||||
{
|
||||
result->emplace_back(boost::str(boost::format(kCommonDbPragmaTemp) % tempStore));
|
||||
result->emplace_back(commonDbPragmaTemp(tempStore));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
@@ -424,7 +425,7 @@ parseSubUnsubJson(
|
||||
if (jv.isMember(jss::mpt_issuance_id) &&
|
||||
(jv.isMember(jss::currency) || jv.isMember(jss::issuer)))
|
||||
{
|
||||
JLOG(j.info()) << boost::format("Bad %s currency or MPT.") % name.cStr();
|
||||
JLOG(j.info()) << std::format("Bad {} currency or MPT.", name.cStr());
|
||||
return RpcInvalidParams;
|
||||
}
|
||||
|
||||
@@ -435,7 +436,7 @@ parseSubUnsubJson(
|
||||
if (!jv.isMember(jss::currency) ||
|
||||
!toCurrency(issue.currency, jv[jss::currency].asString()))
|
||||
{
|
||||
JLOG(j.info()) << boost::format("Bad %s currency.") % name.cStr();
|
||||
JLOG(j.info()) << std::format("Bad {} currency.", name.cStr());
|
||||
return assetError;
|
||||
}
|
||||
|
||||
@@ -445,7 +446,7 @@ parseSubUnsubJson(
|
||||
// Don't allow illegal issuers.
|
||||
|| (!issue.currency != !issue.account) || noAccount() == issue.account)
|
||||
{
|
||||
JLOG(j.info()) << boost::format("Bad %s issuer.") % name.cStr();
|
||||
JLOG(j.info()) << std::format("Bad {} issuer.", name.cStr());
|
||||
return issuerError;
|
||||
}
|
||||
asset = issue;
|
||||
@@ -459,7 +460,7 @@ parseSubUnsubJson(
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.info()) << boost::format("Neither %s currency or MPT is present.") % name.cStr();
|
||||
JLOG(j.info()) << std::format("Neither {} currency or MPT is present.", name.cStr());
|
||||
return assetError;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,9 @@
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <boost/format/free_funcs.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -60,7 +59,7 @@ injectSLE(json::Value& jv, SLE const& sle)
|
||||
md5 = toLower(md5);
|
||||
// VFALCO TODO Give a name to this constant and move it
|
||||
// to a more visible location.
|
||||
jv[jss::urlgravatar] = str(boost::format("https://www.gravatar.com/avatar/%s") % md5);
|
||||
jv[jss::urlgravatar] = std::format("https://www.gravatar.com/avatar/{}", md5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <xrpl/resource/Fees.h>
|
||||
#include <xrpl/server/NetworkOPs.h>
|
||||
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
@@ -32,7 +33,7 @@ validateTakerJSON(json::Value const& taker, json::StaticString const& name)
|
||||
{
|
||||
if (!taker.isMember(jss::currency) && !taker.isMember(jss::mpt_issuance_id))
|
||||
{
|
||||
return rpc::missingFieldError((boost::format("%s.currency") % name.cStr()).str());
|
||||
return rpc::missingFieldError(std::format("{}.currency", name.cStr()));
|
||||
}
|
||||
|
||||
if (taker.isMember(jss::mpt_issuance_id) &&
|
||||
@@ -44,8 +45,7 @@ validateTakerJSON(json::Value const& taker, json::StaticString const& name)
|
||||
if ((taker.isMember(jss::currency) && !taker[jss::currency].isString()) ||
|
||||
(taker.isMember(jss::mpt_issuance_id) && !taker[jss::mpt_issuance_id].isString()))
|
||||
{
|
||||
return rpc::expectedFieldError(
|
||||
(boost::format("%s.currency") % name.cStr()).str(), "string");
|
||||
return rpc::expectedFieldError(std::format("{}.currency", name.cStr()), "string");
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
@@ -70,10 +70,9 @@ parseTakerAssetJSON(
|
||||
|
||||
if (!toCurrency(issue.currency, taker[jss::currency].asString()))
|
||||
{
|
||||
JLOG(j.info()) << boost::format("Bad %s currency.") % name.cStr();
|
||||
JLOG(j.info()) << std::format("Bad {} currency.", name.cStr());
|
||||
return rpc::makeError(
|
||||
assetError,
|
||||
(boost::format("Invalid field '%s.currency', bad currency.") % name.cStr()).str());
|
||||
assetError, std::format("Invalid field '{}.currency', bad currency.", name.cStr()));
|
||||
}
|
||||
asset = issue;
|
||||
}
|
||||
@@ -83,8 +82,7 @@ parseTakerAssetJSON(
|
||||
if (!mptid.parseHex(taker[jss::mpt_issuance_id].asString()))
|
||||
{
|
||||
return rpc::makeError(
|
||||
assetError,
|
||||
(boost::format("Invalid field '%s.mpt_issuance_id'") % name.cStr()).str());
|
||||
assetError, std::format("Invalid field '{}.mpt_issuance_id'", name.cStr()));
|
||||
}
|
||||
asset = mptid;
|
||||
}
|
||||
@@ -113,24 +111,21 @@ parseTakerIssuerJSON(
|
||||
{
|
||||
if (!taker[jss::issuer].isString())
|
||||
{
|
||||
return rpc::expectedFieldError(
|
||||
(boost::format("%s.issuer") % name.cStr()).str(), "string");
|
||||
return rpc::expectedFieldError(std::format("{}.issuer", name.cStr()), "string");
|
||||
}
|
||||
|
||||
if (!toIssuer(issue.account, taker[jss::issuer].asString()))
|
||||
{
|
||||
return rpc::makeError(
|
||||
issuerError,
|
||||
(boost::format("Invalid field '%s.issuer', bad issuer.") % name.cStr()).str());
|
||||
std::format("Invalid field '{}.issuer', bad issuer.", name.cStr()));
|
||||
}
|
||||
|
||||
if (issue.account == noAccount())
|
||||
{
|
||||
return rpc::makeError(
|
||||
issuerError,
|
||||
(boost::format("Invalid field '%s.issuer', bad issuer account one.") %
|
||||
name.cStr())
|
||||
.str());
|
||||
std::format("Invalid field '{}.issuer', bad issuer account one.", name.cStr()));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -142,19 +137,17 @@ parseTakerIssuerJSON(
|
||||
{
|
||||
return rpc::makeError(
|
||||
issuerError,
|
||||
(boost::format(
|
||||
"Unneeded field '%s.issuer' for XRP currency "
|
||||
"specification.") %
|
||||
name.cStr())
|
||||
.str());
|
||||
std::format(
|
||||
"Unneeded field '{}.issuer' for XRP currency "
|
||||
"specification.",
|
||||
name.cStr()));
|
||||
}
|
||||
|
||||
if (!isXRP(issue.currency) && isXRP(issue.account))
|
||||
{
|
||||
return rpc::makeError(
|
||||
issuerError,
|
||||
(boost::format("Invalid field '%s.issuer', expected non-XRP issuer.") % name.cStr())
|
||||
.str());
|
||||
std::format("Invalid field '{}.issuer', expected non-XRP issuer.", name.cStr()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user