mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-07 15:32:34 +00:00
Compare commits
6 Commits
copilot/fi
...
ximinez/le
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f579efc2f | ||
|
|
e464e101be | ||
|
|
4dfa6db32a | ||
|
|
766124ed6d | ||
|
|
5c34a7b8fb | ||
|
|
f6f3542b7e |
8
.github/CODEOWNERS
vendored
Normal file
8
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Allow anyone to review any change by default.
|
||||
*
|
||||
|
||||
# Require the rpc-reviewers team to review changes to the rpc code.
|
||||
include/xrpl/protocol/ @xrplf/rpc-reviewers
|
||||
src/libxrpl/protocol/ @xrplf/rpc-reviewers
|
||||
src/xrpld/rpc/ @xrplf/rpc-reviewers
|
||||
src/xrpld/app/misc/ @xrplf/rpc-reviewers
|
||||
17
.github/scripts/strategy-matrix/generate.py
vendored
17
.github/scripts/strategy-matrix/generate.py
vendored
@@ -196,22 +196,11 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
# Enable code coverage for Debian Bookworm using GCC 15 in Debug on
|
||||
# linux/amd64
|
||||
if (
|
||||
f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
|
||||
|
||||
# Enable unity build for Ubuntu Jammy using GCC 12 in Debug on
|
||||
# linux/amd64.
|
||||
if (
|
||||
f"{os['distro_name']}-{os['distro_version']}" == "ubuntu-jammy"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"{cmake_args} -Dunity=ON"
|
||||
cmake_args = f"-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}"
|
||||
|
||||
# Generate a unique name for the configuration, e.g. macos-arm64-debug
|
||||
# or debian-bookworm-gcc-12-amd64-release.
|
||||
@@ -228,8 +217,6 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
config_name += f"-{build_type.lower()}"
|
||||
if "-Dcoverage=ON" in cmake_args:
|
||||
config_name += "-coverage"
|
||||
if "-Dunity=ON" in cmake_args:
|
||||
config_name += "-unity"
|
||||
|
||||
# Add the configuration to the list, with the most unique fields first,
|
||||
# so that they are easier to identify in the GitHub Actions UI, as long
|
||||
|
||||
@@ -75,10 +75,6 @@ This release contains bug fixes only and no API changes.
|
||||
|
||||
[Version 2.5.0](https://github.com/XRPLF/rippled/releases/tag/2.5.0) was released on Jun 24, 2025.
|
||||
|
||||
### Breaking changes in 2.5.0
|
||||
|
||||
- `feature`: The `vetoed` field is now always a boolean. Obsolete amendments now have `"vetoed": true` and a new `"obsolete": true` field, instead of the previous `"vetoed": "Obsolete"` string value. This change improves type safety for API clients.
|
||||
|
||||
### Additions and bugfixes in 2.5.0
|
||||
|
||||
- `tx`: Added `ctid` field to the response and improved error handling. ([#4738](https://github.com/XRPLF/rippled/pull/4738))
|
||||
|
||||
7
BUILD.md
7
BUILD.md
@@ -575,16 +575,10 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
|
||||
| `assert` | OFF | Enable assertions. |
|
||||
| `coverage` | OFF | Prepare the coverage report. |
|
||||
| `tests` | OFF | Build tests. |
|
||||
| `unity` | OFF | Configure a unity build. |
|
||||
| `xrpld` | OFF | Build the xrpld application, and not just the libxrpl library. |
|
||||
| `werr` | OFF | Treat compilation warnings as errors |
|
||||
| `wextra` | OFF | Enable additional compilation warnings |
|
||||
|
||||
[Unity builds][5] may be faster for the first build (at the cost of much more
|
||||
memory) since they concatenate sources into fewer translation units. Non-unity
|
||||
builds may be faster for incremental builds, and can be helpful for detecting
|
||||
`#include` omissions.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Conan
|
||||
@@ -651,7 +645,6 @@ If you want to experiment with a new package, follow these steps:
|
||||
[1]: https://github.com/conan-io/conan-center-index/issues/13168
|
||||
[2]: https://en.cppreference.com/w/cpp/compiler_support/20
|
||||
[3]: https://docs.conan.io/en/latest/getting_started.html
|
||||
[5]: https://en.wikipedia.org/wiki/Unity_build
|
||||
[6]: https://github.com/boostorg/beast/issues/2648
|
||||
[7]: https://github.com/boostorg/beast/issues/2661
|
||||
[gcovr]: https://gcovr.com/en/stable/getting-started.html
|
||||
|
||||
@@ -940,7 +940,23 @@
|
||||
#
|
||||
# path Location to store the database
|
||||
#
|
||||
# Optional keys for NuDB and RocksDB:
|
||||
# Optional keys
|
||||
#
|
||||
# cache_size Size of cache for database records. Default is 16384.
|
||||
# Setting this value to 0 will use the default value.
|
||||
#
|
||||
# cache_age Length of time in minutes to keep database records
|
||||
# cached. Default is 5 minutes. Setting this value to
|
||||
# 0 will use the default value.
|
||||
#
|
||||
# Note: if neither cache_size nor cache_age is
|
||||
# specified, the cache for database records will not
|
||||
# be created. If only one of cache_size or cache_age
|
||||
# is specified, the cache will be created using the
|
||||
# default value for the unspecified parameter.
|
||||
#
|
||||
# Note: the cache will not be created if online_delete
|
||||
# is specified.
|
||||
#
|
||||
# fast_load Boolean. If set, load the last persisted ledger
|
||||
# from disk upon process start before syncing to
|
||||
@@ -948,6 +964,8 @@
|
||||
# if sufficient IOPS capacity is available.
|
||||
# Default 0.
|
||||
#
|
||||
# Optional keys for NuDB or RocksDB:
|
||||
#
|
||||
# earliest_seq The default is 32570 to match the XRP ledger
|
||||
# network's earliest allowed sequence. Alternate
|
||||
# networks may set this value. Minimum value of 1.
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
|
||||
include(target_protobuf_sources)
|
||||
|
||||
# Protocol buffers cannot participate in a unity build,
|
||||
# because all the generated sources
|
||||
# define a bunch of `static const` variables with the same names,
|
||||
# so we just build them as a separate library.
|
||||
add_library(xrpl.libpb)
|
||||
set_target_properties(xrpl.libpb PROPERTIES UNITY_BUILD OFF)
|
||||
target_protobuf_sources(xrpl.libpb xrpl/proto LANGUAGE cpp IMPORT_DIRS include/xrpl/proto
|
||||
PROTOS include/xrpl/proto/xrpl.proto)
|
||||
|
||||
|
||||
@@ -30,14 +30,6 @@ if (tests)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
option(unity "Creates a build using UNITY support in cmake." OFF)
|
||||
if (unity)
|
||||
if (NOT is_ci)
|
||||
set(CMAKE_UNITY_BUILD_BATCH_SIZE 15 CACHE STRING "")
|
||||
endif ()
|
||||
set(CMAKE_UNITY_BUILD ON CACHE BOOL "Do a unity build")
|
||||
endif ()
|
||||
|
||||
if (is_clang AND is_linux)
|
||||
option(voidstar "Enable Antithesis instrumentation." OFF)
|
||||
endif ()
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
"secp256k1/0.7.1#3a61e95e220062ef32c48d019e9c81f7%1770306721.686",
|
||||
"secp256k1/0.7.0#0fda78daa3b864deb8a2fbc083398356%1770226294.524",
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
|
||||
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
|
||||
@@ -23,7 +23,6 @@ class Xrpl(ConanFile):
|
||||
"shared": [True, False],
|
||||
"static": [True, False],
|
||||
"tests": [True, False],
|
||||
"unity": [True, False],
|
||||
"xrpld": [True, False],
|
||||
}
|
||||
|
||||
@@ -33,7 +32,7 @@ class Xrpl(ConanFile):
|
||||
"libarchive/3.8.1",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.5.5",
|
||||
"secp256k1/0.7.1",
|
||||
"secp256k1/0.7.0",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
@@ -55,7 +54,6 @@ class Xrpl(ConanFile):
|
||||
"shared": False,
|
||||
"static": True,
|
||||
"tests": False,
|
||||
"unity": False,
|
||||
"xrpld": False,
|
||||
"date/*:header_only": True,
|
||||
"ed25519/*:shared": False,
|
||||
@@ -168,7 +166,6 @@ class Xrpl(ConanFile):
|
||||
tc.variables["rocksdb"] = self.options.rocksdb
|
||||
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
|
||||
tc.variables["static"] = self.options.static
|
||||
tc.variables["unity"] = self.options.unity
|
||||
tc.variables["xrpld"] = self.options.xrpld
|
||||
tc.generate()
|
||||
|
||||
|
||||
@@ -133,6 +133,10 @@ public:
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback);
|
||||
|
||||
/** Remove expired entries from the positive and negative caches. */
|
||||
virtual void
|
||||
sweep() = 0;
|
||||
|
||||
/** Gather statistics pertaining to read and write activities.
|
||||
*
|
||||
* @param obj Json object reference into which to place counters.
|
||||
|
||||
@@ -23,6 +23,32 @@ public:
|
||||
beast::Journal j)
|
||||
: Database(scheduler, readThreads, config, j), backend_(std::move(backend))
|
||||
{
|
||||
std::optional<int> cacheSize, cacheAge;
|
||||
|
||||
if (config.exists("cache_size"))
|
||||
{
|
||||
cacheSize = get<int>(config, "cache_size");
|
||||
if (cacheSize.value() < 0)
|
||||
{
|
||||
Throw<std::runtime_error>("Specified negative value for cache_size");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.exists("cache_age"))
|
||||
{
|
||||
cacheAge = get<int>(config, "cache_age");
|
||||
if (cacheAge.value() < 0)
|
||||
{
|
||||
Throw<std::runtime_error>("Specified negative value for cache_age");
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheSize != 0 || cacheAge != 0)
|
||||
{
|
||||
cache_ = std::make_shared<TaggedCache<uint256, NodeObject>>(
|
||||
"DatabaseNodeImp", cacheSize.value_or(0), std::chrono::minutes(cacheAge.value_or(0)), stopwatch(), j);
|
||||
}
|
||||
|
||||
XRPL_ASSERT(
|
||||
backend_,
|
||||
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
|
||||
@@ -77,7 +103,13 @@ public:
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback) override;
|
||||
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
// Cache for database objects. This cache is not always initialized. Check
|
||||
// for null before using.
|
||||
std::shared_ptr<TaggedCache<uint256, NodeObject>> cache_;
|
||||
// Persistent key/value storage
|
||||
std::shared_ptr<Backend> backend_;
|
||||
|
||||
|
||||
@@ -55,6 +55,9 @@ public:
|
||||
void
|
||||
sync() override;
|
||||
|
||||
void
|
||||
sweep() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Backend> writableBackend_;
|
||||
std::shared_ptr<Backend> archiveBackend_;
|
||||
|
||||
@@ -449,7 +449,6 @@ JSS(node_write_retries); // out: GetCounts
|
||||
JSS(node_writes_delayed); // out::GetCounts
|
||||
JSS(nth); // out: RPC server_definitions
|
||||
JSS(obligations); // out: GatewayBalances
|
||||
JSS(obsolete); // out: AmendmentTableImpl
|
||||
JSS(offers); // out: NetworkOPs, AccountOffers, Subscribe
|
||||
JSS(offer_id); // out: insertNFTokenOfferID
|
||||
JSS(offline); // in: TransactionSign
|
||||
|
||||
@@ -1893,6 +1893,93 @@ rippleSendIOU(
|
||||
return terResult;
|
||||
}
|
||||
|
||||
template <class TAsset>
|
||||
static TER
|
||||
doSendMulti(
|
||||
std::string const& name,
|
||||
ApplyView& view,
|
||||
AccountID const& senderID,
|
||||
TAsset const& issue,
|
||||
MultiplePaymentDestinations const& receivers,
|
||||
STAmount& actual,
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee,
|
||||
// Don't pass back parameters that the caller already has
|
||||
std::function<TER(AccountID const& senderID, AccountID const& receiverID, STAmount const& amount, bool checkIssuer)>
|
||||
doCredit,
|
||||
std::function<TER(AccountID const& issuer, STAmount const& takeFromSender, STAmount const& amount)> preMint = {})
|
||||
{
|
||||
// Use the same pattern for all the SendMulti functions to help avoid
|
||||
// divergence and copy/paste errors.
|
||||
auto const& issuer = issue.getIssuer();
|
||||
|
||||
// These values may not stay in sync
|
||||
STAmount takeFromSender{issue};
|
||||
actual = takeFromSender;
|
||||
|
||||
// Failures return immediately.
|
||||
for (auto const& r : receivers)
|
||||
{
|
||||
auto const& receiverID = r.first;
|
||||
STAmount amount{issue, r.second};
|
||||
|
||||
if (amount < beast::zero)
|
||||
{
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
/* If we aren't sending anything or if the sender is the same as the
|
||||
* receiver then we don't need to do anything.
|
||||
*/
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
|
||||
using namespace std::string_literals;
|
||||
XRPL_ASSERT(!isXRP(receiverID), ("xrpl::"s + name + " : receiver is not XRP").c_str());
|
||||
|
||||
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
|
||||
{
|
||||
if (preMint)
|
||||
{
|
||||
if (auto const ter = preMint(issuer, takeFromSender, amount))
|
||||
return ter;
|
||||
}
|
||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||
if (auto const ter = doCredit(senderID, receiverID, amount, false))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because doCredit took
|
||||
// it.
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sending 3rd party: transit.
|
||||
|
||||
// Calculate the amount to transfer accounting
|
||||
// for any transfer fees if the fee is not waived:
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes || issue.native())
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, amount));
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
JLOG(j.debug()) << name << "> " << to_string(senderID) << " - > " << to_string(receiverID)
|
||||
<< " : deliver=" << amount.getFullText() << " cost=" << actualSend.getFullText();
|
||||
|
||||
if (TER const terResult = doCredit(issuer, receiverID, amount, true))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = doCredit(senderID, issuer, takeFromSender, true))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
// Send regardless of limits.
|
||||
// --> receivers: Amount/currency/issuer to deliver to receivers.
|
||||
// <-- saActual: Amount actually cost to sender. Sender pays fees.
|
||||
@@ -1906,63 +1993,14 @@ rippleSendMultiIOU(
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
auto const& issuer = issue.getIssuer();
|
||||
|
||||
XRPL_ASSERT(!isXRP(senderID), "xrpl::rippleSendMultiIOU : sender is not XRP");
|
||||
|
||||
// These may diverge
|
||||
STAmount takeFromSender{issue};
|
||||
actual = takeFromSender;
|
||||
auto doCredit =
|
||||
[&view, j](AccountID const& senderID, AccountID const& receiverID, STAmount const& amount, bool checkIssuer) {
|
||||
return rippleCreditIOU(view, senderID, receiverID, amount, checkIssuer, j);
|
||||
};
|
||||
|
||||
// Failures return immediately.
|
||||
for (auto const& r : receivers)
|
||||
{
|
||||
auto const& receiverID = r.first;
|
||||
STAmount amount{issue, r.second};
|
||||
|
||||
/* If we aren't sending anything or if the sender is the same as the
|
||||
* receiver then we don't need to do anything.
|
||||
*/
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
|
||||
XRPL_ASSERT(!isXRP(receiverID), "xrpl::rippleSendMultiIOU : receiver is not XRP");
|
||||
|
||||
if (senderID == issuer || receiverID == issuer || issuer == noAccount())
|
||||
{
|
||||
// Direct send: redeeming IOUs and/or sending own IOUs.
|
||||
if (auto const ter = rippleCreditIOU(view, senderID, receiverID, amount, false, j))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because rippleCreditIOU took
|
||||
// it.
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sending 3rd party IOUs: transit.
|
||||
|
||||
// Calculate the amount to transfer accounting
|
||||
// for any transfer fees if the fee is not waived:
|
||||
STAmount actualSend =
|
||||
(waiveFee == WaiveTransferFee::Yes) ? amount : multiply(amount, transferRate(view, issuer));
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
JLOG(j.debug()) << "rippleSendMultiIOU> " << to_string(senderID) << " - > " << to_string(receiverID)
|
||||
<< " : deliver=" << amount.getFullText() << " cost=" << actual.getFullText();
|
||||
|
||||
if (TER const terResult = rippleCreditIOU(view, issuer, receiverID, amount, true, j))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = rippleCreditIOU(view, senderID, issuer, takeFromSender, true, j))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
return doSendMulti("rippleSendMultiIOU", view, senderID, issue, receivers, actual, j, waiveFee, doCredit);
|
||||
}
|
||||
|
||||
static TER
|
||||
@@ -2091,9 +2129,9 @@ accountSendMultiIOU(
|
||||
{
|
||||
XRPL_ASSERT_PARTS(receivers.size() > 1, "xrpl::accountSendMultiIOU", "multiple recipients provided");
|
||||
|
||||
STAmount actual;
|
||||
if (!issue.native())
|
||||
{
|
||||
STAmount actual;
|
||||
JLOG(j.trace()) << "accountSendMultiIOU: " << to_string(senderID) << " sending " << receivers.size() << " IOUs";
|
||||
|
||||
return rippleSendMultiIOU(view, senderID, issue, receivers, actual, j, waiveFee);
|
||||
@@ -2118,6 +2156,81 @@ accountSendMultiIOU(
|
||||
<< " receivers.";
|
||||
}
|
||||
|
||||
auto doCredit = [&view, &sender, &receivers, j](
|
||||
AccountID const& senderID,
|
||||
AccountID const& receiverID,
|
||||
STAmount const& amount,
|
||||
bool /*checkIssuer*/) -> TER {
|
||||
if (!senderID)
|
||||
{
|
||||
SLE::pointer receiver = receiverID != beast::zero ? view.peek(keylet::account(receiverID)) : SLE::pointer();
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
std::string receiver_bal("-");
|
||||
|
||||
if (receiver)
|
||||
receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
|
||||
|
||||
stream << "accountSendMultiIOU> " << to_string(senderID) << " -> " << to_string(receiverID) << " ("
|
||||
<< receiver_bal << ") : " << amount.getFullText();
|
||||
}
|
||||
|
||||
if (receiver)
|
||||
{
|
||||
// Increment XRP balance.
|
||||
auto const rcvBal = receiver->getFieldAmount(sfBalance);
|
||||
receiver->setFieldAmount(sfBalance, rcvBal + amount);
|
||||
view.creditHook(xrpAccount(), receiverID, amount, -rcvBal);
|
||||
|
||||
view.update(receiver);
|
||||
}
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
std::string receiver_bal("-");
|
||||
|
||||
if (receiver)
|
||||
receiver_bal = receiver->getFieldAmount(sfBalance).getFullText();
|
||||
|
||||
stream << "accountSendMultiIOU< " << to_string(senderID) << " -> " << to_string(receiverID) << " ("
|
||||
<< receiver_bal << ") : " << amount.getFullText();
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
// Sender
|
||||
if (sender)
|
||||
{
|
||||
if (sender->getFieldAmount(sfBalance) < amount)
|
||||
{
|
||||
return TER{tecFAILED_PROCESSING};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const sndBal = sender->getFieldAmount(sfBalance);
|
||||
view.creditHook(senderID, xrpAccount(), amount, sndBal);
|
||||
|
||||
// Decrement XRP balance.
|
||||
sender->setFieldAmount(sfBalance, sndBal - amount);
|
||||
view.update(sender);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto stream = j.trace())
|
||||
{
|
||||
std::string sender_bal("-");
|
||||
|
||||
if (sender)
|
||||
sender_bal = sender->getFieldAmount(sfBalance).getFullText();
|
||||
|
||||
stream << "accountSendMultiIOU< " << to_string(senderID) << " (" << sender_bal << ") -> "
|
||||
<< receivers.size() << " receivers.";
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
};
|
||||
return doSendMulti("accountSendMultiIOU", view, senderID, issue, receivers, actual, j, waiveFee, doCredit);
|
||||
|
||||
// Failures return immediately.
|
||||
STAmount takeFromSender{issue};
|
||||
for (auto const& r : receivers)
|
||||
@@ -2330,80 +2443,33 @@ rippleSendMultiMPT(
|
||||
beast::Journal j,
|
||||
WaiveTransferFee waiveFee)
|
||||
{
|
||||
// Safe to get MPT since rippleSendMultiMPT is only called by
|
||||
// accountSendMultiMPT
|
||||
auto const& issuer = mptIssue.getIssuer();
|
||||
|
||||
auto const sle = view.read(keylet::mptIssuance(mptIssue.getMptID()));
|
||||
if (!sle)
|
||||
return tecOBJECT_NOT_FOUND;
|
||||
|
||||
// These may diverge
|
||||
STAmount takeFromSender{mptIssue};
|
||||
actual = takeFromSender;
|
||||
|
||||
for (auto const& r : receivers)
|
||||
{
|
||||
auto const& receiverID = r.first;
|
||||
STAmount amount{mptIssue, r.second};
|
||||
|
||||
if (amount < beast::zero)
|
||||
auto preMint = [&](AccountID const& issuer, STAmount const& takeFromSender, STAmount const& amount) -> TER {
|
||||
// if sender is issuer, check that the new OutstandingAmount will
|
||||
// not exceed MaximumAmount
|
||||
if (senderID == issuer)
|
||||
{
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
XRPL_ASSERT_PARTS(
|
||||
takeFromSender == beast::zero,
|
||||
"rippler::rippleSendMultiMPT",
|
||||
"sender == issuer, takeFromSender == zero");
|
||||
auto const sendAmount = amount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount || sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
/* If we aren't sending anything or if the sender is the same as the
|
||||
* receiver then we don't need to do anything.
|
||||
*/
|
||||
if (!amount || (senderID == receiverID))
|
||||
continue;
|
||||
return tesSUCCESS;
|
||||
};
|
||||
auto doCredit = [&view, j](AccountID const& senderID, AccountID const& receiverID, STAmount const& amount, bool) {
|
||||
return rippleCreditMPT(view, senderID, receiverID, amount, j);
|
||||
};
|
||||
|
||||
if (senderID == issuer || receiverID == issuer)
|
||||
{
|
||||
// if sender is issuer, check that the new OutstandingAmount will
|
||||
// not exceed MaximumAmount
|
||||
if (senderID == issuer)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
takeFromSender == beast::zero,
|
||||
"rippler::rippleSendMultiMPT",
|
||||
"sender == issuer, takeFromSender == zero");
|
||||
auto const sendAmount = amount.mpt().value();
|
||||
auto const maximumAmount = sle->at(~sfMaximumAmount).value_or(maxMPTokenAmount);
|
||||
if (sendAmount > maximumAmount || sle->getFieldU64(sfOutstandingAmount) > maximumAmount - sendAmount)
|
||||
return tecPATH_DRY;
|
||||
}
|
||||
|
||||
// Direct send: redeeming MPTs and/or sending own MPTs.
|
||||
if (auto const ter = rippleCreditMPT(view, senderID, receiverID, amount, j))
|
||||
return ter;
|
||||
actual += amount;
|
||||
// Do not add amount to takeFromSender, because rippleCreditMPT took
|
||||
// it
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sending 3rd party MPTs: transit.
|
||||
STAmount actualSend = (waiveFee == WaiveTransferFee::Yes)
|
||||
? amount
|
||||
: multiply(amount, transferRate(view, amount.get<MPTIssue>().getMptID()));
|
||||
actual += actualSend;
|
||||
takeFromSender += actualSend;
|
||||
|
||||
JLOG(j.debug()) << "rippleSendMultiMPT> " << to_string(senderID) << " - > " << to_string(receiverID)
|
||||
<< " : deliver=" << amount.getFullText() << " cost=" << actualSend.getFullText();
|
||||
|
||||
if (auto const terResult = rippleCreditMPT(view, issuer, receiverID, amount, j))
|
||||
return terResult;
|
||||
}
|
||||
if (senderID != issuer && takeFromSender)
|
||||
{
|
||||
if (TER const terResult = rippleCreditMPT(view, senderID, issuer, takeFromSender, j))
|
||||
return terResult;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
return doSendMulti(
|
||||
"rippleSendMultiMPT", view, senderID, mptIssue, receivers, actual, j, waiveFee, doCredit, preMint);
|
||||
}
|
||||
|
||||
static TER
|
||||
|
||||
@@ -85,7 +85,8 @@ registerSSLCerts(boost::asio::ssl::context& ctx, boost::system::error_code& ec,
|
||||
// There is a very unpleasant interaction between <wincrypt> and
|
||||
// openssl x509 types (namely the former has macros that stomp
|
||||
// on the latter), these undefs allow this TU to be safely used in
|
||||
// unity builds without messing up subsequent TUs.
|
||||
// unity builds without messing up subsequent TUs. Although we
|
||||
// no longer use unity builds, leaving the undefs here does no harm.
|
||||
#if BOOST_OS_WINDOWS
|
||||
#undef X509_NAME
|
||||
#undef X509_EXTENSIONS
|
||||
|
||||
@@ -10,6 +10,11 @@ DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, st
|
||||
|
||||
auto obj = NodeObject::createObject(type, std::move(data), hash);
|
||||
backend_->store(obj);
|
||||
if (cache_)
|
||||
{
|
||||
// After the store, replace a negative cache entry if there is one
|
||||
cache_->canonicalize(hash, obj, [](std::shared_ptr<NodeObject> const& n) { return n->getType() == hotDUMMY; });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -18,36 +23,77 @@ DatabaseNodeImp::asyncFetch(
|
||||
std::uint32_t ledgerSeq,
|
||||
std::function<void(std::shared_ptr<NodeObject> const&)>&& callback)
|
||||
{
|
||||
if (cache_)
|
||||
{
|
||||
std::shared_ptr<NodeObject> obj = cache_->fetch(hash);
|
||||
if (obj)
|
||||
{
|
||||
callback(obj->getType() == hotDUMMY ? nullptr : obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Database::asyncFetch(hash, ledgerSeq, std::move(callback));
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseNodeImp::sweep()
|
||||
{
|
||||
if (cache_)
|
||||
cache_->sweep();
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
|
||||
{
|
||||
std::shared_ptr<NodeObject> nodeObject = nullptr;
|
||||
Status status;
|
||||
std::shared_ptr<NodeObject> nodeObject = cache_ ? cache_->fetch(hash) : nullptr;
|
||||
|
||||
try
|
||||
if (!nodeObject)
|
||||
{
|
||||
status = backend_->fetch(hash.data(), &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
|
||||
Rethrow();
|
||||
}
|
||||
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record not " << (cache_ ? "cached" : "found");
|
||||
|
||||
switch (status)
|
||||
Status status;
|
||||
|
||||
try
|
||||
{
|
||||
status = backend_->fetch(hash.data(), &nodeObject);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": Exception fetching from backend: " << e.what();
|
||||
Rethrow();
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case ok:
|
||||
if (cache_)
|
||||
{
|
||||
if (nodeObject)
|
||||
cache_->canonicalize_replace_client(hash, nodeObject);
|
||||
else
|
||||
{
|
||||
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
|
||||
cache_->canonicalize_replace_client(hash, notFound);
|
||||
if (notFound->getType() != hotDUMMY)
|
||||
nodeObject = notFound;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case notFound:
|
||||
break;
|
||||
case dataCorrupt:
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
|
||||
break;
|
||||
default:
|
||||
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
case ok:
|
||||
case notFound:
|
||||
break;
|
||||
case dataCorrupt:
|
||||
JLOG(j_.fatal()) << "fetchNodeObject " << hash << ": nodestore data is corrupted";
|
||||
break;
|
||||
default:
|
||||
JLOG(j_.warn()) << "fetchNodeObject " << hash << ": backend returns unknown result " << status;
|
||||
break;
|
||||
JLOG(j_.trace()) << "fetchNodeObject " << hash << ": record found in cache";
|
||||
if (nodeObject->getType() == hotDUMMY)
|
||||
nodeObject.reset();
|
||||
}
|
||||
|
||||
if (nodeObject)
|
||||
@@ -59,36 +105,66 @@ DatabaseNodeImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport
|
||||
std::vector<std::shared_ptr<NodeObject>>
|
||||
DatabaseNodeImp::fetchBatch(std::vector<uint256> const& hashes)
|
||||
{
|
||||
std::vector<std::shared_ptr<NodeObject>> results{hashes.size()};
|
||||
using namespace std::chrono;
|
||||
auto const before = steady_clock::now();
|
||||
|
||||
std::vector<uint256 const*> batch{};
|
||||
batch.reserve(hashes.size());
|
||||
std::unordered_map<uint256 const*, size_t> indexMap;
|
||||
std::vector<uint256 const*> cacheMisses;
|
||||
uint64_t hits = 0;
|
||||
uint64_t fetches = 0;
|
||||
for (size_t i = 0; i < hashes.size(); ++i)
|
||||
{
|
||||
auto const& hash = hashes[i];
|
||||
batch.push_back(&hash);
|
||||
}
|
||||
|
||||
// Get the node objects that match the hashes from the backend. To protect
|
||||
// against the backends returning fewer or more results than expected, the
|
||||
// container is resized to the number of hashes.
|
||||
auto results = backend_->fetchBatch(batch).first;
|
||||
XRPL_ASSERT(
|
||||
results.size() == hashes.size() || results.empty(),
|
||||
"number of output objects either matches number of input hashes or is empty");
|
||||
results.resize(hashes.size());
|
||||
for (size_t i = 0; i < results.size(); ++i)
|
||||
{
|
||||
if (!results[i])
|
||||
// See if the object already exists in the cache
|
||||
auto nObj = cache_ ? cache_->fetch(hash) : nullptr;
|
||||
++fetches;
|
||||
if (!nObj)
|
||||
{
|
||||
JLOG(j_.error()) << "fetchBatch - "
|
||||
<< "record not found in db. hash = " << strHex(hashes[i]);
|
||||
// Try the database
|
||||
indexMap[&hash] = i;
|
||||
cacheMisses.push_back(&hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
results[i] = nObj->getType() == hotDUMMY ? nullptr : nObj;
|
||||
// It was in the cache.
|
||||
++hits;
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(j_.debug()) << "fetchBatch - cache hits = " << (hashes.size() - cacheMisses.size())
|
||||
<< " - cache misses = " << cacheMisses.size();
|
||||
auto dbResults = backend_->fetchBatch(cacheMisses).first;
|
||||
|
||||
for (size_t i = 0; i < dbResults.size(); ++i)
|
||||
{
|
||||
auto nObj = std::move(dbResults[i]);
|
||||
size_t index = indexMap[cacheMisses[i]];
|
||||
auto const& hash = hashes[index];
|
||||
|
||||
if (nObj)
|
||||
{
|
||||
// Ensure all threads get the same object
|
||||
if (cache_)
|
||||
cache_->canonicalize_replace_client(hash, nObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.error()) << "fetchBatch - "
|
||||
<< "record not found in db or cache. hash = " << strHex(hash);
|
||||
if (cache_)
|
||||
{
|
||||
auto notFound = NodeObject::createObject(hotDUMMY, {}, hash);
|
||||
cache_->canonicalize_replace_client(hash, notFound);
|
||||
if (notFound->getType() != hotDUMMY)
|
||||
nObj = std::move(notFound);
|
||||
}
|
||||
}
|
||||
results[index] = std::move(nObj);
|
||||
}
|
||||
|
||||
auto fetchDurationUs = std::chrono::duration_cast<std::chrono::microseconds>(steady_clock::now() - before).count();
|
||||
updateFetchMetrics(hashes.size(), 0, fetchDurationUs);
|
||||
updateFetchMetrics(fetches, hits, fetchDurationUs);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,12 @@ DatabaseRotatingImp::store(NodeObjectType type, Blob&& data, uint256 const& hash
|
||||
storeStats(1, nObj->getData().size());
|
||||
}
|
||||
|
||||
void
|
||||
DatabaseRotatingImp::sweep()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeObject>
|
||||
DatabaseRotatingImp::fetchNodeObject(uint256 const& hash, std::uint32_t, FetchReport& fetchReport, bool duplicate)
|
||||
{
|
||||
|
||||
@@ -592,18 +592,20 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
Number const overpaymentAmount{50};
|
||||
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset, loanScale, overpaymentAmount, TenthBips32(0), TenthBips32(0), managementFeeRate);
|
||||
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -634,20 +636,20 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
loanProperties.loanState.interestDue - newState.interestDue == 0,
|
||||
loanProperites.loanState.interestDue - newState.interestDue == 0,
|
||||
" interest change mismatch: expected 0, got " +
|
||||
to_string(loanProperties.loanState.interestDue - newState.interestDue));
|
||||
to_string(loanProperites.loanState.interestDue - newState.interestDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
}
|
||||
|
||||
@@ -670,7 +672,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -678,15 +680,17 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(10'000), // 10% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -717,21 +721,21 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
// With no Loan interest, interest outstanding should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperties.loanState.interestDue - newState.interestDue == 0,
|
||||
loanProperites.loanState.interestDue - newState.interestDue == 0,
|
||||
" interest change mismatch: expected 0, got " +
|
||||
to_string(loanProperties.loanState.interestDue - newState.interestDue));
|
||||
to_string(loanProperites.loanState.interestDue - newState.interestDue));
|
||||
|
||||
// With no Loan management fee, management fee due should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
}
|
||||
|
||||
@@ -754,7 +758,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -762,15 +766,17 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(0), // 0% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -806,22 +812,22 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange == newState.interestDue - loanProperties.loanState.interestDue,
|
||||
actualPaymentParts.valueChange == newState.interestDue - loanProperites.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
|
||||
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
|
||||
to_string(actualPaymentParts.valueChange));
|
||||
|
||||
// With no Loan management fee, management fee due should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -843,7 +849,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -851,15 +857,17 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(0), // 0% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -896,26 +904,26 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
|
||||
// The change in interest is equal to the value change sans the
|
||||
// overpayment interest
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
|
||||
newState.interestDue - loanProperties.loanState.interestDue,
|
||||
newState.interestDue - loanProperites.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(
|
||||
newState.interestDue - loanProperties.loanState.interestDue + actualPaymentParts.interestPaid) +
|
||||
newState.interestDue - loanProperites.loanState.interestDue + actualPaymentParts.interestPaid) +
|
||||
", got " + to_string(actualPaymentParts.valueChange));
|
||||
|
||||
// With no Loan management fee, management fee due should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -939,7 +947,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -947,15 +955,17 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(0), // 0% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -994,23 +1004,23 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
|
||||
// Note that the management fee value change is not captured, as this
|
||||
// value is not needed to correctly update the Vault state.
|
||||
BEAST_EXPECTS(
|
||||
(newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-20592, -5}),
|
||||
(newState.managementFeeDue - loanProperites.loanState.managementFeeDue == Number{-20592, -5}),
|
||||
" management fee change mismatch: expected " + to_string(Number{-20592, -5}) + ", got " +
|
||||
to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
|
||||
to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
|
||||
newState.interestDue - loanProperties.loanState.interestDue,
|
||||
newState.interestDue - loanProperites.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
|
||||
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
|
||||
to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
|
||||
}
|
||||
|
||||
@@ -1033,7 +1043,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -1041,15 +1051,17 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(10'000), // 10% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -1089,23 +1101,23 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
|
||||
// Note that the management fee value change is not captured, as this
|
||||
// value is not needed to correctly update the Vault state.
|
||||
BEAST_EXPECTS(
|
||||
(newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-18304, -5}),
|
||||
(newState.managementFeeDue - loanProperites.loanState.managementFeeDue == Number{-18304, -5}),
|
||||
" management fee change mismatch: expected " + to_string(Number{-18304, -5}) + ", got " +
|
||||
to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
|
||||
to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
|
||||
newState.interestDue - loanProperties.loanState.interestDue,
|
||||
newState.interestDue - loanProperites.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
|
||||
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
|
||||
to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
|
||||
}
|
||||
|
||||
|
||||
@@ -827,13 +827,8 @@ public:
|
||||
|
||||
// applyManifest should accept new manifests with
|
||||
// higher sequence numbers
|
||||
auto const seq0 = cache.sequence();
|
||||
BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(cache.sequence() > seq0);
|
||||
|
||||
auto const seq1 = cache.sequence();
|
||||
BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
|
||||
BEAST_EXPECT(cache.sequence() == seq1);
|
||||
|
||||
BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::accepted);
|
||||
BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
|
||||
|
||||
@@ -490,8 +490,19 @@ public:
|
||||
Env env(*this, envconfig(onlineDelete));
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Create NodeStore with two backends to allow online deletion of data.
|
||||
// Normally, SHAMapStoreImp handles all these details.
|
||||
// Create the backend. Normally, SHAMapStoreImp handles all these
|
||||
// details
|
||||
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
|
||||
|
||||
// Provide default values:
|
||||
if (!nscfg.exists("cache_size"))
|
||||
nscfg.set(
|
||||
"cache_size", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
|
||||
|
||||
if (!nscfg.exists("cache_age"))
|
||||
nscfg.set(
|
||||
"cache_age", std::to_string(env.app().config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
|
||||
|
||||
NodeStoreScheduler scheduler(env.app().getJobQueue());
|
||||
|
||||
std::string const writableDb = "write";
|
||||
@@ -499,8 +510,9 @@ public:
|
||||
auto writableBackend = makeBackendRotating(env, scheduler, writableDb);
|
||||
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
|
||||
|
||||
// Create NodeStore with two backends to allow online deletion of
|
||||
// data
|
||||
constexpr int readThreads = 4;
|
||||
auto nscfg = env.app().config().section(ConfigSection::nodeDatabase());
|
||||
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
|
||||
scheduler,
|
||||
readThreads,
|
||||
|
||||
@@ -140,17 +140,10 @@ class Feature_test : public beast::unit_test::suite
|
||||
feature.isMember(jss::enabled) && !feature[jss::enabled].asBool(),
|
||||
feature[jss::name].asString() + " enabled");
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool(),
|
||||
feature[jss::name].asString() + " vetoed is bool");
|
||||
BEAST_EXPECTS(
|
||||
feature[jss::vetoed].asBool() == (expectVeto || expectObsolete),
|
||||
feature[jss::name].asString() + " vetoed value");
|
||||
if (expectObsolete)
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::obsolete) && feature[jss::obsolete].asBool() == true,
|
||||
feature[jss::name].asString() + " obsolete");
|
||||
else
|
||||
BEAST_EXPECTS(!feature.isMember(jss::obsolete), feature[jss::name].asString() + " no obsolete");
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
|
||||
(!feature[jss::vetoed].isBool() || feature[jss::vetoed].asBool() == expectVeto) &&
|
||||
(feature[jss::vetoed].isBool() || feature[jss::vetoed].asString() == "Obsolete"),
|
||||
feature[jss::name].asString() + " vetoed");
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::supported) && feature[jss::supported].asBool(),
|
||||
feature[jss::name].asString() + " supported");
|
||||
@@ -249,7 +242,6 @@ class Feature_test : public beast::unit_test::suite
|
||||
(*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported,
|
||||
(*it)[jss::name].asString() + " supported");
|
||||
BEAST_EXPECT(!(*it).isMember(jss::vetoed));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::obsolete));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::majority));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::count));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::validations));
|
||||
@@ -310,20 +302,11 @@ class Feature_test : public beast::unit_test::suite
|
||||
if (expectEnabled)
|
||||
BEAST_EXPECTS(!(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed");
|
||||
else
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
(*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool(),
|
||||
(*it)[jss::name].asString() + " vetoed is bool");
|
||||
BEAST_EXPECTS(
|
||||
(*it)[jss::vetoed].asBool() == (expectVeto || expectObsolete),
|
||||
(*it)[jss::name].asString() + " vetoed value");
|
||||
if (expectObsolete)
|
||||
BEAST_EXPECTS(
|
||||
(*it).isMember(jss::obsolete) && (*it)[jss::obsolete].asBool() == true,
|
||||
(*it)[jss::name].asString() + " obsolete");
|
||||
else
|
||||
BEAST_EXPECTS(!(*it).isMember(jss::obsolete), (*it)[jss::name].asString() + " no obsolete");
|
||||
}
|
||||
(*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete &&
|
||||
(!(*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asBool() == expectVeto) &&
|
||||
((*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asString() == "Obsolete"),
|
||||
(*it)[jss::name].asString() + " vetoed");
|
||||
BEAST_EXPECTS(
|
||||
(*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported,
|
||||
(*it)[jss::name].asString() + " supported");
|
||||
@@ -386,17 +369,10 @@ class Feature_test : public beast::unit_test::suite
|
||||
(expectVeto || expectObsolete) ^ feature.isMember(jss::majority),
|
||||
feature[jss::name].asString() + " majority");
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool(),
|
||||
feature[jss::name].asString() + " vetoed is bool");
|
||||
BEAST_EXPECTS(
|
||||
feature[jss::vetoed].asBool() == (expectVeto || expectObsolete),
|
||||
feature[jss::name].asString() + " vetoed value");
|
||||
if (expectObsolete)
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::obsolete) && feature[jss::obsolete].asBool() == true,
|
||||
feature[jss::name].asString() + " obsolete");
|
||||
else
|
||||
BEAST_EXPECTS(!feature.isMember(jss::obsolete), feature[jss::name].asString() + " no obsolete");
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
|
||||
(!feature[jss::vetoed].isBool() || feature[jss::vetoed].asBool() == expectVeto) &&
|
||||
(feature[jss::vetoed].isBool() || feature[jss::vetoed].asString() == "Obsolete"),
|
||||
feature[jss::name].asString() + " vetoed");
|
||||
BEAST_EXPECTS(feature.isMember(jss::count), feature[jss::name].asString() + " count");
|
||||
BEAST_EXPECTS(feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold");
|
||||
BEAST_EXPECTS(feature.isMember(jss::validations), feature[jss::name].asString() + " validations");
|
||||
@@ -484,8 +460,7 @@ class Feature_test : public beast::unit_test::suite
|
||||
return;
|
||||
auto feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed");
|
||||
|
||||
jrr = env.rpc("feature", featureName, "reject")[jss::result];
|
||||
if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||
@@ -495,8 +470,7 @@ class Feature_test : public beast::unit_test::suite
|
||||
return;
|
||||
feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed");
|
||||
|
||||
jrr = env.rpc("feature", featureName, "accept")[jss::result];
|
||||
if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||
@@ -506,8 +480,7 @@ class Feature_test : public beast::unit_test::suite
|
||||
return;
|
||||
feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed");
|
||||
|
||||
// anything other than accept or reject is an error
|
||||
jrr = env.rpc("feature", featureName, "maybe");
|
||||
|
||||
@@ -908,6 +908,10 @@ public:
|
||||
JLOG(m_journal.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize
|
||||
<< "; size after: " << masterTxCache.size();
|
||||
}
|
||||
{
|
||||
// Does not appear to have an associated cache.
|
||||
getNodeStore().sweep();
|
||||
}
|
||||
{
|
||||
std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize();
|
||||
|
||||
|
||||
@@ -130,6 +130,14 @@ std::unique_ptr<NodeStore::Database>
|
||||
SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
{
|
||||
auto nscfg = app_.config().section(ConfigSection::nodeDatabase());
|
||||
|
||||
// Provide default values:
|
||||
if (!nscfg.exists("cache_size"))
|
||||
nscfg.set("cache_size", std::to_string(app_.config().getValueFor(SizedItem::treeCacheSize, std::nullopt)));
|
||||
|
||||
if (!nscfg.exists("cache_age"))
|
||||
nscfg.set("cache_age", std::to_string(app_.config().getValueFor(SizedItem::treeCacheAge, std::nullopt)));
|
||||
|
||||
std::unique_ptr<NodeStore::Database> db;
|
||||
|
||||
if (deleteInterval_)
|
||||
@@ -218,6 +226,8 @@ SHAMapStoreImp::run()
|
||||
LedgerIndex lastRotated = state_db_.getState().lastRotated;
|
||||
netOPs_ = &app_.getOPs();
|
||||
ledgerMaster_ = &app_.getLedgerMaster();
|
||||
fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache());
|
||||
treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache());
|
||||
|
||||
if (advisoryDelete_)
|
||||
canDelete_ = state_db_.getCanDelete();
|
||||
@@ -480,19 +490,16 @@ void
|
||||
SHAMapStoreImp::clearCaches(LedgerIndex validatedSeq)
|
||||
{
|
||||
ledgerMaster_->clearLedgerCachePrior(validatedSeq);
|
||||
// Also clear the FullBelowCache so its generation counter is bumped.
|
||||
// This prevents stale "full below" markers from persisting across
|
||||
// backend rotation/online deletion and interfering with SHAMap sync.
|
||||
app_.getNodeFamily().getFullBelowCache()->clear();
|
||||
fullBelowCache_->clear();
|
||||
}
|
||||
|
||||
void
|
||||
SHAMapStoreImp::freshenCaches()
|
||||
{
|
||||
if (freshenCache(*app_.getNodeFamily().getTreeNodeCache()))
|
||||
if (freshenCache(*treeNodeCache_))
|
||||
return;
|
||||
if (freshenCache(app_.getMasterTransaction().getCache()))
|
||||
return;
|
||||
|
||||
freshenCache(app_.getMasterTransaction().getCache());
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -93,6 +93,8 @@ private:
|
||||
// as of run() or before
|
||||
NetworkOPs* netOPs_ = nullptr;
|
||||
LedgerMaster* ledgerMaster_ = nullptr;
|
||||
FullBelowCache* fullBelowCache_ = nullptr;
|
||||
TreeNodeCache* treeNodeCache_ = nullptr;
|
||||
|
||||
static constexpr auto nodeStoreName_ = "NodeStore";
|
||||
|
||||
|
||||
@@ -903,10 +903,7 @@ AmendmentTableImpl::injectJson(
|
||||
if (!fs.enabled && isAdmin)
|
||||
{
|
||||
if (fs.vote == AmendmentVote::obsolete)
|
||||
{
|
||||
v[jss::vetoed] = true;
|
||||
v[jss::obsolete] = true;
|
||||
}
|
||||
v[jss::vetoed] = "Obsolete";
|
||||
else
|
||||
v[jss::vetoed] = fs.vote == AmendmentVote::down;
|
||||
}
|
||||
|
||||
@@ -459,10 +459,6 @@ ManifestCache::applyManifest(Manifest m)
|
||||
|
||||
auto masterKey = m.masterKey;
|
||||
map_.emplace(std::move(masterKey), std::move(m));
|
||||
|
||||
// Something has changed. Keep track of it.
|
||||
seq_++;
|
||||
|
||||
return ManifestDisposition::accepted;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user