Compare commits

..

2 Commits

Author SHA1 Message Date
Valentin Balaschenko
607d0f7e02 remove temp vector 2025-07-25 14:03:31 +01:00
Valentin Balaschenko
8461c7d0b6 sweep every 10 sec instead of 1 min 2025-06-18 17:00:17 +01:00
13 changed files with 46 additions and 293 deletions

View File

@@ -1,6 +1,6 @@
name: Check libXRPL compatibility with Clio name: Check libXRPL compatibility with Clio
env: env:
CONAN_URL: http://18.143.149.228:8081/artifactory/api/conan/dev CONAN_URL: http://18.143.149.228:8081/artifactory/api/conan/conan-non-prod
CONAN_LOGIN_USERNAME_RIPPLE: ${{ secrets.CONAN_USERNAME }} CONAN_LOGIN_USERNAME_RIPPLE: ${{ secrets.CONAN_USERNAME }}
CONAN_PASSWORD_RIPPLE: ${{ secrets.CONAN_TOKEN }} CONAN_PASSWORD_RIPPLE: ${{ secrets.CONAN_TOKEN }}
on: on:

View File

@@ -36,7 +36,7 @@ namespace BuildInfo {
// and follow the format described at http://semver.org/ // and follow the format described at http://semver.org/
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// clang-format off // clang-format off
char const* const versionString = "2.5.0" char const* const versionString = "2.5.0-rc2"
// clang-format on // clang-format on
#if defined(DEBUG) || defined(SANITIZER) #if defined(DEBUG) || defined(SANITIZER)

View File

@@ -760,12 +760,6 @@ isRawTransactionOkay(STObject const& st, std::string& reason)
{ {
TxType const tt = TxType const tt =
safe_cast<TxType>(raw.getFieldU16(sfTransactionType)); safe_cast<TxType>(raw.getFieldU16(sfTransactionType));
if (tt == ttBATCH)
{
reason = "Raw Transactions may not contain batch transactions.";
return false;
}
raw.applyTemplate(getTxFormat(tt)->getSOTemplate()); raw.applyTemplate(getTxFormat(tt)->getSOTemplate());
} }
catch (std::exception const& e) catch (std::exception const& e)

View File

@@ -18,10 +18,8 @@
//============================================================================== //==============================================================================
#include <test/jtx.h> #include <test/jtx.h>
#include <test/jtx/permissioned_domains.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/jss.h> #include <xrpl/protocol/jss.h>
namespace ripple { namespace ripple {
@@ -1253,48 +1251,21 @@ public:
} }
} }
void
testDeletePermissionedDomainOwner()
{
using namespace jtx;
Env env{*this};
Account alice{"alice"};
Account becky{"becky"};
env.fund(XRP(1000), alice, becky);
env.close();
env(fset(becky, asfDepositAuth));
env.close();
pdomain::Credentials const credentials1{
{.issuer = alice, .credType = "foobaz"}};
env(pdomain::setTx(alice, credentials1));
// Close 256 ledgers
for (int i = 0; i < 256; ++i)
env.close();
auto const acctDelFee{drops(env.current()->fees().increment)};
env(acctdelete(alice, becky), fee(acctDelFee), ter{tecNO_PERMISSION});
env.close();
}
void void
run() override run() override
{ {
// testBasics(); testBasics();
// testDirectories(); testDirectories();
// testOwnedTypes(); testOwnedTypes();
// testResurrection(); testResurrection();
// testAmendmentEnable(); testAmendmentEnable();
// testTooManyOffers(); testTooManyOffers();
// testImplicitlyCreatedTrustline(); testImplicitlyCreatedTrustline();
// testBalanceTooSmallForFee(); testBalanceTooSmallForFee();
// testWithTickets(); testWithTickets();
// testDest(); testDest();
// testDestinationDepositAuthCredentials(); testDestinationDepositAuthCredentials();
// testDeleteCredentialsOwner(); testDeleteCredentialsOwner();
testDeletePermissionedDomainOwner();
} }
}; };

View File

@@ -24,7 +24,6 @@
#include <xrpld/app/misc/HashRouter.h> #include <xrpld/app/misc/HashRouter.h>
#include <xrpld/app/misc/Transaction.h> #include <xrpld/app/misc/Transaction.h>
#include <xrpld/app/tx/apply.h> #include <xrpld/app/tx/apply.h>
#include <xrpld/app/tx/detail/Batch.h>
#include <xrpl/protocol/Batch.h> #include <xrpl/protocol/Batch.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
@@ -318,8 +317,7 @@ class Batch_test : public beast::unit_test::suite
env.close(); env.close();
} }
// DEFENSIVE: temINVALID: Batch: batch cannot have inner batch txn. // temINVALID: Batch: batch cannot have inner batch txn.
// ACTUAL: telENV_RPC_FAILED: isRawTransactionOkay()
{ {
auto const seq = env.seq(alice); auto const seq = env.seq(alice);
auto const batchFee = batch::calcBatchFee(env, 0, 2); auto const batchFee = batch::calcBatchFee(env, 0, 2);
@@ -327,7 +325,7 @@ class Batch_test : public beast::unit_test::suite
batch::inner( batch::inner(
batch::outer(alice, seq, batchFee, tfAllOrNothing), seq), batch::outer(alice, seq, batchFee, tfAllOrNothing), seq),
batch::inner(pay(alice, bob, XRP(1)), seq + 2), batch::inner(pay(alice, bob, XRP(1)), seq + 2),
ter(telENV_RPC_FAILED)); ter(temINVALID));
env.close(); env.close();
} }
@@ -3955,176 +3953,6 @@ class Batch_test : public beast::unit_test::suite
} }
} }
void
testValidateRPCResponse(FeatureBitset features)
{
// Verifying that the RPC response from submit includes
// the account_sequence_available, account_sequence_next,
// open_ledger_cost and validated_ledger_index fields.
testcase("Validate RPC response");
using namespace jtx;
Env env(*this);
Account const alice("alice");
Account const bob("bob");
env.fund(XRP(10000), alice, bob);
env.close();
// tes
{
auto const baseFee = env.current()->fees().base;
auto const aliceSeq = env.seq(alice);
auto jtx = env.jt(pay(alice, bob, XRP(1)));
Serializer s;
jtx.stx->add(s);
auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result];
env.close();
BEAST_EXPECT(jr.isMember(jss::account_sequence_available));
BEAST_EXPECT(
jr[jss::account_sequence_available].asUInt() == aliceSeq + 1);
BEAST_EXPECT(jr.isMember(jss::account_sequence_next));
BEAST_EXPECT(
jr[jss::account_sequence_next].asUInt() == aliceSeq + 1);
BEAST_EXPECT(jr.isMember(jss::open_ledger_cost));
BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee));
BEAST_EXPECT(jr.isMember(jss::validated_ledger_index));
}
// tec failure
{
auto const baseFee = env.current()->fees().base;
auto const aliceSeq = env.seq(alice);
env(fset(bob, asfRequireDest));
auto jtx = env.jt(pay(alice, bob, XRP(1)), seq(aliceSeq));
Serializer s;
jtx.stx->add(s);
auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result];
env.close();
BEAST_EXPECT(jr.isMember(jss::account_sequence_available));
BEAST_EXPECT(
jr[jss::account_sequence_available].asUInt() == aliceSeq + 1);
BEAST_EXPECT(jr.isMember(jss::account_sequence_next));
BEAST_EXPECT(
jr[jss::account_sequence_next].asUInt() == aliceSeq + 1);
BEAST_EXPECT(jr.isMember(jss::open_ledger_cost));
BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee));
BEAST_EXPECT(jr.isMember(jss::validated_ledger_index));
}
// tem failure
{
auto const baseFee = env.current()->fees().base;
auto const aliceSeq = env.seq(alice);
auto jtx = env.jt(pay(alice, bob, XRP(1)), seq(aliceSeq + 1));
Serializer s;
jtx.stx->add(s);
auto const jr = env.rpc("submit", strHex(s.slice()))[jss::result];
env.close();
BEAST_EXPECT(jr.isMember(jss::account_sequence_available));
BEAST_EXPECT(
jr[jss::account_sequence_available].asUInt() == aliceSeq);
BEAST_EXPECT(jr.isMember(jss::account_sequence_next));
BEAST_EXPECT(jr[jss::account_sequence_next].asUInt() == aliceSeq);
BEAST_EXPECT(jr.isMember(jss::open_ledger_cost));
BEAST_EXPECT(jr[jss::open_ledger_cost] == to_string(baseFee));
BEAST_EXPECT(jr.isMember(jss::validated_ledger_index));
}
}
void
testBatchCalculateBaseFee(FeatureBitset features)
{
using namespace jtx;
Env env(*this);
Account const alice("alice");
Account const bob("bob");
Account const carol("carol");
env.fund(XRP(10000), alice, bob, carol);
env.close();
auto getBaseFee = [&](JTx const& jtx) -> XRPAmount {
Serializer s;
jtx.stx->add(s);
return Batch::calculateBaseFee(*env.current(), *jtx.stx);
};
// bad: Inner Batch transaction found
{
auto const seq = env.seq(alice);
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
auto jtx = env.jt(
batch::outer(alice, seq, batchFee, tfAllOrNothing),
batch::inner(
batch::outer(alice, seq, batchFee, tfAllOrNothing), seq),
batch::inner(pay(alice, bob, XRP(1)), seq + 2));
XRPAmount const txBaseFee = getBaseFee(jtx);
BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP));
}
// bad: Raw Transactions array exceeds max entries.
{
auto const seq = env.seq(alice);
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
auto jtx = env.jt(
batch::outer(alice, seq, batchFee, tfAllOrNothing),
batch::inner(pay(alice, bob, XRP(1)), seq + 1),
batch::inner(pay(alice, bob, XRP(1)), seq + 2),
batch::inner(pay(alice, bob, XRP(1)), seq + 3),
batch::inner(pay(alice, bob, XRP(1)), seq + 4),
batch::inner(pay(alice, bob, XRP(1)), seq + 5),
batch::inner(pay(alice, bob, XRP(1)), seq + 6),
batch::inner(pay(alice, bob, XRP(1)), seq + 7),
batch::inner(pay(alice, bob, XRP(1)), seq + 8),
batch::inner(pay(alice, bob, XRP(1)), seq + 9));
XRPAmount const txBaseFee = getBaseFee(jtx);
BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP));
}
// bad: Signers array exceeds max entries.
{
auto const seq = env.seq(alice);
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
auto jtx = env.jt(
batch::outer(alice, seq, batchFee, tfAllOrNothing),
batch::inner(pay(alice, bob, XRP(10)), seq + 1),
batch::inner(pay(alice, bob, XRP(5)), seq + 2),
batch::sig(
bob,
carol,
alice,
bob,
carol,
alice,
bob,
carol,
alice,
alice));
XRPAmount const txBaseFee = getBaseFee(jtx);
BEAST_EXPECT(txBaseFee == XRPAmount(INITIAL_XRP));
}
// good:
{
auto const seq = env.seq(alice);
XRPAmount const batchFee = batch::calcBatchFee(env, 0, 2);
auto jtx = env.jt(
batch::outer(alice, seq, batchFee, tfAllOrNothing),
batch::inner(pay(alice, bob, XRP(1)), seq + 1),
batch::inner(pay(bob, alice, XRP(2)), seq + 2));
XRPAmount const txBaseFee = getBaseFee(jtx);
BEAST_EXPECT(txBaseFee == batchFee);
}
}
void void
testWithFeats(FeatureBitset features) testWithFeats(FeatureBitset features)
{ {
@@ -4155,8 +3983,6 @@ class Batch_test : public beast::unit_test::suite
testBatchTxQueue(features); testBatchTxQueue(features);
testBatchNetworkOps(features); testBatchNetworkOps(features);
testBatchDelegate(features); testBatchDelegate(features);
testValidateRPCResponse(features);
testBatchCalculateBaseFee(features);
} }
public: public:

View File

@@ -96,7 +96,7 @@ Env::AppBundle::~AppBundle()
if (app) if (app)
{ {
app->getJobQueue().rendezvous(); app->getJobQueue().rendezvous();
app->signalStop("~AppBundle"); app->signalStop();
} }
if (thread.joinable()) if (thread.joinable())
thread.join(); thread.join();

View File

@@ -384,17 +384,10 @@ public:
{ {
auto const start = m_clock.now(); auto const start = m_clock.now();
// Make a list of things to sweep, while holding the lock
std::vector<MapType::mapped_type> stuffToSweep;
std::size_t total;
{ {
ScopedLockType sl(mLock); ScopedLockType sl(mLock);
MapType::iterator it(mLedgers.begin()); MapType::iterator it(mLedgers.begin());
total = mLedgers.size();
stuffToSweep.reserve(total);
while (it != mLedgers.end()) while (it != mLedgers.end())
{ {
auto const la = it->second->getLastAction(); auto const la = it->second->getLastAction();
@@ -404,11 +397,8 @@ public:
it->second->touch(); it->second->touch();
++it; ++it;
} }
else if ((la + std::chrono::minutes(1)) < start) else if ((la + std::chrono::seconds(10)) < start)
{ {
stuffToSweep.push_back(it->second);
// shouldn't cause the actual final delete
// since we are holding a reference in the vector.
it = mLedgers.erase(it); it = mLedgers.erase(it);
} }
else else
@@ -419,14 +409,6 @@ public:
beast::expire(mRecentFailures, kReacquireInterval); beast::expire(mRecentFailures, kReacquireInterval);
} }
JLOG(j_.debug())
<< "Swept " << stuffToSweep.size() << " out of " << total
<< " inbound ledgers. Duration: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
m_clock.now() - start)
.count()
<< "ms";
} }
void void

View File

@@ -285,7 +285,7 @@ public:
config_->CONFIG_DIR), config_->CONFIG_DIR),
*this, *this,
logs_->journal("PerfLog"), logs_->journal("PerfLog"),
[this] { signalStop("PerfLog"); })) [this] { signalStop(); }))
, m_txMaster(*this) , m_txMaster(*this)
@@ -505,7 +505,7 @@ public:
void void
run() override; run() override;
void void
signalStop(std::string msg) override; signalStop(std::string msg = "") override;
bool bool
checkSigs() const override; checkSigs() const override;
void void
@@ -977,7 +977,7 @@ public:
if (!config_->standalone() && if (!config_->standalone() &&
!getRelationalDatabase().transactionDbHasSpace(*config_)) !getRelationalDatabase().transactionDbHasSpace(*config_))
{ {
signalStop("Out of transaction DB space"); signalStop();
} }
// VFALCO NOTE Does the order of calls matter? // VFALCO NOTE Does the order of calls matter?
@@ -1193,7 +1193,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
JLOG(m_journal.info()) << "Received signal " << signum; JLOG(m_journal.info()) << "Received signal " << signum;
if (signum == SIGTERM || signum == SIGINT) if (signum == SIGTERM || signum == SIGINT)
signalStop("Signal: " + to_string(signum)); signalStop();
}); });
auto debug_log = config_->getDebugLogFile(); auto debug_log = config_->getDebugLogFile();

View File

@@ -141,7 +141,7 @@ public:
virtual void virtual void
run() = 0; run() = 0;
virtual void virtual void
signalStop(std::string msg) = 0; signalStop(std::string msg = "") = 0;
virtual bool virtual bool
checkSigs() const = 0; checkSigs() const = 0;
virtual void virtual void

View File

@@ -1701,7 +1701,7 @@ NetworkOPsImp::apply(std::unique_lock<std::mutex>& batchLock)
} }
} }
if (validatedLedgerIndex) if (!isTemMalformed(e.result) && validatedLedgerIndex)
{ {
auto [fee, accountSeq, availableSeq] = auto [fee, accountSeq, availableSeq] =
app_.getTxQ().getTxRequiredFeeAndSeq( app_.getTxQ().getTxRequiredFeeAndSeq(

View File

@@ -61,10 +61,7 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
// LCOV_EXCL_START // LCOV_EXCL_START
if (baseFee > maxAmount - view.fees().base) if (baseFee > maxAmount - view.fees().base)
{ throw std::overflow_error("XRPAmount overflow");
JLOG(debugLog().error()) << "BatchTrace: Base fee overflow detected.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
XRPAmount const batchBase = view.fees().base + baseFee; XRPAmount const batchBase = view.fees().base + baseFee;
@@ -75,36 +72,32 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
{ {
auto const& txns = tx.getFieldArray(sfRawTransactions); auto const& txns = tx.getFieldArray(sfRawTransactions);
XRPL_ASSERT(
txns.size() <= maxBatchTxCount,
"Raw Transactions array exceeds max entries.");
// LCOV_EXCL_START // LCOV_EXCL_START
if (txns.size() > maxBatchTxCount) if (txns.size() > maxBatchTxCount)
{ throw std::length_error(
JLOG(debugLog().error()) "Raw Transactions array exceeds max entries");
<< "BatchTrace: Raw Transactions array exceeds max entries.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
for (STObject txn : txns) for (STObject txn : txns)
{ {
STTx const stx = STTx{std::move(txn)}; STTx const stx = STTx{std::move(txn)};
XRPL_ASSERT(
stx.getTxnType() != ttBATCH, "Inner Batch transaction found.");
// LCOV_EXCL_START // LCOV_EXCL_START
if (stx.getTxnType() == ttBATCH) if (stx.getTxnType() == ttBATCH)
{ throw std::invalid_argument("Inner Batch transaction found");
JLOG(debugLog().error())
<< "BatchTrace: Inner Batch transaction found.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
auto const fee = ripple::calculateBaseFee(view, stx); auto const fee = ripple::calculateBaseFee(view, stx);
// LCOV_EXCL_START // LCOV_EXCL_START
if (txnFees > maxAmount - fee) if (txnFees > maxAmount - fee)
{ throw std::overflow_error("XRPAmount overflow");
JLOG(debugLog().error())
<< "BatchTrace: XRPAmount overflow in txnFees calculation.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
txnFees += fee; txnFees += fee;
} }
@@ -115,14 +108,13 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
if (tx.isFieldPresent(sfBatchSigners)) if (tx.isFieldPresent(sfBatchSigners))
{ {
auto const& signers = tx.getFieldArray(sfBatchSigners); auto const& signers = tx.getFieldArray(sfBatchSigners);
XRPL_ASSERT(
signers.size() <= maxBatchTxCount,
"Batch Signers array exceeds max entries.");
// LCOV_EXCL_START // LCOV_EXCL_START
if (signers.size() > maxBatchTxCount) if (signers.size() > maxBatchTxCount)
{ throw std::length_error("Batch Signers array exceeds max entries");
JLOG(debugLog().error())
<< "BatchTrace: Batch Signers array exceeds max entries.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
for (STObject const& signer : signers) for (STObject const& signer : signers)
@@ -136,28 +128,16 @@ Batch::calculateBaseFee(ReadView const& view, STTx const& tx)
// LCOV_EXCL_START // LCOV_EXCL_START
if (signerCount > 0 && view.fees().base > maxAmount / signerCount) if (signerCount > 0 && view.fees().base > maxAmount / signerCount)
{ throw std::overflow_error("XRPAmount overflow");
JLOG(debugLog().error())
<< "BatchTrace: XRPAmount overflow in signerCount calculation.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
XRPAmount signerFees = signerCount * view.fees().base; XRPAmount signerFees = signerCount * view.fees().base;
// LCOV_EXCL_START // LCOV_EXCL_START
if (signerFees > maxAmount - txnFees) if (signerFees > maxAmount - txnFees)
{ throw std::overflow_error("XRPAmount overflow");
JLOG(debugLog().error())
<< "BatchTrace: XRPAmount overflow in signerFees calculation.";
return XRPAmount{INITIAL_XRP};
}
if (txnFees + signerFees > maxAmount - batchBase) if (txnFees + signerFees > maxAmount - batchBase)
{ throw std::overflow_error("XRPAmount overflow");
JLOG(debugLog().error())
<< "BatchTrace: XRPAmount overflow in total fee calculation.";
return XRPAmount{INITIAL_XRP};
}
// LCOV_EXCL_STOP // LCOV_EXCL_STOP
// 10 drops per batch signature + sum of inner tx fees + batchBase // 10 drops per batch signature + sum of inner tx fees + batchBase

View File

@@ -3440,7 +3440,7 @@ PeerImp::processLedgerRequest(std::shared_ptr<protocol::TMGetLedger> const& m)
if (!m->has_ledgerhash()) if (!m->has_ledgerhash())
info += ", no hash specified"; info += ", no hash specified";
JLOG(p_journal_.warn()) JLOG(p_journal_.error())
<< "processLedgerRequest: getNodeFat with nodeId " << "processLedgerRequest: getNodeFat with nodeId "
<< *shaMapNodeId << " and ledger info type " << info << *shaMapNodeId << " and ledger info type " << info
<< " throws exception: " << e.what(); << " throws exception: " << e.what();

View File

@@ -31,7 +31,7 @@ struct JsonContext;
Json::Value Json::Value
doStop(RPC::JsonContext& context) doStop(RPC::JsonContext& context)
{ {
context.app.signalStop("RPC"); context.app.signalStop();
return RPC::makeObjectValue(systemName() + " server stopping"); return RPC::makeObjectValue(systemName() + " server stopping");
} }