fix(import): allow ticket xpop network id

This commit is contained in:
Nicholas Dudfield
2026-06-26 20:48:26 +07:00
parent f3526952b6
commit a78f09a0fe
2 changed files with 18 additions and 12 deletions

View File

@@ -1248,9 +1248,11 @@ struct Export_test : public beast::unit_test::suite
Account const alice{"alice"};
Account const carol{"carol"};
std::uint32_t const xahauNetworkID = 21337;
std::uint32_t const targetNetworkID = 31337;
// ── Xahau env: export the inner tx ─────────────────────────────
Env xahau{*this, xpopCtx.makeEnvConfig(21337), features};
Env xahau{*this, xpopCtx.makeEnvConfig(xahauNetworkID), features};
xahau.fund(XRP(10000), alice, carol);
xahau.close();
@@ -1264,9 +1266,9 @@ struct Export_test : public beast::unit_test::suite
// TicketSequence (required for exports — avoids sequence jams
// if the tx bounces on XRPL).
//
// OperationLimit tells Import::preflight which network this
// tx targets (must match Xahau's NETWORK_ID).
//
// sfNetworkID targets the destination network. Ticket-path imports
// must accept it back because the shadow ticket hash binds this exact
// inner transaction; OperationLimit is only for B2M imports.
// We'll create the matching ticket on the XRPL side later.
std::uint32_t const ticketSeq = 2; // alice's first ticket on XRPL
@@ -1279,6 +1281,7 @@ struct Export_test : public beast::unit_test::suite
innerObj.setFieldU32(sfFlags, tfFullyCanonicalSig);
innerObj.setFieldU32(sfSequence, 0);
innerObj.setFieldU32(sfTicketSequence, ticketSeq);
innerObj.setFieldU32(sfNetworkID, targetNetworkID);
innerObj.setFieldU32(sfLastLedgerSequence, 100);
innerObj.setFieldAmount(sfAmount, XRPAmount{1000000});
innerObj.setFieldAmount(sfFee, XRPAmount{20});
@@ -1336,7 +1339,7 @@ struct Export_test : public beast::unit_test::suite
BEAST_EXPECT(xahau.current()->exists(stKey));
// ── XRPL env: submit the multisigned blob ──────────────────────
Env xrpl{*this};
Env xrpl{*this, xpopCtx.makeEnvConfig(targetNetworkID)};
xrpl.fund(XRP(10000), alice, carol);
xrpl.close();
@@ -1397,6 +1400,7 @@ struct Export_test : public beast::unit_test::suite
noop.setFieldU16(sfTransactionType, ttACCOUNT_SET);
noop.setFieldU32(sfFlags, tfFullyCanonicalSig);
noop.setFieldU32(sfSequence, xrpl.seq(alice));
noop.setFieldU32(sfNetworkID, targetNetworkID);
noop.setFieldAmount(sfFee, XRPAmount{20}); // (1+1)*base
noop.setFieldVL(sfSigningPubKey, Blob{});
noop.setAccountID(sfAccount, alice.id());

View File

@@ -215,8 +215,9 @@ Import::preflight(PreflightContext const& ctx)
<< " hasResult="
<< meta->isFieldPresent(sfTransactionResult);
if (stpTrans->isFieldPresent(sfTicketSequence) &&
!ctx.rules.enabled(featureExport))
bool const hasTicket = stpTrans->isFieldPresent(sfTicketSequence);
if (hasTicket && !ctx.rules.enabled(featureExport))
{
JLOG(ctx.j.warn()) << "Import: cannot use TicketSequence XPOP.";
return temMALFORMED;
@@ -269,9 +270,10 @@ Import::preflight(PreflightContext const& ctx)
return temMALFORMED;
}
// ensure inner txn is for networkid = 0 (network id must therefore be
// missing)
if (stpTrans->isFieldPresent(sfNetworkID))
// B2M imports use OperationLimit to target this network and therefore
// reject inner NetworkID. Export callbacks may carry a target NetworkID;
// the shadow ticket binds that exact inner transaction hash.
if (!hasTicket && stpTrans->isFieldPresent(sfNetworkID))
{
JLOG(ctx.j.warn()) << "Import: attempted to import xpop containing a "
"txn with a sfNetworkID field. "
@@ -283,7 +285,7 @@ Import::preflight(PreflightContext const& ctx)
// tx was destined for this network. For the export callback path
// (sfTicketSequence present), the shadow ticket already establishes
// the relationship, so OperationLimit is not required.
if (!stpTrans->isFieldPresent(sfTicketSequence))
if (!hasTicket)
{
if (!stpTrans->isFieldPresent(sfOperationLimit))
{
@@ -311,7 +313,7 @@ Import::preflight(PreflightContext const& ctx)
// export callback path and is validator-multisigned (not alice-signed).
// The shadow ticket already proves the relationship, so skip the
// signing key match check.
if (!stpTrans->isFieldPresent(sfTicketSequence))
if (!hasTicket)
{
auto outer = tx.getSigningPubKey();
auto inner = stpTrans->getSigningPubKey();