Compare commits

..

8 Commits

Author SHA1 Message Date
Mayukha Vadari
2fb832f886 Merge remote-tracking branch 'upstream/develop' into copilot/fix-peer-crawler-port-type 2026-02-05 13:33:40 -05:00
Bart
0a626d95f4 refactor: Update secp256k1 to 0.7.1 (#6331)
The latest secp256k1 release, 0.7.1, contains bug fixes that we may benefit from, see https://github.com/bitcoin-core/secp256k1/blob/master/CHANGELOG.md.
2026-02-05 16:45:57 +00:00
Niq Dudfield
6006c281e2 fix: Increment sequence when accepting new manifests (#6059)
The `ManifestCache::applyManifest` function was returning early without incrementing `seq_`. `OverlayImpl `uses this sequence to identify/invalidate a cached `TMManifests` message, which is exchanged with peers on connection. Depending on network size, startup sequencing, and topology, this can cause syncing issues. This change therefore increments `seq_` when a new manifest is accepted.
2026-02-05 10:40:27 -05:00
Vito Tumas
e79673cf40 fix typo in LendingHelpers unit-test (#6215) 2026-02-05 10:23:44 +00:00
Mayukha Vadari
1c59ac3e11 Merge remote-tracking branch 'upstream/develop' into copilot/fix-peer-crawler-port-type 2026-02-04 16:30:32 -05:00
Mayukha Vadari
aea548b2b9 fix API changelog 2026-02-03 11:36:54 -05:00
copilot-swe-agent[bot]
f4e1f71b7a Fix peer crawler port field type inconsistency
- Change outbound peer port from string to integer in getOverlayInfo()
- Add "active", "in", "out" JSS constants to jss.h
- Update API-CHANGELOG.md with bugfix note

Co-authored-by: mvadari <8029314+mvadari@users.noreply.github.com>
2026-02-03 11:21:30 -05:00
copilot-swe-agent[bot]
136ec1ca9e Initial plan 2026-02-03 11:21:30 -05:00
18 changed files with 298 additions and 128 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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",

View File

@@ -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",
]

View File

@@ -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.

View File

@@ -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_;

View File

@@ -55,6 +55,9 @@ public:
void
sync() override;
void
sweep() override;
private:
std::shared_ptr<Backend> writableBackend_;
std::shared_ptr<Backend> archiveBackend_;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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)
{

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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

View File

@@ -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";

View File

@@ -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;
}

View File

@@ -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();
}
}