mirror of
https://github.com/Xahau/xahaud.git
synced 2026-08-18 22:17:12 +00:00
test(export): pin expiry and no-veto boundaries
This commit is contained in:
@@ -70,9 +70,12 @@ async def scenario(ctx, log):
|
||||
"(need 4 for 80% quorum) -- check runtime_config no_export_sig"
|
||||
)
|
||||
|
||||
# Should be tecEXPORT_EXPIRED (LLS reached without quorum)
|
||||
# Should be tecEXPORT_EXPIRED (LLS reached without quorum). Be exact here:
|
||||
# any other non-success means the retry/expiry boundary regressed.
|
||||
if engine_result != "tecEXPORT_EXPIRED":
|
||||
log(f"WARNING: expected tecEXPORT_EXPIRED, got {engine_result}")
|
||||
raise AssertionError(
|
||||
f"Expected tecEXPORT_EXPIRED below quorum, got {engine_result}"
|
||||
)
|
||||
|
||||
log(f"Export failed as expected ({engine_result})")
|
||||
|
||||
|
||||
@@ -84,6 +84,11 @@ async def scenario(ctx, log, expect_success=True):
|
||||
raise AssertionError(
|
||||
"Export should NOT have succeeded below active-view quorum"
|
||||
)
|
||||
if engine_result != "tecEXPORT_EXPIRED":
|
||||
raise AssertionError(
|
||||
"Expected tecEXPORT_EXPIRED below active-view quorum, "
|
||||
f"got {engine_result}"
|
||||
)
|
||||
log(f"Export failed as expected ({engine_result})")
|
||||
|
||||
# No shadow ticket should exist
|
||||
|
||||
@@ -58,8 +58,13 @@ async def scenario(ctx, log):
|
||||
"Export should not succeed without a ledger-anchored UNLReport view"
|
||||
)
|
||||
|
||||
# Be exact: without a UNLReport view the export should retry until LLS and
|
||||
# expire, not fail by some unrelated terminal code.
|
||||
if engine_result != "tecEXPORT_EXPIRED":
|
||||
log(f"WARNING: expected tecEXPORT_EXPIRED, got {engine_result}")
|
||||
raise AssertionError(
|
||||
"Expected tecEXPORT_EXPIRED without UNLReport view, "
|
||||
f"got {engine_result}"
|
||||
)
|
||||
|
||||
warning_logs = ctx.assert_log(
|
||||
r"Export: retrying without ledger-anchored validator view",
|
||||
|
||||
@@ -908,6 +908,98 @@ struct Export_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(!next->read(keylet::shadowTicket(alice.id(), ticketSeq)));
|
||||
}
|
||||
|
||||
void
|
||||
testExportNetworkLastLedgerSequenceBoundary(FeatureBitset features)
|
||||
{
|
||||
testcase("ttEXPORT network mode LastLedgerSequence boundary");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
auto applyAtLastLedger = [&](bool withQuorum) {
|
||||
Env env{*this, exportTestConfig(), features};
|
||||
|
||||
Account const alice{"alice"};
|
||||
Account const carol{"carol"};
|
||||
|
||||
env.fund(XRP(10000), alice, carol);
|
||||
env.close();
|
||||
|
||||
auto const& valKeys = env.app().getValidatorKeys();
|
||||
BEAST_EXPECT(valKeys.keys);
|
||||
if (!valKeys.keys)
|
||||
return;
|
||||
|
||||
auto const& valPK = valKeys.keys->publicKey;
|
||||
auto const& valSK = valKeys.keys->secretKey;
|
||||
seedUNLReportLedger(env, {valPK});
|
||||
forceNonStandalone(env.app());
|
||||
BEAST_EXPECT(!env.app().config().standalone());
|
||||
|
||||
auto const parent = env.app().getLedgerMaster().getClosedLedger();
|
||||
auto const applySeq = parent->seq() + 1;
|
||||
auto const ticketSeq =
|
||||
withQuorum ? std::uint32_t{1} : std::uint32_t{2};
|
||||
auto innerObj = buildExportedPayment(
|
||||
alice.id(), carol.id(), applySeq, applySeq, ticketSeq);
|
||||
auto const innerTx = makeSTTx(innerObj);
|
||||
auto jt = makeExportJTx(env, alice, innerObj, applySeq);
|
||||
auto const exportTx = jt.stx;
|
||||
BEAST_EXPECT(exportTx);
|
||||
if (!exportTx)
|
||||
return;
|
||||
auto const txHash = exportTx->getTransactionID();
|
||||
|
||||
auto& ce = env.app().getConsensusExtensions();
|
||||
ce.setExportEnabledThisRound(true);
|
||||
ce.cacheUNLReport(parent);
|
||||
auto const view = ce.activeValidatorView();
|
||||
BEAST_EXPECT(view->fromUNLReport);
|
||||
ce.cacheConsensusTxSet(makeRCLTxSet(env.app(), {exportTx}));
|
||||
|
||||
if (withQuorum)
|
||||
{
|
||||
auto const sig =
|
||||
ExportResultBuilder::signExportedTxn(innerTx, valPK, valSK);
|
||||
ce.exportSigCollector().addVerifiedSignature(
|
||||
txHash, valPK, sig, applySeq);
|
||||
auto const agreedHash = ce.buildExportSigSet(applySeq);
|
||||
BEAST_EXPECT(ce.isSidecarSet(agreedHash));
|
||||
}
|
||||
|
||||
auto next = std::make_shared<Ledger>(
|
||||
*parent, env.app().timeKeeper().closeTime());
|
||||
BEAST_EXPECT(next->seq() == applySeq);
|
||||
OpenView accum(&*next);
|
||||
auto const result = ripple::apply(
|
||||
env.app(), accum, *exportTx, tapNONE, env.journal);
|
||||
|
||||
if (withQuorum)
|
||||
{
|
||||
BEAST_EXPECT(result.ter == tesSUCCESS);
|
||||
BEAST_EXPECT(result.applied);
|
||||
accum.apply(*next);
|
||||
BEAST_EXPECT(
|
||||
next->read(keylet::shadowTicket(alice.id(), ticketSeq)));
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(result.ter == tecEXPORT_EXPIRED);
|
||||
// tecEXPORT_EXPIRED is a terminal tec result: it applies to
|
||||
// consume the sequence/fee, but must not create export state.
|
||||
BEAST_EXPECT(result.applied);
|
||||
accum.apply(*next);
|
||||
BEAST_EXPECT(
|
||||
!next->read(keylet::shadowTicket(alice.id(), ticketSeq)));
|
||||
}
|
||||
};
|
||||
|
||||
// Quorum at ledger == LastLedgerSequence still succeeds; no quorum at
|
||||
// the same boundary expires cleanly instead of falling through to a
|
||||
// later tefMAX_LEDGER preclaim.
|
||||
applyAtLastLedger(true);
|
||||
applyAtLastLedger(false);
|
||||
}
|
||||
|
||||
void
|
||||
testOpenLedgerExportLimit(FeatureBitset features)
|
||||
{
|
||||
@@ -1358,6 +1450,7 @@ struct Export_test : public beast::unit_test::suite
|
||||
testExportNetworkRetryWithoutQuorum(allWithExport);
|
||||
testExportNetworkApplyUsesAgreedSidecar(allWithExport);
|
||||
testExportNetworkRetryWithoutUNLReport(allWithExport);
|
||||
testExportNetworkLastLedgerSequenceBoundary(allWithExport);
|
||||
testOpenLedgerExportLimit(allWithExport);
|
||||
testShadowTicketLimit(allWithExport);
|
||||
testShadowTicketLifecycle(allWithExport);
|
||||
|
||||
@@ -1124,7 +1124,7 @@ extensionsTick(Ext& ext, Ctx const& ctx)
|
||||
exportState = inspectExportPeers(ctx.getPosition(), true);
|
||||
}
|
||||
|
||||
//@@start export-no-veto-quorum-branch
|
||||
//@@start export-no-veto-quorum-branches
|
||||
if (exportState.conflict && quorumAligned())
|
||||
{
|
||||
// Export sidecar roots are signed through ExtendedPosition
|
||||
@@ -1142,7 +1142,6 @@ extensionsTick(Ext& ext, Ctx const& ctx)
|
||||
<< " peersSeen=" << exportState.peersSeen
|
||||
<< " txConverged=" << exportState.txConverged;
|
||||
}
|
||||
//@@end export-no-veto-quorum-branch
|
||||
else if (quorumAligned() && !exportState.fullObservation())
|
||||
{
|
||||
JLOG(ext.j_.info())
|
||||
@@ -1156,6 +1155,7 @@ extensionsTick(Ext& ext, Ctx const& ctx)
|
||||
<< " peersSeen=" << exportState.peersSeen
|
||||
<< " txConverged=" << exportState.txConverged;
|
||||
}
|
||||
//@@end export-no-veto-quorum-branches
|
||||
else if (exportState.conflict || !quorumAligned())
|
||||
{
|
||||
auto const elapsed =
|
||||
|
||||
Reference in New Issue
Block a user