test: improve export rng coverage

This commit is contained in:
Nicholas Dudfield
2026-06-09 19:56:46 +07:00
parent 16a72172b4
commit dd21024c0e
14 changed files with 2116 additions and 92 deletions

View File

@@ -23,6 +23,7 @@
#include <test/jtx/import.h>
#include <test/jtx/xpop.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/RuntimeConfig.h>
#include <xrpld/app/tx/detail/ExportLedgerOps.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/ExportLimits.h>
@@ -51,6 +52,12 @@ struct Export_test : public beast::unit_test::suite
return cfg;
}
static void
forceNonStandalone(Application& app)
{
const_cast<Config&>(app.config()).setupControl(true, true, false);
}
// Build a minimal unsigned Payment STObject suitable for sfExportedTxn.
static STObject
buildExportedPayment(
@@ -613,6 +620,64 @@ struct Export_test : public beast::unit_test::suite
env.close();
}
void
testExportNetworkRetryWithoutQuorum(FeatureBitset features)
{
testcase("ttEXPORT network mode retries without quorum");
using namespace jtx;
Env env{*this, exportTestConfig(), features};
Account const alice{"alice"};
Account const carol{"carol"};
env.fund(XRP(10000), alice, carol);
env.close();
forceNonStandalone(env.app());
BEAST_EXPECT(!env.app().config().standalone());
ConfigVals cfg;
cfg.noExportSig = true;
env.app().getRuntimeConfig().setConfig("*", cfg);
auto const seq = env.current()->seq();
auto const ticketSeq = std::uint32_t{1};
auto const lls = seq + 5;
auto innerObj = buildExportedPayment(
alice.id(), carol.id(), seq + 1, lls, ticketSeq);
Json::Value jv;
jv[jss::TransactionType] = jss::Export;
jv[jss::Account] = alice.human();
jv[jss::LastLedgerSequence] = lls;
jv[sfExportedTxn.jsonName] = innerObj.getJson(JsonOptions::none);
env(jv, fee(XRP(1)), ter(tesSUCCESS));
BEAST_EXPECT(env.close(
env.now() + std::chrono::seconds{5}, std::chrono::milliseconds{0}));
std::optional<TER> closedResult;
for (auto const& [stx, meta] : env.closed()->txs)
{
if (!stx || stx->getTxnType() != ttEXPORT ||
stx->getAccountID(sfAccount) != alice.id())
{
continue;
}
auto const& exported =
stx->peekAtField(sfExportedTxn).downcast<STObject>();
if (exported.getFieldU32(sfTicketSequence) != ticketSeq)
continue;
if (meta)
closedResult = TER::fromInt((*meta)[sfTransactionResult]);
}
BEAST_EXPECT(!closedResult);
BEAST_EXPECT(!env.le(keylet::shadowTicket(alice.id(), ticketSeq)));
}
void
testOpenLedgerExportLimit(FeatureBitset features)
{
@@ -1060,6 +1125,7 @@ struct Export_test : public beast::unit_test::suite
// ttEXPORT transactor tests
testExportTxnOpenLedger(allWithExport);
testExportNetworkRetryWithoutQuorum(allWithExport);
testOpenLedgerExportLimit(allWithExport);
testShadowTicketLimit(allWithExport);
testShadowTicketLifecycle(allWithExport);