From a1478fac39084c1d1bf4e9c2113eddbb606b70f2 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 20 Aug 2026 16:28:43 +0000 Subject: [PATCH 1/7] docs: Fix yum installation baseurl (#8066) --- docs/install.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/install.md b/docs/install.md index a3e2fefa02..ee9c31868b 100644 --- a/docs/install.md +++ b/docs/install.md @@ -92,11 +92,11 @@ wherever it appears in the repository configuration. 2. Add the repository, using the channel you picked in [Release channels](#release-channels): ```bash - cat << REPOFILE | sudo tee /etc/yum.repos.d/xrplf.repo + cat << 'REPOFILE' | sudo tee /etc/yum.repos.d/xrplf.repo [xrplf-stable] name=XRP Ledger Packages enabled=1 - baseurl=https://packages.xrplf.org/repository/rpm-stable/ + baseurl=https://packages.xrplf.org/repository/rpm-stable/$basearch/ gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.xrplf.org/xrplf.asc From d0dbf9163c66288e37d1c5bc9dce313d457f732b Mon Sep 17 00:00:00 2001 From: Kassaking7 <96991820+Kassaking7@users.noreply.github.com> Date: Thu, 20 Aug 2026 19:04:04 +0000 Subject: [PATCH 2/7] fix: Prevent AMM auction slots from being acquired at zero cost when trading fee is zero (#7430) --- include/xrpl/protocol/AMMCore.h | 11 +++++ src/libxrpl/tx/transactors/dex/AMMBid.cpp | 30 +++++++------ src/test/app/AMMMPT_test.cpp | 25 +++++++---- src/test/app/AMM_test.cpp | 51 +++++++++++++++++++---- 4 files changed, 87 insertions(+), 30 deletions(-) diff --git a/include/xrpl/protocol/AMMCore.h b/include/xrpl/protocol/AMMCore.h index 1e11f6cd8b..3f6b12f460 100644 --- a/include/xrpl/protocol/AMMCore.h +++ b/include/xrpl/protocol/AMMCore.h @@ -91,6 +91,17 @@ getFee(std::uint16_t tfee) return Number{tfee} / kAuctionSlotFeeScaleFactor; } +/** + * Minimum auction slot price: LPTokens * TradingFee / kAuctionSlotMinFeeFraction + * @param lptAMMBalance AMM LP token balance + * @param tradingFee trading fee in {0, 1000} + */ +inline Number +ammAuctionMinSlotPrice(Number const& lptAMMBalance, std::uint16_t tradingFee) +{ + return lptAMMBalance * getFee(tradingFee) / kAuctionSlotMinFeeFraction; +} + /** * Get fee multiplier (1 - tfee) * @tfee trading fee in basis points diff --git a/src/libxrpl/tx/transactors/dex/AMMBid.cpp b/src/libxrpl/tx/transactors/dex/AMMBid.cpp index 3454559e82..154e64ca8e 100644 --- a/src/libxrpl/tx/transactors/dex/AMMBid.cpp +++ b/src/libxrpl/tx/transactors/dex/AMMBid.cpp @@ -193,10 +193,10 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa auto const current = duration_cast(ctx.view().header().parentCloseTime.time_since_epoch()).count(); // Auction slot discounted fee - auto const discountedFee = (*ammSle)[sfTradingFee] / kAuctionSlotDiscountedFeeFraction; - auto const tradingFee = getFee((*ammSle)[sfTradingFee]); + auto const ammTradingFee = (*ammSle)[sfTradingFee]; + auto const discountedFee = ammTradingFee / kAuctionSlotDiscountedFeeFraction; // Min price - auto const minSlotPrice = lptAMMBalance * tradingFee / kAuctionSlotMinFeeFraction; + auto const minSlotPrice = ammAuctionMinSlotPrice(lptAMMBalance, ammTradingFee); static constexpr std::uint32_t kTailingSlot = kAuctionSlotTimeIntervals - 1; @@ -260,31 +260,37 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa auto const bidMax = ctx.tx[~sfBidMax]; auto getPayPrice = [&](Number const& computedPrice) -> std::expected { + auto effectivePrice = computedPrice; + if (ctx.view().rules().enabled(fixCleanup3_4_0) && ammTradingFee == 0) + { + // Prevent zero-fee pools from granting auction slots at zero or dust prices. + effectivePrice = std::max(effectivePrice, ammAuctionMinSlotPrice(lptAMMBalance, 1)); + } auto const payPrice = [&]() -> std::optional { // Both min/max bid price are defined if (bidMin && bidMax) { - if (computedPrice <= *bidMax) - return std::max(computedPrice, Number(*bidMin)); - JLOG(ctx.journal.debug()) << "AMM Bid: not in range " << computedPrice << " " + if (effectivePrice <= *bidMax) + return std::max(effectivePrice, Number(*bidMin)); + JLOG(ctx.journal.debug()) << "AMM Bid: not in range " << effectivePrice << " " << *bidMin << " " << *bidMax; return std::nullopt; } - // Bidder pays max(bidPrice, computedPrice) + // Bidder pays max(bidPrice, effectivePrice) if (bidMin) { - return std::max(computedPrice, Number(*bidMin)); + return std::max(effectivePrice, Number(*bidMin)); } if (bidMax) { - if (computedPrice <= *bidMax) - return computedPrice; + if (effectivePrice <= *bidMax) + return effectivePrice; JLOG(ctx.journal.debug()) - << "AMM Bid: not in range " << computedPrice << " " << *bidMax; + << "AMM Bid: not in range " << effectivePrice << " " << *bidMax; return std::nullopt; } - return computedPrice; + return effectivePrice; }(); if (!payPrice) { diff --git a/src/test/app/AMMMPT_test.cpp b/src/test/app/AMMMPT_test.cpp index bfd2d529b5..ac9728ede1 100644 --- a/src/test/app/AMMMPT_test.cpp +++ b/src/test/app/AMMMPT_test.cpp @@ -3992,24 +3992,30 @@ private: [&](AMM& ammAlice, Env& env) { // Bid a tiny amount auto const tiny = Number{STAmount::kMinValue, STAmount::kMinOffset}; + auto const cleanup340 = env.current()->rules().enabled(fixCleanup3_4_0); + auto const minBidPrice = IOUAmount{ammAuctionMinSlotPrice(ammAlice.tokens(), 1)}; + auto const firstPrice = cleanup340 ? minBidPrice : IOUAmount{tiny}; env(ammAlice.bid({.account = alice_, .bidMin = IOUAmount{tiny}})); - // Auction slot purchase price is equal to the tiny amount - // since the minSlotPrice is 0 with no trading fee. - BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny})); - // The purchase price is too small to affect the total tokens + BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, firstPrice)); BEAST_EXPECT(ammAlice.expectBalances( - MPT(ammAlice[0])(10'000'000'000), USD(10'000), ammAlice.tokens())); + MPT(ammAlice[0])(10'000'000'000), + USD(10'000), + cleanup340 ? IOUAmount{Number{ammAlice.tokens()} - Number{minBidPrice}} + : ammAlice.tokens())); // Bid the tiny amount env(ammAlice.bid({ .account = alice_, .bidMin = IOUAmount{STAmount::kMinValue, STAmount::kMinOffset}, })); // Pay slightly higher price - BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}})); - // The purchase price is still too small to affect the total - // tokens + BEAST_EXPECT(ammAlice.expectAuctionSlot( + 0, 0, IOUAmount{Number{firstPrice} * Number{105, -2}})); BEAST_EXPECT(ammAlice.expectBalances( - MPT(ammAlice[0])(10'000'000'000), USD(10'000), ammAlice.tokens())); + MPT(ammAlice[0])(10'000'000'000), + USD(10'000), + cleanup340 + ? IOUAmount{Number{ammAlice.tokens()} - Number{minBidPrice} * Number{11, -1}} + : ammAlice.tokens())); }, {{gAmmmpt(10'000'000'000), USD(10'000)}}); @@ -7489,6 +7495,7 @@ private: testFeeVote(); testInvalidBid(); testBid(all); + testBid(all - fixCleanup3_4_0); testClawback(); testClawbackFromAMMAccount(all); testClawbackFromAMMAccount(all - featureSingleAssetVault); diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index e1732aaf0e..0212035c6e 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -3127,27 +3127,59 @@ private: std::nullopt, {features}); + // Zero-fee bid without an explicit price pays a floor with fixCleanup3_4_0. + testAMM( + [&](AMM& ammAlice, Env& env) { + auto const minBidPrice = IOUAmount{ammAuctionMinSlotPrice(ammAlice.tokens(), 1)}; + auto const cleanup340 = features[fixCleanup3_4_0]; + auto const expectedPrice = cleanup340 ? minBidPrice : IOUAmount{0}; + auto const expectedTokens = cleanup340 + ? IOUAmount{Number{ammAlice.tokens()} - Number{minBidPrice}} + : ammAlice.tokens(); + + env.close(seconds(kTotalTimeSlotSecs + 1)); + env.close(); + env(ammAlice.bid({.account = alice_})); + BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, expectedPrice)); + BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), expectedTokens)); + + ammAlice.vote(alice_, 1'000); + BEAST_EXPECT(ammAlice.expectAuctionSlot(100, 0, expectedPrice)); + }, + std::nullopt, + 0, + std::nullopt, + {features}); + // Bid tiny amount testAMM( [&](AMM& ammAlice, Env& env) { // Bid a tiny amount auto const tiny = Number{STAmount::kMinValue, STAmount::kMinOffset}; + auto const cleanup340 = features[fixCleanup3_4_0]; + auto const minBidPrice = IOUAmount{ammAuctionMinSlotPrice(ammAlice.tokens(), 1)}; + auto const firstPrice = cleanup340 ? minBidPrice : IOUAmount{tiny}; env(ammAlice.bid({.account = alice_, .bidMin = IOUAmount{tiny}})); - // Auction slot purchase price is equal to the tiny amount - // since the minSlotPrice is 0 with no trading fee. - BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny})); - // The purchase price is too small to affect the total tokens - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens())); + BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, firstPrice)); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10'000), + USD(10'000), + cleanup340 ? IOUAmount{Number{ammAlice.tokens()} - Number{minBidPrice}} + : ammAlice.tokens())); // Bid the tiny amount env(ammAlice.bid({ .account = alice_, .bidMin = IOUAmount{STAmount::kMinValue, STAmount::kMinOffset}, })); // Pay slightly higher price - BEAST_EXPECT(ammAlice.expectAuctionSlot(0, 0, IOUAmount{tiny * Number{105, -2}})); - // The purchase price is still too small to affect the total - // tokens - BEAST_EXPECT(ammAlice.expectBalances(XRP(10'000), USD(10'000), ammAlice.tokens())); + BEAST_EXPECT(ammAlice.expectAuctionSlot( + 0, 0, IOUAmount{Number{firstPrice} * Number{105, -2}})); + BEAST_EXPECT(ammAlice.expectBalances( + XRP(10'000), + USD(10'000), + cleanup340 + ? IOUAmount{Number{ammAlice.tokens()} - Number{minBidPrice} * Number{11, -1}} + : ammAlice.tokens())); }, std::nullopt, 0, @@ -7436,6 +7468,7 @@ private: testFeeVote(); testInvalidBid(); testBid(all); + testBid(all - fixCleanup3_4_0); testBid(all - fixAMMv1_3); testBid(all - fixAMMv1_1 - fixAMMv1_3); testInvalidAMMPayment(); From 3ab5288ef24e2c822ec8c95699558f402438e706 Mon Sep 17 00:00:00 2001 From: Gregory Tsipenyuk Date: Thu, 20 Aug 2026 19:05:28 +0000 Subject: [PATCH 3/7] fix: Enforce MPT balance invariants under the latest cleanup amendment (#7889) --- src/libxrpl/tx/invariants/MPTInvariant.cpp | 64 +++- src/test/app/Invariants_test.cpp | 377 ++++++++++++++++++--- 2 files changed, 390 insertions(+), 51 deletions(-) diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 9a7e96e44f..045d03ab02 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -144,6 +144,8 @@ ValidMPTIssuance::finalize( // must not dangle outside that controlled lifecycle. if (rules.enabled(fixCleanup3_2_0)) { + // Not an amendment gate like the same-named flags below, just an + // accumulator, so that every violation gets logged before returning. bool invariantPasses = true; if (referenceHoldingMutated_) { @@ -474,7 +476,9 @@ ValidMPTBalanceChanges::finalize( ReadView const& view, beast::Journal const& j) { - if (isTesSuccess(result)) + auto const fix340Enabled = view.rules().enabled(fixCleanup3_4_0); + + if (isTesSuccess(result) || fix340Enabled) { // Confidential transactions are validated by ValidConfidentialMPToken. // They modify encrypted fields and sfConfidentialOutstandingAmount @@ -486,7 +490,9 @@ ValidMPTBalanceChanges::finalize( return true; } - bool const invariantPasses = !view.rules().enabled(featureMPTokensV2); + // Returned when a violation is found below, so this is the log-only + // condition. Either amendment makes the checks enforcing. + auto const invariantPasses = !(view.rules().enabled(featureMPTokensV2) || fix340Enabled); if (overflow_) { JLOG(j.fatal()) << "Invariant failed: OutstandingAmount overflow"; @@ -510,6 +516,18 @@ ValidMPTBalanceChanges::finalize( << " " << data.mptAmount; return invariantPasses; } + + // A failed transaction must not have moved MPT value; the check + // above ties mptAmount to the OutstandingAmount delta. No result + // code is exempt: on any tec the transactor discards the view and + // re-applies only offer, trust line, NFT offer and credential + // deletions (Transactor::typesForResult), none of which touch MPTs. + if (!isTesSuccess(result) && data.mptAmount != 0) + { + JLOG(j.fatal()) << "Invariant failed: OutstandingAmount balance changed on failure " + << tx.getTxnType() << " " << result; + return invariantPasses; + } } } @@ -833,7 +851,7 @@ ValidMPTTransfer::isAuthorized( bool ValidMPTTransfer::finalize( STTx const& tx, - TER const, + TER const result, XRPAmount const, ReadView const& view, beast::Journal const& j) @@ -864,9 +882,19 @@ ValidMPTTransfer::finalize( return txnType == ttAMM_CREATE || txnType == ttAMM_DEPOSIT || txnType == ttOFFER_CREATE; }(); - // Only enforce once MPTokensV2 is enabled to preserve consensus with non-V2 nodes. - // Log invariant failure error even if MPTokensV2 is disabled. - auto const invariantPasses = !view.rules().enabled(featureMPTokensV2); + auto const fix340Enabled = view.rules().enabled(fixCleanup3_4_0); + // Returned when a violation is found below, so this is the log-only + // condition. Either amendment makes the checks enforcing. + auto const invariantPasses = !(view.rules().enabled(featureMPTokensV2) || fix340Enabled); + + // A failed transaction must not persist an MPToken deletion. Pre-loop + // because deletedAuthorized_ is not issuance-scoped and orphans continue. + if (fix340Enabled && !isTesSuccess(result) && !deletedAuthorized_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: MPToken deleted on failure " << txnType << " " + << result; + return invariantPasses; + } for (auto const& [mptID, values] : amount_) { @@ -876,6 +904,20 @@ ValidMPTTransfer::finalize( auto const sleIssuance = view.read(keylet::mptokenIssuance(mptID)); if (!sleIssuance) { + // MPTokenIssuanceDestroy only requires a zero OutstandingAmount, so + // an orphaned MPToken can outlive its issuance and be cleaned up + // later by a transaction of any type. There are no transfer rules + // left to check, but its balance is zero and nothing can raise it, + // so any change other than deletion is a bug. + for (auto const& [account, value] : values) + { + if (value.amtAfter.has_value() && value.amtBefore.value_or(0) != *value.amtAfter) + { + JLOG(j.fatal()) << "Invariant failed: orphaned MPToken balance changed " + << txnType << " " << result; + return invariantPasses; + } + } continue; } @@ -939,6 +981,16 @@ ValidMPTTransfer::finalize( JLOG(j.fatal()) << "Invariant failed: invalid MPToken transfer between holders"; return invariantPasses; } + + // A failed transaction must not have changed a holder's balance. One + // side is enough, unlike the transfer check above, so this also catches + // a lock/unlock moving value between sfMPTAmount and sfLockedAmount. + if (fix340Enabled && !isTesSuccess(result) && (senders > 0 || receivers > 0)) + { + JLOG(j.fatal()) << "Invariant failed: MPToken balance changed on failure " << txnType + << " " << result; + return invariantPasses; + } } return true; diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index ced2dea9bb..dcd22ffda6 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -142,7 +142,11 @@ class Invariants_test : public beast::unit_test::Suite std::initializer_list ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, Preclose const& preclose = {}, TxAccount setTxAccount = TxAccount::None, - std::source_location const& loc = std::source_location::current()) + std::source_location const& loc = std::source_location::current(), + // Result fed to the invariant checker on the first pass. Set it to a + // tec to exercise result-dependent invariants; the harness runs no + // transactor, so one never arises on its own. + TER initialResult = tesSUCCESS) { doInvariantCheck( makeEnv(defaultAmendments()), @@ -153,7 +157,8 @@ class Invariants_test : public beast::unit_test::Suite ters, preclose, setTxAccount, - loc); + loc, + initialResult); } void @@ -166,7 +171,8 @@ class Invariants_test : public beast::unit_test::Suite std::initializer_list ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, Preclose const& preclose = {}, TxAccount setTxAccount = TxAccount::None, - std::source_location const& loc = std::source_location::current()) + std::source_location const& loc = std::source_location::current(), + TER initialResult = tesSUCCESS) { using namespace test::jtx; @@ -180,7 +186,8 @@ class Invariants_test : public beast::unit_test::Suite if (setTxAccount != TxAccount::None) tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? a1.id() : a2.id()); - doInvariantCheck(std::move(env), a1, a2, expectLogs, precheck, fee, tx, ters, loc); + doInvariantCheck( + std::move(env), a1, a2, expectLogs, precheck, fee, tx, ters, loc, initialResult); } void @@ -194,7 +201,8 @@ class Invariants_test : public beast::unit_test::Suite XRPAmount fee = XRPAmount{}, STTx tx = STTx{ttACCOUNT_SET, [](STObject&) {}}, std::initializer_list ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, - std::source_location const& loc = std::source_location::current()) + std::source_location const& loc = std::source_location::current(), + TER initialResult = tesSUCCESS) { using namespace test::jtx; @@ -213,13 +221,18 @@ class Invariants_test : public beast::unit_test::Suite if (!BEAST_EXPECT(transactor)) return; - // invoke check twice to cover tec and tef cases + // Invoke the check twice to cover the tec and tef cases. Both passes run + // against the same view -- production would discard it in between -- so + // the second sees the same violation and escalates tec -> tef. A + // {tec, tef} pair therefore means "enforced whatever the incoming + // result", not that the transaction ends in tef on ledger. if (!BEAST_EXPECT(ters.size() == 2)) return; - TER terActual = tesSUCCESS; + TER terActual = initialResult; for (TER const& terExpect : ters) { + TER const terInput = terActual; terActual = transactor->checkInvariants(terActual, fee, Transactor::InvariantScope::Full); expect( @@ -229,7 +242,10 @@ class Invariants_test : public beast::unit_test::Suite loc.line()); auto const messages = sink.messages().str(); - if (!isTesSuccess(terActual)) + // checkInvariants returns its input unchanged unless something + // fires, so a changed result means an invariant fired, and a firing + // invariant must log. + if (terActual != terInput) { expect( messages.starts_with("Invariant failed:") || @@ -3441,7 +3457,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -3522,7 +3538,7 @@ class Invariants_test : public beast::unit_test::Suite XRPAmount{}, STTx{ ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -3608,7 +3624,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -3629,7 +3645,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -3738,6 +3754,7 @@ class Invariants_test : public beast::unit_test::Suite { "created vault must be empty", "create operation must not have updated a vault", + "invalid OutstandingAmount balance 0 9 0", }, [&](Account const& a1, Account const& a2, ApplyContext& ac) { auto const keylet = keylet::vault(a1.id(), SeqProxy::rawSequence(ac.view().seq())); @@ -3754,7 +3771,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_CREATE, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, [&](Account const& a1, Account const& a2, Env& env) { Vault const vault{env}; auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()}); @@ -3998,7 +4015,7 @@ class Invariants_test : public beast::unit_test::Suite XRPAmount{}, STTx{ ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4029,7 +4046,7 @@ class Invariants_test : public beast::unit_test::Suite tx[sfFee] = XRPAmount(100); tx[sfAccount] = a3.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp); doInvariantCheck( @@ -4055,7 +4072,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4077,7 +4094,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4091,7 +4108,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4106,7 +4123,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4124,7 +4141,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(5); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4148,7 +4165,7 @@ class Invariants_test : public beast::unit_test::Suite tx[sfDelegate] = a3.id(); tx[sfFee] = XRPAmount(2000); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4164,7 +4181,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4211,7 +4228,7 @@ class Invariants_test : public beast::unit_test::Suite // This commented out line causes the invariant violation. // tx[sfDestination] = A4.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp); doInvariantCheck( @@ -4239,7 +4256,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4260,7 +4277,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [&](STObject& tx) { tx.setAccountID(sfDestination, a3.id()); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4274,7 +4291,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4288,7 +4305,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4305,7 +4322,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4321,7 +4338,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4345,7 +4362,7 @@ class Invariants_test : public beast::unit_test::Suite tx[sfDelegate] = a3.id(); tx[sfFee] = XRPAmount(2000); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseXrp, TxAccount::A2); @@ -4408,7 +4425,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [&](STObject& tx) { tx[sfAccount] = a3.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseMpt, TxAccount::A2); @@ -4424,7 +4441,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_CLAWBACK, [&](STObject& tx) { tx[sfAccount] = a3.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseMpt); // Not the same as below check: attempt to clawback XRP @@ -4470,7 +4487,7 @@ class Invariants_test : public beast::unit_test::Suite tx[sfAccount] = a3.id(); tx[sfHolder] = a4.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseMpt); doInvariantCheck( @@ -4489,7 +4506,7 @@ class Invariants_test : public beast::unit_test::Suite tx[sfAccount] = a3.id(); tx[sfHolder] = a4.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseMpt); doInvariantCheck( @@ -4512,7 +4529,7 @@ class Invariants_test : public beast::unit_test::Suite tx[sfAccount] = a3.id(); tx[sfHolder] = a4.id(); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseMpt); // ───────────────────────────────────────────────────────────── @@ -4686,7 +4703,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseClosedEnded(/*advanceBySub=*/1, /*doDeposit=*/true), TxAccount::A2); @@ -4701,7 +4718,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttVAULT_WITHDRAW, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseClosedEnded(/*advanceBySub=*/1, /*doDeposit=*/true), TxAccount::A2); @@ -4910,7 +4927,7 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttPAYMENT, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, [&](Account const& a1, Account const& a2, Env& env) { Account const gw("gw"); env.fund(XRP(1'000), gw); @@ -4940,6 +4957,199 @@ class Invariants_test : public beast::unit_test::Suite return true; }); + // The on-failure MPT checks (OutstandingAmount balance / transfer) apply + // to every non-tesSUCCESS result, with no per-result exemption: on a tec + // the transactor discards the view and re-applies only offer, trust + // line, NFT offer and credential deletions, so an MPT change reaching + // the invariant is a bug whatever the code. Seeded via initialResult. + { + MPTID id; + // preclose: gw issues an MPT held by A1 and A2. + auto const setup = [&](Account const& a1, Account const& a2, Env& env) { + Account const gw("gw"); + env.fund(XRP(1'000), gw); + MPTTester const mpt( + {.env = env, .issuer = gw, .holders = {a1, a2}, .pay = 50, .maxAmt = 1'000}); + id = mpt.issuanceID(); + return true; + }; + + // Consistent mint: OutstandingAmount and A1's balance both grow by + // 10, so conservation holds and only the on-failure check fires. + Precheck const mint = [&](Account const& a1, Account const&, ApplyContext& ac) { + auto sleIss = ac.view().peek(keylet::mptokenIssuance(id)); + auto sleTok = ac.view().peek(keylet::mptoken(id, a1.id())); + if (!sleIss || !sleTok) + return false; + (*sleIss)[sfOutstandingAmount] = (*sleIss)[sfOutstandingAmount] + 10; + (*sleTok)[sfMPTAmount] = (*sleTok)[sfMPTAmount] + 10; + ac.view().update(sleIss); + ac.view().update(sleTok); + return true; + }; + + // Holder-to-holder transfer (A1 -> A2 by 10). OutstandingAmount is + // unchanged, and CanTransfer keeps the ordinary transfer check + // quiet, so only the on-failure check fires. + Precheck const transfer = [&](Account const& a1, Account const& a2, ApplyContext& ac) { + auto sleIss = ac.view().peek(keylet::mptokenIssuance(id)); + auto sleA = ac.view().peek(keylet::mptoken(id, a1.id())); + auto sleB = ac.view().peek(keylet::mptoken(id, a2.id())); + if (!sleIss || !sleA || !sleB) + return false; + (*sleIss)[sfFlags] = (*sleIss)[sfFlags] | lsfMPTCanTransfer; + (*sleA)[sfMPTAmount] = (*sleA)[sfMPTAmount] - 10; + (*sleB)[sfMPTAmount] = (*sleB)[sfMPTAmount] + 10; + ac.view().update(sleIss); + ac.view().update(sleA); + ac.view().update(sleB); + return true; + }; + + STTx const payment{ttPAYMENT, [](STObject&) {}}; + + // Negative controls: nothing fires on tesSUCCESS. Without these, the + // cases below would still pass if the result guard were dropped. + doInvariantCheck({}, mint, XRPAmount{}, payment, {tesSUCCESS, tesSUCCESS}, setup); + doInvariantCheck({}, transfer, XRPAmount{}, payment, {tesSUCCESS, tesSUCCESS}, setup); + + // tecKILLED and tecINCOMPLETE are not special: an MPT change paired + // with either fires, as with any other failure. + doInvariantCheck( + {{"OutstandingAmount balance changed on failure"}}, + mint, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecKILLED); + doInvariantCheck( + {{"OutstandingAmount balance changed on failure"}}, + mint, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecINCOMPLETE); + doInvariantCheck( + {{"MPToken balance changed on failure"}}, + transfer, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecKILLED); + doInvariantCheck( + {{"MPToken balance changed on failure"}}, + transfer, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecINCOMPLETE); + // The same change under a third failure result: the check keys off + // "not tesSUCCESS", nothing finer. + doInvariantCheck( + {{"OutstandingAmount balance changed on failure"}}, + mint, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecEXPIRED); + doInvariantCheck( + {{"MPToken balance changed on failure"}}, + transfer, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecEXPIRED); + + // A lock moves value within one holder, so it is not a two-sided + // transfer and the `senders || receivers` form is what catches it. + // OutstandingAmount and the holder total are unchanged, so the + // balance check stays quiet. + Precheck const lock = [&](Account const& a1, Account const&, ApplyContext& ac) { + auto sleTok = ac.view().peek(keylet::mptoken(id, a1.id())); + if (!sleTok || (*sleTok)[sfMPTAmount] < 10) + return false; + // A fresh MPToken has no locked amount, so set it directly. + (*sleTok)[sfMPTAmount] = (*sleTok)[sfMPTAmount] - 10; + sleTok->setFieldU64(sfLockedAmount, 10); + ac.view().update(sleTok); + return true; + }; + // Negative control: a lock is legitimate on tesSUCCESS. + doInvariantCheck({}, lock, XRPAmount{}, payment, {tesSUCCESS, tesSUCCESS}, setup); + doInvariantCheck( + {{"MPToken balance changed on failure"}}, + lock, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecKILLED); + // The lock is caught under any failure result. + doInvariantCheck( + {{"MPToken balance changed on failure"}}, + lock, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setup, + TxAccount::None, + std::source_location::current(), + tecEXPIRED); + + // A deleted MPToken has no amtAfter, so the sender/receiver counts + // skip it and only the deletedAuthorized_ term can catch it. That + // needs holders authorized but never paid, so the MPToken can be + // erased with a zero balance and OutstandingAmount untouched -- + // otherwise the holder would register as a sender instead. + MPTID emptyId; + auto const setupEmpty = [&](Account const& a1, Account const& a2, Env& env) { + Account const gw("gw"); + env.fund(XRP(1'000), gw); + MPTTester const mpt({.env = env, .issuer = gw, .holders = {a1, a2}, .maxAmt = 100}); + emptyId = mpt.issuanceID(); + return true; + }; + Precheck const eraseToken = [&](Account const& a1, Account const&, ApplyContext& ac) { + auto sleTok = ac.view().peek(keylet::mptoken(emptyId, a1.id())); + if (!sleTok || (*sleTok)[sfMPTAmount] != 0) + return false; + ac.view().erase(sleTok); + return true; + }; + // ValidMPTIssuance also reports the deletion, so assert on + // ValidMPTTransfer's message, which only the new check can produce. + doInvariantCheck( + {{"MPToken deleted on failure"}}, + eraseToken, + XRPAmount{}, + payment, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setupEmpty, + TxAccount::None, + std::source_location::current(), + tecEXPIRED); + } + // Invalid IOU clawback delta must fail once MPTokensV2 enforces before/after validation. { Env env(*this, defaultAmendments()); @@ -5635,7 +5845,13 @@ class Invariants_test : public beast::unit_test::Suite std::make_pair(ttAMM_WITHDRAW, false), std::make_pair(ttPAYMENT, false), std::make_pair(ttPAYMENT, true)}; - for (auto const enabled : {true, false}) + // The two amendments that gate enforcement, in all four combinations. + FeatureBitset const gatesEnabled{featureMPTokensV2, fixCleanup3_4_0}; + for (auto const gates : + {gatesEnabled, + gatesEnabled - featureMPTokensV2, + gatesEnabled - fixCleanup3_4_0, + FeatureBitset{}}) { for (auto const& [tx, crossCurrencyPayment] : invalidTransferTests) { @@ -5646,7 +5862,7 @@ class Invariants_test : public beast::unit_test::Suite 0u}) { MPTID id{}; - auto const isSuccess = !enabled || flag == 0 || + auto const isSuccess = !gates.any() || flag == 0 || (tx == ttPAYMENT && !crossCurrencyPayment && (flag == ~lsfMPTCanTrade)) || (tx == ttAMM_WITHDRAW && (flag == ~lsfMPTCanTrade || flag == ~lsfMPTCanTransfer)); @@ -5697,16 +5913,83 @@ class Invariants_test : public beast::unit_test::Suite MPTTester const usd( {.env = env, .issuer = gw, .holders = {a1, a2}, .pay = 100}); id = usd.issuanceID(); - if (!enabled) - { + // Either gate enforces, so both must be off to stay + // advisory. Disable after setting up the MPT; the + // next env.close() is what makes it take effect. + if (!gates[featureMPTokensV2]) env.disableFeature(featureMPTokensV2); - } + if (!gates[fixCleanup3_4_0]) + env.disableFeature(fixCleanup3_4_0); return true; }); } } } + // An orphan has a zero balance, so only deletion is legitimate (see + // "Skipping Deleted MPTs" in testConfidentialMPTTransfer). + { + MPTID orphanID; + auto const setupOrphan = [&](Account const& a1, Account const& a2, Env& env) { + MPTTester mpt(env, a1, {.holders = {a2}, .fund = false}); + mpt.create({.flags = tfMPTCanTransfer}); + orphanID = mpt.issuanceID(); + // A2 is authorized but never paid, so its balance is zero and + // the issuance can be destroyed while its MPToken lives on. + mpt.authorize({.account = a2}); + mpt.destroy(); + return true; + }; + // ValidMPTBalanceChanges also reports this, so assert on the + // orphan message, which only the missing-issuance branch produces. + doInvariantCheck( + {{"orphaned MPToken balance changed"}}, + [&](Account const&, Account const& a2, ApplyContext& ac) { + auto sleTok = ac.view().peek(keylet::mptoken(orphanID, a2.id())); + if (!sleTok || (*sleTok)[sfMPTAmount] != 0) + return false; + (*sleTok)[sfMPTAmount] = (*sleTok)[sfMPTAmount] + 10; + ac.view().update(sleTok); + return true; + }, + XRPAmount{}, + STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setupOrphan); + // Negative control: erasing the orphan is how it gets cleaned up. + doInvariantCheck( + {}, + [&](Account const&, Account const& a2, ApplyContext& ac) { + auto sleTok = ac.view().peek(keylet::mptoken(orphanID, a2.id())); + if (!sleTok) + return false; + ac.view().erase(sleTok); + return true; + }, + XRPAmount{}, + STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}}, + {tesSUCCESS, tesSUCCESS}, + setupOrphan); + // The same erase on a failure. The orphan branch continues, so only + // the pre-loop deletion check can report this one. + doInvariantCheck( + {{"MPToken deleted on failure"}}, + [&](Account const&, Account const& a2, ApplyContext& ac) { + auto sleTok = ac.view().peek(keylet::mptoken(orphanID, a2.id())); + if (!sleTok) + return false; + ac.view().erase(sleTok); + return true; + }, + XRPAmount{}, + STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}}, + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + setupOrphan, + TxAccount::None, + std::source_location::current(), + tecEXPIRED); + } + // Vault-share freeze invariant: isVaultPseudoAccountFrozen descends // through sfReferenceHolding to test the vault's underlying asset for // each changed holder. @@ -5881,7 +6164,9 @@ class Invariants_test : public beast::unit_test::Suite for (bool const isMPT : {false, true}) { - auto const error = isMPT ? TER(tecINVARIANT_FAILED) : TER(tefINVARIANT_FAILED); + // Under fixCleanup3_4_0 the MPT balance invariants also fire on the + // second pass, so both IOU and MPT pools now escalate to tef. + auto const error = TER(tefINVARIANT_FAILED); for (auto txType : {ttAMM_CREATE, ttAMM_DEPOSIT, ttAMM_CLAWBACK, ttAMM_WITHDRAW}) { test(txType, deleteAMMAccount, isMPT, tefINVARIANT_FAILED); @@ -6700,7 +6985,9 @@ class Invariants_test : public beast::unit_test::Suite }, XRPAmount{}, STTx{ttCONFIDENTIAL_MPT_SEND, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}, + // Second pass is tef: the bumped MPTAmount also trips + // ValidMPTTransfer's on-failure check, which escalates the tec. + {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, precloseConfidential); // badVersion From 85512541ad78f61555e6f06b8463190a0bbcf908 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 20 Aug 2026 19:25:27 +0000 Subject: [PATCH 4/7] refactor: Collapse transactions.macro settings into a TxSettings struct (#8001) Co-authored-by: Timur Yalymov <36795566+tyalymov@users.noreply.github.com> Co-authored-by: Cursor Co-authored-by: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Co-authored-by: Ayaz Salikhov --- cmake/scripts/codegen/generate_tx_classes.py | 82 ++- include/xrpl/protocol/Permissions.h | 8 +- include/xrpl/protocol/TxSettings.h | 96 ++++ .../xrpl/protocol/detail/transactions.macro | 500 ++++++++---------- .../protocol_autogen/transactions/AMMBid.h | 2 +- .../transactions/AMMClawback.h | 2 +- .../protocol_autogen/transactions/AMMCreate.h | 2 +- .../protocol_autogen/transactions/AMMDelete.h | 2 +- .../transactions/AMMDeposit.h | 2 +- .../protocol_autogen/transactions/AMMVote.h | 2 +- .../transactions/AMMWithdraw.h | 2 +- .../transactions/AccountDelete.h | 2 +- .../transactions/AccountSet.h | 2 +- .../protocol_autogen/transactions/Batch.h | 2 +- .../transactions/CheckCancel.h | 2 +- .../protocol_autogen/transactions/CheckCash.h | 2 +- .../transactions/CheckCreate.h | 2 +- .../protocol_autogen/transactions/Clawback.h | 2 +- .../transactions/ConfidentialMPTClawback.h | 2 +- .../transactions/ConfidentialMPTConvert.h | 2 +- .../transactions/ConfidentialMPTConvertBack.h | 2 +- .../transactions/ConfidentialMPTMergeInbox.h | 2 +- .../transactions/ConfidentialMPTSend.h | 2 +- .../transactions/CredentialAccept.h | 2 +- .../transactions/CredentialCreate.h | 2 +- .../transactions/CredentialDelete.h | 2 +- .../protocol_autogen/transactions/DIDDelete.h | 2 +- .../protocol_autogen/transactions/DIDSet.h | 2 +- .../transactions/DelegateSet.h | 2 +- .../transactions/DepositPreauth.h | 2 +- .../transactions/EnableAmendment.h | 2 +- .../transactions/EscrowCancel.h | 2 +- .../transactions/EscrowCreate.h | 2 +- .../transactions/EscrowFinish.h | 2 +- .../transactions/LedgerStateFix.h | 2 +- .../transactions/LoanBrokerCoverClawback.h | 2 +- .../transactions/LoanBrokerCoverDeposit.h | 2 +- .../transactions/LoanBrokerCoverWithdraw.h | 2 +- .../transactions/LoanBrokerDelete.h | 2 +- .../transactions/LoanBrokerSet.h | 2 +- .../transactions/LoanDelete.h | 2 +- .../transactions/LoanManage.h | 2 +- .../protocol_autogen/transactions/LoanPay.h | 2 +- .../protocol_autogen/transactions/LoanSet.h | 2 +- .../transactions/MPTokenAuthorize.h | 2 +- .../transactions/MPTokenIssuanceCreate.h | 2 +- .../transactions/MPTokenIssuanceDestroy.h | 2 +- .../transactions/MPTokenIssuanceSet.h | 2 +- .../transactions/NFTokenAcceptOffer.h | 2 +- .../transactions/NFTokenBurn.h | 2 +- .../transactions/NFTokenCancelOffer.h | 2 +- .../transactions/NFTokenCreateOffer.h | 2 +- .../transactions/NFTokenMint.h | 2 +- .../transactions/NFTokenModify.h | 2 +- .../transactions/OfferCancel.h | 2 +- .../transactions/OfferCreate.h | 2 +- .../transactions/OracleDelete.h | 2 +- .../protocol_autogen/transactions/OracleSet.h | 2 +- .../protocol_autogen/transactions/Payment.h | 2 +- .../transactions/PaymentChannelClaim.h | 2 +- .../transactions/PaymentChannelCreate.h | 2 +- .../transactions/PaymentChannelFund.h | 2 +- .../transactions/PermissionedDomainDelete.h | 2 +- .../transactions/PermissionedDomainSet.h | 2 +- .../protocol_autogen/transactions/SetFee.h | 2 +- .../transactions/SetRegularKey.h | 2 +- .../transactions/SignerListSet.h | 2 +- .../transactions/SponsorshipSet.h | 2 +- .../transactions/SponsorshipTransfer.h | 2 +- .../transactions/TicketCreate.h | 2 +- .../protocol_autogen/transactions/TrustSet.h | 2 +- .../protocol_autogen/transactions/UNLModify.h | 2 +- .../transactions/VaultClawback.h | 2 +- .../transactions/VaultCreate.h | 2 +- .../transactions/VaultDelete.h | 2 +- .../transactions/VaultDeposit.h | 2 +- .../protocol_autogen/transactions/VaultSet.h | 2 +- .../transactions/VaultWithdraw.h | 2 +- .../transactions/XChainAccountCreateCommit.h | 2 +- .../XChainAddAccountCreateAttestation.h | 2 +- .../transactions/XChainAddClaimAttestation.h | 2 +- .../transactions/XChainClaim.h | 2 +- .../transactions/XChainCommit.h | 2 +- .../transactions/XChainCreateBridge.h | 2 +- .../transactions/XChainCreateClaimID.h | 2 +- .../transactions/XChainModifyBridge.h | 2 +- .../tx/invariants/InvariantCheckPrivilege.h | 37 +- src/libxrpl/protocol/Permissions.cpp | 15 +- src/libxrpl/protocol/TxFormats.cpp | 2 +- src/libxrpl/tx/invariants/FreezeInvariant.cpp | 3 +- src/libxrpl/tx/invariants/InvariantCheck.cpp | 21 +- src/libxrpl/tx/invariants/MPTInvariant.cpp | 17 +- src/libxrpl/tx/invariants/NFTInvariant.cpp | 2 +- src/libxrpl/tx/invariants/VaultInvariant.cpp | 5 +- src/test/app/Delegate_test.cpp | 14 +- 95 files changed, 520 insertions(+), 446 deletions(-) create mode 100644 include/xrpl/protocol/TxSettings.h diff --git a/cmake/scripts/codegen/generate_tx_classes.py b/cmake/scripts/codegen/generate_tx_classes.py index 07baefd8b6..09fb898840 100644 --- a/cmake/scripts/codegen/generate_tx_classes.py +++ b/cmake/scripts/codegen/generate_tx_classes.py @@ -8,6 +8,7 @@ Uses pcpp to preprocess the macro file and pyparsing to parse the DSL. import io import argparse +import re from pathlib import Path import pyparsing as pp @@ -53,28 +54,89 @@ def create_transaction_parser(): return macro_parser +# Defaults for xrpl::TxSettings members, mirroring +# include/xrpl/protocol/TxSettings.h. A transaction's settings blob only names +# the members that differ from these. +SETTING_DEFAULTS = { + "delegable": "Delegation::NotDelegable", + "amendment": "uint256{}", + "privileges": "Privilege::NoPriv", +} + + +def parse_settings(settings_str): + """Parse a TxSettings blob into a dict, filling in defaults. + + Args: + settings_str: A string like '({.delegable = Delegation::NotDelegable, + .privileges = Privilege::CreateAcct})', or '({})'. + + Returns: + A dict with a value for every key in SETTING_DEFAULTS. + """ + body = settings_str.strip() + if not (body.startswith("(") and body.endswith(")")): + raise ValueError( + f"Malformed settings blob, expected '({{...}})': {settings_str!r}" + ) + body = body[1:-1].strip() + if not (body.startswith("{") and body.endswith("}")): + raise ValueError( + f"Malformed settings blob, expected '({{...}})': {settings_str!r}" + ) + body = body[1:-1] + + # Strip comments, which may be interleaved with the designated initializers. + body = re.sub(r"//[^\n]*", "", body) + + settings = dict(SETTING_DEFAULTS) + seen = set() + # Each entry runs from '.key =' up to the next '.key =' or the end. + for key, value in re.findall( + r"\.(\w+)\s*=\s*(.*?)(?=,\s*\.\w+\s*=|,?\s*$)", body, re.S + ): + if key not in SETTING_DEFAULTS: + raise ValueError(f"Unknown TxSettings member '.{key}' in {settings_str!r}") + settings[key] = " ".join(value.split()).rstrip(",") + seen.add(key) + + # Catch a typo'd or unparsed initializer rather than silently defaulting it. + # Every '.member' in the blob must have been consumed above. + if len(re.findall(r"\.\w+", body)) != len(seen): + raise ValueError(f"Could not parse every setting in {settings_str!r}") + + # A blob with content but no designated initializer is positional, which + # would otherwise be read as "all defaults" and silently generate the + # wrong output. + if body.strip() and not seen: + raise ValueError( + "TxSettings requires designated initializers (.member = value), " + f"got {settings_str!r}" + ) + + return settings + + def parse_transaction_args(args_list): """Parse the arguments of a TRANSACTION macro call. Args: args_list: A list of parsed arguments from pyparsing, e.g., - ['ttPAYMENT', '0', 'Payment', 'Delegation::delegable', - 'uint256{}', 'createAcct', '({...})'] + ['ttPAYMENT', '0', 'Payment', + '({.privileges = Privilege::CreateAcct})', '({...})'] Returns: A dict with parsed transaction information. """ - if len(args_list) < 7: + if len(args_list) < 5: raise ValueError( - f"Expected at least 7 parts in TRANSACTION, got {len(args_list)}: {args_list}" + f"Expected at least 5 parts in TRANSACTION, got {len(args_list)}: {args_list}" ) tag = args_list[0] value = args_list[1] name = args_list[2] - delegable = args_list[3] - amendments = args_list[4] - privileges = args_list[5] + settings = parse_settings(args_list[3]) fields_str = args_list[-1] # Parse fields: ({field1, field2, ...}) @@ -84,9 +146,9 @@ def parse_transaction_args(args_list): "tag": tag, "value": value, "name": name, - "delegable": delegable, - "amendments": amendments, - "privileges": privileges, + "delegable": settings["delegable"], + "amendments": settings["amendment"], + "privileges": settings["privileges"], "fields": fields, } diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h index 703a0939c9..2a3f561a10 100644 --- a/include/xrpl/protocol/Permissions.h +++ b/include/xrpl/protocol/Permissions.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -38,11 +39,6 @@ enum GranularPermissionType : std::uint32_t { #pragma pop_macro("GRANULAR_PERMISSION") }; -// Injected bare enumerators (xrpl::delegable / xrpl::notDelegable) are required by preprocessor -// tricks in tests and macro-generated code; enum class would break that. -// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) -enum Delegation { Delegable, NotDelegable }; - class Permission { private: @@ -65,7 +61,7 @@ private: struct TxDelegationEntry { uint256 amendment; - Delegation delegable{NotDelegable}; + Delegation delegable{Delegation::NotDelegable}; }; std::unordered_set granularTxTypes_; diff --git a/include/xrpl/protocol/TxSettings.h b/include/xrpl/protocol/TxSettings.h new file mode 100644 index 0000000000..8ea249856a --- /dev/null +++ b/include/xrpl/protocol/TxSettings.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include +#include + +namespace xrpl { + +enum class Delegation { Delegable, NotDelegable }; + +/** + * Operations a transaction is permitted to perform, as a bitfield. + * + * These are declared per-transaction in transactions.macro (via + * TxSettings::privileges) and enforced in InvariantCheck.cpp. + */ +enum class Privilege : std::uint16_t { + NoPriv = 0x0000, // The transaction can not do any of the enumerated operations + CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object. + CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account, + // which implies createAcct + MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object + MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT + // object, but does not have to + OverrideFreeze = 0x0010, // The transaction can override some freeze rules + ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT + CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance + DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance + MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT + // object (except by issuer) + MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT + // object (except by issuer) + MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create. + MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault + MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault + MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer. +}; + +// The inner static_cast is not redundant: the underlying type is narrower than +// `int`, so the operands integer-promote and the result has to be narrowed back. +// safeCast rejects that narrowing, but every input bit is a Privilege bit by +// construction, so the result is always representable. +constexpr Privilege +operator|(Privilege lhs, Privilege rhs) +{ + using Underlying = std::underlying_type_t; + return static_cast( + static_cast(safeCast(lhs) | safeCast(rhs))); +} + +constexpr Privilege +operator&(Privilege lhs, Privilege rhs) +{ + using Underlying = std::underlying_type_t; + return static_cast( + static_cast(safeCast(lhs) & safeCast(rhs))); +} + +/** + * Per-transaction metadata declared in transactions.macro. + * + * Every member has a default, so a transaction only needs to name the settings + * that differ from the common case. See the documentation at the top of + * transactions.macro for the authoring syntax. + * + * This is deliberately not a constexpr-friendly type: amendment identifiers are + * runtime-initialized `extern uint256 const` globals (see Feature.h), so a + * TxSettings can only be built at runtime. + */ +struct TxSettings +{ + /** + * Whether an account may delegate this transaction to another account. + */ + Delegation delegable{Delegation::NotDelegable}; + + /** + * The amendment gating this transaction, or uint256{} if always available. + */ + // The `{}` looks redundant, because BaseUInt's default constructor already + // zeroes the value. It is not: without a default member initializer here, + // every partial designated initializer in transactions.macro trips the + // missing-designated-field-initializers warning, which the build treats as + // an error. + // NOLINTNEXTLINE(readability-redundant-member-init) + uint256 amendment{}; + + /** + * Operations this transaction is permitted to perform. + */ + Privilege privileges{Privilege::NoPriv}; +}; + +} // namespace xrpl diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 997f368638..dbf9b66ac7 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -3,7 +3,7 @@ #endif /** - * TRANSACTION(tag, value, name, delegable, amendments, privileges, fields) + * TRANSACTION(tag, value, name, settings, fields) * * To ease maintenance, you may replace any unneeded values with "..." * e.g. #define TRANSACTION(tag, value, name, ...) @@ -15,9 +15,31 @@ * # include * #endif * - * The `privileges` parameter of the TRANSACTION macro is a bitfield - * defining which operations the transaction can perform. - * The values are defined and used in InvariantCheck.cpp + * `settings` is a parenthesized brace-init-list for xrpl::TxSettings, declared + * in : + * + * struct TxSettings + * { + * Delegation delegable{Delegation::NotDelegable}; + * uint256 amendment{}; + * Privilege privileges{Privilege::NoPriv}; + * }; + * + * Name only the settings that differ from those defaults, in declaration + * order; use `({})` when none of them do: + * + * ({.delegable = Delegation::Delegable, .amendment = featureFoo}) + * + * You must use designated initializers, as shown above. Positional + * initialization such as `({Delegation::NotDelegable})` is not supported, + * because the code generator reads these settings by member name. + * + * The `privileges` setting is a bitfield defining which operations the + * transaction can perform. The values are defined in TxSettings.h and + * enforced in InvariantCheck.cpp. + * + * A consumer that only needs some of the settings can unwrap the blob with + * `#define UNWRAP(...) __VA_ARGS__` and write `TxSettings UNWRAP settings`. */ /** This transaction type executes a payment. */ @@ -25,9 +47,7 @@ # include #endif TRANSACTION(ttPAYMENT, 0, Payment, - Delegation::Delegable, - uint256{}, - CreateAcct | MayCreateMpt, + ({.delegable = Delegation::Delegable, .privileges = Privilege::CreateAcct | Privilege::MayCreateMpt}), ({ {sfDestination, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, @@ -44,11 +64,7 @@ TRANSACTION(ttPAYMENT, 0, Payment, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, ({.delegable = Delegation::Delegable}), ({ {sfDestination, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, {sfCondition, SoeOptional}, @@ -61,11 +77,7 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, ({.delegable = Delegation::Delegable}), ({ {sfOwner, SoeRequired}, {sfOfferSequence, SoeRequired}, {sfFulfillment, SoeOptional}, @@ -79,9 +91,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, # include #endif TRANSACTION(ttACCOUNT_SET, 3, AccountSet, - Delegation::NotDelegable, - uint256{}, - NoPriv, + ({}), ({ {sfEmailHash, SoeOptional}, {sfWalletLocator, SoeOptional}, @@ -99,11 +109,7 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, ({.delegable = Delegation::Delegable}), ({ {sfOwner, SoeRequired}, {sfOfferSequence, SoeRequired}, })) @@ -113,9 +119,7 @@ TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, # include #endif TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, - Delegation::NotDelegable, - uint256{}, - NoPriv, + ({}), ({ {sfRegularKey, SoeOptional}, })) @@ -127,9 +131,7 @@ TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, # include #endif TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, - Delegation::Delegable, - uint256{}, - MayCreateMpt, + ({.delegable = Delegation::Delegable, .privileges = Privilege::MayCreateMpt}), ({ {sfTakerPays, SoeRequired, SoeMptSupported}, {sfTakerGets, SoeRequired, SoeMptSupported}, @@ -142,11 +144,7 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, ({.delegable = Delegation::Delegable}), ({ {sfOfferSequence, SoeRequired}, })) @@ -156,11 +154,7 @@ TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, ({.delegable = Delegation::Delegable}), ({ {sfTicketCount, SoeRequired}, })) @@ -173,9 +167,7 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, # include #endif TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, - Delegation::NotDelegable, - uint256{}, - NoPriv, + ({}), ({ {sfSignerQuorum, SoeRequired}, {sfSignerEntries, SoeOptional}, @@ -185,11 +177,7 @@ TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, ({.delegable = Delegation::Delegable}), ({ {sfDestination, SoeRequired}, {sfAmount, SoeRequired}, {sfSettleDelay, SoeRequired}, @@ -202,11 +190,7 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, ({.delegable = Delegation::Delegable}), ({ {sfChannel, SoeRequired}, {sfAmount, SoeRequired}, {sfExpiration, SoeOptional}, @@ -216,11 +200,7 @@ TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, ({.delegable = Delegation::Delegable}), ({ {sfChannel, SoeRequired}, {sfAmount, SoeOptional}, {sfBalance, SoeOptional}, @@ -233,11 +213,7 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, ({.delegable = Delegation::Delegable}), ({ {sfDestination, SoeRequired}, {sfSendMax, SoeRequired, SoeMptSupported}, {sfExpiration, SoeOptional}, @@ -250,9 +226,7 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, # include #endif TRANSACTION(ttCHECK_CASH, 17, CheckCash, - Delegation::Delegable, - uint256{}, - MayCreateMpt, + ({.delegable = Delegation::Delegable, .privileges = Privilege::MayCreateMpt}), ({ {sfCheckID, SoeRequired}, {sfAmount, SoeOptional, SoeMptSupported}, @@ -263,11 +237,7 @@ TRANSACTION(ttCHECK_CASH, 17, CheckCash, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, ({.delegable = Delegation::Delegable}), ({ {sfCheckID, SoeRequired}, })) @@ -275,11 +245,7 @@ TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, ({.delegable = Delegation::Delegable}), ({ {sfAuthorize, SoeOptional}, {sfUnauthorize, SoeOptional}, {sfAuthorizeCredentials, SoeOptional}, @@ -290,11 +256,7 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttTRUST_SET, 20, TrustSet, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttTRUST_SET, 20, TrustSet, ({.delegable = Delegation::Delegable}), ({ {sfLimitAmount, SoeOptional}, {sfQualityIn, SoeOptional}, {sfQualityOut, SoeOptional}, @@ -305,9 +267,9 @@ TRANSACTION(ttTRUST_SET, 20, TrustSet, # include #endif TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, - Delegation::NotDelegable, - uint256{}, - MustDeleteAcct, + ({ + .privileges = Privilege::MustDeleteAcct, + }), ({ {sfDestination, SoeRequired}, {sfDestinationTag, SoeOptional}, @@ -321,9 +283,7 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, # include #endif TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, - Delegation::Delegable, - uint256{}, - ChangeNftCounts, + ({.delegable = Delegation::Delegable, .privileges = Privilege::ChangeNftCounts}), ({ {sfNFTokenTaxon, SoeRequired}, {sfTransferFee, SoeOptional}, @@ -339,9 +299,7 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, # include #endif TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, - Delegation::Delegable, - uint256{}, - ChangeNftCounts, + ({.delegable = Delegation::Delegable, .privileges = Privilege::ChangeNftCounts}), ({ {sfNFTokenID, SoeRequired}, {sfOwner, SoeOptional}, @@ -351,11 +309,7 @@ TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, ({.delegable = Delegation::Delegable}), ({ {sfNFTokenID, SoeRequired}, {sfAmount, SoeRequired}, {sfDestination, SoeOptional}, @@ -367,11 +321,7 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, ({.delegable = Delegation::Delegable}), ({ {sfNFTokenOffers, SoeRequired}, })) @@ -379,11 +329,7 @@ TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, ({.delegable = Delegation::Delegable}), ({ {sfNFTokenBuyOffer, SoeOptional}, {sfNFTokenSellOffer, SoeOptional}, {sfNFTokenBrokerFee, SoeOptional}, @@ -393,11 +339,7 @@ TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, #if TRANSACTION_INCLUDE # include #endif -TRANSACTION(ttCLAWBACK, 30, Clawback, - Delegation::Delegable, - uint256{}, - NoPriv, - ({ +TRANSACTION(ttCLAWBACK, 30, Clawback, ({.delegable = Delegation::Delegable}), ({ {sfAmount, SoeRequired, SoeMptSupported}, {sfHolder, SoeOptional}, })) @@ -407,9 +349,12 @@ TRANSACTION(ttCLAWBACK, 30, Clawback, # include #endif TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, - Delegation::Delegable, - featureAMMClawback, - MayDeleteAcct | OverrideFreeze | MayAuthorizeMpt, + ({ + .delegable = Delegation::Delegable, + .amendment = featureAMMClawback, + .privileges = Privilege::MayDeleteAcct | Privilege::OverrideFreeze | + Privilege::MayAuthorizeMpt, + }), ({ {sfHolder, SoeRequired}, {sfAsset, SoeRequired, SoeMptSupported}, @@ -422,9 +367,11 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, # include #endif TRANSACTION(ttAMM_CREATE, 35, AMMCreate, - Delegation::Delegable, - featureAMM, - CreatePseudoAcct | MayCreateMpt, + ({ + .delegable = Delegation::Delegable, + .amendment = featureAMM, + .privileges = Privilege::CreatePseudoAcct | Privilege::MayCreateMpt, + }), ({ {sfAmount, SoeRequired, SoeMptSupported}, {sfAmount2, SoeRequired, SoeMptSupported}, @@ -436,9 +383,7 @@ TRANSACTION(ttAMM_CREATE, 35, AMMCreate, # include #endif TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, - Delegation::Delegable, - featureAMM, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureAMM}), ({ {sfAsset, SoeRequired, SoeMptSupported}, {sfAsset2, SoeRequired, SoeMptSupported}, @@ -454,9 +399,11 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, # include #endif TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, - Delegation::Delegable, - featureAMM, - MayDeleteAcct | MayAuthorizeMpt, + ({ + .delegable = Delegation::Delegable, + .amendment = featureAMM, + .privileges = Privilege::MayDeleteAcct | Privilege::MayAuthorizeMpt, + }), ({ {sfAsset, SoeRequired, SoeMptSupported}, {sfAsset2, SoeRequired, SoeMptSupported}, @@ -471,9 +418,7 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, # include #endif TRANSACTION(ttAMM_VOTE, 38, AMMVote, - Delegation::Delegable, - featureAMM, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureAMM}), ({ {sfAsset, SoeRequired, SoeMptSupported}, {sfAsset2, SoeRequired, SoeMptSupported}, @@ -485,9 +430,7 @@ TRANSACTION(ttAMM_VOTE, 38, AMMVote, # include #endif TRANSACTION(ttAMM_BID, 39, AMMBid, - Delegation::Delegable, - featureAMM, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureAMM}), ({ {sfAsset, SoeRequired, SoeMptSupported}, {sfAsset2, SoeRequired, SoeMptSupported}, @@ -501,9 +444,11 @@ TRANSACTION(ttAMM_BID, 39, AMMBid, # include #endif TRANSACTION(ttAMM_DELETE, 40, AMMDelete, - Delegation::Delegable, - featureAMM, - MustDeleteAcct | MayDeleteMpt, + ({ + .delegable = Delegation::Delegable, + .amendment = featureAMM, + .privileges = Privilege::MustDeleteAcct | Privilege::MayDeleteMpt, + }), ({ {sfAsset, SoeRequired, SoeMptSupported}, {sfAsset2, SoeRequired, SoeMptSupported}, @@ -514,9 +459,7 @@ TRANSACTION(ttAMM_DELETE, 40, AMMDelete, # include #endif TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, - Delegation::Delegable, - featureXChainBridge, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}), ({ {sfXChainBridge, SoeRequired}, {sfSignatureReward, SoeRequired}, @@ -525,9 +468,7 @@ TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, /** This transactions initiates a crosschain transaction */ TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, - Delegation::Delegable, - featureXChainBridge, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}), ({ {sfXChainBridge, SoeRequired}, {sfXChainClaimID, SoeRequired}, @@ -537,9 +478,7 @@ TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, /** This transaction completes a crosschain transaction */ TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, - Delegation::Delegable, - featureXChainBridge, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}), ({ {sfXChainBridge, SoeRequired}, {sfXChainClaimID, SoeRequired}, @@ -550,9 +489,7 @@ TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, /** This transaction initiates a crosschain account create transaction */ TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, - Delegation::Delegable, - featureXChainBridge, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}), ({ {sfXChainBridge, SoeRequired}, {sfDestination, SoeRequired}, @@ -562,9 +499,11 @@ TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, /** This transaction adds an attestation to a claim */ TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, - Delegation::Delegable, - featureXChainBridge, - CreateAcct, + ({ + .delegable = Delegation::Delegable, + .amendment = featureXChainBridge, + .privileges = Privilege::CreateAcct, + }), ({ {sfXChainBridge, SoeRequired}, @@ -581,11 +520,12 @@ TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, })) /** This transaction adds an attestation to an account */ -TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, - XChainAddAccountCreateAttestation, - Delegation::Delegable, - featureXChainBridge, - CreateAcct, +TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, + ({ + .delegable = Delegation::Delegable, + .amendment = featureXChainBridge, + .privileges = Privilege::CreateAcct, + }), ({ {sfXChainBridge, SoeRequired}, @@ -604,9 +544,7 @@ TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, /** This transaction modifies a sidechain */ TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, - Delegation::Delegable, - featureXChainBridge, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}), ({ {sfXChainBridge, SoeRequired}, {sfSignatureReward, SoeOptional}, @@ -615,9 +553,7 @@ TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, /** This transactions creates a sidechain */ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, - Delegation::Delegable, - featureXChainBridge, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureXChainBridge}), ({ {sfXChainBridge, SoeRequired}, {sfSignatureReward, SoeRequired}, @@ -629,9 +565,7 @@ TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, # include #endif TRANSACTION(ttDID_SET, 49, DIDSet, - Delegation::Delegable, - featureDID, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureDID}), ({ {sfDIDDocument, SoeOptional}, {sfURI, SoeOptional}, @@ -643,9 +577,7 @@ TRANSACTION(ttDID_SET, 49, DIDSet, # include #endif TRANSACTION(ttDID_DELETE, 50, DIDDelete, - Delegation::Delegable, - featureDID, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureDID}), ({})) /** This transaction type creates an Oracle instance */ @@ -653,9 +585,7 @@ TRANSACTION(ttDID_DELETE, 50, DIDDelete, # include #endif TRANSACTION(ttORACLE_SET, 51, OracleSet, - Delegation::Delegable, - featurePriceOracle, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featurePriceOracle}), ({ {sfOracleDocumentID, SoeRequired}, {sfProvider, SoeOptional}, @@ -670,9 +600,7 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet, # include #endif TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, - Delegation::Delegable, - featurePriceOracle, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featurePriceOracle}), ({ {sfOracleDocumentID, SoeRequired}, })) @@ -682,9 +610,7 @@ TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, # include #endif TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, - Delegation::Delegable, - fixNFTokenPageLinks, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = fixNFTokenPageLinks}), ({ {sfLedgerFixType, SoeRequired}, {sfOwner, SoeOptional}, @@ -696,9 +622,11 @@ TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, # include #endif TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, - Delegation::Delegable, - featureMPTokensV1, - CreateMptIssuance, + ({ + .delegable = Delegation::Delegable, + .amendment = featureMPTokensV1, + .privileges = Privilege::CreateMptIssuance, + }), ({ {sfAssetScale, SoeOptional}, {sfTransferFee, SoeOptional}, @@ -713,9 +641,11 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, # include #endif TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, - Delegation::Delegable, - featureMPTokensV1, - DestroyMptIssuance, + ({ + .delegable = Delegation::Delegable, + .amendment = featureMPTokensV1, + .privileges = Privilege::DestroyMptIssuance, + }), ({ {sfMPTokenIssuanceID, SoeRequired}, })) @@ -725,9 +655,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, # include #endif TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, - Delegation::Delegable, - featureMPTokensV1, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureMPTokensV1}), ({ {sfMPTokenIssuanceID, SoeRequired}, {sfHolder, SoeOptional}, @@ -744,9 +672,11 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, # include #endif TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, - Delegation::Delegable, - featureMPTokensV1, - MustAuthorizeMpt, + ({ + .delegable = Delegation::Delegable, + .amendment = featureMPTokensV1, + .privileges = Privilege::MustAuthorizeMpt, + }), ({ {sfMPTokenIssuanceID, SoeRequired}, {sfHolder, SoeOptional}, @@ -757,9 +687,7 @@ TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, # include #endif TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, - Delegation::Delegable, - featureCredentials, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureCredentials}), ({ {sfSubject, SoeRequired}, {sfCredentialType, SoeRequired}, @@ -772,9 +700,7 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, # include #endif TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, - Delegation::Delegable, - featureCredentials, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureCredentials}), ({ {sfIssuer, SoeRequired}, {sfCredentialType, SoeRequired}, @@ -785,9 +711,7 @@ TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, # include #endif TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, - Delegation::Delegable, - featureCredentials, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureCredentials}), ({ {sfSubject, SoeOptional}, {sfIssuer, SoeOptional}, @@ -799,9 +723,7 @@ TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, # include #endif TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, - Delegation::Delegable, - featureDynamicNFT, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureDynamicNFT}), ({ {sfNFTokenID, SoeRequired}, {sfOwner, SoeOptional}, @@ -813,9 +735,7 @@ TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, # include #endif TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, - Delegation::Delegable, - featurePermissionedDomains, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featurePermissionedDomains}), ({ {sfDomainID, SoeOptional}, {sfAcceptedCredentials, SoeRequired}, @@ -826,9 +746,7 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, # include #endif TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, - Delegation::Delegable, - featurePermissionedDomains, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featurePermissionedDomains}), ({ {sfDomainID, SoeRequired}, })) @@ -838,9 +756,9 @@ TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, # include #endif TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, - Delegation::NotDelegable, - featurePermissionDelegationV1_1, - NoPriv, + ({ + .amendment = featurePermissionDelegationV1_1, + }), ({ {sfAuthorize, SoeRequired}, {sfPermissions, SoeRequired}, @@ -851,9 +769,11 @@ TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, # include #endif TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, - Delegation::NotDelegable, - featureSingleAssetVault, - CreatePseudoAcct | CreateMptIssuance | MustModifyVault, + ({ + .amendment = featureSingleAssetVault, + .privileges = Privilege::CreatePseudoAcct | Privilege::CreateMptIssuance | + Privilege::MustModifyVault, + }), ({ {sfAsset, SoeRequired, SoeMptSupported}, {sfAssetsMaximum, SoeOptional}, @@ -872,9 +792,10 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, # include #endif TRANSACTION(ttVAULT_SET, 66, VaultSet, - Delegation::NotDelegable, - featureSingleAssetVault, - MustModifyVault, + ({ + .amendment = featureSingleAssetVault, + .privileges = Privilege::MustModifyVault, + }), ({ {sfVaultID, SoeRequired}, {sfAssetsMaximum, SoeOptional}, @@ -887,9 +808,11 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, # include #endif TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, - Delegation::NotDelegable, - featureSingleAssetVault, - MustDeleteAcct | DestroyMptIssuance | MustModifyVault, + ({ + .amendment = featureSingleAssetVault, + .privileges = Privilege::MustDeleteAcct | Privilege::DestroyMptIssuance | + Privilege::MustModifyVault, + }), ({ {sfVaultID, SoeRequired}, {sfMemoData, SoeOptional}, @@ -900,9 +823,10 @@ TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, # include #endif TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, - Delegation::NotDelegable, - featureSingleAssetVault, - MayAuthorizeMpt | MustModifyVault, + ({ + .amendment = featureSingleAssetVault, + .privileges = Privilege::MayAuthorizeMpt | Privilege::MustModifyVault, + }), ({ {sfVaultID, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, @@ -913,9 +837,11 @@ TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, # include #endif TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, - Delegation::NotDelegable, - featureSingleAssetVault, - MayDeleteMpt | MayAuthorizeMpt | MustModifyVault, + ({ + .amendment = featureSingleAssetVault, + .privileges = Privilege::MayDeleteMpt | Privilege::MayAuthorizeMpt | + Privilege::MustModifyVault, + }), ({ {sfVaultID, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, @@ -929,9 +855,10 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, # include #endif TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, - Delegation::NotDelegable, - featureSingleAssetVault, - MayDeleteMpt | MustModifyVault, + ({ + .amendment = featureSingleAssetVault, + .privileges = Privilege::MayDeleteMpt | Privilege::MustModifyVault, + }), ({ {sfVaultID, SoeRequired}, {sfHolder, SoeRequired}, @@ -943,9 +870,9 @@ TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, # include #endif TRANSACTION(ttBATCH, 71, Batch, - Delegation::NotDelegable, - featureBatchV1_1, - NoPriv, + ({ + .amendment = featureBatchV1_1, + }), ({ {sfRawTransactions, SoeRequired}, {sfBatchSigners, SoeOptional}, @@ -958,9 +885,11 @@ TRANSACTION(ttBATCH, 71, Batch, # include #endif TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, - Delegation::NotDelegable, - featureLendingProtocol, - CreatePseudoAcct | MayAuthorizeMpt, ({ + ({ + .amendment = featureLendingProtocol, + .privileges = Privilege::CreatePseudoAcct | Privilege::MayAuthorizeMpt, + }), + ({ {sfVaultID, SoeRequired}, {sfLoanBrokerID, SoeOptional}, {sfData, SoeOptional}, @@ -975,9 +904,11 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet, # include #endif TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, - Delegation::NotDelegable, - featureLendingProtocol, - MustDeleteAcct | MayAuthorizeMpt, ({ + ({ + .amendment = featureLendingProtocol, + .privileges = Privilege::MustDeleteAcct | Privilege::MayAuthorizeMpt, + }), + ({ {sfLoanBrokerID, SoeRequired}, })) @@ -986,9 +917,10 @@ TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, - Delegation::NotDelegable, - featureLendingProtocol, - NoPriv, ({ + ({ + .amendment = featureLendingProtocol, + }), + ({ {sfLoanBrokerID, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, })) @@ -998,9 +930,11 @@ TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, - Delegation::NotDelegable, - featureLendingProtocol, - MayAuthorizeMpt, ({ + ({ + .amendment = featureLendingProtocol, + .privileges = Privilege::MayAuthorizeMpt, + }), + ({ {sfLoanBrokerID, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, {sfDestination, SoeOptional}, @@ -1014,9 +948,10 @@ TRANSACTION(ttLOAN_BROKER_COVER_WITHDRAW, 77, LoanBrokerCoverWithdraw, # include #endif TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, - Delegation::NotDelegable, - featureLendingProtocol, - NoPriv, ({ + ({ + .amendment = featureLendingProtocol, + }), + ({ {sfLoanBrokerID, SoeOptional}, {sfAmount, SoeOptional, SoeMptSupported}, })) @@ -1026,9 +961,11 @@ TRANSACTION(ttLOAN_BROKER_COVER_CLAWBACK, 78, LoanBrokerCoverClawback, # include #endif TRANSACTION(ttLOAN_SET, 80, LoanSet, - Delegation::NotDelegable, - featureLendingProtocol, - MayAuthorizeMpt | MustModifyVault, ({ + ({ + .amendment = featureLendingProtocol, + .privileges = Privilege::MayAuthorizeMpt | Privilege::MustModifyVault, + }), + ({ {sfLoanBrokerID, SoeRequired}, {sfData, SoeOptional}, {sfCounterparty, SoeOptional}, @@ -1053,9 +990,10 @@ TRANSACTION(ttLOAN_SET, 80, LoanSet, # include #endif TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, - Delegation::NotDelegable, - featureLendingProtocol, - NoPriv, ({ + ({ + .amendment = featureLendingProtocol, + }), + ({ {sfLoanID, SoeRequired}, })) @@ -1064,12 +1002,14 @@ TRANSACTION(ttLOAN_DELETE, 81, LoanDelete, # include #endif TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, - Delegation::NotDelegable, - featureLendingProtocol, - // All of the LoanManage options will modify the vault, but the - // transaction can succeed without options, essentially making it - // a noop. - MayModifyVault, ({ + ({ + .amendment = featureLendingProtocol, + // All of the LoanManage options will modify the vault, but the + // transaction can succeed without options, essentially making it + // a noop. + .privileges = Privilege::MayModifyVault, + }), + ({ {sfLoanID, SoeRequired}, })) @@ -1078,9 +1018,11 @@ TRANSACTION(ttLOAN_MANAGE, 82, LoanManage, # include #endif TRANSACTION(ttLOAN_PAY, 84, LoanPay, - Delegation::NotDelegable, - featureLendingProtocol, - MayAuthorizeMpt | MustModifyVault, ({ + ({ + .amendment = featureLendingProtocol, + .privileges = Privilege::MayAuthorizeMpt | Privilege::MustModifyVault, + }), + ({ {sfLoanID, SoeRequired}, {sfAmount, SoeRequired, SoeMptSupported}, })) @@ -1090,9 +1032,9 @@ TRANSACTION(ttLOAN_PAY, 84, LoanPay, # include #endif TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert, - Delegation::NotDelegable, - featureConfidentialTransfer, - NoPriv, + ({ + .amendment = featureConfidentialTransfer, + }), ({ {sfMPTokenIssuanceID, SoeRequired}, {sfMPTAmount, SoeRequired}, @@ -1109,9 +1051,7 @@ TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT, 85, ConfidentialMPTConvert, # include #endif TRANSACTION(ttCONFIDENTIAL_MPT_MERGE_INBOX, 86, ConfidentialMPTMergeInbox, - Delegation::Delegable, - featureConfidentialTransfer, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureConfidentialTransfer}), ({ {sfMPTokenIssuanceID, SoeRequired}, })) @@ -1121,9 +1061,7 @@ TRANSACTION(ttCONFIDENTIAL_MPT_MERGE_INBOX, 86, ConfidentialMPTMergeInbox, # include #endif TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT_BACK, 87, ConfidentialMPTConvertBack, - Delegation::Delegable, - featureConfidentialTransfer, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureConfidentialTransfer}), ({ {sfMPTokenIssuanceID, SoeRequired}, {sfMPTAmount, SoeRequired}, @@ -1139,9 +1077,7 @@ TRANSACTION(ttCONFIDENTIAL_MPT_CONVERT_BACK, 87, ConfidentialMPTConvertBack, # include #endif TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend, - Delegation::Delegable, - featureConfidentialTransfer, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureConfidentialTransfer}), ({ {sfMPTokenIssuanceID, SoeRequired}, {sfDestination, SoeRequired}, @@ -1160,9 +1096,7 @@ TRANSACTION(ttCONFIDENTIAL_MPT_SEND, 88, ConfidentialMPTSend, # include #endif TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback, - Delegation::Delegable, - featureConfidentialTransfer, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureConfidentialTransfer}), ({ {sfMPTokenIssuanceID, SoeRequired}, {sfHolder, SoeRequired}, @@ -1175,9 +1109,9 @@ TRANSACTION(ttCONFIDENTIAL_MPT_CLAWBACK, 89, ConfidentialMPTClawback, # include #endif TRANSACTION(ttSPONSORSHIP_TRANSFER, 90, SponsorshipTransfer, - Delegation::NotDelegable, - featureSponsor, - NoPriv, + ({ + .amendment = featureSponsor, + }), ({ {sfObjectID, SoeOptional}, {sfSponsee, SoeOptional}, @@ -1188,9 +1122,7 @@ TRANSACTION(ttSPONSORSHIP_TRANSFER, 90, SponsorshipTransfer, # include #endif TRANSACTION(ttSPONSORSHIP_SET, 91, SponsorshipSet, - Delegation::Delegable, - featureSponsor, - NoPriv, + ({.delegable = Delegation::Delegable, .amendment = featureSponsor}), ({ {sfCounterpartySponsor, SoeOptional}, {sfSponsee, SoeOptional}, @@ -1207,9 +1139,7 @@ TRANSACTION(ttSPONSORSHIP_SET, 91, SponsorshipSet, # include #endif TRANSACTION(ttAMENDMENT, 100, EnableAmendment, - Delegation::NotDelegable, - uint256{}, - NoPriv, + ({}), ({ {sfLedgerSequence, SoeRequired}, {sfAmendment, SoeRequired}, @@ -1219,9 +1149,7 @@ TRANSACTION(ttAMENDMENT, 100, EnableAmendment, For details, see: https://xrpl.org/fee-voting.html */ TRANSACTION(ttFEE, 101, SetFee, - Delegation::NotDelegable, - uint256{}, - NoPriv, + ({}), ({ {sfLedgerSequence, SoeOptional}, // Old version uses raw numbers @@ -1240,9 +1168,7 @@ TRANSACTION(ttFEE, 101, SetFee, For details, see: https://xrpl.org/negative-unl.html */ TRANSACTION(ttUNL_MODIFY, 102, UNLModify, - Delegation::NotDelegable, - uint256{}, - NoPriv, + ({}), ({ {sfUNLModifyDisabling, SoeRequired}, {sfLedgerSequence, SoeRequired}, diff --git a/include/xrpl/protocol_autogen/transactions/AMMBid.h b/include/xrpl/protocol_autogen/transactions/AMMBid.h index 30a2b6f2ab..94d0672699 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMBid.h +++ b/include/xrpl/protocol_autogen/transactions/AMMBid.h @@ -21,7 +21,7 @@ class AMMBidBuilder; * Type: ttAMM_BID (39) * Delegable: Delegation::Delegable * Amendment: featureAMM - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use AMMBidBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AMMClawback.h b/include/xrpl/protocol_autogen/transactions/AMMClawback.h index 38aba892c4..c837b5cee6 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMClawback.h +++ b/include/xrpl/protocol_autogen/transactions/AMMClawback.h @@ -21,7 +21,7 @@ class AMMClawbackBuilder; * Type: ttAMM_CLAWBACK (31) * Delegable: Delegation::Delegable * Amendment: featureAMMClawback - * Privileges: MayDeleteAcct | OverrideFreeze | MayAuthorizeMpt + * Privileges: Privilege::MayDeleteAcct | Privilege::OverrideFreeze | Privilege::MayAuthorizeMpt * * Immutable wrapper around STTx providing type-safe field access. * Use AMMClawbackBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AMMCreate.h b/include/xrpl/protocol_autogen/transactions/AMMCreate.h index c6ccd4e860..e2e50f87ff 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMCreate.h +++ b/include/xrpl/protocol_autogen/transactions/AMMCreate.h @@ -21,7 +21,7 @@ class AMMCreateBuilder; * Type: ttAMM_CREATE (35) * Delegable: Delegation::Delegable * Amendment: featureAMM - * Privileges: CreatePseudoAcct | MayCreateMpt + * Privileges: Privilege::CreatePseudoAcct | Privilege::MayCreateMpt * * Immutable wrapper around STTx providing type-safe field access. * Use AMMCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AMMDelete.h b/include/xrpl/protocol_autogen/transactions/AMMDelete.h index 05899a46c8..86e91bf52b 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMDelete.h +++ b/include/xrpl/protocol_autogen/transactions/AMMDelete.h @@ -21,7 +21,7 @@ class AMMDeleteBuilder; * Type: ttAMM_DELETE (40) * Delegable: Delegation::Delegable * Amendment: featureAMM - * Privileges: MustDeleteAcct | MayDeleteMpt + * Privileges: Privilege::MustDeleteAcct | Privilege::MayDeleteMpt * * Immutable wrapper around STTx providing type-safe field access. * Use AMMDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AMMDeposit.h b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h index 5416547dab..fed1bd3195 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMDeposit.h +++ b/include/xrpl/protocol_autogen/transactions/AMMDeposit.h @@ -21,7 +21,7 @@ class AMMDepositBuilder; * Type: ttAMM_DEPOSIT (36) * Delegable: Delegation::Delegable * Amendment: featureAMM - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use AMMDepositBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AMMVote.h b/include/xrpl/protocol_autogen/transactions/AMMVote.h index 7dce3c252f..3fca42a232 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMVote.h +++ b/include/xrpl/protocol_autogen/transactions/AMMVote.h @@ -21,7 +21,7 @@ class AMMVoteBuilder; * Type: ttAMM_VOTE (38) * Delegable: Delegation::Delegable * Amendment: featureAMM - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use AMMVoteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h index 81258f22d6..e177011801 100644 --- a/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h +++ b/include/xrpl/protocol_autogen/transactions/AMMWithdraw.h @@ -21,7 +21,7 @@ class AMMWithdrawBuilder; * Type: ttAMM_WITHDRAW (37) * Delegable: Delegation::Delegable * Amendment: featureAMM - * Privileges: MayDeleteAcct | MayAuthorizeMpt + * Privileges: Privilege::MayDeleteAcct | Privilege::MayAuthorizeMpt * * Immutable wrapper around STTx providing type-safe field access. * Use AMMWithdrawBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AccountDelete.h b/include/xrpl/protocol_autogen/transactions/AccountDelete.h index cf6e97bb63..87ecab0c7b 100644 --- a/include/xrpl/protocol_autogen/transactions/AccountDelete.h +++ b/include/xrpl/protocol_autogen/transactions/AccountDelete.h @@ -21,7 +21,7 @@ class AccountDeleteBuilder; * Type: ttACCOUNT_DELETE (21) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: MustDeleteAcct + * Privileges: Privilege::MustDeleteAcct * * Immutable wrapper around STTx providing type-safe field access. * Use AccountDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/AccountSet.h b/include/xrpl/protocol_autogen/transactions/AccountSet.h index 55c449e78e..9f85603e22 100644 --- a/include/xrpl/protocol_autogen/transactions/AccountSet.h +++ b/include/xrpl/protocol_autogen/transactions/AccountSet.h @@ -21,7 +21,7 @@ class AccountSetBuilder; * Type: ttACCOUNT_SET (3) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use AccountSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/Batch.h b/include/xrpl/protocol_autogen/transactions/Batch.h index 1a59d2b4c0..f92aaa5348 100644 --- a/include/xrpl/protocol_autogen/transactions/Batch.h +++ b/include/xrpl/protocol_autogen/transactions/Batch.h @@ -21,7 +21,7 @@ class BatchBuilder; * Type: ttBATCH (71) * Delegable: Delegation::NotDelegable * Amendment: featureBatchV1_1 - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use BatchBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/CheckCancel.h b/include/xrpl/protocol_autogen/transactions/CheckCancel.h index b75b717e3f..cf300d3b9b 100644 --- a/include/xrpl/protocol_autogen/transactions/CheckCancel.h +++ b/include/xrpl/protocol_autogen/transactions/CheckCancel.h @@ -21,7 +21,7 @@ class CheckCancelBuilder; * Type: ttCHECK_CANCEL (18) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use CheckCancelBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/CheckCash.h b/include/xrpl/protocol_autogen/transactions/CheckCash.h index c742a15154..b80429875f 100644 --- a/include/xrpl/protocol_autogen/transactions/CheckCash.h +++ b/include/xrpl/protocol_autogen/transactions/CheckCash.h @@ -21,7 +21,7 @@ class CheckCashBuilder; * Type: ttCHECK_CASH (17) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: MayCreateMpt + * Privileges: Privilege::MayCreateMpt * * Immutable wrapper around STTx providing type-safe field access. * Use CheckCashBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/CheckCreate.h b/include/xrpl/protocol_autogen/transactions/CheckCreate.h index 63e55f8604..db51b5eb5f 100644 --- a/include/xrpl/protocol_autogen/transactions/CheckCreate.h +++ b/include/xrpl/protocol_autogen/transactions/CheckCreate.h @@ -21,7 +21,7 @@ class CheckCreateBuilder; * Type: ttCHECK_CREATE (16) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use CheckCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/Clawback.h b/include/xrpl/protocol_autogen/transactions/Clawback.h index 9a3a7f9feb..ad79f1d1fe 100644 --- a/include/xrpl/protocol_autogen/transactions/Clawback.h +++ b/include/xrpl/protocol_autogen/transactions/Clawback.h @@ -21,7 +21,7 @@ class ClawbackBuilder; * Type: ttCLAWBACK (30) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use ClawbackBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h index c80fc81dc5..bf204a35cb 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h @@ -21,7 +21,7 @@ class ConfidentialMPTClawbackBuilder; * Type: ttCONFIDENTIAL_MPT_CLAWBACK (89) * Delegable: Delegation::Delegable * Amendment: featureConfidentialTransfer - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use ConfidentialMPTClawbackBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h index 284b7f9e70..d23e6409d9 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h @@ -21,7 +21,7 @@ class ConfidentialMPTConvertBuilder; * Type: ttCONFIDENTIAL_MPT_CONVERT (85) * Delegable: Delegation::NotDelegable * Amendment: featureConfidentialTransfer - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use ConfidentialMPTConvertBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h index 53a8e64125..80ec81e6f3 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h @@ -21,7 +21,7 @@ class ConfidentialMPTConvertBackBuilder; * Type: ttCONFIDENTIAL_MPT_CONVERT_BACK (87) * Delegable: Delegation::Delegable * Amendment: featureConfidentialTransfer - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use ConfidentialMPTConvertBackBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h index 848da42a41..e3ec886acf 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h @@ -21,7 +21,7 @@ class ConfidentialMPTMergeInboxBuilder; * Type: ttCONFIDENTIAL_MPT_MERGE_INBOX (86) * Delegable: Delegation::Delegable * Amendment: featureConfidentialTransfer - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use ConfidentialMPTMergeInboxBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h index 806a2586e9..b8aac2bd48 100644 --- a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h @@ -21,7 +21,7 @@ class ConfidentialMPTSendBuilder; * Type: ttCONFIDENTIAL_MPT_SEND (88) * Delegable: Delegation::Delegable * Amendment: featureConfidentialTransfer - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use ConfidentialMPTSendBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/CredentialAccept.h b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h index f2ab546320..7ee2464460 100644 --- a/include/xrpl/protocol_autogen/transactions/CredentialAccept.h +++ b/include/xrpl/protocol_autogen/transactions/CredentialAccept.h @@ -21,7 +21,7 @@ class CredentialAcceptBuilder; * Type: ttCREDENTIAL_ACCEPT (59) * Delegable: Delegation::Delegable * Amendment: featureCredentials - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use CredentialAcceptBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/CredentialCreate.h b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h index 6cf09c852b..6ccc4e3059 100644 --- a/include/xrpl/protocol_autogen/transactions/CredentialCreate.h +++ b/include/xrpl/protocol_autogen/transactions/CredentialCreate.h @@ -21,7 +21,7 @@ class CredentialCreateBuilder; * Type: ttCREDENTIAL_CREATE (58) * Delegable: Delegation::Delegable * Amendment: featureCredentials - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use CredentialCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/CredentialDelete.h b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h index 24a2bfa62a..74039e50bf 100644 --- a/include/xrpl/protocol_autogen/transactions/CredentialDelete.h +++ b/include/xrpl/protocol_autogen/transactions/CredentialDelete.h @@ -21,7 +21,7 @@ class CredentialDeleteBuilder; * Type: ttCREDENTIAL_DELETE (60) * Delegable: Delegation::Delegable * Amendment: featureCredentials - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use CredentialDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/DIDDelete.h b/include/xrpl/protocol_autogen/transactions/DIDDelete.h index 304287883d..885f84718d 100644 --- a/include/xrpl/protocol_autogen/transactions/DIDDelete.h +++ b/include/xrpl/protocol_autogen/transactions/DIDDelete.h @@ -21,7 +21,7 @@ class DIDDeleteBuilder; * Type: ttDID_DELETE (50) * Delegable: Delegation::Delegable * Amendment: featureDID - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use DIDDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/DIDSet.h b/include/xrpl/protocol_autogen/transactions/DIDSet.h index 67e5ba23c5..0679170780 100644 --- a/include/xrpl/protocol_autogen/transactions/DIDSet.h +++ b/include/xrpl/protocol_autogen/transactions/DIDSet.h @@ -21,7 +21,7 @@ class DIDSetBuilder; * Type: ttDID_SET (49) * Delegable: Delegation::Delegable * Amendment: featureDID - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use DIDSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/DelegateSet.h b/include/xrpl/protocol_autogen/transactions/DelegateSet.h index 592a778952..1d70166920 100644 --- a/include/xrpl/protocol_autogen/transactions/DelegateSet.h +++ b/include/xrpl/protocol_autogen/transactions/DelegateSet.h @@ -21,7 +21,7 @@ class DelegateSetBuilder; * Type: ttDELEGATE_SET (64) * Delegable: Delegation::NotDelegable * Amendment: featurePermissionDelegationV1_1 - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use DelegateSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/DepositPreauth.h b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h index b5d575aac5..66c5b390e6 100644 --- a/include/xrpl/protocol_autogen/transactions/DepositPreauth.h +++ b/include/xrpl/protocol_autogen/transactions/DepositPreauth.h @@ -21,7 +21,7 @@ class DepositPreauthBuilder; * Type: ttDEPOSIT_PREAUTH (19) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use DepositPreauthBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/EnableAmendment.h b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h index e811ca16df..08a57540ec 100644 --- a/include/xrpl/protocol_autogen/transactions/EnableAmendment.h +++ b/include/xrpl/protocol_autogen/transactions/EnableAmendment.h @@ -21,7 +21,7 @@ class EnableAmendmentBuilder; * Type: ttAMENDMENT (100) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use EnableAmendmentBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCancel.h b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h index e7e49eca0d..3727bbaa2a 100644 --- a/include/xrpl/protocol_autogen/transactions/EscrowCancel.h +++ b/include/xrpl/protocol_autogen/transactions/EscrowCancel.h @@ -21,7 +21,7 @@ class EscrowCancelBuilder; * Type: ttESCROW_CANCEL (4) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use EscrowCancelBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/EscrowCreate.h b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h index b994e4ec07..3d28a12cee 100644 --- a/include/xrpl/protocol_autogen/transactions/EscrowCreate.h +++ b/include/xrpl/protocol_autogen/transactions/EscrowCreate.h @@ -21,7 +21,7 @@ class EscrowCreateBuilder; * Type: ttESCROW_CREATE (1) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use EscrowCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/EscrowFinish.h b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h index 2476def5c2..1cbc60c738 100644 --- a/include/xrpl/protocol_autogen/transactions/EscrowFinish.h +++ b/include/xrpl/protocol_autogen/transactions/EscrowFinish.h @@ -21,7 +21,7 @@ class EscrowFinishBuilder; * Type: ttESCROW_FINISH (2) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use EscrowFinishBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h index af86dea0b0..4c02989f09 100644 --- a/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h +++ b/include/xrpl/protocol_autogen/transactions/LedgerStateFix.h @@ -21,7 +21,7 @@ class LedgerStateFixBuilder; * Type: ttLEDGER_STATE_FIX (53) * Delegable: Delegation::Delegable * Amendment: fixNFTokenPageLinks - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use LedgerStateFixBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h index 875e0a4c5e..468ce054c2 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverClawback.h @@ -21,7 +21,7 @@ class LoanBrokerCoverClawbackBuilder; * Type: ttLOAN_BROKER_COVER_CLAWBACK (78) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use LoanBrokerCoverClawbackBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h index 38cc113844..0fe1bd7b91 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverDeposit.h @@ -21,7 +21,7 @@ class LoanBrokerCoverDepositBuilder; * Type: ttLOAN_BROKER_COVER_DEPOSIT (76) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use LoanBrokerCoverDepositBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h index 148db4292c..4992fb8bbd 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerCoverWithdraw.h @@ -21,7 +21,7 @@ class LoanBrokerCoverWithdrawBuilder; * Type: ttLOAN_BROKER_COVER_WITHDRAW (77) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: MayAuthorizeMpt + * Privileges: Privilege::MayAuthorizeMpt * * Immutable wrapper around STTx providing type-safe field access. * Use LoanBrokerCoverWithdrawBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h index 29b3a787fd..c449ebaff0 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerDelete.h @@ -21,7 +21,7 @@ class LoanBrokerDeleteBuilder; * Type: ttLOAN_BROKER_DELETE (75) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: MustDeleteAcct | MayAuthorizeMpt + * Privileges: Privilege::MustDeleteAcct | Privilege::MayAuthorizeMpt * * Immutable wrapper around STTx providing type-safe field access. * Use LoanBrokerDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h index 41c87c281d..18f14b7a37 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h +++ b/include/xrpl/protocol_autogen/transactions/LoanBrokerSet.h @@ -21,7 +21,7 @@ class LoanBrokerSetBuilder; * Type: ttLOAN_BROKER_SET (74) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: CreatePseudoAcct | MayAuthorizeMpt + * Privileges: Privilege::CreatePseudoAcct | Privilege::MayAuthorizeMpt * * Immutable wrapper around STTx providing type-safe field access. * Use LoanBrokerSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanDelete.h b/include/xrpl/protocol_autogen/transactions/LoanDelete.h index 8ed537b37a..2696b542da 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanDelete.h +++ b/include/xrpl/protocol_autogen/transactions/LoanDelete.h @@ -21,7 +21,7 @@ class LoanDeleteBuilder; * Type: ttLOAN_DELETE (81) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use LoanDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanManage.h b/include/xrpl/protocol_autogen/transactions/LoanManage.h index 5eb95d21b1..4a665b372f 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanManage.h +++ b/include/xrpl/protocol_autogen/transactions/LoanManage.h @@ -21,7 +21,7 @@ class LoanManageBuilder; * Type: ttLOAN_MANAGE (82) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: MayModifyVault + * Privileges: Privilege::MayModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use LoanManageBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanPay.h b/include/xrpl/protocol_autogen/transactions/LoanPay.h index 8e1faeb981..c9224fd697 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanPay.h +++ b/include/xrpl/protocol_autogen/transactions/LoanPay.h @@ -21,7 +21,7 @@ class LoanPayBuilder; * Type: ttLOAN_PAY (84) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: MayAuthorizeMpt | MustModifyVault + * Privileges: Privilege::MayAuthorizeMpt | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use LoanPayBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/LoanSet.h b/include/xrpl/protocol_autogen/transactions/LoanSet.h index 2cadebd02e..eb04a468f0 100644 --- a/include/xrpl/protocol_autogen/transactions/LoanSet.h +++ b/include/xrpl/protocol_autogen/transactions/LoanSet.h @@ -21,7 +21,7 @@ class LoanSetBuilder; * Type: ttLOAN_SET (80) * Delegable: Delegation::NotDelegable * Amendment: featureLendingProtocol - * Privileges: MayAuthorizeMpt | MustModifyVault + * Privileges: Privilege::MayAuthorizeMpt | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use LoanSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h index 2fb93eaf35..89d026928d 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenAuthorize.h @@ -21,7 +21,7 @@ class MPTokenAuthorizeBuilder; * Type: ttMPTOKEN_AUTHORIZE (57) * Delegable: Delegation::Delegable * Amendment: featureMPTokensV1 - * Privileges: MustAuthorizeMpt + * Privileges: Privilege::MustAuthorizeMpt * * Immutable wrapper around STTx providing type-safe field access. * Use MPTokenAuthorizeBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h index 82ffba9996..b83de9d843 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h @@ -21,7 +21,7 @@ class MPTokenIssuanceCreateBuilder; * Type: ttMPTOKEN_ISSUANCE_CREATE (54) * Delegable: Delegation::Delegable * Amendment: featureMPTokensV1 - * Privileges: CreateMptIssuance + * Privileges: Privilege::CreateMptIssuance * * Immutable wrapper around STTx providing type-safe field access. * Use MPTokenIssuanceCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h index cbcd206097..6d1c9b1eaa 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceDestroy.h @@ -21,7 +21,7 @@ class MPTokenIssuanceDestroyBuilder; * Type: ttMPTOKEN_ISSUANCE_DESTROY (55) * Delegable: Delegation::Delegable * Amendment: featureMPTokensV1 - * Privileges: DestroyMptIssuance + * Privileges: Privilege::DestroyMptIssuance * * Immutable wrapper around STTx providing type-safe field access. * Use MPTokenIssuanceDestroyBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h index ed7e1f0f6c..43def05194 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h @@ -21,7 +21,7 @@ class MPTokenIssuanceSetBuilder; * Type: ttMPTOKEN_ISSUANCE_SET (56) * Delegable: Delegation::Delegable * Amendment: featureMPTokensV1 - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use MPTokenIssuanceSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h index 325d2d7fbd..6c858be721 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenAcceptOffer.h @@ -21,7 +21,7 @@ class NFTokenAcceptOfferBuilder; * Type: ttNFTOKEN_ACCEPT_OFFER (29) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use NFTokenAcceptOfferBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h index ec423ea468..ac831bf45e 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenBurn.h @@ -21,7 +21,7 @@ class NFTokenBurnBuilder; * Type: ttNFTOKEN_BURN (26) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: ChangeNftCounts + * Privileges: Privilege::ChangeNftCounts * * Immutable wrapper around STTx providing type-safe field access. * Use NFTokenBurnBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h index 4c4fb1dc65..81f4f3a848 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCancelOffer.h @@ -21,7 +21,7 @@ class NFTokenCancelOfferBuilder; * Type: ttNFTOKEN_CANCEL_OFFER (28) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use NFTokenCancelOfferBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h index a535a578e0..683436f4fd 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenCreateOffer.h @@ -21,7 +21,7 @@ class NFTokenCreateOfferBuilder; * Type: ttNFTOKEN_CREATE_OFFER (27) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use NFTokenCreateOfferBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenMint.h b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h index 5af41eb3dd..5a4e3b5b1c 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenMint.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenMint.h @@ -21,7 +21,7 @@ class NFTokenMintBuilder; * Type: ttNFTOKEN_MINT (25) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: ChangeNftCounts + * Privileges: Privilege::ChangeNftCounts * * Immutable wrapper around STTx providing type-safe field access. * Use NFTokenMintBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/NFTokenModify.h b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h index 9b9701fed6..84f1e395d4 100644 --- a/include/xrpl/protocol_autogen/transactions/NFTokenModify.h +++ b/include/xrpl/protocol_autogen/transactions/NFTokenModify.h @@ -21,7 +21,7 @@ class NFTokenModifyBuilder; * Type: ttNFTOKEN_MODIFY (61) * Delegable: Delegation::Delegable * Amendment: featureDynamicNFT - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use NFTokenModifyBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/OfferCancel.h b/include/xrpl/protocol_autogen/transactions/OfferCancel.h index 5e6010e0dd..3e52ebf24b 100644 --- a/include/xrpl/protocol_autogen/transactions/OfferCancel.h +++ b/include/xrpl/protocol_autogen/transactions/OfferCancel.h @@ -21,7 +21,7 @@ class OfferCancelBuilder; * Type: ttOFFER_CANCEL (8) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use OfferCancelBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/OfferCreate.h b/include/xrpl/protocol_autogen/transactions/OfferCreate.h index ffc1216297..774921d87a 100644 --- a/include/xrpl/protocol_autogen/transactions/OfferCreate.h +++ b/include/xrpl/protocol_autogen/transactions/OfferCreate.h @@ -21,7 +21,7 @@ class OfferCreateBuilder; * Type: ttOFFER_CREATE (7) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: MayCreateMpt + * Privileges: Privilege::MayCreateMpt * * Immutable wrapper around STTx providing type-safe field access. * Use OfferCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/OracleDelete.h b/include/xrpl/protocol_autogen/transactions/OracleDelete.h index ebdc8fb7e9..e50b6f6b02 100644 --- a/include/xrpl/protocol_autogen/transactions/OracleDelete.h +++ b/include/xrpl/protocol_autogen/transactions/OracleDelete.h @@ -21,7 +21,7 @@ class OracleDeleteBuilder; * Type: ttORACLE_DELETE (52) * Delegable: Delegation::Delegable * Amendment: featurePriceOracle - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use OracleDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/OracleSet.h b/include/xrpl/protocol_autogen/transactions/OracleSet.h index 0ec6d5cad0..03e4ffc518 100644 --- a/include/xrpl/protocol_autogen/transactions/OracleSet.h +++ b/include/xrpl/protocol_autogen/transactions/OracleSet.h @@ -21,7 +21,7 @@ class OracleSetBuilder; * Type: ttORACLE_SET (51) * Delegable: Delegation::Delegable * Amendment: featurePriceOracle - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use OracleSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/Payment.h b/include/xrpl/protocol_autogen/transactions/Payment.h index 389900bf12..cb177a8d08 100644 --- a/include/xrpl/protocol_autogen/transactions/Payment.h +++ b/include/xrpl/protocol_autogen/transactions/Payment.h @@ -21,7 +21,7 @@ class PaymentBuilder; * Type: ttPAYMENT (0) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: CreateAcct | MayCreateMpt + * Privileges: Privilege::CreateAcct | Privilege::MayCreateMpt * * Immutable wrapper around STTx providing type-safe field access. * Use PaymentBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h index 4c567b13f4..06892955db 100644 --- a/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelClaim.h @@ -21,7 +21,7 @@ class PaymentChannelClaimBuilder; * Type: ttPAYCHAN_CLAIM (15) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use PaymentChannelClaimBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h index 0a513d575a..2a3aebca4c 100644 --- a/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelCreate.h @@ -21,7 +21,7 @@ class PaymentChannelCreateBuilder; * Type: ttPAYCHAN_CREATE (13) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use PaymentChannelCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h index 51210dd796..9a8c452b0b 100644 --- a/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h +++ b/include/xrpl/protocol_autogen/transactions/PaymentChannelFund.h @@ -21,7 +21,7 @@ class PaymentChannelFundBuilder; * Type: ttPAYCHAN_FUND (14) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use PaymentChannelFundBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h index 3db921776c..1b16b13116 100644 --- a/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainDelete.h @@ -21,7 +21,7 @@ class PermissionedDomainDeleteBuilder; * Type: ttPERMISSIONED_DOMAIN_DELETE (63) * Delegable: Delegation::Delegable * Amendment: featurePermissionedDomains - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use PermissionedDomainDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h index 3e352cad76..30832aec8c 100644 --- a/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h +++ b/include/xrpl/protocol_autogen/transactions/PermissionedDomainSet.h @@ -21,7 +21,7 @@ class PermissionedDomainSetBuilder; * Type: ttPERMISSIONED_DOMAIN_SET (62) * Delegable: Delegation::Delegable * Amendment: featurePermissionedDomains - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use PermissionedDomainSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/SetFee.h b/include/xrpl/protocol_autogen/transactions/SetFee.h index 177f39199b..9513723e94 100644 --- a/include/xrpl/protocol_autogen/transactions/SetFee.h +++ b/include/xrpl/protocol_autogen/transactions/SetFee.h @@ -21,7 +21,7 @@ class SetFeeBuilder; * Type: ttFEE (101) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use SetFeeBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/SetRegularKey.h b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h index a943bb0279..042676251b 100644 --- a/include/xrpl/protocol_autogen/transactions/SetRegularKey.h +++ b/include/xrpl/protocol_autogen/transactions/SetRegularKey.h @@ -21,7 +21,7 @@ class SetRegularKeyBuilder; * Type: ttREGULAR_KEY_SET (5) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use SetRegularKeyBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/SignerListSet.h b/include/xrpl/protocol_autogen/transactions/SignerListSet.h index 6e9d0e41ba..253bcccc1a 100644 --- a/include/xrpl/protocol_autogen/transactions/SignerListSet.h +++ b/include/xrpl/protocol_autogen/transactions/SignerListSet.h @@ -21,7 +21,7 @@ class SignerListSetBuilder; * Type: ttSIGNER_LIST_SET (12) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use SignerListSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h index dfd12a329f..bb3eb2ccf0 100644 --- a/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipSet.h @@ -21,7 +21,7 @@ class SponsorshipSetBuilder; * Type: ttSPONSORSHIP_SET (91) * Delegable: Delegation::Delegable * Amendment: featureSponsor - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use SponsorshipSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h b/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h index ab26e887e3..5bd5bc1319 100644 --- a/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h +++ b/include/xrpl/protocol_autogen/transactions/SponsorshipTransfer.h @@ -21,7 +21,7 @@ class SponsorshipTransferBuilder; * Type: ttSPONSORSHIP_TRANSFER (90) * Delegable: Delegation::NotDelegable * Amendment: featureSponsor - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use SponsorshipTransferBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/TicketCreate.h b/include/xrpl/protocol_autogen/transactions/TicketCreate.h index 0d8670a76a..4cb109b8f2 100644 --- a/include/xrpl/protocol_autogen/transactions/TicketCreate.h +++ b/include/xrpl/protocol_autogen/transactions/TicketCreate.h @@ -21,7 +21,7 @@ class TicketCreateBuilder; * Type: ttTICKET_CREATE (10) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use TicketCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/TrustSet.h b/include/xrpl/protocol_autogen/transactions/TrustSet.h index 22891b94ec..9d939eb1d0 100644 --- a/include/xrpl/protocol_autogen/transactions/TrustSet.h +++ b/include/xrpl/protocol_autogen/transactions/TrustSet.h @@ -21,7 +21,7 @@ class TrustSetBuilder; * Type: ttTRUST_SET (20) * Delegable: Delegation::Delegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use TrustSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/UNLModify.h b/include/xrpl/protocol_autogen/transactions/UNLModify.h index 6569e4bf7d..f5c94071d7 100644 --- a/include/xrpl/protocol_autogen/transactions/UNLModify.h +++ b/include/xrpl/protocol_autogen/transactions/UNLModify.h @@ -21,7 +21,7 @@ class UNLModifyBuilder; * Type: ttUNL_MODIFY (102) * Delegable: Delegation::NotDelegable * Amendment: uint256{} - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use UNLModifyBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/VaultClawback.h b/include/xrpl/protocol_autogen/transactions/VaultClawback.h index 270ccc94bb..d859b4a446 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultClawback.h +++ b/include/xrpl/protocol_autogen/transactions/VaultClawback.h @@ -21,7 +21,7 @@ class VaultClawbackBuilder; * Type: ttVAULT_CLAWBACK (70) * Delegable: Delegation::NotDelegable * Amendment: featureSingleAssetVault - * Privileges: MayDeleteMpt | MustModifyVault + * Privileges: Privilege::MayDeleteMpt | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use VaultClawbackBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/VaultCreate.h b/include/xrpl/protocol_autogen/transactions/VaultCreate.h index e206925e02..2925302dec 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultCreate.h +++ b/include/xrpl/protocol_autogen/transactions/VaultCreate.h @@ -21,7 +21,7 @@ class VaultCreateBuilder; * Type: ttVAULT_CREATE (65) * Delegable: Delegation::NotDelegable * Amendment: featureSingleAssetVault - * Privileges: CreatePseudoAcct | CreateMptIssuance | MustModifyVault + * Privileges: Privilege::CreatePseudoAcct | Privilege::CreateMptIssuance | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use VaultCreateBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/VaultDelete.h b/include/xrpl/protocol_autogen/transactions/VaultDelete.h index 67cc32f543..3cef0ce599 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultDelete.h +++ b/include/xrpl/protocol_autogen/transactions/VaultDelete.h @@ -21,7 +21,7 @@ class VaultDeleteBuilder; * Type: ttVAULT_DELETE (67) * Delegable: Delegation::NotDelegable * Amendment: featureSingleAssetVault - * Privileges: MustDeleteAcct | DestroyMptIssuance | MustModifyVault + * Privileges: Privilege::MustDeleteAcct | Privilege::DestroyMptIssuance | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use VaultDeleteBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/VaultDeposit.h b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h index 5bb5362114..099342aa0c 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultDeposit.h +++ b/include/xrpl/protocol_autogen/transactions/VaultDeposit.h @@ -21,7 +21,7 @@ class VaultDepositBuilder; * Type: ttVAULT_DEPOSIT (68) * Delegable: Delegation::NotDelegable * Amendment: featureSingleAssetVault - * Privileges: MayAuthorizeMpt | MustModifyVault + * Privileges: Privilege::MayAuthorizeMpt | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use VaultDepositBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/VaultSet.h b/include/xrpl/protocol_autogen/transactions/VaultSet.h index 14df70f13b..33dfe8bf21 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultSet.h +++ b/include/xrpl/protocol_autogen/transactions/VaultSet.h @@ -21,7 +21,7 @@ class VaultSetBuilder; * Type: ttVAULT_SET (66) * Delegable: Delegation::NotDelegable * Amendment: featureSingleAssetVault - * Privileges: MustModifyVault + * Privileges: Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use VaultSetBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h index 17208cd76c..dfa662f8fd 100644 --- a/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h +++ b/include/xrpl/protocol_autogen/transactions/VaultWithdraw.h @@ -21,7 +21,7 @@ class VaultWithdrawBuilder; * Type: ttVAULT_WITHDRAW (69) * Delegable: Delegation::NotDelegable * Amendment: featureSingleAssetVault - * Privileges: MayDeleteMpt | MayAuthorizeMpt | MustModifyVault + * Privileges: Privilege::MayDeleteMpt | Privilege::MayAuthorizeMpt | Privilege::MustModifyVault * * Immutable wrapper around STTx providing type-safe field access. * Use VaultWithdrawBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h index b8d551c5e1..a9aa7c2343 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h +++ b/include/xrpl/protocol_autogen/transactions/XChainAccountCreateCommit.h @@ -21,7 +21,7 @@ class XChainAccountCreateCommitBuilder; * Type: ttXCHAIN_ACCOUNT_CREATE_COMMIT (44) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use XChainAccountCreateCommitBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h index 22b57803dc..9cb1f2eaaf 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h +++ b/include/xrpl/protocol_autogen/transactions/XChainAddAccountCreateAttestation.h @@ -21,7 +21,7 @@ class XChainAddAccountCreateAttestationBuilder; * Type: ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION (46) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: CreateAcct + * Privileges: Privilege::CreateAcct * * Immutable wrapper around STTx providing type-safe field access. * Use XChainAddAccountCreateAttestationBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h index 5e80c05aae..9184c83958 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h +++ b/include/xrpl/protocol_autogen/transactions/XChainAddClaimAttestation.h @@ -21,7 +21,7 @@ class XChainAddClaimAttestationBuilder; * Type: ttXCHAIN_ADD_CLAIM_ATTESTATION (45) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: CreateAcct + * Privileges: Privilege::CreateAcct * * Immutable wrapper around STTx providing type-safe field access. * Use XChainAddClaimAttestationBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainClaim.h b/include/xrpl/protocol_autogen/transactions/XChainClaim.h index ec403b5eb8..e49434c878 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainClaim.h +++ b/include/xrpl/protocol_autogen/transactions/XChainClaim.h @@ -21,7 +21,7 @@ class XChainClaimBuilder; * Type: ttXCHAIN_CLAIM (43) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use XChainClaimBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainCommit.h b/include/xrpl/protocol_autogen/transactions/XChainCommit.h index 48b2263645..471a58dc53 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainCommit.h +++ b/include/xrpl/protocol_autogen/transactions/XChainCommit.h @@ -21,7 +21,7 @@ class XChainCommitBuilder; * Type: ttXCHAIN_COMMIT (42) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use XChainCommitBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h index 9614b0bd88..ae1269e825 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateBridge.h @@ -21,7 +21,7 @@ class XChainCreateBridgeBuilder; * Type: ttXCHAIN_CREATE_BRIDGE (48) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use XChainCreateBridgeBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h index d17759619f..4c6f98e48f 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h +++ b/include/xrpl/protocol_autogen/transactions/XChainCreateClaimID.h @@ -21,7 +21,7 @@ class XChainCreateClaimIDBuilder; * Type: ttXCHAIN_CREATE_CLAIM_ID (41) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use XChainCreateClaimIDBuilder to construct new transactions. diff --git a/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h index e79c9139ce..a3f2930668 100644 --- a/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h +++ b/include/xrpl/protocol_autogen/transactions/XChainModifyBridge.h @@ -21,7 +21,7 @@ class XChainModifyBridgeBuilder; * Type: ttXCHAIN_MODIFY_BRIDGE (47) * Delegable: Delegation::Delegable * Amendment: featureXChainBridge - * Privileges: NoPriv + * Privileges: Privilege::NoPriv * * Immutable wrapper around STTx providing type-safe field access. * Use XChainModifyBridgeBuilder to construct new transactions. diff --git a/include/xrpl/tx/invariants/InvariantCheckPrivilege.h b/include/xrpl/tx/invariants/InvariantCheckPrivilege.h index b2f1c62a54..ca9755ea1c 100644 --- a/include/xrpl/tx/invariants/InvariantCheckPrivilege.h +++ b/include/xrpl/tx/invariants/InvariantCheckPrivilege.h @@ -1,9 +1,7 @@ #pragma once -#include #include - -#include +#include // IWYU pragma: export namespace xrpl { @@ -26,37 +24,8 @@ not have the relevant amendments enabled_. It's intentionally a pain in the neck so that bad code gets caught and fixed as early as possible. */ -// Bitwise flags, 86 files, used in macros files -// NOLINTNEXTLINE(cppcoreguidelines-use-enum-class) -enum Privilege { - NoPriv = 0x0000, // The transaction can not do any of the enumerated operations - CreateAcct = 0x0001, // The transaction can create a new ACCOUNT_ROOT object. - CreatePseudoAcct = 0x0002, // The transaction can create a pseudo account, - // which implies createAcct - MustDeleteAcct = 0x0004, // The transaction must delete an ACCOUNT_ROOT object - MayDeleteAcct = 0x0008, // The transaction may delete an ACCOUNT_ROOT - // object, but does not have to - OverrideFreeze = 0x0010, // The transaction can override some freeze rules - ChangeNftCounts = 0x0020, // The transaction can mint or burn an NFT - CreateMptIssuance = 0x0040, // The transaction can create a new MPT issuance - DestroyMptIssuance = 0x0080, // The transaction can destroy an MPT issuance - MustAuthorizeMpt = 0x0100, // The transaction MUST create or delete an MPT - // object (except by issuer) - MayAuthorizeMpt = 0x0200, // The transaction MAY create or delete an MPT - // object (except by issuer) - MayDeleteMpt = 0x0400, // The transaction MAY delete an MPT object. May not create. - MustModifyVault = 0x0800, // The transaction must modify, delete or create, a vault - MayModifyVault = 0x1000, // The transaction MAY modify, delete or create, a vault - MayCreateMpt = 0x2000, // The transaction MAY create an MPT object, except for issuer. -}; - -constexpr Privilege -operator|(Privilege lhs, Privilege rhs) -{ - return safeCast( - safeCast>(lhs) | - safeCast>(rhs)); -} +// `enum Privilege` and its `operator|` live in , +// alongside the TxSettings struct that carries them out of transactions.macro. bool hasPrivilege(STTx const& tx, Privilege priv); diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index 2f3e25f823..a5adb294e9 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -10,6 +10,7 @@ #include #include // IWYU pragma: keep #include +#include #include #include @@ -40,16 +41,24 @@ Permission::GranularPermissionEntry::GranularPermissionEntry( Permission::Permission() { { +#pragma push_macro("UNWRAP") +#undef UNWRAP #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, delegable, amendment, ...) \ - txDelegationMap_[static_cast(value)] = {amendment, delegable}; +#define UNWRAP(...) __VA_ARGS__ +#define TRANSACTION(tag, value, name, settings, ...) \ + { \ + TxSettings const s = UNWRAP settings; \ + txDelegationMap_[static_cast(value)] = {s.amendment, s.delegable}; \ + } #include #undef TRANSACTION #pragma pop_macro("TRANSACTION") +#undef UNWRAP +#pragma pop_macro("UNWRAP") } granularPermissionsByName_ = { @@ -242,7 +251,7 @@ Permission::isDelegable(std::uint32_t permissionValue, Rules const& rules) const // Tx-level permissions require the transaction type itself to be delegable, and // the corresponding amendment enabled. - return txIt != txDelegationMap_.end() && txIt->second.delegable != NotDelegable && + return txIt != txDelegationMap_.end() && txIt->second.delegable != Delegation::NotDelegable && amendmentEnabled(txIt->second); } diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index e4d4c4b03c..c393c606fe 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -45,7 +45,7 @@ TxFormats::TxFormats() #undef TRANSACTION #define UNWRAP(...) __VA_ARGS__ -#define TRANSACTION(tag, value, name, delegable, amendment, privileges, fields) \ +#define TRANSACTION(tag, value, name, settings, fields) \ add(jss::name, tag, UNWRAP fields, getCommonFields()); #include diff --git a/src/libxrpl/tx/invariants/FreezeInvariant.cpp b/src/libxrpl/tx/invariants/FreezeInvariant.cpp index d6039eabd8..272e52f09a 100644 --- a/src/libxrpl/tx/invariants/FreezeInvariant.cpp +++ b/src/libxrpl/tx/invariants/FreezeInvariant.cpp @@ -288,7 +288,8 @@ TransfersNotFrozen::validateFrozenState( // individually-frozen or deep-frozen AMM trust lines. // Post-fixCleanup3_4_0: AMMClawbacks are allowed to override all freeze types. bool const isAMMLine = change.line->isFlag(lsfAMMNode); - if ((fixOverrideFreeze || !isAMMLine || globalFreeze) && hasPrivilege(tx, OverrideFreeze)) + if ((fixOverrideFreeze || !isAMMLine || globalFreeze) && + hasPrivilege(tx, Privilege::OverrideFreeze)) { JLOG(j.debug()) << "Invariant check allowing funds to be moved " << (change.balanceChangeSign > 0 ? "to" : "from") diff --git a/src/libxrpl/tx/invariants/InvariantCheck.cpp b/src/libxrpl/tx/invariants/InvariantCheck.cpp index 369206d9e6..aa4df8db42 100644 --- a/src/libxrpl/tx/invariants/InvariantCheck.cpp +++ b/src/libxrpl/tx/invariants/InvariantCheck.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -40,12 +41,15 @@ namespace xrpl { +#pragma push_macro("UNWRAP") +#undef UNWRAP #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, delegable, amendment, privileges, ...) \ - case tag: { \ - return (privileges) & priv; \ +#define UNWRAP(...) __VA_ARGS__ +#define TRANSACTION(tag, value, name, settings, ...) \ + case tag: { \ + return ((TxSettings UNWRAP settings).privileges & priv) != Privilege::NoPriv; \ } bool @@ -63,6 +67,8 @@ hasPrivilege(STTx const& tx, Privilege priv) #undef TRANSACTION #pragma pop_macro("TRANSACTION") +#undef UNWRAP +#pragma pop_macro("UNWRAP") // Returns the human-readable name of a ledger entry's type, falling back to // the numeric type if the format is somehow unknown. @@ -436,7 +442,7 @@ AccountRootsNotDeleted::finalize( // transaction when the total AMM LP Tokens balance goes to 0. // A successful AccountDelete or AMMDelete MUST delete exactly // one account root. - if (hasPrivilege(tx, MustDeleteAcct) && isTesSuccess(result)) + if (hasPrivilege(tx, Privilege::MustDeleteAcct) && isTesSuccess(result)) { if (accountsDeleted_ == 1) return true; @@ -457,7 +463,7 @@ AccountRootsNotDeleted::finalize( // A successful AMMWithdraw/AMMClawback MAY delete one account root // when the total AMM LP Tokens balance goes to 0. Not every AMM withdraw // deletes the AMM account, accountsDeleted_ is set if it is deleted. - if (hasPrivilege(tx, MayDeleteAcct) && isTesSuccess(result) && accountsDeleted_ == 1) + if (hasPrivilege(tx, Privilege::MayDeleteAcct) && isTesSuccess(result) && accountsDeleted_ == 1) return true; if (accountsDeleted_ == 0) @@ -760,14 +766,15 @@ ValidNewAccountRoot::finalize( } // From this point on we know exactly one account was created. - if (hasPrivilege(tx, CreateAcct | CreatePseudoAcct) && isTesSuccess(result)) + if (hasPrivilege(tx, Privilege::CreateAcct | Privilege::CreatePseudoAcct) && + isTesSuccess(result)) { bool const pseudoAccount = (pseudoAccount_ && (view.rules().enabled(featureSingleAssetVault) || view.rules().enabled(featureLendingProtocol))); - if (pseudoAccount && !hasPrivilege(tx, CreatePseudoAcct)) + if (pseudoAccount && !hasPrivilege(tx, Privilege::CreatePseudoAcct)) { JLOG(j.fatal()) << "Invariant failed: pseudo-account created by a " "wrong transaction type"; diff --git a/src/libxrpl/tx/invariants/MPTInvariant.cpp b/src/libxrpl/tx/invariants/MPTInvariant.cpp index 045d03ab02..89ade024e6 100644 --- a/src/libxrpl/tx/invariants/MPTInvariant.cpp +++ b/src/libxrpl/tx/invariants/MPTInvariant.cpp @@ -211,7 +211,7 @@ ValidMPTIssuance::finalize( } auto const txnType = tx.getTxnType(); - if (hasPrivilege(tx, CreateMptIssuance)) + if (hasPrivilege(tx, Privilege::CreateMptIssuance)) { if (mptIssuancesCreated_ == 0) { @@ -232,7 +232,7 @@ ValidMPTIssuance::finalize( return mptIssuancesCreated_ == 1 && mptIssuancesDeleted_ == 0; } - if (hasPrivilege(tx, DestroyMptIssuance)) + if (hasPrivilege(tx, Privilege::DestroyMptIssuance)) { if (mptIssuancesDeleted_ == 0) { @@ -259,7 +259,8 @@ ValidMPTIssuance::finalize( // non-amendment-gated side effects. bool const enforceEscrowFinish = (txnType == ttESCROW_FINISH) && (rules.enabled(featureSingleAssetVault) || lendingProtocolEnabled); - if (hasPrivilege(tx, MustAuthorizeMpt | MayAuthorizeMpt) || enforceEscrowFinish) + if (hasPrivilege(tx, Privilege::MustAuthorizeMpt | Privilege::MayAuthorizeMpt) || + enforceEscrowFinish) { bool const submittedByIssuer = tx.isFieldPresent(sfHolder); @@ -275,7 +276,7 @@ ValidMPTIssuance::finalize( "succeeded but deleted issuances"; return false; } - if (mptV2Enabled && hasPrivilege(tx, MayAuthorizeMpt) && + if (mptV2Enabled && hasPrivilege(tx, Privilege::MayAuthorizeMpt) && (txnType == ttAMM_WITHDRAW || txnType == ttAMM_CLAWBACK)) { if (submittedByIssuer && txnType == ttAMM_WITHDRAW && mptokensCreated_ > 0) @@ -311,7 +312,7 @@ ValidMPTIssuance::finalize( return false; } else if ( - !submittedByIssuer && hasPrivilege(tx, MustAuthorizeMpt) && + !submittedByIssuer && hasPrivilege(tx, Privilege::MustAuthorizeMpt) && (mptokensCreated_ + mptokensDeleted_ != 1)) { // if the holder submitted this tx, then a mptoken must be @@ -324,7 +325,7 @@ ValidMPTIssuance::finalize( return true; } - if (hasPrivilege(tx, MayCreateMpt)) + if (hasPrivilege(tx, Privilege::MayCreateMpt)) { bool const submittedByIssuer = tx.isFieldPresent(sfHolder); @@ -379,7 +380,7 @@ ValidMPTIssuance::finalize( return true; } - if (hasPrivilege(tx, MayDeleteMpt) && + if (hasPrivilege(tx, Privilege::MayDeleteMpt) && ((txnType == ttAMM_DELETE && mptokensDeleted_ <= 2) || mptokensDeleted_ == 1) && mptokensCreated_ == 0 && mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 0) return true; @@ -856,7 +857,7 @@ ValidMPTTransfer::finalize( ReadView const& view, beast::Journal const& j) { - if (hasPrivilege(tx, OverrideFreeze)) + if (hasPrivilege(tx, Privilege::OverrideFreeze)) return true; // XLS-0066: a broker must be able to default an already-late loan diff --git a/src/libxrpl/tx/invariants/NFTInvariant.cpp b/src/libxrpl/tx/invariants/NFTInvariant.cpp index 52ecbcd9d1..b3b1601018 100644 --- a/src/libxrpl/tx/invariants/NFTInvariant.cpp +++ b/src/libxrpl/tx/invariants/NFTInvariant.cpp @@ -206,7 +206,7 @@ NFTokenCountTracking::finalize( ReadView const& view, beast::Journal const& j) const { - if (!hasPrivilege(tx, ChangeNftCounts)) + if (!hasPrivilege(tx, Privilege::ChangeNftCounts)) { if (beforeMintedTotal_ != afterMintedTotal_) { diff --git a/src/libxrpl/tx/invariants/VaultInvariant.cpp b/src/libxrpl/tx/invariants/VaultInvariant.cpp index 5c25a22987..7ba42383ad 100644 --- a/src/libxrpl/tx/invariants/VaultInvariant.cpp +++ b/src/libxrpl/tx/invariants/VaultInvariant.cpp @@ -346,7 +346,7 @@ ValidVault::finalize( if (afterVault_.empty() && beforeVault_.empty()) { - if (hasPrivilege(tx, MustModifyVault)) + if (hasPrivilege(tx, Privilege::MustModifyVault)) { JLOG(j.fatal()) << // "Invariant failed: vault operation succeeded without modifying " @@ -357,7 +357,8 @@ ValidVault::finalize( return true; // Not a vault operation } - if (!(hasPrivilege(tx, MustModifyVault) || hasPrivilege(tx, MayModifyVault))) + if (!(hasPrivilege(tx, Privilege::MustModifyVault) || + hasPrivilege(tx, Privilege::MayModifyVault))) { JLOG(j.fatal()) << // "Invariant failed: vault updated by a wrong transaction type"; diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 1166816115..3ff90c2a8f 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -2718,19 +2719,24 @@ class Delegate_test : public beast::unit_test::Suite std::size_t delegableCount = 0; +#pragma push_macro("UNWRAP") +#undef UNWRAP #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, txDelegable, ...) \ - if (txDelegable == xrpl::Delegable) \ - { \ - delegableCount++; \ +#define UNWRAP(...) __VA_ARGS__ +#define TRANSACTION(tag, value, name, settings, ...) \ + if ((xrpl::TxSettings UNWRAP settings).delegable == xrpl::Delegation::Delegable) \ + { \ + delegableCount++; \ } #include #undef TRANSACTION #pragma pop_macro("TRANSACTION") +#undef UNWRAP +#pragma pop_macro("UNWRAP") // ==================================================================== // IMPORTANT NOTICE: From d27beef500943c7fb920a9f51eda87a23ccf8ae3 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Thu, 20 Aug 2026 19:40:42 +0000 Subject: [PATCH 5/7] perf: Speed up addition time for drastically different exponents (#7825) --- src/libxrpl/basics/Number.cpp | 60 ++++++++-- src/test/protocol/STNumber_test.cpp | 80 +++++--------- src/tests/libxrpl/basics/Number.cpp | 165 ++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 61 deletions(-) diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index 1f2c41809a..0917627073 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -260,6 +260,11 @@ public: unsigned pop() noexcept; + // if true, there are no recoverable digits in the guard, though there may be dropped digits + // (xbit_) + [[nodiscard]] bool + unrecoverable() const noexcept; + // if true, there are no digits in the guard, including dropped digits (xbit_) [[nodiscard]] bool empty() const noexcept; @@ -277,6 +282,17 @@ public: void doDropDigit(T& mantissa, int& exponent) noexcept; + /** + * Drop a digit from the mantissa, and increment the exponent, storing the dropped digit in + * this Guard. + * + * If a drop will not do anything meaningful (there are no recoverable digits in the guard, and + * the mantissa is 0), and if targetExponent > exponent, simply set exponent to targetExponent. + */ + template + void + doDropDigitWithTarget(T& mantissa, int& exponent, int const targetExponent) noexcept; + // Modify the result to the correctly rounded value template void @@ -374,10 +390,16 @@ Number::Guard::pop() noexcept return d; } +inline bool +Number::Guard::unrecoverable() const noexcept +{ + return digits_ == 0; +} + inline bool Number::Guard::empty() const noexcept { - return digits_ == 0 && !xbit_; + return unrecoverable() && !xbit_; } template @@ -401,6 +423,25 @@ Number::Guard::doDropDigit(uint128_t& mantissa, int& exponent) noexce ++exponent; } +template +void +Number::Guard::doDropDigitWithTarget(T& mantissa, int& exponent, int const targetExponent) noexcept +{ + XRPL_ASSERT( + exponent < targetExponent, "xrpl::Number::Guard::doDropDigitWithTarget : something to do"); + while (exponent < targetExponent) + { + if (mantissa == 0 && unrecoverable()) + { + // No number of dropped digits is going to change anything except the exponent at this + // point, so just jump to the result + exponent = targetExponent; + return; + } + doDropDigit(mantissa, exponent); + } +} + template void Number::Guard::pushOverflow(T mantissa) @@ -928,6 +969,7 @@ Number::operator+=(Number const& y) // to match, if necessary. auto const adjust = [&g, &upperLimit]( uint128_t& expandM, int& expandE, uint128_t& shrinkM, int& shrinkE) { + XRPL_ASSERT(shrinkE < expandE, "xrpl::Number::operator+= : exponents ordered correctly"); // Adjust up and down until the exponents match if (g.cuspRoundingFix == MantissaRange::CuspRoundingFix::Enabled330) { @@ -935,6 +977,8 @@ Number::operator+=(Number const& y) // 1. First, shrink the mantissa of shrinkM/shrinkE while shrinkM ends in 0. while (shrinkE < expandE && shrinkM % 10 == 0) { + // Don't use doDropDigitWithTarget here, because the loop will stop before the + // mantissa gets to 0. g.doDropDigit(shrinkM, shrinkE); } @@ -950,10 +994,11 @@ Number::operator+=(Number const& y) // 3. Finally, shrink the mantissa of shrinkM/shrinkE until the exponents match. Any removed // digits will be put into the Guard. This is the only step for non-Enabled330 modes. - while (shrinkE < expandE) + if (shrinkE < expandE) { - g.doDropDigit(shrinkM, shrinkE); + g.doDropDigitWithTarget(shrinkM, shrinkE, expandE); } + XRPL_ASSERT(shrinkE == expandE, "xrpl::Number::operator+= : exponents are equal"); }; // Shrink the mantissa and raise the exponent of the value with the lower exponent. Store any @@ -996,7 +1041,7 @@ Number::operator+=(Number const& y) // round. XRPL_ASSERT( xm > maxMantissa || g.empty(), - "xrpl::Number::operator+ : rounding state expected after add"); + "xrpl::Number::operator+= : rounding state expected after add"); } else { @@ -1038,7 +1083,7 @@ Number::operator+=(Number const& y) } XRPL_ASSERT( xm > maxMantissa || g.empty(), - "xrpl::Number::operator+ : rounding state expected after subtract"); + "xrpl::Number::operator+= : rounding state expected after subtract"); } else { @@ -1330,9 +1375,10 @@ operator rep() const g.setNegative(); drops = -drops; } - while (offset < 0) + if (offset < 0) { - g.doDropDigit(drops, offset); + g.doDropDigitWithTarget(drops, offset, 0); + XRPL_ASSERT(offset == 0, "xrpl::Number::operator rep() : exponents are equal"); } for (; offset > 0; --offset) { diff --git a/src/test/protocol/STNumber_test.cpp b/src/test/protocol/STNumber_test.cpp index 74792e0a70..1e5027df49 100644 --- a/src/test/protocol/STNumber_test.cpp +++ b/src/test/protocol/STNumber_test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -176,61 +177,32 @@ struct STNumber_test : public beast::unit_test::Suite numberFromJson(sfNumber, std::to_string(kUMax)) == STNumber(sfNumber, Number(kUMax, 0))); + auto const expectJsonThrows = [this]( + json::Value const& num, std::string const& expected) { + try + { + numberFromJson(sfNumber, num); + fail(); + } + catch (std::exception const& e) + { + std::ostringstream out; + out << "Json: " << num.asString() << " got exception: " << e.what() + << ", expected: " << expected; + BEAST_EXPECTS(std::string(e.what()) == expected, out.str()); + } + }; + + // Obvious overflows tested here + expectJsonThrows("1e2000000", "Number::normalize 2"); + expectJsonThrows("1e2000000000", "Number::normalize 2"); + // Obvious non-numbers tested here - try - { - auto _ = numberFromJson(sfNumber, ""); - BEAST_EXPECT(false); - } - catch (std::runtime_error const& e) - { - std::string const expected = "'' is not a number"; - BEAST_EXPECT(e.what() == expected); - } - - try - { - auto _ = numberFromJson(sfNumber, "e"); - BEAST_EXPECT(false); - } - catch (std::runtime_error const& e) - { - std::string const expected = "'e' is not a number"; - BEAST_EXPECT(e.what() == expected); - } - - try - { - auto _ = numberFromJson(sfNumber, "1e"); - BEAST_EXPECT(false); - } - catch (std::runtime_error const& e) - { - std::string const expected = "'1e' is not a number"; - BEAST_EXPECT(e.what() == expected); - } - - try - { - auto _ = numberFromJson(sfNumber, "e2"); - BEAST_EXPECT(false); - } - catch (std::runtime_error const& e) - { - std::string const expected = "'e2' is not a number"; - BEAST_EXPECT(e.what() == expected); - } - - try - { - auto _ = numberFromJson(sfNumber, json::Value()); - BEAST_EXPECT(false); - } - catch (std::runtime_error const& e) - { - std::string const expected = "not a number"; - BEAST_EXPECT(e.what() == expected); - } + expectJsonThrows("", "'' is not a number"); + expectJsonThrows("e", "'e' is not a number"); + expectJsonThrows("1e", "'1e' is not a number"); + expectJsonThrows("e2", "'e2' is not a number"); + expectJsonThrows(json::Value(), "not a number"); try { diff --git a/src/tests/libxrpl/basics/Number.cpp b/src/tests/libxrpl/basics/Number.cpp index 32f93eb1f7..8e958b40d4 100644 --- a/src/tests/libxrpl/basics/Number.cpp +++ b/src/tests/libxrpl/basics/Number.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +185,17 @@ TEST(NumberTest, limits) } EXPECT_TRUE(caught); + try + { + Number{1, 2000000, Number::Normalized{}}; + ADD_FAILURE(); + } + catch (std::overflow_error const& e) + { + std::string const expected = "Number::normalize 2"; + EXPECT_EQ(e.what(), expected) << e.what(); + } + if (scale == MantissaRange::MantissaScale::Large330) { // Normalization with the other scales, including the older large mantissa scales, will @@ -406,6 +419,158 @@ TEST(NumberTest, add) } } +TEST(NumberTest, add_sub_extreme_exponents) +{ + for (auto const mantissaScale : MantissaRange::getAllScales()) + { + NumberMantissaScaleGuard const sg(mantissaScale); + + auto const scale = Number::getMantissaScale(); + + EXPECT_EQ(Number::getround(), Number::RoundingMode::ToNearest) + << to_string(Number::getround()); + + // Special cases: Exponents at each end of the allowable range + for (auto const round : + {Number::RoundingMode::ToNearest, + Number::RoundingMode::TowardsZero, + Number::RoundingMode::Downward, + Number::RoundingMode::Upward}) + { + NumberRoundModeGuard const rg{round}; + + auto const bigMantissa = std::invoke([scale, round] { + auto m = Number::maxMantissa(); + if (scale != MantissaRange::MantissaScale::Small) + { + // At the large scales, the maxMantissa is not representable, so we need to + // shrink it down to a representable value. + m /= 10; + } + if (round == Number::RoundingMode::Upward) + { + // Rounding upward will overflow if the mantissa is at maxMantissa. Subtract an + // arbitrary small value to keep the mantissa near the limit, but with a + // little room to grow. 67 has no meaning, except that it's, you know, + // six seven. + m -= 67; + } + return m; + }); + auto const params = { + std::make_pair(Number::minMantissa(), 0), + // At the large scales, the maxMantissa is not representable, so we need to shrink + // it down to a representable value. Rounding upward will overflow if the mantissa + // is right at the all nines value. To keep things a little simpler, do those + // modifications unconditionally. + std::make_pair(bigMantissa, 1), + }; + for (auto const& [mantissa, exponentOffset] : params) + { + auto const x = Number{mantissa, Number::kMaxExponent, Number::Normalized{}}; + auto const y = + Number{mantissa, Number::kMinExponent + exponentOffset, Number::Normalized{}}; + + std::ostringstream detail; + detail << "Scale: " << to_string(scale) << ", round: " << to_string(round) + << ", x: " << x << ", y: " << y; + + EXPECT_EQ(x.mantissa(), mantissa); + EXPECT_EQ(x.exponent(), Number::kMaxExponent); + EXPECT_NE(x, beast::kZero); + EXPECT_EQ(y.mantissa(), mantissa); + EXPECT_EQ(y.exponent(), Number::kMinExponent + exponentOffset); + EXPECT_NE(y, beast::kZero); + + { + // x + y + auto const result = x + y; + + if (round == Number::RoundingMode::Upward) + { + // Rounding upward will take that little x-bit and round result up to the + // next representable value. + EXPECT_NE(result, x); + EXPECT_EQ(result, (Number{x.mantissa() + 1, x.exponent()})); + } + else + { + EXPECT_EQ(result, x); + } + } + { + // x - y + auto const result = x - y; + + switch (round) + { + case Number::RoundingMode::TowardsZero: + if (scale < MantissaRange::MantissaScale::Large330) + { + // Rounding TowardsZero was broken before Large330. + EXPECT_EQ(result, x) << detail.str(); + break; + } + [[fallthrough]]; + case Number::RoundingMode::Downward: + // Rounding downward (or toward zero in Large330) will take that little + // x-bit and round result down to the next representable value. + EXPECT_NE(result, x) << detail.str(); + EXPECT_EQ(result, (Number{x.mantissa() - 1, x.exponent()})) + << detail.str(); + break; + default: + // Rounding up and toNearest rounds back to the original value + EXPECT_EQ(result, x) << detail.str(); + } + } + { + // y + x + auto const result = y + x; + + if (round == Number::RoundingMode::Upward) + { + // Rounding upward will take that little x-bit and round result up to the + // next representable value. + EXPECT_NE(result, x); + EXPECT_EQ(result, (Number{x.mantissa() + 1, x.exponent()})); + } + else + { + EXPECT_EQ(result, x); + } + } + { + // y - x + auto const result = y - x; + + switch (round) + { + case Number::RoundingMode::TowardsZero: + if (scale < MantissaRange::MantissaScale::Large330) + { + // Rounding TowardsZero was broken before Large330. + EXPECT_EQ(result, -x) << detail.str(); + break; + } + [[fallthrough]]; + case Number::RoundingMode::Upward: + // Rounding upward (or toward zero in Large330) will take that little + // x-bit and round result up to the next representable negative value. + EXPECT_NE(result, -x) << detail.str(); + EXPECT_EQ(result, (Number{-x.mantissa() + 1, x.exponent()})) + << detail.str(); + break; + default: + // Rounding up and toNearest rounds back to the original value + EXPECT_EQ(result, -x) << detail.str(); + } + } + } + } + } +} + TEST(NumberTest, sub) { for (auto const mantissaScale : MantissaRange::getAllScales()) From 046d4dd4afca8c532962e8d5714e21b4762afe2c Mon Sep 17 00:00:00 2001 From: Timur Yalymov <36795566+tyalymov@users.noreply.github.com> Date: Fri, 21 Aug 2026 12:30:17 +0000 Subject: [PATCH 6/7] fix: Reject vault deposits that move nothing from the depositor (#8014) Co-authored-by: Cursor --- .../tx/transactors/vault/VaultDeposit.cpp | 43 ++++ src/test/app/vault/VaultBugs_test.cpp | 183 +++++++++++++++++- 2 files changed, 225 insertions(+), 1 deletion(-) diff --git a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp index a3c0a94eb5..5ee948bbba 100644 --- a/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultDeposit.cpp @@ -2,12 +2,15 @@ #include #include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -47,6 +50,39 @@ roundToVaultScale(STAmount const& amount, SLE::const_ref vault) return roundToScale(amount, postScale, Number::RoundingMode::Downward); } +// True if debiting `assets` would leave the depositor's balance where it started, so the deposit +// would mint shares against a transfer that never happened. Asking the balance directly whether it +// notices the debit avoids having to infer the rounding step: it has to be the stored balance that +// answers, because that magnitude is what governs the rounding, and it is not the same as the +// spendable amount, which also counts what the counterparty's limit allows. +[[nodiscard]] +static bool +roundsToZeroForDepositor( + ReadView const& view, + AccountID const& account, + STAmount const& assets, + beast::Journal j) +{ + if (assets.integral()) + return false; + + auto const balance = accountHolds( + view, + account, + assets.asset(), + FreezeHandling::ZeroIfFrozen, + AuthHandling::ZeroIfUnauthorized, + j, + SpendableHandling::SimpleBalance); + + if (balance - assets != balance) + return false; + + JLOG(j.warn()) << "VaultDeposit: amount " << assets.getFullText() + << " leaves the depositor's balance " << balance.getFullText() << " unchanged"; + return true; +} + NotTEC VaultDeposit::preflight(PreflightContext const& ctx) { @@ -208,6 +244,7 @@ TER VaultDeposit::doApply() { bool const fix320Enabled = view().rules().enabled(fixCleanup3_2_0); + bool const fix340Enabled = view().rules().enabled(fixCleanup3_4_0); auto const vault = view().peek(keylet::vault(ctx_.tx[sfVaultID])); auto applyViewContext = ctx_.getApplyViewContext(); if (!vault) @@ -308,6 +345,12 @@ VaultDeposit::doApply() return tecINTERNAL; // LCOV_EXCL_STOP } + // What a deposit transfers is not the requested amount but that amount truncated to a + // whole number of shares and converted back, which can be smaller. Only here is that + // value known rather than recomputed, so this is where it can be checked against the + // depositor's balance before anything moves. + if (fix340Enabled && roundsToZeroForDepositor(view(), accountID_, *maybeAssets, j_)) + return tecPRECISION_LOSS; assetsDeposited = *maybeAssets; } catch (std::overflow_error const&) diff --git a/src/test/app/vault/VaultBugs_test.cpp b/src/test/app/vault/VaultBugs_test.cpp index 2dbd20f855..70a350a4f1 100644 --- a/src/test/app/vault/VaultBugs_test.cpp +++ b/src/test/app/vault/VaultBugs_test.cpp @@ -2,9 +2,12 @@ #include #include #include +#include #include +#include #include #include +#include #include #include #include @@ -15,13 +18,17 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -408,10 +415,14 @@ private: }; { + // fixCleanup3_4_0 has to be off as well: its depositor-side check + // rejects alice's deposit for the same reason, so the invariant is + // only reachable with neither guard in place. testcase( "bug: VaultDeposit below Vault precision canonicalized to zero " "(pre-fixCleanup3_2_0)"); - runScenario(testableAmendments() - fixCleanup3_2_0, tecINVARIANT_FAILED); + runScenario( + testableAmendments() - fixCleanup3_2_0 - fixCleanup3_4_0, tecINVARIANT_FAILED); } { testcase( @@ -421,6 +432,175 @@ private: } } + // A deposit does not transfer the requested amount. It transfers the + // request truncated to a whole number of shares and converted back, which + // can be strictly smaller. When that smaller value is below half a ULP at + // the depositor's own trust-line scale, the debit rounds away to nothing: + // the depositor pays nothing, while the vault books the assets and mints + // shares. ValidVault catches the desync at finalize time. + // + // Only a non-power-of-ten assets-to-shares ratio is needed, and that + // happens through ordinary use: LoanPay books accrued interest into + // sfAssetsTotal without minting shares. + // + // The fixCleanup3_2_0 guard in preclaim does not help, because it tests the + // raw requested amount, which is large enough to survive the rounding. + // Post-fixCleanup3_4_0 the post-truncation value is checked as well and the + // deposit is rejected with tecPRECISION_LOSS before anything moves. + void + testBugDepositShareTruncationSubUlp() + { + using namespace test::jtx; + using namespace loan_broker; + using namespace loan; + + // How bob's trust line is set up before he deposits. Holding is the plain case: a large + // positive balance whose ULP swallows the debit. InDebt is the case where the stored + // balance and the spendable amount diverge: bob owes the issuer 1e16, and the issuer's + // limit on the same line lets him spend 1000 anyway. Reading the spendable amount there + // reports a small, finely scaled number, while the rounding of the debit is still governed + // by the 1e16 he actually holds. + enum class Line { Holding, InDebt }; + + auto runScenario = [this](FeatureBitset features, Line line, TER expected) { + std::string logs; + Env env(*this, features, std::make_unique(&logs)); + + Account const issuer{"issuer"}; + Account const alice{"alice"}; + Account const carol{"carol"}; + Account const bob{"bob"}; + + env.fund(XRP(100'000), issuer, alice, carol, bob); + env.close(); + env(fset(issuer, asfDefaultRipple)); + env.close(); + + PrettyAsset const usd{issuer["USD"]}; + PrettyAsset const bobUsd{bob["USD"]}; + STAmount const trustLimit{usd.raw(), Number{99'999'999'999'999'999LL}}; + // Bob's balance sits exactly on a multiple-of-10 boundary at the + // 1e16 IOU precision cusp, where one ULP is 10. + STAmount const bobEdge{usd.raw(), Number{10'000'000'000'000'010LL}}; + STAmount const bobDebt{bobUsd.raw(), Number{10'000'000'000'000'000LL}}; + STAmount const oppositeLimit{bobUsd.raw(), Number{10'000'000'000'001'000LL}}; + + env(trust(alice, trustLimit)); + env(trust(carol, trustLimit)); + env(trust(bob, trustLimit)); + env.close(); + + env(pay(issuer, alice, usd(1'000))); + env(pay(issuer, carol, usd(1'000))); + if (line == Line::Holding) + { + env(pay(issuer, bob, bobEdge)); + } + else + { + // The issuer trusts bob's own USD, so bob can issue 1e16 back and still have + // 1000 of spendable room left on the same line. + env(trust(issuer, oppositeLimit)); + env.close(); + env(pay(bob, issuer, bobDebt)); + } + env.close(); + + Vault const vault{env}; + auto [vaultTx, vaultKeylet] = vault.create({.owner = alice, .asset = usd}); + vaultTx[sfScale] = 0; + env(vaultTx); + env.close(); + + // Alice deposits 1000 USD, minting 1000 shares 1:1. + env(vault.deposit({.depositor = alice, .id = vaultKeylet.key, .amount = usd(1'000)})); + env.close(); + + // A loan broker on the vault, then a bullet loan at 24% interest: + // a single payment, one year out. + auto const brokerKeylet = + keylet::loanBroker(alice.id(), SeqProxy::rawSequence(env.seq(alice))); + env(set(alice, vaultKeylet.key)); + env.close(); + + auto const loanKeylet = keylet::loan(brokerKeylet.key, SeqProxy::rawSequence(1)); + env(set(carol, brokerKeylet.key, usd(1'000).value()), + loan::kInterestRate(percentageToTenthBips(24)), + kGracePeriod(60), + kPaymentInterval(365 * 24 * 60 * 60), + kPaymentTotal(1), + Sig(sfCounterpartySignature, alice), + Fee(env.current()->fees().base * 2), + Ter(tesSUCCESS)); + env.close(); + + // Advance to just before the single payment falls due and let carol + // repay principal plus interest. LoanPay is what books the accrued + // interest into sfAssetsTotal; under cash-basis accounting LoanSet + // alone does not. Share supply stays at 1000, so + // assetsTotal/sharesTotal becomes 1240/1000. + env.close(std::chrono::seconds{(365 * 24 * 60 * 60) - 3600}); + env(pay(carol, loanKeylet.key, usd(2'000).value()), Ter(tesSUCCESS)); + env.close(); + + // Pin the ratio the rest of the scenario reasons about, so the test cannot quietly + // stop exercising the bug if the setup drifts. + auto const sleVault = env.le(vaultKeylet); + BEAST_EXPECT(sleVault && sleVault->at(sfAssetsTotal) == Number{1'240}); + auto const sleIssuance = env.le(keylet::mptokenIssuance(sleVault->at(sfShareMPTID))); + BEAST_EXPECT(sleIssuance && sleIssuance->at(sfOutstandingAmount) == 1'000); + + // Bob deposits 6 USD, which rounds to 10 at his own trust-line + // scale and so clears the fixCleanup3_2_0 guard. But + // floor(1000 * 6 / 1240) is 4 shares, worth 4 * 1240 / 1000 = 4.96, + // and that is below half a ULP of his balance, so it rounds away to + // nothing when subtracted. + env(vault.deposit({.depositor = bob, .id = vaultKeylet.key, .amount = usd(6)}), + Ter(expected)); + env.close(); + }; + + { + testcase( + "bug: VaultDeposit share truncation lets depositor debit " + "round away to zero (pre-fixCleanup3_4_0)"); + runScenario(testableAmendments() - fixCleanup3_4_0, Line::Holding, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit share truncation lets depositor debit " + "round away to zero (pre-fixCleanup3_2_0 and pre-fixCleanup3_4_0)"); + runScenario( + testableAmendments() - fixCleanup3_2_0 - fixCleanup3_4_0, + Line::Holding, + tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit share truncation rejected with " + "tecPRECISION_LOSS (post-fixCleanup3_4_0)"); + runScenario(testableAmendments(), Line::Holding, tecPRECISION_LOSS); + } + { + testcase( + "bug: VaultDeposit share truncation rejected with " + "tecPRECISION_LOSS (post-fixCleanup3_4_0, pre-fixCleanup3_2_0)"); + runScenario(testableAmendments() - fixCleanup3_2_0, Line::Holding, tecPRECISION_LOSS); + } + { + testcase( + "bug: VaultDeposit share truncation against a debt balance " + "round away to zero (pre-fixCleanup3_4_0)"); + runScenario(testableAmendments() - fixCleanup3_4_0, Line::InDebt, tecINVARIANT_FAILED); + } + { + testcase( + "bug: VaultDeposit share truncation against a debt balance rejected with " + "tecPRECISION_LOSS (post-fixCleanup3_4_0)"); + runScenario(testableAmendments(), Line::InDebt, tecPRECISION_LOSS); + } + } + // Bug: ValidVault::visitEntry computes destinationDelta.scale as // max(before_exponent, after_exponent) for RippleState entries. When a // withdrawal credits a destination whose IOU balance sits just below a @@ -801,6 +981,7 @@ public: testBugMakeDeltaPosteriorScale(); testBugMakeDeltaAnteriorScale(); testVaultDepositCanonicalizeToZero(); + testBugDepositShareTruncationSubUlp(); testVaultWithdrawCanonicalizeToZero(); testBugVaultDustDebitCanonicalizesToNoOp(); testVaultDepositNegativeBalanceFromOppositeLimit(); From fe4ccdf7500dfccadda8560261853da44a530cac Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 21 Aug 2026 15:20:44 +0000 Subject: [PATCH 7/7] fix: Add assert for account_info flags (#7987) --- .../rpc/handlers/account/AccountInfo.cpp | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/xrpld/rpc/handlers/account/AccountInfo.cpp b/src/xrpld/rpc/handlers/account/AccountInfo.cpp index 6b244af1a9..2276f98a0e 100644 --- a/src/xrpld/rpc/handlers/account/AccountInfo.cpp +++ b/src/xrpld/rpc/handlers/account/AccountInfo.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include namespace xrpl { @@ -115,29 +117,37 @@ doAccountInfo(rpc::JsonContext& context) } auto const accountID{id.value()}; - static constexpr std::array, 9> kLsFlags{ - {{"defaultRipple", lsfDefaultRipple}, - {"depositAuth", lsfDepositAuth}, - {"disableMasterKey", lsfDisableMaster}, - {"disallowIncomingXRP", lsfDisallowXRP}, - {"globalFreeze", lsfGlobalFreeze}, - {"noFreeze", lsfNoFreeze}, - {"passwordSpent", lsfPasswordSpent}, - {"requireAuthorization", lsfRequireAuth}, - {"requireDestinationTag", lsfRequireDestTag}}}; - - static constexpr std::array, 4> - kDisallowIncomingFlags{ - {{"disallowIncomingNFTokenOffer", lsfDisallowIncomingNFTokenOffer}, + // Flags that are always reported. + static constexpr auto kAccountRootFlags = + std::to_array>( + {{"allowTrustLineClawback", lsfAllowTrustLineClawback}, + {"defaultRipple", lsfDefaultRipple}, + {"depositAuth", lsfDepositAuth}, + {"disableMasterKey", lsfDisableMaster}, {"disallowIncomingCheck", lsfDisallowIncomingCheck}, + {"disallowIncomingNFTokenOffer", lsfDisallowIncomingNFTokenOffer}, {"disallowIncomingPayChan", lsfDisallowIncomingPayChan}, - {"disallowIncomingTrustline", lsfDisallowIncomingTrustline}}}; + {"disallowIncomingTrustline", lsfDisallowIncomingTrustline}, + {"disallowIncomingXRP", lsfDisallowXRP}, + {"globalFreeze", lsfGlobalFreeze}, + {"noFreeze", lsfNoFreeze}, + {"passwordSpent", lsfPasswordSpent}, + {"requireAuthorization", lsfRequireAuth}, + {"requireDestinationTag", lsfRequireDestTag}}); - static constexpr std::pair kAllowTrustLineClawbackFlag{ - "allowTrustLineClawback", lsfAllowTrustLineClawback}; + // Flags that are only reported when their amendment is enabled. This can't be `constexpr`, + // since the amendment IDs are computed at runtime. + static auto const kAmendmentGatedFlags = + std::to_array>( + {{"allowTrustLineLocking", lsfAllowTrustLineLocking, featureTokenEscrow}}); - static constexpr std::pair kAllowTrustLineLockingFlag{ - "allowTrustLineLocking", lsfAllowTrustLineLocking}; + // Every `AccountRoot` flag must be reported by `account_info`, so if a new flag is added, it + // needs to be added to one of the arrays above. This can't be a `static_assert` because + // `getAccountRootFlags()` builds its map at runtime. + XRPL_ASSERT_PARTS( + kAccountRootFlags.size() + kAmendmentGatedFlags.size() == getAccountRootFlags().size(), + "xrpl::doAccountInfo", + "number of account flags"); auto const sleAccepted = ledger->read(keylet::account(accountID)); if (sleAccepted) @@ -157,19 +167,13 @@ doAccountInfo(rpc::JsonContext& context) result[jss::account_data] = jvAccepted; json::Value acctFlags{json::ValueType::Object}; - for (auto const& lsf : kLsFlags) - acctFlags[lsf.first.data()] = sleAccepted->isFlag(lsf.second); + for (auto const& [name, flag] : kAccountRootFlags) + acctFlags[name.data()] = sleAccepted->isFlag(flag); - for (auto const& lsf : kDisallowIncomingFlags) - acctFlags[lsf.first.data()] = sleAccepted->isFlag(lsf.second); - - acctFlags[kAllowTrustLineClawbackFlag.first.data()] = - sleAccepted->isFlag(kAllowTrustLineClawbackFlag.second); - - if (ledger->rules().enabled(featureTokenEscrow)) + for (auto const& [name, flag, amendment] : kAmendmentGatedFlags) { - acctFlags[kAllowTrustLineLockingFlag.first.data()] = - sleAccepted->isFlag(kAllowTrustLineLockingFlag.second); + if (ledger->rules().enabled(amendment)) + acctFlags[name.data()] = sleAccepted->isFlag(flag); } result[jss::account_flags] = std::move(acctFlags);