mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-06 06:55:26 +00:00
Compare commits
12 Commits
ximinez/on
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e15e332130 | ||
|
|
0a626d95f4 | ||
|
|
6006c281e2 | ||
|
|
e79673cf40 | ||
|
|
f66a60c842 | ||
|
|
af5e8ebad1 | ||
|
|
7fac09ccc3 | ||
|
|
d18a9e2e54 | ||
|
|
a7abee3c7b | ||
|
|
0affb48188 | ||
|
|
c0f1a1f094 | ||
|
|
72bf625bfe |
@@ -71,6 +71,12 @@ This release contains bug fixes only and no API changes.
|
||||
|
||||
This release contains bug fixes only and no API changes.
|
||||
|
||||
## Unreleased Changes
|
||||
|
||||
### Additions and bugfixes
|
||||
|
||||
- `submit`: Augmented response fields (`accepted`, `applied`, `broadcast`, `queued`, `kept`, `account_sequence_next`, `account_sequence_available`, `open_ledger_cost`, `validated_ledger_index`) are now included in sign-and-submit mode. Previously, these fields were only returned when submitting a binary transaction blob. ([#6304](https://github.com/XRPLF/rippled/pull/6304))
|
||||
|
||||
## XRP Ledger server version 2.5.0
|
||||
|
||||
[Version 2.5.0](https://github.com/XRPLF/rippled/releases/tag/2.5.0) was released on Jun 24, 2025.
|
||||
|
||||
@@ -1051,11 +1051,10 @@
|
||||
# The online delete process checks periodically
|
||||
# that xrpld is still in sync with the network,
|
||||
# and that the validated ledger is less than
|
||||
# 'age_threshold_seconds' old, and that all
|
||||
# recent ledgers are available. If not, then continue
|
||||
# 'age_threshold_seconds' old. If not, then continue
|
||||
# sleeping for this number of seconds and
|
||||
# checking until healthy.
|
||||
# Default is 1.
|
||||
# Default is 5.
|
||||
#
|
||||
# Notes:
|
||||
# The 'node_db' entry configures the primary, persistent storage.
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/misc/SHAMapStore.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
@@ -94,71 +93,6 @@ class LedgerMaster_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testCompleteLedgerRange(FeatureBitset features)
|
||||
{
|
||||
// Note that this test is intentionally very similar to
|
||||
// SHAMapStore_test::testLedgerGaps, but has a different
|
||||
// focus.
|
||||
|
||||
testcase("Complete Ledger operations");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
auto const deleteInterval = 8;
|
||||
|
||||
Env env{*this, envconfig([](auto cfg) { return online_delete(std::move(cfg), deleteInterval); })};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
auto& lm = env.app().getLedgerMaster();
|
||||
LedgerIndex minSeq = 2;
|
||||
LedgerIndex maxSeq = env.closed()->header().seq;
|
||||
auto& store = env.app().getSHAMapStore();
|
||||
store.rendezvous();
|
||||
LedgerIndex lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECTS(maxSeq == 3, to_string(maxSeq));
|
||||
BEAST_EXPECTS(lm.getCompleteLedgers() == "2-3", lm.getCompleteLedgers());
|
||||
BEAST_EXPECTS(lastRotated == 3, to_string(lastRotated));
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 1, maxSeq - 1) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
|
||||
|
||||
// Close enough ledgers to rotate a few times
|
||||
for (int i = 0; i < 24; ++i)
|
||||
{
|
||||
for (int t = 0; t < 3; ++t)
|
||||
{
|
||||
env(noop(alice));
|
||||
}
|
||||
env.close();
|
||||
store.rendezvous();
|
||||
|
||||
++maxSeq;
|
||||
|
||||
if (maxSeq == lastRotated + deleteInterval)
|
||||
{
|
||||
minSeq = lastRotated;
|
||||
lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECT(lastRotated = maxSeq + 2);
|
||||
}
|
||||
BEAST_EXPECTS(env.closed()->header().seq == maxSeq, to_string(env.closed()->header().seq));
|
||||
BEAST_EXPECTS(store.getLastRotated() == lastRotated, to_string(store.getLastRotated()));
|
||||
std::stringstream expectedRange;
|
||||
expectedRange << minSeq << "-" << maxSeq;
|
||||
BEAST_EXPECTS(lm.getCompleteLedgers() == expectedRange.str(), lm.getCompleteLedgers());
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 1, maxSeq - 1) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -172,7 +106,6 @@ public:
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
testTxnIdFromIndex(features);
|
||||
testCompleteLedgerRange(features);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/envconfig.h>
|
||||
|
||||
#include <xrpld/app/ledger/LedgerMaster.h>
|
||||
#include <xrpld/app/main/Application.h>
|
||||
#include <xrpld/app/main/NodeStoreScheduler.h>
|
||||
#include <xrpld/app/misc/SHAMapStore.h>
|
||||
@@ -11,8 +10,6 @@
|
||||
#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
|
||||
@@ -23,8 +20,10 @@ class SHAMapStore_test : public beast::unit_test::suite
|
||||
static auto
|
||||
onlineDelete(std::unique_ptr<Config> cfg)
|
||||
{
|
||||
using namespace jtx;
|
||||
return online_delete(std::move(cfg), deleteInterval);
|
||||
cfg->LEDGER_HISTORY = deleteInterval;
|
||||
auto& section = cfg->section(ConfigSection::nodeDatabase());
|
||||
section.set("online_delete", std::to_string(deleteInterval));
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static auto
|
||||
@@ -205,10 +204,9 @@ public:
|
||||
|
||||
store.rendezvous();
|
||||
|
||||
BEAST_EXPECT(env.closed()->info().seq == deleteInterval + 3);
|
||||
BEAST_EXPECT(store.getLastRotated() == deleteInterval + 3);
|
||||
lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECT(lastRotated == deleteInterval + 5);
|
||||
BEAST_EXPECT(lastRotated == 13);
|
||||
BEAST_EXPECT(lastRotated == 11);
|
||||
|
||||
// That took care of the fake hashes
|
||||
ledgerCheck(env, deleteInterval + 1, 3);
|
||||
@@ -216,10 +214,8 @@ public:
|
||||
accountTransactionCheck(env, 2 * deleteInterval);
|
||||
|
||||
// The last iteration of this loop should trigger a rotate
|
||||
for (auto i = lastRotated - 3; i < lastRotated + deleteInterval - 1; ++i)
|
||||
for (auto i = lastRotated - 1; i < lastRotated + deleteInterval - 1; ++i)
|
||||
{
|
||||
BEAST_EXPECT(store.getLastRotated() == lastRotated);
|
||||
|
||||
env.close();
|
||||
|
||||
ledgerTmp = env.rpc("ledger", "current");
|
||||
@@ -232,7 +228,7 @@ public:
|
||||
|
||||
store.rendezvous();
|
||||
|
||||
BEAST_EXPECT(store.getLastRotated() == deleteInterval + lastRotated + 2);
|
||||
BEAST_EXPECT(store.getLastRotated() == deleteInterval + lastRotated);
|
||||
|
||||
ledgerCheck(env, deleteInterval + 1, lastRotated);
|
||||
transactionCheck(env, 0);
|
||||
@@ -365,8 +361,8 @@ public:
|
||||
|
||||
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
|
||||
|
||||
lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECT(lastRotated == ledgerSeq + 1);
|
||||
BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
|
||||
lastRotated = ledgerSeq - 1;
|
||||
|
||||
for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
|
||||
{
|
||||
@@ -391,10 +387,10 @@ public:
|
||||
|
||||
store.rendezvous();
|
||||
|
||||
ledgerCheck(env, ledgerSeq - firstBatch - 2, firstBatch + 2);
|
||||
ledgerCheck(env, ledgerSeq - firstBatch, firstBatch);
|
||||
|
||||
lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECT(lastRotated == ledgerSeq + 1);
|
||||
BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
|
||||
lastRotated = ledgerSeq - 1;
|
||||
|
||||
// This does not kick off a cleanup
|
||||
canDelete = env.rpc("can_delete", "always");
|
||||
@@ -426,13 +422,13 @@ public:
|
||||
|
||||
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
|
||||
|
||||
lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECT(lastRotated == ledgerSeq + 1);
|
||||
BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
|
||||
lastRotated = ledgerSeq - 1;
|
||||
|
||||
// This does not kick off a cleanup
|
||||
canDelete = env.rpc("can_delete", "now");
|
||||
BEAST_EXPECT(!RPC::contains_error(canDelete[jss::result]));
|
||||
BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == lastRotated);
|
||||
BEAST_EXPECT(canDelete[jss::result][jss::can_delete] == ledgerSeq - 1);
|
||||
|
||||
for (; ledgerSeq < lastRotated + deleteInterval; ++ledgerSeq)
|
||||
{
|
||||
@@ -459,8 +455,8 @@ public:
|
||||
|
||||
ledgerCheck(env, ledgerSeq - lastRotated, lastRotated);
|
||||
|
||||
lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECT(lastRotated == ledgerSeq + 1);
|
||||
BEAST_EXPECT(store.getLastRotated() == ledgerSeq - 1);
|
||||
lastRotated = ledgerSeq - 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Backend>
|
||||
@@ -571,176 +567,6 @@ public:
|
||||
BEAST_EXPECT(dbr->getName() == "3");
|
||||
}
|
||||
|
||||
void
|
||||
testLedgerGaps()
|
||||
{
|
||||
// Note that this test is intentionally very similar to
|
||||
// LedgerMaster_test::testCompleteLedgerRange, but has a different
|
||||
// focus.
|
||||
|
||||
testcase("Wait for ledger gaps to fill in");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this, envconfig(onlineDelete)};
|
||||
|
||||
auto& ns = env.app().getNodeStore();
|
||||
std::map<LedgerIndex, uint256> hashes;
|
||||
auto storeHash = [&]() {
|
||||
hashes.emplace(env.current()->info().seq, env.current()->info().hash);
|
||||
|
||||
auto const& root = ns.fetchNodeObject(hashes[env.current()->info().seq]);
|
||||
BEAST_EXPECT(root);
|
||||
};
|
||||
|
||||
auto failureMessage = [&](char const* label, auto expected, auto actual) {
|
||||
std::stringstream ss;
|
||||
ss << label << ": Expected: " << expected << ", Got: " << actual;
|
||||
return ss.str();
|
||||
};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
storeHash();
|
||||
|
||||
auto& lm = env.app().getLedgerMaster();
|
||||
LedgerIndex minSeq = 2;
|
||||
LedgerIndex maxSeq = env.closed()->header().seq;
|
||||
auto& store = env.app().getSHAMapStore();
|
||||
store.rendezvous();
|
||||
LedgerIndex lastRotated = store.getLastRotated();
|
||||
BEAST_EXPECTS(maxSeq == 3, to_string(maxSeq));
|
||||
BEAST_EXPECTS(lm.getCompleteLedgers() == "2-3", lm.getCompleteLedgers());
|
||||
BEAST_EXPECTS(lastRotated == 3, to_string(lastRotated));
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 1, maxSeq - 1) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
|
||||
|
||||
// Close enough ledgers to rotate a few times
|
||||
while (maxSeq < 28)
|
||||
{
|
||||
for (int t = 0; t < 3; ++t)
|
||||
{
|
||||
env(noop(alice));
|
||||
}
|
||||
env.close();
|
||||
storeHash();
|
||||
store.rendezvous();
|
||||
|
||||
++maxSeq;
|
||||
|
||||
if (maxSeq + 1 == lastRotated + deleteInterval)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// The next ledger will trigger a rotation. Delete the
|
||||
// current ledger from LedgerMaster.
|
||||
std::this_thread::sleep_for(100ms);
|
||||
LedgerIndex const deleteSeq = maxSeq;
|
||||
while (!lm.haveLedger(deleteSeq))
|
||||
{
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
lm.clearLedger(deleteSeq);
|
||||
|
||||
auto expectedRange = [](auto minSeq, auto deleteSeq, auto maxSeq) {
|
||||
std::stringstream expectedRange;
|
||||
expectedRange << minSeq << "-" << (deleteSeq - 1);
|
||||
if (deleteSeq + 1 == maxSeq)
|
||||
expectedRange << "," << maxSeq;
|
||||
else if (deleteSeq < maxSeq)
|
||||
expectedRange << "," << (deleteSeq + 1) << "-" << maxSeq;
|
||||
return expectedRange.str();
|
||||
};
|
||||
BEAST_EXPECTS(
|
||||
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeq, maxSeq),
|
||||
failureMessage(
|
||||
"Complete ledgers", expectedRange(minSeq, deleteSeq, maxSeq), lm.getCompleteLedgers()));
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 1);
|
||||
|
||||
// Close another ledger, which will trigger a rotation, but the
|
||||
// rotation will be stuck until the missing ledger is filled in.
|
||||
env.close();
|
||||
storeHash();
|
||||
// DO NOT CALL rendezvous()! You'll end up with a deadlock.
|
||||
++maxSeq;
|
||||
|
||||
// Nothing has changed
|
||||
BEAST_EXPECTS(
|
||||
store.getLastRotated() == lastRotated,
|
||||
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
|
||||
BEAST_EXPECTS(
|
||||
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeq, maxSeq),
|
||||
failureMessage(
|
||||
"Complete ledgers", expectedRange(minSeq, deleteSeq, maxSeq), lm.getCompleteLedgers()));
|
||||
|
||||
// Close 5 more ledgers, waiting one second in between to
|
||||
// simulate the ledger making progress while online delete waits
|
||||
// for the missing ledger to be filled in.
|
||||
// This ensures the healthWait check has time to run and
|
||||
// detect the gap.
|
||||
for (int l = 0; l < 5; ++l)
|
||||
{
|
||||
env.close();
|
||||
storeHash();
|
||||
// DO NOT CALL rendezvous()! You'll end up with a deadlock.
|
||||
++maxSeq;
|
||||
// Nothing has changed
|
||||
BEAST_EXPECTS(
|
||||
store.getLastRotated() == lastRotated,
|
||||
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
|
||||
BEAST_EXPECTS(
|
||||
lm.getCompleteLedgers() == expectedRange(minSeq, deleteSeq, maxSeq),
|
||||
failureMessage(
|
||||
"Complete Ledgers", expectedRange(minSeq, deleteSeq, maxSeq), lm.getCompleteLedgers()));
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
|
||||
// Put the missing ledger back in LedgerMaster
|
||||
lm.setLedgerRangePresent(deleteSeq, deleteSeq);
|
||||
|
||||
// Wait for the rotation to finish
|
||||
store.rendezvous();
|
||||
|
||||
minSeq = lastRotated;
|
||||
lastRotated = maxSeq + 2;
|
||||
|
||||
// Bypass caching and try to load the ledger roots from the node
|
||||
// store
|
||||
for (auto const& [seq, hash] : hashes)
|
||||
{
|
||||
auto const nodeObject = ns.fetchNodeObject(hash);
|
||||
std::stringstream ss;
|
||||
ss << "minSeq: " << minSeq << ", maxSeq: " << maxSeq << ", search: " << seq << ". Should "
|
||||
<< (seq < minSeq ? "NOT " : "") << "be found";
|
||||
if (seq < minSeq)
|
||||
BEAST_EXPECTS(!nodeObject, ss.str());
|
||||
else
|
||||
BEAST_EXPECTS(nodeObject, ss.str());
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(maxSeq != lastRotated + deleteInterval);
|
||||
BEAST_EXPECTS(
|
||||
env.closed()->header().seq == maxSeq, failureMessage("maxSeq", maxSeq, env.closed()->header().seq));
|
||||
BEAST_EXPECTS(
|
||||
store.getLastRotated() == lastRotated,
|
||||
failureMessage("lastRotated", lastRotated, store.getLastRotated()));
|
||||
std::stringstream expectedRange;
|
||||
expectedRange << minSeq << "-" << maxSeq;
|
||||
BEAST_EXPECTS(
|
||||
lm.getCompleteLedgers() == expectedRange.str(),
|
||||
failureMessage("CompleteLedgers", expectedRange.str(), lm.getCompleteLedgers()));
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq, maxSeq) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 1, maxSeq - 1) == 0);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 1, maxSeq + 1) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq - 2, maxSeq - 2) == 2);
|
||||
BEAST_EXPECT(lm.missingFromCompleteLedgerRange(minSeq + 2, maxSeq + 2) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
@@ -748,7 +574,6 @@ public:
|
||||
testAutomatic();
|
||||
testCanDelete();
|
||||
testRotate();
|
||||
testLedgerGaps();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -57,17 +57,6 @@ envconfig(F&& modfunc, Args&&... args)
|
||||
return modfunc(envconfig(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// @brief adjust config to enable online_delete
|
||||
///
|
||||
/// @param cfg config instance to be modified
|
||||
///
|
||||
/// @param deleteInterval how many new ledgers should be available before
|
||||
/// rotating. Defaults to 8, because the standalone minimum is 8.
|
||||
///
|
||||
/// @return unique_ptr to Config instance
|
||||
std::unique_ptr<Config>
|
||||
online_delete(std::unique_ptr<Config> cfg, std::uint32_t deleteInterval = 8);
|
||||
|
||||
/// @brief adjust config so no admin ports are enabled
|
||||
///
|
||||
/// this is intended for use with envconfig, as in
|
||||
|
||||
@@ -53,15 +53,6 @@ setupConfigForUnitTests(Config& cfg)
|
||||
|
||||
namespace jtx {
|
||||
|
||||
std::unique_ptr<Config>
|
||||
online_delete(std::unique_ptr<Config> cfg, std::uint32_t deleteInterval)
|
||||
{
|
||||
cfg->LEDGER_HISTORY = deleteInterval;
|
||||
auto& section = cfg->section(ConfigSection::nodeDatabase());
|
||||
section.set("online_delete", std::to_string(deleteInterval));
|
||||
return cfg;
|
||||
}
|
||||
|
||||
std::unique_ptr<Config>
|
||||
no_admin(std::unique_ptr<Config> cfg)
|
||||
{
|
||||
|
||||
96
src/test/rpc/Submit_test.cpp
Normal file
96
src/test/rpc/Submit_test.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include <test/jtx.h>
|
||||
|
||||
#include <xrpld/core/ConfigSections.h>
|
||||
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Submit_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testAugmentedFields()
|
||||
{
|
||||
testcase("Augmented fields in sign-and-submit mode");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
// Enable signing support in config
|
||||
Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
|
||||
cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue");
|
||||
return cfg;
|
||||
})};
|
||||
|
||||
Account const alice{"alice"};
|
||||
Account const bob{"bob"};
|
||||
|
||||
env.fund(XRP(10000), alice, bob);
|
||||
env.close();
|
||||
|
||||
// Test 1: Sign-and-submit mode should return augmented fields
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::tx_json][jss::TransactionType] = jss::Payment;
|
||||
jv[jss::tx_json][jss::Account] = alice.human();
|
||||
jv[jss::tx_json][jss::Destination] = bob.human();
|
||||
jv[jss::tx_json][jss::Amount] = XRP(100).value().getJson(JsonOptions::none);
|
||||
jv[jss::secret] = alice.name();
|
||||
|
||||
auto const result = env.rpc("json", "submit", to_string(jv))[jss::result];
|
||||
|
||||
// These are the augmented fields that should be present
|
||||
BEAST_EXPECT(result.isMember(jss::engine_result));
|
||||
BEAST_EXPECT(result.isMember(jss::engine_result_code));
|
||||
BEAST_EXPECT(result.isMember(jss::engine_result_message));
|
||||
|
||||
// New augmented fields from issue #3125
|
||||
BEAST_EXPECT(result.isMember(jss::accepted));
|
||||
BEAST_EXPECT(result.isMember(jss::applied));
|
||||
BEAST_EXPECT(result.isMember(jss::broadcast));
|
||||
BEAST_EXPECT(result.isMember(jss::queued));
|
||||
BEAST_EXPECT(result.isMember(jss::kept));
|
||||
|
||||
// Current ledger state fields
|
||||
BEAST_EXPECT(result.isMember(jss::account_sequence_next));
|
||||
BEAST_EXPECT(result.isMember(jss::account_sequence_available));
|
||||
BEAST_EXPECT(result.isMember(jss::open_ledger_cost));
|
||||
BEAST_EXPECT(result.isMember(jss::validated_ledger_index));
|
||||
|
||||
// Verify basic transaction fields
|
||||
BEAST_EXPECT(result.isMember(jss::tx_blob));
|
||||
BEAST_EXPECT(result.isMember(jss::tx_json));
|
||||
}
|
||||
|
||||
// Test 2: Binary blob mode should also return augmented fields (regression test)
|
||||
{
|
||||
auto jt = env.jt(pay(alice, bob, XRP(100)));
|
||||
Serializer s;
|
||||
jt.stx->add(s);
|
||||
|
||||
auto const result = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||
|
||||
// Verify augmented fields are present in binary mode too
|
||||
BEAST_EXPECT(result.isMember(jss::engine_result));
|
||||
BEAST_EXPECT(result.isMember(jss::accepted));
|
||||
BEAST_EXPECT(result.isMember(jss::applied));
|
||||
BEAST_EXPECT(result.isMember(jss::broadcast));
|
||||
BEAST_EXPECT(result.isMember(jss::queued));
|
||||
BEAST_EXPECT(result.isMember(jss::kept));
|
||||
BEAST_EXPECT(result.isMember(jss::account_sequence_next));
|
||||
BEAST_EXPECT(result.isMember(jss::account_sequence_available));
|
||||
BEAST_EXPECT(result.isMember(jss::open_ledger_cost));
|
||||
BEAST_EXPECT(result.isMember(jss::validated_ledger_index));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testAugmentedFields();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Submit, rpc, xrpl);
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -104,10 +104,7 @@ public:
|
||||
failedSave(std::uint32_t seq, uint256 const& hash);
|
||||
|
||||
std::string
|
||||
getCompleteLedgers() const;
|
||||
|
||||
std::size_t
|
||||
missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const;
|
||||
getCompleteLedgers();
|
||||
|
||||
/** Apply held transactions to the open ledger
|
||||
This is normally called as we close the ledger.
|
||||
@@ -321,7 +318,7 @@ private:
|
||||
// A set of transactions to replay during the next close
|
||||
std::unique_ptr<LedgerReplay> replayData;
|
||||
|
||||
std::recursive_mutex mutable mCompleteLock;
|
||||
std::recursive_mutex mCompleteLock;
|
||||
RangeSet<std::uint32_t> mCompleteLedgers;
|
||||
|
||||
// Publish thread is running.
|
||||
|
||||
@@ -1475,34 +1475,12 @@ LedgerMaster::getPublishedLedger()
|
||||
}
|
||||
|
||||
std::string
|
||||
LedgerMaster::getCompleteLedgers() const
|
||||
LedgerMaster::getCompleteLedgers()
|
||||
{
|
||||
std::lock_guard sl(mCompleteLock);
|
||||
return to_string(mCompleteLedgers);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LedgerMaster::missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const
|
||||
{
|
||||
// Make a copy of the range to avoid holding the lock
|
||||
auto const range = [&] {
|
||||
std::lock_guard sl(mCompleteLock);
|
||||
return mCompleteLedgers;
|
||||
}();
|
||||
|
||||
std::size_t missing = 0;
|
||||
|
||||
for (LedgerIndex idx = first; idx <= last; ++idx)
|
||||
{
|
||||
if (!boost::icl::contains(range, idx))
|
||||
{
|
||||
++missing;
|
||||
}
|
||||
}
|
||||
|
||||
return missing;
|
||||
}
|
||||
|
||||
std::optional<NetClock::time_point>
|
||||
LedgerMaster::getCloseTimeBySeq(LedgerIndex ledgerIndex)
|
||||
{
|
||||
|
||||
@@ -264,25 +264,13 @@ SHAMapStoreImp::run()
|
||||
bool const readyToRotate =
|
||||
validatedSeq >= lastRotated + deleteInterval_ && canDelete_ >= lastRotated - 1 && healthWait() == keepGoing;
|
||||
|
||||
JLOG(journal_.debug()) << "run: Setting lastGoodValidatedLedger_ to " << validatedSeq;
|
||||
|
||||
{
|
||||
// Note that this is set after the healthWait() check, so that we
|
||||
// don't start the rotation until the validated ledger is fully
|
||||
// processed. It is not guaranteed to be done at this point. It also
|
||||
// allows the testLedgerGaps unit test to work.
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
lastGoodValidatedLedger_ = validatedSeq;
|
||||
}
|
||||
|
||||
// will delete up to (not including) lastRotated
|
||||
if (readyToRotate)
|
||||
{
|
||||
JLOG(journal_.warn()) << "rotating validatedSeq " << validatedSeq << " lastRotated " << lastRotated
|
||||
<< " deleteInterval " << deleteInterval_ << " canDelete_ " << canDelete_ << " state "
|
||||
<< app_.getOPs().strOperatingMode(false) << " age "
|
||||
<< ledgerMaster_->getValidatedLedgerAge().count()
|
||||
<< "s. Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
|
||||
<< ledgerMaster_->getValidatedLedgerAge().count() << 's';
|
||||
|
||||
clearPrior(lastRotated);
|
||||
if (healthWait() == stopping)
|
||||
@@ -322,13 +310,7 @@ SHAMapStoreImp::run()
|
||||
if (healthWait() == stopping)
|
||||
return;
|
||||
|
||||
// The rotation process takes time, and more ledgers - possibly many
|
||||
// more - may have been written to the now-archive database before
|
||||
// the writable database was created. Update the validatedSeq to the
|
||||
// current validated ledger sequence plus a buffer to ensure that
|
||||
// all modified nodes for that ledger forward are all written to the
|
||||
// new writable database.
|
||||
lastRotated = ledgerMaster_->getValidLedgerIndex() + 2;
|
||||
lastRotated = validatedSeq;
|
||||
|
||||
dbRotating_->rotate(
|
||||
std::move(newBackend), [&](std::string const& writableName, std::string const& archiveName) {
|
||||
@@ -341,9 +323,7 @@ SHAMapStoreImp::run()
|
||||
clearCaches(validatedSeq);
|
||||
});
|
||||
|
||||
JLOG(journal_.warn()) << "finished rotation. validatedSeq: " << validatedSeq
|
||||
<< ", lastRotated: " << lastRotated
|
||||
<< ". Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
|
||||
JLOG(journal_.warn()) << "finished rotation " << validatedSeq;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -570,37 +550,21 @@ SHAMapStoreImp::clearPrior(LedgerIndex lastRotated)
|
||||
SHAMapStoreImp::HealthResult
|
||||
SHAMapStoreImp::healthWait()
|
||||
{
|
||||
auto index = ledgerMaster_->getValidLedgerIndex();
|
||||
auto age = ledgerMaster_->getValidatedLedgerAge();
|
||||
OperatingMode mode = netOPs_->getOperatingMode();
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
auto numMissing = ledgerMaster_->missingFromCompleteLedgerRange(lastGoodValidatedLedger_, index);
|
||||
while (!stop_ && (mode != OperatingMode::FULL || age > ageThreshold_ || numMissing > 0))
|
||||
while (!stop_ && (mode != OperatingMode::FULL || age > ageThreshold_))
|
||||
{
|
||||
// this value shouldn't change, so grab it while we have the
|
||||
// lock
|
||||
auto const lastGood = lastGoodValidatedLedger_;
|
||||
|
||||
lock.unlock();
|
||||
|
||||
auto const stream = mode != OperatingMode::FULL || age > ageThreshold_ ? journal_.warn() : journal_.info();
|
||||
JLOG(stream) << "Waiting " << recoveryWaitTime_.count()
|
||||
<< "s for node to stabilize. state: " << app_.getOPs().strOperatingMode(mode, false) << ". age "
|
||||
<< age.count() << "s. Missing ledgers: " << numMissing << ". Expect: " << lastGood << "-" << index
|
||||
<< ". Complete ledgers: " << ledgerMaster_->getCompleteLedgers();
|
||||
JLOG(journal_.warn()) << "Waiting " << recoveryWaitTime_.count()
|
||||
<< "s for node to stabilize. state: " << app_.getOPs().strOperatingMode(mode, false)
|
||||
<< ". age " << age.count() << 's';
|
||||
std::this_thread::sleep_for(recoveryWaitTime_);
|
||||
index = ledgerMaster_->getValidLedgerIndex();
|
||||
age = ledgerMaster_->getValidatedLedgerAge();
|
||||
mode = netOPs_->getOperatingMode();
|
||||
numMissing = ledgerMaster_->missingFromCompleteLedgerRange(lastGood, index);
|
||||
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
JLOG(journal_.debug()) << "healthWait: Setting lastGoodValidatedLedger_ to " << index;
|
||||
lastGoodValidatedLedger_ = index;
|
||||
|
||||
return stop_ ? stopping : keepGoing;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,11 +70,6 @@ private:
|
||||
std::thread thread_;
|
||||
bool stop_ = false;
|
||||
bool healthy_ = true;
|
||||
// Used to prevent ledger gaps from forming during online deletion. Keeps
|
||||
// track of the last validated ledger that was processed without gaps. There
|
||||
// are no guarantees about gaps while online delete is not running. For
|
||||
// that, use advisory_delete and check for gaps externally.
|
||||
LedgerIndex lastGoodValidatedLedger_ = 0;
|
||||
mutable std::condition_variable cond_;
|
||||
mutable std::condition_variable rendezvous_;
|
||||
mutable std::mutex mutex_;
|
||||
@@ -88,11 +83,11 @@ private:
|
||||
std::uint32_t deleteBatch_ = 100;
|
||||
std::chrono::milliseconds backOff_{100};
|
||||
std::chrono::seconds ageThreshold_{60};
|
||||
/// If the node is out of sync, or any recent ledgers are not
|
||||
/// available during an online_delete healthWait() call, sleep
|
||||
/// the thread for this time, and continue checking until recovery.
|
||||
/// If the node is out of sync during an online_delete healthWait()
|
||||
/// call, sleep the thread for this time, and continue checking until
|
||||
/// recovery.
|
||||
/// See also: "recovery_wait_seconds" in xrpld-example.cfg
|
||||
std::chrono::seconds recoveryWaitTime_{1};
|
||||
std::chrono::seconds recoveryWaitTime_{5};
|
||||
|
||||
// these do not exist upon SHAMapStore creation, but do exist
|
||||
// as of run() or before
|
||||
@@ -211,8 +206,6 @@ private:
|
||||
enum HealthResult { stopping, keepGoing };
|
||||
[[nodiscard]] HealthResult
|
||||
healthWait();
|
||||
bool
|
||||
hasCompleteRange(LedgerIndex first, LedgerIndex last);
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -699,6 +699,8 @@ transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion)
|
||||
jvResult[jss::engine_result] = sToken;
|
||||
jvResult[jss::engine_result_code] = tpTrans->getResult();
|
||||
jvResult[jss::engine_result_message] = sHuman;
|
||||
|
||||
RPC::populateAugmentedSubmitFields(jvResult, tpTrans);
|
||||
}
|
||||
}
|
||||
catch (std::exception&)
|
||||
@@ -712,6 +714,28 @@ transactionFormatResultImpl(Transaction::pointer tpTrans, unsigned apiVersion)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
populateAugmentedSubmitFields(Json::Value& jvResult, std::shared_ptr<Transaction> const& transaction)
|
||||
{
|
||||
auto const submitResult = transaction->getSubmitResult();
|
||||
|
||||
jvResult[jss::accepted] = submitResult.any();
|
||||
jvResult[jss::applied] = submitResult.applied;
|
||||
jvResult[jss::broadcast] = submitResult.broadcast;
|
||||
jvResult[jss::queued] = submitResult.queued;
|
||||
jvResult[jss::kept] = submitResult.kept;
|
||||
|
||||
if (auto currentLedgerState = transaction->getCurrentLedgerState())
|
||||
{
|
||||
jvResult[jss::account_sequence_next] = safe_cast<Json::Value::UInt>(currentLedgerState->accountSeqNext);
|
||||
jvResult[jss::account_sequence_available] = safe_cast<Json::Value::UInt>(currentLedgerState->accountSeqAvail);
|
||||
jvResult[jss::open_ledger_cost] = to_string(currentLedgerState->minFeeRequired);
|
||||
jvResult[jss::validated_ledger_index] = safe_cast<Json::Value::UInt>(currentLedgerState->validatedLedger);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
[[nodiscard]] static XRPAmount
|
||||
getTxFee(Application const& app, Config const& config, Json::Value tx)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,18 @@ class TxQ;
|
||||
|
||||
namespace RPC {
|
||||
|
||||
/** Populate augmented submit fields into a JSON result.
|
||||
This helper populates the submit result flags (accepted, applied,
|
||||
broadcast, queued, kept) and current ledger state fields
|
||||
(account_sequence_next, account_sequence_available, open_ledger_cost,
|
||||
validated_ledger_index) from a Transaction pointer.
|
||||
|
||||
@param jvResult The JSON result to populate
|
||||
@param transaction The transaction containing the submit result and state
|
||||
*/
|
||||
void
|
||||
populateAugmentedSubmitFields(Json::Value& jvResult, std::shared_ptr<Transaction> const& transaction);
|
||||
|
||||
Json::Value
|
||||
getCurrentNetworkFee(
|
||||
Role const role,
|
||||
|
||||
@@ -127,23 +127,7 @@ doSubmit(RPC::JsonContext& context)
|
||||
jvResult[jss::engine_result_code] = transaction->getResult();
|
||||
jvResult[jss::engine_result_message] = sHuman;
|
||||
|
||||
auto const submitResult = transaction->getSubmitResult();
|
||||
|
||||
jvResult[jss::accepted] = submitResult.any();
|
||||
jvResult[jss::applied] = submitResult.applied;
|
||||
jvResult[jss::broadcast] = submitResult.broadcast;
|
||||
jvResult[jss::queued] = submitResult.queued;
|
||||
jvResult[jss::kept] = submitResult.kept;
|
||||
|
||||
if (auto currentLedgerState = transaction->getCurrentLedgerState())
|
||||
{
|
||||
jvResult[jss::account_sequence_next] = safe_cast<Json::Value::UInt>(currentLedgerState->accountSeqNext);
|
||||
jvResult[jss::account_sequence_available] =
|
||||
safe_cast<Json::Value::UInt>(currentLedgerState->accountSeqAvail);
|
||||
jvResult[jss::open_ledger_cost] = to_string(currentLedgerState->minFeeRequired);
|
||||
jvResult[jss::validated_ledger_index] =
|
||||
safe_cast<Json::Value::UInt>(currentLedgerState->validatedLedger);
|
||||
}
|
||||
RPC::populateAugmentedSubmitFields(jvResult, transaction);
|
||||
}
|
||||
|
||||
return jvResult;
|
||||
|
||||
Reference in New Issue
Block a user