mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-05 22:45:35 +00:00
Compare commits
8 Commits
bthomee/di
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fb832f886 | ||
|
|
0a626d95f4 | ||
|
|
6006c281e2 | ||
|
|
e79673cf40 | ||
|
|
1c59ac3e11 | ||
|
|
aea548b2b9 | ||
|
|
f4e1f71b7a | ||
|
|
136ec1ca9e |
@@ -22,6 +22,12 @@ API version 2 is available in `rippled` version 2.0.0 and later. See [API-VERSIO
|
||||
|
||||
This version is supported by all `rippled` versions. For WebSocket and HTTP JSON-RPC requests, it is currently the default API version used when no `api_version` is specified.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Bugfixes
|
||||
|
||||
- Peer Crawler: The `port` field in `overlay.active[]` now consistently returns an integer instead of a string for outbound peers. [#6318](https://github.com/XRPLF/rippled/pull/6318)
|
||||
|
||||
## XRP Ledger server version 3.1.0
|
||||
|
||||
[Version 3.1.0](https://github.com/XRPLF/rippled/releases/tag/3.1.0) was released on Jan 27, 2026.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.0#0fda78daa3b864deb8a2fbc083398356%1770226294.524",
|
||||
"secp256k1/0.7.1#3a61e95e220062ef32c48d019e9c81f7%1770306721.686",
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
|
||||
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
|
||||
@@ -32,7 +32,7 @@ class Xrpl(ConanFile):
|
||||
"libarchive/3.8.1",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.5.5",
|
||||
"secp256k1/0.7.0",
|
||||
"secp256k1/0.7.1",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -111,6 +111,7 @@ JSS(accounts); // in: LedgerEntry, Subscribe,
|
||||
// handlers/Ledger, Unsubscribe
|
||||
JSS(accounts_proposed); // in: Subscribe, Unsubscribe
|
||||
JSS(action);
|
||||
JSS(active); // out: OverlayImpl
|
||||
JSS(acquiring); // out: LedgerRequest
|
||||
JSS(address); // out: PeerImp
|
||||
JSS(affected); // out: AcceptedLedgerTx
|
||||
@@ -298,6 +299,7 @@ JSS(id); // websocket.
|
||||
JSS(ident); // in: AccountCurrencies, AccountInfo,
|
||||
// OwnerInfo
|
||||
JSS(ignore_default); // in: AccountLines
|
||||
JSS(in); // out: OverlayImpl
|
||||
JSS(inLedger); // out: tx/Transaction
|
||||
JSS(inbound); // out: PeerImp
|
||||
JSS(index); // in: LedgerEntry
|
||||
@@ -459,6 +461,7 @@ JSS(open_ledger_fee); // out: TxQ
|
||||
JSS(open_ledger_level); // out: TxQ
|
||||
JSS(oracles); // in: get_aggregate_price
|
||||
JSS(oracle_document_id); // in: get_aggregate_price
|
||||
JSS(out); // out: OverlayImpl
|
||||
JSS(owner); // in: LedgerEntry, out: NetworkOPs
|
||||
JSS(owner_funds); // in/out: Ledger, NetworkOPs, AcceptedLedgerTx
|
||||
JSS(page_index);
|
||||
|
||||
@@ -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,20 +592,18 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
Number const overpaymentAmount{50};
|
||||
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset, loanScale, overpaymentAmount, TenthBips32(0), TenthBips32(0), managementFeeRate);
|
||||
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -636,20 +634,20 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
loanProperites.loanState.interestDue - newState.interestDue == 0,
|
||||
loanProperties.loanState.interestDue - newState.interestDue == 0,
|
||||
" interest change mismatch: expected 0, got " +
|
||||
to_string(loanProperites.loanState.interestDue - newState.interestDue));
|
||||
to_string(loanProperties.loanState.interestDue - newState.interestDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
}
|
||||
|
||||
@@ -672,7 +670,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -680,17 +678,15 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(10'000), // 10% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -721,21 +717,21 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
// With no Loan interest, interest outstanding should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperites.loanState.interestDue - newState.interestDue == 0,
|
||||
loanProperties.loanState.interestDue - newState.interestDue == 0,
|
||||
" interest change mismatch: expected 0, got " +
|
||||
to_string(loanProperites.loanState.interestDue - newState.interestDue));
|
||||
to_string(loanProperties.loanState.interestDue - newState.interestDue));
|
||||
|
||||
// With no Loan management fee, management fee due should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
}
|
||||
|
||||
@@ -758,7 +754,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -766,17 +762,15 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(0), // 0% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -812,22 +806,22 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperties.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(actualPaymentParts.principalPaid));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange == newState.interestDue - loanProperites.loanState.interestDue,
|
||||
actualPaymentParts.valueChange == newState.interestDue - loanProperties.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
|
||||
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
|
||||
to_string(actualPaymentParts.valueChange));
|
||||
|
||||
// With no Loan management fee, management fee due should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -849,7 +843,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -857,17 +851,15 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(0), // 0% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -904,26 +896,26 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperties.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 - loanProperites.loanState.interestDue,
|
||||
newState.interestDue - loanProperties.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(
|
||||
newState.interestDue - loanProperites.loanState.interestDue + actualPaymentParts.interestPaid) +
|
||||
newState.interestDue - loanProperties.loanState.interestDue + actualPaymentParts.interestPaid) +
|
||||
", got " + to_string(actualPaymentParts.valueChange));
|
||||
|
||||
// With no Loan management fee, management fee due should not change
|
||||
BEAST_EXPECTS(
|
||||
loanProperites.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
loanProperties.loanState.managementFeeDue - newState.managementFeeDue == 0,
|
||||
" management fee change mismatch: expected 0, got " +
|
||||
to_string(loanProperites.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
to_string(loanProperties.loanState.managementFeeDue - newState.managementFeeDue));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -947,7 +939,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -955,17 +947,15 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(0), // 0% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -1004,23 +994,23 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
// =========== VALIDATE STATE CHANGES ===========
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperties.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 - loanProperites.loanState.managementFeeDue == Number{-20592, -5}),
|
||||
(newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-20592, -5}),
|
||||
" management fee change mismatch: expected " + to_string(Number{-20592, -5}) + ", got " +
|
||||
to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
|
||||
to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
|
||||
newState.interestDue - loanProperites.loanState.interestDue,
|
||||
newState.interestDue - loanProperties.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
|
||||
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
|
||||
to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
|
||||
}
|
||||
|
||||
@@ -1043,7 +1033,7 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
std::uint32_t const paymentsRemaining = 10;
|
||||
auto const periodicRate = loanPeriodicRate(loanInterestRate, paymentInterval);
|
||||
|
||||
ExtendedPaymentComponents const overpaymentComponents = computeOverpaymentComponents(
|
||||
auto const overpaymentComponents = computeOverpaymentComponents(
|
||||
asset,
|
||||
loanScale,
|
||||
Number{50, 0},
|
||||
@@ -1051,17 +1041,15 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
TenthBips32(10'000), // 10% overpayment fee
|
||||
managementFeeRate);
|
||||
|
||||
auto const loanProperites = computeLoanProperties(
|
||||
auto const loanProperties = computeLoanProperties(
|
||||
asset, loanPrincipal, loanInterestRate, paymentInterval, paymentsRemaining, managementFeeRate, loanScale);
|
||||
|
||||
Number const periodicPayment = loanProperites.periodicPayment;
|
||||
|
||||
auto const ret = tryOverpayment(
|
||||
asset,
|
||||
loanScale,
|
||||
overpaymentComponents,
|
||||
loanProperites.loanState,
|
||||
periodicPayment,
|
||||
loanProperties.loanState,
|
||||
loanProperties.periodicPayment,
|
||||
periodicRate,
|
||||
paymentsRemaining,
|
||||
managementFeeRate,
|
||||
@@ -1101,23 +1089,23 @@ class LendingHelpers_test : public beast::unit_test::suite
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.principalPaid ==
|
||||
loanProperites.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
loanProperties.loanState.principalOutstanding - newState.principalOutstanding,
|
||||
" principalPaid mismatch: expected " +
|
||||
to_string(loanProperites.loanState.principalOutstanding - newState.principalOutstanding) + ", got " +
|
||||
to_string(loanProperties.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 - loanProperites.loanState.managementFeeDue == Number{-18304, -5}),
|
||||
(newState.managementFeeDue - loanProperties.loanState.managementFeeDue == Number{-18304, -5}),
|
||||
" management fee change mismatch: expected " + to_string(Number{-18304, -5}) + ", got " +
|
||||
to_string(newState.managementFeeDue - loanProperites.loanState.managementFeeDue));
|
||||
to_string(newState.managementFeeDue - loanProperties.loanState.managementFeeDue));
|
||||
|
||||
BEAST_EXPECTS(
|
||||
actualPaymentParts.valueChange - actualPaymentParts.interestPaid ==
|
||||
newState.interestDue - loanProperites.loanState.interestDue,
|
||||
newState.interestDue - loanProperties.loanState.interestDue,
|
||||
" valueChange mismatch: expected " +
|
||||
to_string(newState.interestDue - loanProperites.loanState.interestDue) + ", got " +
|
||||
to_string(newState.interestDue - loanProperties.loanState.interestDue) + ", got " +
|
||||
to_string(actualPaymentParts.valueChange - actualPaymentParts.interestPaid));
|
||||
}
|
||||
|
||||
|
||||
@@ -827,8 +827,13 @@ 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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -459,6 +459,10 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -630,12 +630,12 @@ OverlayImpl::getOverlayInfo()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
Json::Value jv;
|
||||
auto& av = jv["active"] = Json::Value(Json::arrayValue);
|
||||
auto& av = jv[jss::active] = Json::Value(Json::arrayValue);
|
||||
|
||||
for_each([&](std::shared_ptr<PeerImp>&& sp) {
|
||||
auto& pv = av.append(Json::Value(Json::objectValue));
|
||||
pv[jss::public_key] = base64_encode(sp->getNodePublic().data(), sp->getNodePublic().size());
|
||||
pv[jss::type] = sp->slot()->inbound() ? "in" : "out";
|
||||
pv[jss::type] = sp->slot()->inbound() ? jss::in : jss::out;
|
||||
pv[jss::uptime] = static_cast<std::uint32_t>(duration_cast<seconds>(sp->uptime()).count());
|
||||
if (sp->crawl())
|
||||
{
|
||||
@@ -647,7 +647,7 @@ OverlayImpl::getOverlayInfo()
|
||||
}
|
||||
else
|
||||
{
|
||||
pv[jss::port] = std::to_string(sp->getRemoteAddress().port());
|
||||
pv[jss::port] = sp->getRemoteAddress().port();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user