From 6bbe2ca1e38c5b66b138e12ee21c3b086e258841 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Mon, 18 Nov 2024 11:23:42 -0500 Subject: [PATCH 1/5] Add AccountPermissions --- include/xrpl/protocol/Feature.h | 2 +- include/xrpl/protocol/Indexes.h | 11 ++ include/xrpl/protocol/Permissions.h | 88 ++++++++++ include/xrpl/protocol/detail/features.macro | 1 + .../xrpl/protocol/detail/ledger_entries.macro | 13 ++ include/xrpl/protocol/detail/sfields.macro | 4 + .../xrpl/protocol/detail/transactions.macro | 6 + include/xrpl/protocol/jss.h | 2 + src/libxrpl/protocol/Indexes.cpp | 18 +++ src/libxrpl/protocol/InnerObjectFormats.cpp | 4 + src/libxrpl/protocol/Permissions.cpp | 97 +++++++++++ src/libxrpl/protocol/STParsedJSON.cpp | 43 ++++- src/libxrpl/protocol/TxFormats.cpp | 1 + src/test/app/AMM_test.cpp | 3 + src/test/app/NFTokenBurn_test.cpp | 6 + src/test/ledger/Invariants_test.cpp | 3 + src/xrpld/app/tx/applySteps.h | 25 ++- src/xrpld/app/tx/detail/AMMBid.cpp | 3 +- src/xrpld/app/tx/detail/AMMClawback.cpp | 10 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 2 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 2 +- src/xrpld/app/tx/detail/AMMVote.cpp | 2 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 7 +- .../app/tx/detail/AccountPermissionSet.cpp | 152 ++++++++++++++++++ .../app/tx/detail/AccountPermissionSet.h | 56 +++++++ src/xrpld/app/tx/detail/ApplyContext.cpp | 6 + src/xrpld/app/tx/detail/ApplyContext.h | 7 + src/xrpld/app/tx/detail/CancelCheck.cpp | 2 +- src/xrpld/app/tx/detail/CancelOffer.cpp | 2 +- src/xrpld/app/tx/detail/CashCheck.cpp | 2 +- src/xrpld/app/tx/detail/Change.cpp | 2 +- src/xrpld/app/tx/detail/Clawback.cpp | 10 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 4 +- src/xrpld/app/tx/detail/CreateOffer.cpp | 2 +- src/xrpld/app/tx/detail/CreateTicket.cpp | 2 +- src/xrpld/app/tx/detail/Credentials.cpp | 7 +- src/xrpld/app/tx/detail/DeleteAccount.cpp | 20 ++- src/xrpld/app/tx/detail/DeleteOracle.cpp | 8 +- src/xrpld/app/tx/detail/DepositPreauth.cpp | 4 +- src/xrpld/app/tx/detail/Escrow.cpp | 15 +- src/xrpld/app/tx/detail/InvariantCheck.cpp | 1 + src/xrpld/app/tx/detail/MPTokenAuthorize.cpp | 4 +- .../app/tx/detail/MPTokenIssuanceDestroy.cpp | 2 +- .../app/tx/detail/MPTokenIssuanceSet.cpp | 19 ++- .../app/tx/detail/NFTokenAcceptOffer.cpp | 28 ++-- src/xrpld/app/tx/detail/NFTokenBurn.cpp | 6 +- .../app/tx/detail/NFTokenCancelOffer.cpp | 2 +- .../app/tx/detail/NFTokenCreateOffer.cpp | 8 +- src/xrpld/app/tx/detail/NFTokenMint.cpp | 13 +- src/xrpld/app/tx/detail/PayChan.cpp | 35 ++-- src/xrpld/app/tx/detail/Payment.cpp | 22 ++- src/xrpld/app/tx/detail/SetOracle.cpp | 12 +- src/xrpld/app/tx/detail/SetRegularKey.cpp | 2 +- src/xrpld/app/tx/detail/SetSignerList.cpp | 2 +- src/xrpld/app/tx/detail/SetTrust.cpp | 17 +- src/xrpld/app/tx/detail/Transactor.cpp | 66 +++++++- src/xrpld/app/tx/detail/Transactor.h | 15 ++ src/xrpld/app/tx/detail/XChainBridge.cpp | 61 +++---- src/xrpld/app/tx/detail/applySteps.cpp | 56 +++++-- src/xrpld/rpc/detail/RPCHelpers.cpp | 3 +- src/xrpld/rpc/handlers/LedgerEntry.cpp | 4 + 61 files changed, 859 insertions(+), 173 deletions(-) create mode 100644 include/xrpl/protocol/Permissions.h create mode 100644 src/libxrpl/protocol/Permissions.cpp create mode 100644 src/xrpld/app/tx/detail/AccountPermissionSet.cpp create mode 100644 src/xrpld/app/tx/detail/AccountPermissionSet.h diff --git a/include/xrpl/protocol/Feature.h b/include/xrpl/protocol/Feature.h index 90a81c55ef4..df071c69864 100644 --- a/include/xrpl/protocol/Feature.h +++ b/include/xrpl/protocol/Feature.h @@ -80,7 +80,7 @@ namespace detail { // Feature.cpp. Because it's only used to reserve storage, and determine how // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // the actual number of amendments. A LogicError on startup will verify this. -static constexpr std::size_t numFeatures = 83; +static constexpr std::size_t numFeatures = 84; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 72cf0b527b1..f3757fffed2 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -278,6 +278,17 @@ amm(Issue const& issue1, Issue const& issue2) noexcept; Keylet amm(uint256 const& amm) noexcept; +/** An AccountPermission */ +/** @{ */ +Keylet +accountPermission( + AccountID const& account, + AccountID const& authorizedAccount) noexcept; + +Keylet +accountPermission(uint256 const& key) noexcept; +/** @} */ + Keylet bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType); diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h new file mode 100644 index 00000000000..cddbcc64f20 --- /dev/null +++ b/include/xrpl/protocol/Permissions.h @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED +#define RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +/** + * We have transaction type permissions and granular type + * permissions. Since we will reuse the TransactionFormats to parse the + * Transaction Permissions, we only define the GranularPermissionType here. + */ + +enum GranularPermissionType : std::uint32_t { + gpTrustlineAuthorize = 65537, + + gpTrustlineFreeze = 65538, + + gpTrustlineUnfreeze = 65539, + + gpAccountDomainSet = 65540, + + gpAccountEmailHashSet = 65541, + + gpAccountMessageKeySet = 65542, + + gpAccountTransferRateSet = 65543, + + gpAccountTickSizeSet = 65544, + + gpPaymentMint = 65545, + + gpPaymentBurn = 65546, + + gpMPTokenIssuanceLock = 65547, + + gpMPTokenIssuanceUnlock = 65548, +}; + +class Permission +{ +private: + Permission(); + + std::unordered_map + granularPermissionMap; + + std::unordered_map granularTxTypeMap; + +public: + static Permission const& + getInstance(); + + std::optional + getGranularValue(std::string const& name) const; + + std::optional + getGranularTxType(GranularPermissionType const& gpType) const; + + bool + isProhibited(std::string const& name) const; +}; + +} // namespace ripple + +#endif diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 24c6e72ae34..f033c920fc4 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -29,6 +29,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FEATURE(AccountPermission, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(Credentials, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(AMMClawback, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (AMMv1_2, Supported::yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 0cb1ec3416a..766f869e922 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -436,3 +436,16 @@ LEDGER_ENTRY(ltCREDENTIAL, 0x0081, Credential, ({ {sfPreviousTxnID, soeREQUIRED}, {sfPreviousTxnLgrSeq, soeREQUIRED}, })) + +/** A ledger object representing permissions an account has delegated to another account. + + \sa keylet::accountPermission + */ +LEDGER_ENTRY(ltACCOUNT_PERMISSION, 0x0082, AccountPermission, ({ + {sfAccount, soeREQUIRED}, + {sfAuthorize, soeREQUIRED}, + {sfPermissions, soeREQUIRED}, + {sfOwnerNode, soeREQUIRED}, + {sfPreviousTxnID, soeREQUIRED}, + {sfPreviousTxnLgrSeq, soeREQUIRED}, +})) diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index ccf6350cbfc..65f88959074 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -112,6 +112,7 @@ TYPED_SFIELD(sfEmitGeneration, UINT32, 46) TYPED_SFIELD(sfVoteWeight, UINT32, 48) TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50) TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) +TYPED_SFIELD(sfPermissionValue, UINT32, 52) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) @@ -274,6 +275,7 @@ TYPED_SFIELD(sfRegularKey, ACCOUNT, 8) TYPED_SFIELD(sfNFTokenMinter, ACCOUNT, 9) TYPED_SFIELD(sfEmitCallback, ACCOUNT, 10) TYPED_SFIELD(sfHolder, ACCOUNT, 11) +TYPED_SFIELD(sfOnBehalfOf, ACCOUNT, 12) // account (uncommon) TYPED_SFIELD(sfHookAccount, ACCOUNT, 16) @@ -323,6 +325,7 @@ UNTYPED_SFIELD(sfSignerEntry, OBJECT, 11) UNTYPED_SFIELD(sfNFToken, OBJECT, 12) UNTYPED_SFIELD(sfEmitDetails, OBJECT, 13) UNTYPED_SFIELD(sfHook, OBJECT, 14) +UNTYPED_SFIELD(sfPermission, OBJECT, 15) // inner object (uncommon) UNTYPED_SFIELD(sfSigner, OBJECT, 16) @@ -372,3 +375,4 @@ UNTYPED_SFIELD(sfPriceDataSeries, ARRAY, 24) UNTYPED_SFIELD(sfAuthAccounts, ARRAY, 25) UNTYPED_SFIELD(sfAuthorizeCredentials, ARRAY, 26) UNTYPED_SFIELD(sfUnauthorizeCredentials, ARRAY, 27) +UNTYPED_SFIELD(sfPermissions, ARRAY, 28) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 4f4c8f12595..dbdf6734157 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -447,6 +447,12 @@ TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, ({ {sfCredentialType, soeREQUIRED}, })) +/** This transaction type delegates authorized account specified permissions */ +TRANSACTION(ttACCOUNT_PERMISSION_SET, 61, AccountPermissionSet, ({ + {sfAuthorize, soeREQUIRED}, + {sfPermissions, soeREQUIRED}, +})) + /** This system-generated transaction type is used to update the status of the various amendments. diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index f9e0db24949..fc1a2e68e2b 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -45,6 +45,7 @@ namespace jss { JSS(AL_size); // out: GetCounts JSS(AL_hit_rate); // out: GetCounts JSS(Account); // in: TransactionSign; field. +JSS(AccountPermission); // ledger type. JSS(AccountRoot); // ledger type. JSS(AMM); // ledger type JSS(AMMID); // field @@ -132,6 +133,7 @@ JSS(account_hash); // out: LedgerToJson JSS(account_id); // out: WalletPropose JSS(account_nfts); // out: AccountNFTs JSS(account_objects); // out: AccountObjects +JSS(account_permission); // in: AccountPermission JSS(account_root); // in: LedgerEntry JSS(account_sequence_next); // out: SubmitTransaction JSS(account_sequence_available); // out: SubmitTransaction diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 12142879ad5..80ca58a3897 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -77,6 +77,7 @@ enum class LedgerNameSpace : std::uint16_t { MPTOKEN_ISSUANCE = '~', MPTOKEN = 't', CREDENTIAL = 'D', + ACCOUNT_PERMISSION = 'P', // No longer used or supported. Left here to reserve the space // to avoid accidental reuse. @@ -428,6 +429,23 @@ amm(uint256 const& id) noexcept return {ltAMM, id}; } +Keylet +accountPermission( + AccountID const& account, + AccountID const& authorizedAccount) noexcept +{ + return { + ltACCOUNT_PERMISSION, + indexHash( + LedgerNameSpace::ACCOUNT_PERMISSION, account, authorizedAccount)}; +} + +Keylet +accountPermission(uint256 const& key) noexcept +{ + return {ltACCOUNT_PERMISSION, key}; +} + Keylet bridge(STXChainBridge const& bridge, STXChainBridge::ChainType chainType) { diff --git a/src/libxrpl/protocol/InnerObjectFormats.cpp b/src/libxrpl/protocol/InnerObjectFormats.cpp index 87c42a8085f..5499d8050a2 100644 --- a/src/libxrpl/protocol/InnerObjectFormats.cpp +++ b/src/libxrpl/protocol/InnerObjectFormats.cpp @@ -154,6 +154,10 @@ InnerObjectFormats::InnerObjectFormats() {sfIssuer, soeREQUIRED}, {sfCredentialType, soeREQUIRED}, }); + + add(sfPermission.jsonName.c_str(), + sfPermission.getCode(), + {{sfPermissionValue, soeREQUIRED}}); } InnerObjectFormats const& diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp new file mode 100644 index 00000000000..12ea4b12242 --- /dev/null +++ b/src/libxrpl/protocol/Permissions.cpp @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include + +namespace ripple { + +Permission::Permission() +{ + granularPermissionMap = { + {"TrustlineAuthorize", gpTrustlineAuthorize}, + {"TrustlineFreeze", gpTrustlineFreeze}, + {"TrustlineUnfreeze", gpTrustlineUnfreeze}, + {"AccountDomainSet", gpAccountDomainSet}, + {"AccountEmailHashSet", gpAccountEmailHashSet}, + {"AccountMessageKeySet", gpAccountMessageKeySet}, + {"AccountTransferRateSet", gpAccountTransferRateSet}, + {"AccountTickSizeSet", gpAccountTickSizeSet}, + {"PaymentMint", gpPaymentMint}, + {"PaymentBurn", gpPaymentBurn}, + {"MPTokenIssuanceLock", gpMPTokenIssuanceLock}, + {"MPTokenIssuanceUnlock", gpMPTokenIssuanceUnlock}}; + + granularTxTypeMap = { + {gpTrustlineAuthorize, ttTRUST_SET}, + {gpTrustlineFreeze, ttTRUST_SET}, + {gpTrustlineUnfreeze, ttTRUST_SET}, + {gpAccountDomainSet, ttACCOUNT_SET}, + {gpAccountEmailHashSet, ttACCOUNT_SET}, + {gpAccountMessageKeySet, ttACCOUNT_SET}, + {gpAccountTransferRateSet, ttACCOUNT_SET}, + {gpAccountTickSizeSet, ttACCOUNT_SET}, + {gpPaymentMint, ttPAYMENT}, + {gpPaymentBurn, ttPAYMENT}, + {gpMPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET}, + {gpMPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET}}; +} + +Permission const& +Permission::getInstance() +{ + static Permission const instance; + return instance; +} + +std::optional +Permission::getGranularValue(std::string const& name) const +{ + auto const it = granularPermissionMap.find(name); + if (it != granularPermissionMap.end()) + return static_cast(it->second); + + return std::nullopt; +} + +std::optional +Permission::getGranularTxType(GranularPermissionType const& gpType) const +{ + auto const it = granularTxTypeMap.find(gpType); + if (it != granularTxTypeMap.end()) + return it->second; + + return std::nullopt; +} + +bool +Permission::isProhibited(std::string const& name) const +{ + // We do not allow delegating the following transaction permissions to other + // accounts for security reason. + if (name == "AccountSet" || name == "SetRegularKey" || + name == "SignerListSet") + return true; + + return false; +} + +} // namespace ripple diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index 09b6b6679d7..69f5e425604 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -360,10 +361,44 @@ parseLeaf( { if (value.isString()) { - ret = detail::make_stvar( - field, - beast::lexicalCastThrow( - value.asString())); + if (field == sfPermissionValue) + { + std::string const strValue = value.asString(); + auto const granularPermission = + Permission::getInstance().getGranularValue( + strValue); + if (!granularPermission) + { + // if it's not granular permission, parse as + // transaction type permission. + if (Permission::getInstance().isProhibited( + strValue)) + { + // we do not allow delegating some transaction + // type permissions to other accounts for + // security reason. + error = invalid_data(json_name, fieldName); + return ret; + } + else + ret = detail::make_stvar( + field, + static_cast( + TxFormats::getInstance().findTypeByName( + strValue) + + 1)); + } + else + ret = detail::make_stvar( + field, *granularPermission); + } + else + { + ret = detail::make_stvar( + field, + beast::lexicalCastThrow( + value.asString())); + } } else if (value.isInt()) { diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 76b1ae8ad4f..8a76ecc3dd3 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -45,6 +45,7 @@ TxFormats::TxFormats() {sfTxnSignature, soeOPTIONAL}, {sfSigners, soeOPTIONAL}, // submit_multisigned {sfNetworkID, soeOPTIONAL}, + {sfOnBehalfOf, soeOPTIONAL}, }; #pragma push_macro("UNWRAP") diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index f1e81132c5e..a6232417aa0 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -3307,6 +3307,7 @@ struct AMM_test : public jtx::AMMTest *jtx.stx, env.current()->rules(), tapNONE, + AccountID(0), env.journal); auto pf = AMMBid::preflight(pfctx); BEAST_EXPECT(pf == temDISABLED); @@ -3322,6 +3323,7 @@ struct AMM_test : public jtx::AMMTest *jtx.stx, env.current()->rules(), tapNONE, + AccountID(0), env.journal); auto pf = AMMBid::preflight(pfctx); BEAST_EXPECT(pf != tesSUCCESS); @@ -3337,6 +3339,7 @@ struct AMM_test : public jtx::AMMTest *jtx.stx, env.current()->rules(), tapNONE, + AccountID(0), env.journal); auto pf = AMMBid::preflight(pfctx); BEAST_EXPECT(pf == temBAD_AMM_TOKENS); diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index a56f0a45674..f7c8a80f332 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -811,6 +811,9 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite tesSUCCESS, env.current()->fees().base, tapNONE, + false, + AccountID(0), + std::unordered_set{}, jlog}; // Verify that the last page is present and contains one NFT. @@ -855,6 +858,9 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite tesSUCCESS, env.current()->fees().base, tapNONE, + false, + AccountID(0), + std::unordered_set{}, jlog}; // Verify that the middle page is present. diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index 8d7b08fa1ab..fb12e2d24fe 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -98,6 +98,9 @@ class Invariants_test : public beast::unit_test::suite tesSUCCESS, env.current()->fees().base, tapNONE, + false, + AccountID(0), + std::unordered_set{}, jlog}; BEAST_EXPECT(precheck(A1, A2, ac)); diff --git a/src/xrpld/app/tx/applySteps.h b/src/xrpld/app/tx/applySteps.h index 1df537515e9..cc23b8aca39 100644 --- a/src/xrpld/app/tx/applySteps.h +++ b/src/xrpld/app/tx/applySteps.h @@ -22,6 +22,7 @@ #include #include +#include namespace ripple { @@ -160,7 +161,10 @@ struct PreflightResult ApplyFlags const flags; /// From the input - the journal beast::Journal const j; - + /// If the transaction is a delegated transaction + bool const isDelegated; + /// the account that the transaction is operated on + AccountID const account; /// Intermediate transaction result NotTEC const ter; @@ -168,12 +172,16 @@ struct PreflightResult template PreflightResult( Context const& ctx_, + bool const isDelegated, + AccountID const account, std::pair const& result) : tx(ctx_.tx) , rules(ctx_.rules) , consequences(result.second) , flags(ctx_.flags) , j(ctx_.j) + , isDelegated(isDelegated) + , account(account) , ter(result.first) { } @@ -201,21 +209,34 @@ struct PreclaimResult ApplyFlags const flags; /// From the input - the journal beast::Journal const j; + /// If the transaction is a delegated transaction + bool const isDelegated; + /// the account that the transaction is operated on + AccountID const account; /// Intermediate transaction result TER const ter; + /// granular permissions enabled for the transaction. If empty and + /// isDelegated=true, then the entire transaction is authorized. + std::unordered_set gpSet; /// Success flag - whether the transaction is likely to /// claim a fee bool const likelyToClaimFee; /// Constructor template - PreclaimResult(Context const& ctx_, TER ter_) + PreclaimResult( + Context const& ctx_, + TER ter_, + std::unordered_set gpSet_) : view(ctx_.view) , tx(ctx_.tx) , flags(ctx_.flags) , j(ctx_.j) + , isDelegated(ctx_.isDelegated) + , account(ctx_.account) , ter(ter_) + , gpSet(std::move(gpSet_)) , likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags)) { } diff --git a/src/xrpld/app/tx/detail/AMMBid.cpp b/src/xrpld/app/tx/detail/AMMBid.cpp index 9de3762d2e3..66496b4dc1e 100644 --- a/src/xrpld/app/tx/detail/AMMBid.cpp +++ b/src/xrpld/app/tx/detail/AMMBid.cpp @@ -110,8 +110,7 @@ AMMBid::preclaim(PreclaimContext const& ctx) } } - auto const lpTokens = - ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); + auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.account, ctx.j); // Not LP if (lpTokens == beast::zero) { diff --git a/src/xrpld/app/tx/detail/AMMClawback.cpp b/src/xrpld/app/tx/detail/AMMClawback.cpp index 64150ded6da..d3f4c94b1cf 100644 --- a/src/xrpld/app/tx/detail/AMMClawback.cpp +++ b/src/xrpld/app/tx/detail/AMMClawback.cpp @@ -45,7 +45,7 @@ AMMClawback::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfAMMClawbackMask) return temINVALID_FLAG; - AccountID const issuer = ctx.tx[sfAccount]; + AccountID const issuer = ctx.account; AccountID const holder = ctx.tx[sfHolder]; if (issuer == holder) @@ -86,7 +86,7 @@ AMMClawback::preclaim(PreclaimContext const& ctx) { auto const asset = ctx.tx[sfAsset]; auto const asset2 = ctx.tx[sfAsset2]; - auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount])); + auto const sleIssuer = ctx.view.read(keylet::account(ctx.account)); if (!sleIssuer) return terNO_ACCOUNT; // LCOV_EXCL_LINE @@ -136,7 +136,6 @@ TER AMMClawback::applyGuts(Sandbox& sb) { std::optional const clawAmount = ctx_.tx[~sfAmount]; - AccountID const issuer = ctx_.tx[sfAccount]; AccountID const holder = ctx_.tx[sfHolder]; Issue const asset = ctx_.tx[sfAsset]; Issue const asset2 = ctx_.tx[sfAsset2]; @@ -216,7 +215,8 @@ AMMClawback::applyGuts(Sandbox& sb) << to_string(newLPTokenBalance.iou()) << " old balance: " << to_string(lptAMMBalance.iou()); - auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_); + auto const ter = + rippleCredit(sb, holder, account_, amountWithdraw, true, j_); if (ter != tesSUCCESS) return ter; // LCOV_EXCL_LINE @@ -229,7 +229,7 @@ AMMClawback::applyGuts(Sandbox& sb) auto const flags = ctx_.tx.getFlags(); if (flags & tfClawTwoAssets) - return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_); + return rippleCredit(sb, holder, account_, *amount2Withdraw, true, j_); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 31773166d4a..5aeb630445e 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -88,7 +88,7 @@ AMMCreate::calculateBaseFee(ReadView const& view, STTx const& tx) TER AMMCreate::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.tx[sfAccount]; + auto const accountID = ctx.account; auto const amount = ctx.tx[sfAmount]; auto const amount2 = ctx.tx[sfAmount2]; diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 3448401eb79..334a34a7909 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -168,7 +168,7 @@ AMMDeposit::preflight(PreflightContext const& ctx) TER AMMDeposit::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.tx[sfAccount]; + auto const accountID = ctx.account; auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); diff --git a/src/xrpld/app/tx/detail/AMMVote.cpp b/src/xrpld/app/tx/detail/AMMVote.cpp index c4b6c612c63..70a24789940 100644 --- a/src/xrpld/app/tx/detail/AMMVote.cpp +++ b/src/xrpld/app/tx/detail/AMMVote.cpp @@ -72,7 +72,7 @@ AMMVote::preclaim(PreclaimContext const& ctx) else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero) return tecAMM_EMPTY; else if (auto const lpTokensNew = - ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); + ammLPHolds(ctx.view, *ammSle, ctx.account, ctx.j); lpTokensNew == beast::zero) { JLOG(ctx.j.debug()) << "AMM Vote: account is not LP."; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index 118262905c1..f53d64ee062 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -170,7 +170,7 @@ tokensWithdraw( TER AMMWithdraw::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.tx[sfAccount]; + auto const accountID = ctx.account; auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); @@ -250,8 +250,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx) if (auto const ter = checkAmount(amount2, amount2Balance)) return ter; - auto const lpTokens = - ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); + auto const lpTokens = ammLPHolds(ctx.view, *ammSle, accountID, ctx.j); auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags()); @@ -305,7 +304,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (!accountSle) return {tecINTERNAL, false}; // LCOV_EXCL_LINE auto const lpTokens = - ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal); + ammLPHolds(ctx_.view(), *ammSle, account_, ctx_.journal); auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags()); diff --git a/src/xrpld/app/tx/detail/AccountPermissionSet.cpp b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp new file mode 100644 index 00000000000..1d31b9f1e57 --- /dev/null +++ b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp @@ -0,0 +1,152 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +constexpr std::size_t permissionMaxSize = 10; + +NotTEC +AccountPermissionSet::preflight(PreflightContext const& ctx) +{ + if (!ctx.rules.enabled(featureAccountPermission)) + return temDISABLED; + + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + auto const& permissions = ctx.tx.getFieldArray(sfPermissions); + if (permissions.size() > permissionMaxSize) + return temARRAY_TOO_LARGE; + + std::unordered_set permissionSet; + + for (auto const& permission : permissions) + { + auto permissionObj = dynamic_cast(&permission); + + if (!permissionObj || (permissionObj->getFName() != sfPermission)) + return temMALFORMED; + + auto const permissionValue = permission[sfPermissionValue]; + + if (permissionSet.find(permissionValue) != permissionSet.end()) + return temMALFORMED; + + permissionSet.insert(permissionValue); + } + + return preflight2(ctx); +} + +TER +AccountPermissionSet::preclaim(PreclaimContext const& ctx) +{ + auto const account = ctx.view.read(keylet::account(ctx.account)); + if (!account) + return terNO_ACCOUNT; // LCOV_EXCL_LINE + + auto const authAccount = + ctx.view.read(keylet::account(ctx.tx[sfAuthorize])); + if (!authAccount) + return terNO_ACCOUNT; + + return tesSUCCESS; +} + +TER +AccountPermissionSet::doApply() +{ + auto const sleOwner = ctx_.view().peek(keylet::account(account_)); + if (!sleOwner) + return tefINTERNAL; // LCOV_EXCL_LINE + + auto const accountPermissionKey = + keylet::accountPermission(account_, ctx_.tx[sfAuthorize]); + + auto sle = ctx_.view().peek(accountPermissionKey); + if (sle) + { + auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); + sle->setFieldArray(sfPermissions, permissions); + ctx_.view().update(sle); + return tesSUCCESS; + } + + STAmount const reserve{ctx_.view().fees().accountReserve( + sleOwner->getFieldU32(sfOwnerCount) + 1)}; + + if (mPriorBalance < reserve) + return tecINSUFFICIENT_RESERVE; + + sle = std::make_shared(accountPermissionKey); + auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); + sle->setFieldArray(sfPermissions, permissions); + auto const page = ctx_.view().dirInsert( + keylet::ownerDir(account_), + accountPermissionKey, + describeOwnerDir(account_)); + + if (!page) + return tecDIR_FULL; // LCOV_EXCL_LINE + + (*sle)[sfOwnerNode] = *page; + ctx_.view().insert(sle); + adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); + return tesSUCCESS; +} + +TER +AccountPermissionSet::deleteAccountPermission( + ApplyView& view, + std::shared_ptr const& sle, + AccountID const& account, + beast::Journal j) +{ + if (!sle) + return tecINTERNAL; // LCOV_EXCL_LINE + + if (!view.dirRemove( + keylet::ownerDir(account), (*sle)[sfOwnerNode], sle->key(), false)) + { + // LCOV_EXCL_START + JLOG(j.fatal()) << "Unable to delete AccountPermission from owner."; + return tefBAD_LEDGER; + // LCOV_EXCL_STOP + } + + auto const sleOwner = view.peek(keylet::account(account)); + if (!sleOwner) + return tecINTERNAL; // LCOV_EXCL_LINE + + adjustOwnerCount(view, sleOwner, -1, j); + + view.erase(sle); + + return tesSUCCESS; +} + +} // namespace ripple diff --git a/src/xrpld/app/tx/detail/AccountPermissionSet.h b/src/xrpld/app/tx/detail/AccountPermissionSet.h new file mode 100644 index 00000000000..d6096dc794d --- /dev/null +++ b/src/xrpld/app/tx/detail/AccountPermissionSet.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_ACCOUNTPERMISSIONSET_H_INCLUDED +#define RIPPLE_TX_ACCOUNTPERMISSIONSET_H_INCLUDED + +#include + +namespace ripple { + +class AccountPermissionSet : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; + + explicit AccountPermissionSet(ApplyContext& ctx) : Transactor(ctx) + { + } + + static NotTEC + preflight(PreflightContext const& ctx); + + static TER + preclaim(PreclaimContext const& ctx); + + TER + doApply() override; + + // Interface used by DeleteAccount + static TER + deleteAccountPermission( + ApplyView& view, + std::shared_ptr const& sle, + AccountID const& account, + beast::Journal j); +}; + +} // namespace ripple + +#endif diff --git a/src/xrpld/app/tx/detail/ApplyContext.cpp b/src/xrpld/app/tx/detail/ApplyContext.cpp index 969af7960eb..ac73bc499f5 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.cpp +++ b/src/xrpld/app/tx/detail/ApplyContext.cpp @@ -35,11 +35,17 @@ ApplyContext::ApplyContext( TER preclaimResult_, XRPAmount baseFee_, ApplyFlags flags, + bool isDelegated, + AccountID const account, + std::unordered_set const gpSet, beast::Journal journal_) : app(app_) , tx(tx_) , preclaimResult(preclaimResult_) , baseFee(baseFee_) + , isDelegated(isDelegated) + , account(account) + , gpSet(std::move(gpSet)) , journal(journal_) , base_(base) , flags_(flags) diff --git a/src/xrpld/app/tx/detail/ApplyContext.h b/src/xrpld/app/tx/detail/ApplyContext.h index 45de05a73db..5ee3f1f092b 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.h +++ b/src/xrpld/app/tx/detail/ApplyContext.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -42,12 +43,18 @@ class ApplyContext TER preclaimResult, XRPAmount baseFee, ApplyFlags flags, + bool isDelegated, + AccountID const account, + std::unordered_set const gpSet, beast::Journal = beast::Journal{beast::Journal::getNullSink()}); Application& app; STTx const& tx; TER const preclaimResult; XRPAmount const baseFee; + bool isDelegated; + AccountID const account; + std::unordered_set gpSet; beast::Journal const journal; ApplyView& diff --git a/src/xrpld/app/tx/detail/CancelCheck.cpp b/src/xrpld/app/tx/detail/CancelCheck.cpp index 7954e86cf3b..39231e70e54 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.cpp +++ b/src/xrpld/app/tx/detail/CancelCheck.cpp @@ -73,7 +73,7 @@ CancelCheck::preclaim(PreclaimContext const& ctx) { // If the check is not yet expired, then only the creator or the // destination may cancel the check. - AccountID const acctId{ctx.tx[sfAccount]}; + AccountID const acctId{ctx.account}; if (acctId != (*sleCheck)[sfAccount] && acctId != (*sleCheck)[sfDestination]) { diff --git a/src/xrpld/app/tx/detail/CancelOffer.cpp b/src/xrpld/app/tx/detail/CancelOffer.cpp index 30e955a8282..f693f4cb394 100644 --- a/src/xrpld/app/tx/detail/CancelOffer.cpp +++ b/src/xrpld/app/tx/detail/CancelOffer.cpp @@ -53,7 +53,7 @@ CancelOffer::preflight(PreflightContext const& ctx) TER CancelOffer::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.tx[sfAccount]; + auto const id = ctx.account; auto const offerSequence = ctx.tx[sfOfferSequence]; auto const sle = ctx.view.read(keylet::account(id)); diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 8b5ef79b6d4..776396b3e94 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -91,7 +91,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) // Only cash a check with this account as the destination. AccountID const dstId = sleCheck->at(sfDestination); - if (ctx.tx[sfAccount] != dstId) + if (ctx.account != dstId) { JLOG(ctx.j.warn()) << "Cashing a check with wrong Destination."; return tecNO_PERMISSION; diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 909f35fc799..a924fbe0c96 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -38,7 +38,7 @@ Change::preflight(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; - auto account = ctx.tx.getAccountID(sfAccount); + auto account = ctx.account; if (account != beast::zero) { JLOG(ctx.j.warn()) << "Change: Bad source id"; diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index f1040790a42..332d039932c 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -39,7 +39,7 @@ preflightHelper(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfHolder)) return temMALFORMED; - AccountID const issuer = ctx.tx[sfAccount]; + AccountID const issuer = ctx.account; STAmount const clawAmount = ctx.tx[sfAmount]; // The issuer field is used for the token holder instead @@ -65,7 +65,7 @@ preflightHelper(PreflightContext const& ctx) return temMALFORMED; // issuer is the same as holder - if (ctx.tx[sfAccount] == *mptHolder) + if (ctx.account == *mptHolder) return temMALFORMED; if (clawAmount.mpt() > MPTAmount{maxMPTokenAmount} || @@ -197,7 +197,7 @@ preclaimHelper( TER Clawback::preclaim(PreclaimContext const& ctx) { - AccountID const issuer = ctx.tx[sfAccount]; + AccountID const issuer = ctx.account; auto const clawAmount = ctx.tx[sfAmount]; AccountID const holder = clawAmount.holds() ? clawAmount.getIssuer() : ctx.tx[sfHolder]; @@ -226,7 +226,7 @@ template <> TER applyHelper(ApplyContext& ctx) { - AccountID const issuer = ctx.tx[sfAccount]; + AccountID const issuer = ctx.account; STAmount clawAmount = ctx.tx[sfAmount]; AccountID const holder = clawAmount.getIssuer(); // cannot be reference @@ -257,7 +257,7 @@ template <> TER applyHelper(ApplyContext& ctx) { - AccountID const issuer = ctx.tx[sfAccount]; + AccountID const issuer = ctx.account; auto clawAmount = ctx.tx[sfAmount]; AccountID const holder = ctx.tx[sfHolder]; diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 3a278eed738..bf3c97953fc 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -44,7 +44,7 @@ CreateCheck::preflight(PreflightContext const& ctx) JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } - if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + if (ctx.account == ctx.tx[sfDestination]) { // They wrote a check to themselves. JLOG(ctx.j.warn()) << "Malformed transaction: Check to self."; @@ -125,7 +125,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) // // Note that we DO allow create check for a currency that the // account does not yet have a trustline to. - AccountID const srcId{ctx.tx.getAccountID(sfAccount)}; + AccountID const srcId{ctx.account}; if (issuerId != srcId) { // Check if the issuer froze the line diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index 2a5145594a1..65028b65de1 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -128,7 +128,7 @@ CreateOffer::preflight(PreflightContext const& ctx) TER CreateOffer::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.tx[sfAccount]; + auto const id = ctx.account; auto saTakerPays = ctx.tx[sfTakerPays]; auto saTakerGets = ctx.tx[sfTakerGets]; diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index b04f4af1d30..b045498b183 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -56,7 +56,7 @@ CreateTicket::preflight(PreflightContext const& ctx) TER CreateTicket::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.tx[sfAccount]; + auto const id = ctx.account; auto const sleAccountRoot = ctx.view.read(keylet::account(id)); if (!sleAccountRoot) return terNO_ACCOUNT; diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index 4da875f8d7c..7de89bd783f 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -101,8 +101,7 @@ CredentialCreate::preclaim(PreclaimContext const& ctx) return tecNO_TARGET; } - if (ctx.view.exists( - keylet::credential(subject, ctx.tx[sfAccount], credType))) + if (ctx.view.exists(keylet::credential(subject, ctx.account, credType))) { JLOG(ctx.j.trace()) << "Credential already exists."; return tecDUPLICATE; @@ -242,7 +241,7 @@ CredentialDelete::preflight(PreflightContext const& ctx) TER CredentialDelete::preclaim(PreclaimContext const& ctx) { - AccountID const account{ctx.tx[sfAccount]}; + AccountID const account{ctx.account}; auto const subject = ctx.tx[~sfSubject].value_or(account); auto const issuer = ctx.tx[~sfIssuer].value_or(account); auto const credType(ctx.tx[sfCredentialType]); @@ -309,7 +308,7 @@ CredentialAccept::preflight(PreflightContext const& ctx) TER CredentialAccept::preclaim(PreclaimContext const& ctx) { - AccountID const subject = ctx.tx[sfAccount]; + AccountID const subject = ctx.account; AccountID const issuer = ctx.tx[sfIssuer]; auto const credType(ctx.tx[sfCredentialType]); diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index a7f33a3d8dd..90fca354215 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include #include @@ -52,7 +53,7 @@ DeleteAccount::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + if (ctx.account == ctx.tx[sfDestination]) // An account cannot be deleted and give itself the resulting XRP. return temDST_IS_SRC; @@ -179,6 +180,19 @@ removeCredentialFromLedger( return credentials::deleteSLE(view, sleDel, j); } +TER +removeAccountPermissionFromLedger( + Application& app, + ApplyView& view, + AccountID const& account, + uint256 const& delIndex, + std::shared_ptr const& sleDel, + beast::Journal j) +{ + return AccountPermissionSet::deleteAccountPermission( + view, sleDel, account, j); +} + // Return nullptr if the LedgerEntryType represents an obligation that can't // be deleted. Otherwise return the pointer to the function that can delete // the non-obligation @@ -201,6 +215,8 @@ nonObligationDeleter(LedgerEntryType t) return removeDIDFromLedger; case ltORACLE: return removeOracleFromLedger; + case ltACCOUNT_PERMISSION: + return removeAccountPermissionFromLedger; case ltCREDENTIAL: return removeCredentialFromLedger; default: @@ -213,7 +229,7 @@ nonObligationDeleter(LedgerEntryType t) TER DeleteAccount::preclaim(PreclaimContext const& ctx) { - AccountID const account{ctx.tx[sfAccount]}; + AccountID const account{ctx.account}; AccountID const dst{ctx.tx[sfDestination]}; auto sleDst = ctx.view.read(keylet::account(dst)); diff --git a/src/xrpld/app/tx/detail/DeleteOracle.cpp b/src/xrpld/app/tx/detail/DeleteOracle.cpp index 807e52ee6b4..0b9b25103bb 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.cpp +++ b/src/xrpld/app/tx/detail/DeleteOracle.cpp @@ -47,17 +47,17 @@ DeleteOracle::preflight(PreflightContext const& ctx) TER DeleteOracle::preclaim(PreclaimContext const& ctx) { - if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount)))) + if (!ctx.view.exists(keylet::account(ctx.account))) return terNO_ACCOUNT; // LCOV_EXCL_LINE - if (auto const sle = ctx.view.read(keylet::oracle( - ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); + if (auto const sle = ctx.view.read( + keylet::oracle(ctx.account, ctx.tx[sfOracleDocumentID])); !sle) { JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist."; return tecNO_ENTRY; } - else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) + else if (ctx.account != sle->getAccountID(sfOwner)) { // this can't happen because of the above check // LCOV_EXCL_START diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index 73cd19e4120..57bef6db4dd 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -85,7 +85,7 @@ DepositPreauth::preflight(PreflightContext const& ctx) } // An account may not preauthorize itself. - if (optAuth && (target == ctx.tx[sfAccount])) + if (optAuth && (target == ctx.account)) { JLOG(ctx.j.trace()) << "Malformed transaction: Attempting to DepositPreauth self."; @@ -141,7 +141,7 @@ DepositPreauth::preflight(PreflightContext const& ctx) TER DepositPreauth::preclaim(PreclaimContext const& ctx) { - AccountID const account(ctx.tx[sfAccount]); + AccountID const account(ctx.account); // Determine which operation we're performing: authorizing or unauthorizing. if (ctx.tx.isFieldPresent(sfAuthorize)) diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index f98e72f23dd..f855188fd08 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -206,8 +206,7 @@ EscrowCreate::doApply() } } - auto const account = ctx_.tx[sfAccount]; - auto const sle = ctx_.view().peek(keylet::account(account)); + auto const sle = ctx_.view().peek(keylet::account(account_)); if (!sle) return tefINTERNAL; @@ -244,10 +243,10 @@ EscrowCreate::doApply() // Create escrow in ledger. Note that we we use the value from the // sequence or ticket. For more explanation see comments in SeqProxy.h. Keylet const escrowKeylet = - keylet::escrow(account, ctx_.tx.getSeqProxy().value()); + keylet::escrow(account_, ctx_.tx.getSeqProxy().value()); auto const slep = std::make_shared(escrowKeylet); (*slep)[sfAmount] = ctx_.tx[sfAmount]; - (*slep)[sfAccount] = account; + (*slep)[sfAccount] = account_; (*slep)[~sfCondition] = ctx_.tx[~sfCondition]; (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; (*slep)[sfDestination] = ctx_.tx[sfDestination]; @@ -260,14 +259,16 @@ EscrowCreate::doApply() // Add escrow to sender's owner directory { auto page = ctx_.view().dirInsert( - keylet::ownerDir(account), escrowKeylet, describeOwnerDir(account)); + keylet::ownerDir(account_), + escrowKeylet, + describeOwnerDir(account_)); if (!page) return tecDIR_FULL; (*slep)[sfOwnerNode] = *page; } // If it's not a self-send, add escrow to recipient's owner directory. - if (auto const dest = ctx_.tx[sfDestination]; dest != ctx_.tx[sfAccount]) + if (auto const dest = ctx_.tx[sfDestination]; dest != account_) { auto page = ctx_.view().dirInsert( keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest)); @@ -377,7 +378,7 @@ EscrowFinish::preclaim(PreclaimContext const& ctx) if (!ctx.view.rules().enabled(featureCredentials)) return Transactor::preclaim(ctx); - if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); + if (auto const err = credentials::valid(ctx, ctx.account); !isTesSuccess(err)) return err; diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index 90fc399b344..bc982bb12b0 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -458,6 +458,7 @@ LedgerEntryTypesMatch::visitEntry( switch (after->getType()) { case ltACCOUNT_ROOT: + case ltACCOUNT_PERMISSION: case ltDIR_NODE: case ltRIPPLE_STATE: case ltTICKET: diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp index 8042c9c6982..a7f96d6f028 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp @@ -37,7 +37,7 @@ MPTokenAuthorize::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfMPTokenAuthorizeMask) return temINVALID_FLAG; - if (ctx.tx[sfAccount] == ctx.tx[~sfHolder]) + if (ctx.account == ctx.tx[~sfHolder]) return temMALFORMED; return preflight2(ctx); @@ -46,7 +46,7 @@ MPTokenAuthorize::preflight(PreflightContext const& ctx) TER MPTokenAuthorize::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.tx[sfAccount]; + auto const accountID = ctx.account; auto const holderID = ctx.tx[~sfHolder]; // if non-issuer account submits this tx, then they are trying either: diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp index a0f0b9d8602..49d3a2f1f80 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp @@ -52,7 +52,7 @@ MPTokenIssuanceDestroy::preclaim(PreclaimContext const& ctx) return tecOBJECT_NOT_FOUND; // ensure it is issued by the tx submitter - if ((*sleMPT)[sfIssuer] != ctx.tx[sfAccount]) + if ((*sleMPT)[sfIssuer] != ctx.account) return tecNO_PERMISSION; // ensure it has no outstanding balances diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp index 4e395c30be6..a810ee30b92 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp @@ -43,7 +43,7 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) else if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock)) return temINVALID_FLAG; - auto const accountID = ctx.tx[sfAccount]; + auto const accountID = ctx.account; auto const holderID = ctx.tx[~sfHolder]; if (holderID && accountID == holderID) return temMALFORMED; @@ -65,7 +65,7 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; // ensure it is issued by the tx submitter - if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount]) + if ((*sleMptIssuance)[sfIssuer] != ctx.account) return tecNO_PERMISSION; if (auto const holderID = ctx.tx[~sfHolder]) @@ -102,9 +102,20 @@ MPTokenIssuanceSet::doApply() std::uint32_t const flagsIn = sle->getFieldU32(sfFlags); std::uint32_t flagsOut = flagsIn; - if (txFlags & tfMPTLock) + bool bLock = txFlags & tfMPTLock; + bool bUnlock = txFlags & tfMPTUnlock; + if (ctx_.isDelegated && !ctx_.gpSet.empty()) + { + if (bLock && ctx_.gpSet.find(gpMPTokenIssuanceLock) == ctx_.gpSet.end()) + return terNO_AUTH; + if (bUnlock && + ctx_.gpSet.find(gpMPTokenIssuanceUnlock) == ctx_.gpSet.end()) + return terNO_AUTH; + } + + if (bLock) flagsOut |= lsfMPTLocked; - else if (txFlags & tfMPTUnlock) + else if (bUnlock) flagsOut &= ~lsfMPTLocked; if (flagsIn != flagsOut) diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index b884a791e78..c22411348ad 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -125,10 +125,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // a broker. After, it must be whoever is submitting the tx. if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2)) { - if (*dest != ctx.tx[sfAccount]) + if (*dest != ctx.account) return tecNO_PERMISSION; } - else if (*dest != so->at(sfOwner) && *dest != ctx.tx[sfAccount]) + else if (*dest != so->at(sfOwner) && *dest != ctx.account) return tecNFTOKEN_BUY_SELL_MISMATCH; } @@ -139,10 +139,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // a broker. After, it must be whoever is submitting the tx. if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2)) { - if (*dest != ctx.tx[sfAccount]) + if (*dest != ctx.account) return tecNO_PERMISSION; } - else if (*dest != bo->at(sfOwner) && *dest != ctx.tx[sfAccount]) + else if (*dest != bo->at(sfOwner) && *dest != ctx.account) return tecNFTOKEN_BUY_SELL_MISMATCH; } @@ -169,12 +169,11 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) return tecNFTOKEN_OFFER_TYPE_MISMATCH; // An account can't accept an offer it placed: - if ((*bo)[sfOwner] == ctx.tx[sfAccount]) + if ((*bo)[sfOwner] == ctx.account) return tecCANT_ACCEPT_OWN_NFTOKEN_OFFER; // If not in bridged mode, the account must own the token: - if (!so && - !nft::findToken(ctx.view, ctx.tx[sfAccount], (*bo)[sfNFTokenID])) + if (!so && !nft::findToken(ctx.view, ctx.account, (*bo)[sfNFTokenID])) return tecNO_PERMISSION; // If not in bridged mode... @@ -183,7 +182,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // If the offer has a Destination field, the acceptor must be the // Destination. if (auto const dest = bo->at(~sfDestination); - dest.has_value() && *dest != ctx.tx[sfAccount]) + dest.has_value() && *dest != ctx.account) return tecNO_PERMISSION; } @@ -216,7 +215,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) return tecNFTOKEN_OFFER_TYPE_MISMATCH; // An account can't accept an offer it placed: - if ((*so)[sfOwner] == ctx.tx[sfAccount]) + if ((*so)[sfOwner] == ctx.account) return tecCANT_ACCEPT_OWN_NFTOKEN_OFFER; // The seller must own the token. @@ -229,7 +228,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // If the offer has a Destination field, the acceptor must be the // Destination. if (auto const dest = so->at(~sfDestination); - dest.has_value() && *dest != ctx.tx[sfAccount]) + dest.has_value() && *dest != ctx.account) return tecNO_PERMISSION; } @@ -239,7 +238,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { if (accountHolds( ctx.view, - ctx.tx[sfAccount], + ctx.account, needed.getCurrency(), needed.getIssuer(), fhZERO_IF_FROZEN, @@ -261,11 +260,8 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // cover what the buyer will pay, which doesn't make sense, causes // an unnecessary tec, and is also resolved with this amendment. if (accountFunds( - ctx.view, - ctx.tx[sfAccount], - needed, - fhZERO_IF_FROZEN, - ctx.j) < needed) + ctx.view, ctx.account, needed, fhZERO_IF_FROZEN, ctx.j) < + needed) return tecINSUFFICIENT_FUNDS; } } diff --git a/src/xrpld/app/tx/detail/NFTokenBurn.cpp b/src/xrpld/app/tx/detail/NFTokenBurn.cpp index 725e35791f9..e5d795a8e87 100644 --- a/src/xrpld/app/tx/detail/NFTokenBurn.cpp +++ b/src/xrpld/app/tx/detail/NFTokenBurn.cpp @@ -51,7 +51,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx) if (ctx.tx.isFieldPresent(sfOwner)) return ctx.tx.getAccountID(sfOwner); - return ctx.tx[sfAccount]; + return ctx.account; }(); if (!nft::findToken(ctx.view, owner, ctx.tx[sfNFTokenID])) @@ -59,7 +59,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx) // The owner of a token can always burn it, but the issuer can only // do so if the token is marked as burnable. - if (auto const account = ctx.tx[sfAccount]; owner != account) + if (auto const account = ctx.account; owner != account) { if (!(nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagBurnable)) return tecNO_PERMISSION; @@ -93,7 +93,7 @@ NFTokenBurn::doApply() auto const ret = nft::removeToken( view(), ctx_.tx.isFieldPresent(sfOwner) ? ctx_.tx.getAccountID(sfOwner) - : ctx_.tx.getAccountID(sfAccount), + : account_, ctx_.tx[sfNFTokenID]); // Should never happen since preclaim() verified the token is present. diff --git a/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp index ef66ceecd0c..85b2e7f6188 100644 --- a/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp @@ -56,7 +56,7 @@ NFTokenCancelOffer::preflight(PreflightContext const& ctx) TER NFTokenCancelOffer::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.tx[sfAccount]; + auto const account = ctx.account; auto const& ids = ctx.tx[sfNFTokenOffers]; diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp index 43178d31b4a..860c2c0a3ec 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp @@ -45,7 +45,7 @@ NFTokenCreateOffer::preflight(PreflightContext const& ctx) // Use implementation shared with NFTokenMint if (NotTEC notTec = nft::tokenOfferCreatePreflight( - ctx.tx[sfAccount], + ctx.account, ctx.tx[sfAmount], ctx.tx[~sfDestination], ctx.tx[~sfExpiration], @@ -70,14 +70,14 @@ NFTokenCreateOffer::preclaim(PreclaimContext const& ctx) if (!nft::findToken( ctx.view, - ctx.tx[(txFlags & tfSellNFToken) ? sfAccount : sfOwner], + (txFlags & tfSellNFToken) ? ctx.account : ctx.tx[sfOwner], nftokenID)) return tecNO_ENTRY; // Use implementation shared with NFTokenMint return nft::tokenOfferCreatePreclaim( ctx.view, - ctx.tx[sfAccount], + ctx.account, nft::getIssuer(nftokenID), ctx.tx[sfAmount], ctx.tx[~sfDestination], @@ -94,7 +94,7 @@ NFTokenCreateOffer::doApply() // Use implementation shared with NFTokenMint return nft::tokenOfferCreateApply( view(), - ctx_.tx[sfAccount], + account_, ctx_.tx[sfAmount], ctx_.tx[~sfDestination], ctx_.tx[~sfExpiration], diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index d5c3a8707c2..a7c952290ae 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -84,7 +84,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) } // An issuer must only be set if the tx is executed by the minter - if (auto iss = ctx.tx[~sfIssuer]; iss == ctx.tx[sfAccount]) + if (auto iss = ctx.tx[~sfIssuer]; iss == ctx.account) return temMALFORMED; if (auto uri = ctx.tx[~sfURI]) @@ -104,7 +104,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) // do the validation. We pass tfSellNFToken as the transaction flags // because a Mint is only allowed to create a sell offer. if (NotTEC notTec = nft::tokenOfferCreatePreflight( - ctx.tx[sfAccount], + ctx.account, ctx.tx[sfAmount], ctx.tx[~sfDestination], ctx.tx[~sfExpiration], @@ -177,8 +177,7 @@ NFTokenMint::preclaim(PreclaimContext const& ctx) if (!sle) return tecNO_ISSUER; - if (auto const minter = (*sle)[~sfNFTokenMinter]; - minter != ctx.tx[sfAccount]) + if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != ctx.account) return tecNO_PERMISSION; } @@ -193,8 +192,8 @@ NFTokenMint::preclaim(PreclaimContext const& ctx) // because a Mint is only allowed to create a sell offer. if (TER const ter = nft::tokenOfferCreatePreclaim( ctx.view, - ctx.tx[sfAccount], - ctx.tx[~sfIssuer].value_or(ctx.tx[sfAccount]), + ctx.account, + ctx.tx[~sfIssuer].value_or(ctx.account), ctx.tx[sfAmount], ctx.tx[~sfDestination], extractNFTokenFlagsFromTxFlags(ctx.tx.getFlags()), @@ -322,7 +321,7 @@ NFTokenMint::doApply() // because a Mint is only allowed to create a sell offer. if (TER const ter = nft::tokenOfferCreateApply( view(), - ctx_.tx[sfAccount], + account_, ctx_.tx[sfAmount], ctx_.tx[~sfDestination], ctx_.tx[~sfExpiration], diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index b2d4c0c9449..8dcb74094e0 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -181,7 +181,7 @@ PayChanCreate::preflight(PreflightContext const& ctx) if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) return temBAD_AMOUNT; - if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) + if (ctx.account == ctx.tx[sfDestination]) return temDST_IS_SRC; if (!publicKeyType(ctx.tx[sfPublicKey])) @@ -193,7 +193,7 @@ PayChanCreate::preflight(PreflightContext const& ctx) TER PayChanCreate::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.tx[sfAccount]; + auto const account = ctx.account; auto const sle = ctx.view.read(keylet::account(account)); if (!sle) return terNO_ACCOUNT; @@ -245,8 +245,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) TER PayChanCreate::doApply() { - auto const account = ctx_.tx[sfAccount]; - auto const sle = ctx_.view().peek(keylet::account(account)); + auto const sle = ctx_.view().peek(keylet::account(account_)); if (!sle) return tefINTERNAL; @@ -257,14 +256,14 @@ PayChanCreate::doApply() // Note that we we use the value from the sequence or ticket as the // payChan sequence. For more explanation see comments in SeqProxy.h. Keylet const payChanKeylet = - keylet::payChan(account, dst, ctx_.tx.getSeqProxy().value()); + keylet::payChan(account_, dst, ctx_.tx.getSeqProxy().value()); auto const slep = std::make_shared(payChanKeylet); // Funds held in this channel (*slep)[sfAmount] = ctx_.tx[sfAmount]; // Amount channel has already paid (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed(); - (*slep)[sfAccount] = account; + (*slep)[sfAccount] = account_; (*slep)[sfDestination] = dst; (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey]; @@ -277,9 +276,9 @@ PayChanCreate::doApply() // Add PayChan to owner directory { auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account), + keylet::ownerDir(account_), payChanKeylet, - describeOwnerDir(account)); + describeOwnerDir(account_)); if (!page) return tecDIR_FULL; (*slep)[sfOwnerNode] = *page; @@ -335,7 +334,6 @@ PayChanFund::doApply() return tecNO_ENTRY; AccountID const src = (*slep)[sfAccount]; - auto const txAccount = ctx_.tx[sfAccount]; auto const expiration = (*slep)[~sfExpiration]; { @@ -348,7 +346,7 @@ PayChanFund::doApply() slep, ctx_.view(), k.key, ctx_.app.journal("View")); } - if (src != txAccount) + if (src != account_) // only the owner can add funds or extend return tecNO_PERMISSION; @@ -366,7 +364,7 @@ PayChanFund::doApply() ctx_.view().update(slep); } - auto const sle = ctx_.view().peek(keylet::account(txAccount)); + auto const sle = ctx_.view().peek(keylet::account(account_)); if (!sle) return tefINTERNAL; @@ -470,7 +468,7 @@ PayChanClaim::preclaim(PreclaimContext const& ctx) if (!ctx.view.rules().enabled(featureCredentials)) return Transactor::preclaim(ctx); - if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); + if (auto const err = credentials::valid(ctx, ctx.account); !isTesSuccess(err)) return err; @@ -487,7 +485,6 @@ PayChanClaim::doApply() AccountID const src = (*slep)[sfAccount]; AccountID const dst = (*slep)[sfDestination]; - AccountID const txAccount = ctx_.tx[sfAccount]; auto const curExpiration = (*slep)[~sfExpiration]; { @@ -500,7 +497,7 @@ PayChanClaim::doApply() slep, ctx_.view(), k.key, ctx_.app.journal("View")); } - if (txAccount != src && txAccount != dst) + if (account_ != src && account_ != dst) return tecNO_PERMISSION; if (ctx_.tx[~sfBalance]) @@ -509,7 +506,7 @@ PayChanClaim::doApply() auto const chanFunds = slep->getFieldAmount(sfAmount).xrp(); auto const reqBalance = ctx_.tx[sfBalance].xrp(); - if (txAccount == dst && !ctx_.tx[~sfSignature]) + if (account_ == dst && !ctx_.tx[~sfSignature]) return temBAD_SIGNATURE; if (ctx_.tx[~sfSignature]) @@ -534,12 +531,12 @@ PayChanClaim::doApply() // featureDepositAuth to remove the bug. bool const depositAuth{ctx_.view().rules().enabled(featureDepositAuth)}; if (!depositAuth && - (txAccount == src && (sled->getFlags() & lsfDisallowXRP))) + (account_ == src && (sled->getFlags() & lsfDisallowXRP))) return tecNO_TARGET; if (depositAuth) { - if (auto err = verifyDepositPreauth(ctx_, txAccount, dst, sled); + if (auto err = verifyDepositPreauth(ctx_, account_, dst, sled); !isTesSuccess(err)) return err; } @@ -554,7 +551,7 @@ PayChanClaim::doApply() if (ctx_.tx.getFlags() & tfRenew) { - if (src != txAccount) + if (src != account_) return tecNO_PERMISSION; (*slep)[~sfExpiration] = std::nullopt; ctx_.view().update(slep); @@ -563,7 +560,7 @@ PayChanClaim::doApply() if (ctx_.tx.getFlags() & tfClose) { // Channel will close immediately if dry or the receiver closes - if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount]) + if (dst == account_ || (*slep)[sfBalance] == (*slep)[sfAmount]) return closeChannel( slep, ctx_.view(), k.key, ctx_.app.journal("View")); diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 2ea13ffabc8..44a77558e3c 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -105,7 +105,7 @@ Payment::preflight(PreflightContext const& ctx) auto const account = tx.getAccountID(sfAccount); STAmount const maxSourceAmount = - getMaxSourceAmount(account, dstAmount, tx[~sfSendMax]); + getMaxSourceAmount(ctx.account, dstAmount, tx[~sfSendMax]); if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) || (!mptDirect && maxSourceAmount.holds())) @@ -319,7 +319,7 @@ Payment::preclaim(PreclaimContext const& ctx) } } - if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); + if (auto const err = credentials::valid(ctx, ctx.account); !isTesSuccess(err)) return err; @@ -341,6 +341,24 @@ Payment::doApply() AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination)); STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount)); + + if (ctx_.isDelegated && !ctx_.gpSet.empty()) + { + bool authorized = false; + auto const amountIssue = dstAmount.issue(); + if (isXRP(amountIssue)) + return tecNO_AUTH; + if (amountIssue.account == account_ && + ctx_.gpSet.find(gpPaymentMint) == ctx_.gpSet.end()) + authorized = true; + if (amountIssue.account == dstAccountID && + ctx_.gpSet.find(gpPaymentBurn) == ctx_.gpSet.end()) + authorized = true; + + if (!authorized) + return tecNO_AUTH; + } + bool const mptDirect = dstAmount.holds(); STAmount const maxSourceAmount = getMaxSourceAmount(account_, dstAmount, sendMax); diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 055143cc6fd..9d62a8b7c55 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -70,8 +70,7 @@ SetOracle::preflight(PreflightContext const& ctx) TER SetOracle::preclaim(PreclaimContext const& ctx) { - auto const sleSetter = - ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount))); + auto const sleSetter = ctx.view.read(keylet::account(ctx.account)); if (!sleSetter) return terNO_ACCOUNT; // LCOV_EXCL_LINE @@ -92,8 +91,8 @@ SetOracle::preclaim(PreclaimContext const& ctx) lastUpdateTimeEpoch > (closeTime + maxLastUpdateTimeDelta)) return tecINVALID_UPDATE_TIME; - auto const sle = ctx.view.read(keylet::oracle( - ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); + auto const sle = + ctx.view.read(keylet::oracle(ctx.account, ctx.tx[sfOracleDocumentID])); // token pairs to add/update std::set> pairs; @@ -185,8 +184,7 @@ SetOracle::preclaim(PreclaimContext const& ctx) static bool adjustOwnerCount(ApplyContext& ctx, int count) { - if (auto const sleAccount = - ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) + if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.account))) { adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal); return true; @@ -281,7 +279,7 @@ SetOracle::doApply() // create sle = std::make_shared(oracleID); - sle->setAccountID(sfOwner, ctx_.tx.getAccountID(sfAccount)); + sle->setAccountID(sfOwner, account_); sle->setFieldVL(sfProvider, ctx_.tx[sfProvider]); if (ctx_.tx.isFieldPresent(sfURI)) sle->setFieldVL(sfURI, ctx_.tx[sfURI]); diff --git a/src/xrpld/app/tx/detail/SetRegularKey.cpp b/src/xrpld/app/tx/detail/SetRegularKey.cpp index 9f165612f3a..39a556fb0e2 100644 --- a/src/xrpld/app/tx/detail/SetRegularKey.cpp +++ b/src/xrpld/app/tx/detail/SetRegularKey.cpp @@ -64,7 +64,7 @@ SetRegularKey::preflight(PreflightContext const& ctx) if (ctx.rules.enabled(fixMasterKeyAsRegularKey) && ctx.tx.isFieldPresent(sfRegularKey) && - (ctx.tx.getAccountID(sfRegularKey) == ctx.tx.getAccountID(sfAccount))) + (ctx.tx.getAccountID(sfRegularKey) == ctx.account)) { return temBAD_REGKEY; } diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index 0949fbbe775..6093c798392 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -97,7 +97,7 @@ SetSignerList::preflight(PreflightContext const& ctx) if (std::get<3>(result) == set) { // Validate our settings. - auto const account = ctx.tx.getAccountID(sfAccount); + auto const account = ctx.account; NotTEC const ter = validateQuorumAndSignerEntries( std::get<1>(result), std::get<2>(result), diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 954fc6543f1..44976c1d705 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -84,7 +84,7 @@ SetTrust::preflight(PreflightContext const& ctx) TER SetTrust::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.tx[sfAccount]; + auto const id = ctx.account; auto const sle = ctx.view.read(keylet::account(id)); if (!sle) @@ -243,6 +243,21 @@ SetTrust::doApply() bool const bSetFreeze = (uTxFlags & tfSetFreeze); bool const bClearFreeze = (uTxFlags & tfClearFreeze); + if (ctx_.isDelegated && !ctx_.gpSet.empty()) + { + if (bSetNoRipple || bClearNoRipple) + return terNO_AUTH; + if (bSetAuth && + ctx_.gpSet.find(gpTrustlineAuthorize) == ctx_.gpSet.end()) + return terNO_AUTH; + if (bSetFreeze && + ctx_.gpSet.find(gpTrustlineFreeze) == ctx_.gpSet.end()) + return terNO_AUTH; + if (bClearFreeze && + ctx_.gpSet.find(gpTrustlineUnfreeze) == ctx_.gpSet.end()) + return terNO_AUTH; + } + auto viewJ = ctx_.app.journal("View"); // Trust lines to self are impossible but because of the old bug there are diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 052a735a2fd..3c0cd359b3e 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -150,15 +150,16 @@ PreflightContext::PreflightContext( STTx const& tx_, Rules const& rules_, ApplyFlags flags_, + AccountID const account_, beast::Journal j_) - : app(app_), tx(tx_), rules(rules_), flags(flags_), j(j_) + : app(app_), tx(tx_), rules(rules_), flags(flags_), account(account_), j(j_) { } //------------------------------------------------------------------------------ Transactor::Transactor(ApplyContext& ctx) - : ctx_(ctx), j_(ctx.journal), account_(ctx.tx.getAccountID(sfAccount)) + : ctx_(ctx), j_(ctx.journal), account_(ctx.account) { } @@ -180,6 +181,52 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) return baseFee + (signerCount * baseFee); } +TER +Transactor::checkAuthorization( + ReadView const& view, + STTx const& tx, + std::unordered_set& gpSet) +{ + auto const onBehalfOfAccount = view.read(keylet::account(tx[sfOnBehalfOf])); + if (!onBehalfOfAccount) + return terNO_ACCOUNT; + + auto const accountPermissionKey = + keylet::accountPermission(tx[sfOnBehalfOf], tx[sfAccount]); + auto const sle = view.read(accountPermissionKey); + if (!sle) + return temMALFORMED; // todo: change error code + + auto const permissions = sle->getFieldArray(sfPermissions); + auto const transactionType = tx.getTxnType(); + + for (auto const& permission : permissions) + { + auto const permissionValue = permission[sfPermissionValue]; + if (permissionValue == transactionType + 1) + { + // if the transaction permission is authorized, do not need to check + // granular permission. + gpSet.clear(); + return tesSUCCESS; + } + + auto const gpType = + static_cast(permissionValue); + auto const& type = Permission::getInstance().getGranularTxType(gpType); + if (type && *type == transactionType) + gpSet.insert(gpType); + } + + if (gpSet.empty()) + return terNO_AUTH; + + // When the code reaches here, the transaction permission is not authorized. + // But one or more of its granular permission under this transaction type is + // authorized. And the granular types are stored in gpSet. + return tesSUCCESS; +} + XRPAmount Transactor::minimumFee( Application& app, @@ -247,6 +294,21 @@ TER Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); + if (ctx_.isDelegated) + { + // if the transaction is being delegated to another account, + // the sender account will pay the fee. + auto const sender = ctx_.tx.getAccountID(sfAccount); + auto const sleSender = view().peek(keylet::account(sender)); + if (!sleSender) + return tefINTERNAL; + + auto senderBalance = STAmount{(*sleSender)[sfBalance]}.xrp(); + senderBalance -= feePaid; + sleSender->setFieldAmount(sfBalance, senderBalance); + view().update(sleSender); + return tesSUCCESS; + } auto const sle = view().peek(keylet::account(account_)); if (!sle) diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index c587e5e1994..b6718ef16e1 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace ripple { @@ -35,6 +36,7 @@ struct PreflightContext STTx const& tx; Rules const rules; ApplyFlags flags; + AccountID const account; beast::Journal const j; PreflightContext( @@ -42,6 +44,7 @@ struct PreflightContext STTx const& tx_, Rules const& rules_, ApplyFlags flags_, + AccountID const account_, beast::Journal j_); PreflightContext& @@ -57,6 +60,8 @@ struct PreclaimContext TER preflightResult; STTx const& tx; ApplyFlags flags; + bool isDelegated; + AccountID const account; beast::Journal const j; PreclaimContext( @@ -65,12 +70,16 @@ struct PreclaimContext TER preflightResult_, STTx const& tx_, ApplyFlags flags_, + bool isDelegated, + AccountID const account, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) : app(app_) , view(view_) , preflightResult(preflightResult_) , tx(tx_) , flags(flags_) + , isDelegated(isDelegated) + , account(account) , j(j_) { } @@ -141,6 +150,12 @@ class Transactor static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx); + static TER + checkAuthorization( + ReadView const& view, + STTx const& tx, + std::unordered_set& gpSet); + static TER preclaim(PreclaimContext const& ctx) { diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index f5633903567..e94941cd583 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1384,7 +1384,7 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; - auto const account = ctx.tx[sfAccount]; + auto const account = ctx.account; auto const reward = ctx.tx[sfSignatureReward]; auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; @@ -1459,7 +1459,7 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) TER XChainCreateBridge::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.tx[sfAccount]; + auto const account = ctx.account; auto const bridgeSpec = ctx.tx[sfXChainBridge]; STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); @@ -1510,22 +1510,21 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) TER XChainCreateBridge::doApply() { - auto const account = ctx_.tx[sfAccount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[sfSignatureReward]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; - auto const sleAcct = ctx_.view().peek(keylet::account(account)); + auto const sleAcct = ctx_.view().peek(keylet::account(account_)); if (!sleAcct) return tecINTERNAL; STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::srcChain(account_ == bridgeSpec.lockingChainDoor()); Keylet const bridgeKeylet = keylet::bridge(bridgeSpec, chainType); auto const sleBridge = std::make_shared(bridgeKeylet); - (*sleBridge)[sfAccount] = account; + (*sleBridge)[sfAccount] = account_; (*sleBridge)[sfSignatureReward] = reward; if (minAccountCreate) (*sleBridge)[sfMinAccountCreateAmount] = *minAccountCreate; @@ -1537,7 +1536,9 @@ XChainCreateBridge::doApply() // Add to owner directory { auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account), bridgeKeylet, describeOwnerDir(account)); + keylet::ownerDir(account_), + bridgeKeylet, + describeOwnerDir(account_)); if (!page) return tecDIR_FULL; (*sleBridge)[sfOwnerNode] = *page; @@ -1565,7 +1566,7 @@ BridgeModify::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfBridgeModifyMask) return temINVALID_FLAG; - auto const account = ctx.tx[sfAccount]; + auto const account = ctx.account; auto const reward = ctx.tx[~sfSignatureReward]; auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; @@ -1609,11 +1610,10 @@ BridgeModify::preflight(PreflightContext const& ctx) TER BridgeModify::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::srcChain(ctx.account == bridgeSpec.lockingChainDoor()); if (!ctx.view.read(keylet::bridge(bridgeSpec, chainType))) { @@ -1626,19 +1626,18 @@ BridgeModify::preclaim(PreclaimContext const& ctx) TER BridgeModify::doApply() { - auto const account = ctx_.tx[sfAccount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[~sfSignatureReward]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; bool const clearAccountCreate = ctx_.tx.getFlags() & tfClearAccountCreateAmount; - auto const sleAcct = ctx_.view().peek(keylet::account(account)); + auto const sleAcct = ctx_.view().peek(keylet::account(account_)); if (!sleAcct) return tecINTERNAL; STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); + STXChainBridge::srcChain(account_ == bridgeSpec.lockingChainDoor()); auto const sleBridge = ctx_.view().peek(keylet::bridge(bridgeSpec, chainType)); @@ -1691,7 +1690,6 @@ XChainClaim::preflight(PreflightContext const& ctx) TER XChainClaim::preclaim(PreclaimContext const& ctx) { - AccountID const account = ctx.tx[sfAccount]; STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; STAmount const& thisChainAmount = ctx.tx[sfAmount]; auto const claimID = ctx.tx[sfXChainClaimID]; @@ -1761,7 +1759,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx) return tecXCHAIN_NO_CLAIM_ID; } - if ((*sleClaimID)[sfAccount] != account) + if ((*sleClaimID)[sfAccount] != ctx.account) { // Sequence number isn't owned by the sender of this transaction return tecXCHAIN_BAD_CLAIM_ID; @@ -1777,7 +1775,6 @@ XChainClaim::doApply() { PaymentSandbox psb(&ctx_.view()); - AccountID const account = ctx_.tx[sfAccount]; auto const dst = ctx_.tx[sfDestination]; STXChainBridge const bridgeSpec = ctx_.tx[sfXChainBridge]; STAmount const& thisChainAmount = ctx_.tx[sfAmount]; @@ -1799,7 +1796,7 @@ XChainClaim::doApply() // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child // views, it's important that the sle's lifetime doesn't overlap. - auto const sleAcct = psb.peek(keylet::account(account)); + auto const sleAcct = psb.peek(keylet::account(account_)); auto const sleBridge = peekBridge(psb, bridgeSpec); auto const sleClaimID = psb.peek(claimIDKeylet); @@ -1868,7 +1865,7 @@ XChainClaim::doApply() bridgeSpec, dst, dstTag, - /*claimOwner*/ account, + /*claimOwner*/ account_, sendingAmount, rewardPoolSrc, signatureReward, @@ -1939,9 +1936,8 @@ XChainCommit::preclaim(PreclaimContext const& ctx) } AccountID const thisDoor = (*sleBridge)[sfAccount]; - AccountID const account = ctx.tx[sfAccount]; - if (thisDoor == account) + if (thisDoor == ctx.account) { // Door account can't lock funds onto itself return tecXCHAIN_SELF_COMMIT; @@ -1976,11 +1972,10 @@ XChainCommit::doApply() { PaymentSandbox psb(&ctx_.view()); - auto const account = ctx_.tx[sfAccount]; auto const amount = ctx_.tx[sfAmount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; - if (!psb.read(keylet::account(account))) + if (!psb.read(keylet::account(account_))) return tecINTERNAL; auto const sleBridge = readBridge(psb, bridgeSpec); @@ -1995,7 +1990,7 @@ XChainCommit::doApply() auto const thTer = transferHelper( psb, - account, + account_, dst, /*dstTag*/ std::nullopt, /*claimOwner*/ std::nullopt, @@ -2038,7 +2033,6 @@ XChainCreateClaimID::preflight(PreflightContext const& ctx) TER XChainCreateClaimID::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; auto const sleBridge = readBridge(ctx.view, bridgeSpec); @@ -2057,7 +2051,7 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) { // Check reserve - auto const sleAcc = ctx.view.read(keylet::account(account)); + auto const sleAcc = ctx.view.read(keylet::account(ctx.account)); if (!sleAcc) return terNO_ACCOUNT; @@ -2075,12 +2069,11 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) TER XChainCreateClaimID::doApply() { - auto const account = ctx_.tx[sfAccount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[sfSignatureReward]; auto const otherChainSrc = ctx_.tx[sfOtherChainSource]; - auto const sleAcct = ctx_.view().peek(keylet::account(account)); + auto const sleAcct = ctx_.view().peek(keylet::account(account_)); if (!sleAcct) return tecINTERNAL; @@ -2100,7 +2093,7 @@ XChainCreateClaimID::doApply() auto const sleClaimID = std::make_shared(claimIDKeylet); - (*sleClaimID)[sfAccount] = account; + (*sleClaimID)[sfAccount] = account_; (*sleClaimID)[sfXChainBridge] = bridgeSpec; (*sleClaimID)[sfXChainClaimID] = claimID; (*sleClaimID)[sfOtherChainSource] = otherChainSrc; @@ -2111,9 +2104,9 @@ XChainCreateClaimID::doApply() // Add to owner directory { auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account), + keylet::ownerDir(account_), claimIDKeylet, - describeOwnerDir(account)); + describeOwnerDir(account_)); if (!page) return tecDIR_FULL; (*sleClaimID)[sfOwnerNode] = *page; @@ -2228,8 +2221,7 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) return tecXCHAIN_BAD_TRANSFER_ISSUE; AccountID const thisDoor = (*sleBridge)[sfAccount]; - AccountID const account = ctx.tx[sfAccount]; - if (thisDoor == account) + if (thisDoor == ctx.account) { // Door account can't lock funds onto itself return tecXCHAIN_SELF_COMMIT; @@ -2261,12 +2253,11 @@ XChainCreateAccountCommit::doApply() { PaymentSandbox psb(&ctx_.view()); - AccountID const account = ctx_.tx[sfAccount]; STAmount const amount = ctx_.tx[sfAmount]; STAmount const reward = ctx_.tx[sfSignatureReward]; STXChainBridge const bridge = ctx_.tx[sfXChainBridge]; - auto const sle = psb.peek(keylet::account(account)); + auto const sle = psb.peek(keylet::account(account_)); if (!sle) return tecINTERNAL; @@ -2282,7 +2273,7 @@ XChainCreateAccountCommit::doApply() STAmount const toTransfer = amount + reward; auto const thTer = transferHelper( psb, - account, + account_, dst, /*dstTag*/ std::nullopt, /*claimOwner*/ std::nullopt, diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index b3c711084dc..17f6c2b658c 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -162,7 +163,7 @@ invoke_preflight(PreflightContext const& ctx) } } -static TER +static std::pair> invoke_preclaim(PreclaimContext const& ctx) { try @@ -174,31 +175,40 @@ invoke_preclaim(PreclaimContext const& ctx) // doesn't list one, preflight will have already a flagged a // failure. auto const id = ctx.tx.getAccountID(sfAccount); + std::unordered_set gpSet; if (id != beast::zero) { TER result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j); if (result != tesSUCCESS) - return result; + return std::make_pair(result, gpSet); result = T::checkPriorTxAndLastLedger(ctx); if (result != tesSUCCESS) - return result; + return std::make_pair(result, gpSet); result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); if (result != tesSUCCESS) - return result; + return std::make_pair(result, gpSet); result = T::checkSign(ctx); if (result != tesSUCCESS) - return result; + return std::make_pair(result, gpSet); + + if (ctx.isDelegated) + // if this is a delegated transaction, check if the account + // has authorization. + result = T::checkAuthorization(ctx.view, ctx.tx, gpSet); + + if (result != tesSUCCESS) + return std::make_pair(result, gpSet); } - return T::preclaim(ctx); + return std::make_pair(T::preclaim(ctx), gpSet); }); } catch (UnknownTxnType const& e) @@ -207,7 +217,7 @@ invoke_preclaim(PreclaimContext const& ctx) JLOG(ctx.j.fatal()) << "Unknown transaction type in preclaim: " << e.txnType; assert(false); - return temUNKNOWN; + return {temUNKNOWN, std::unordered_set{}}; } } @@ -294,15 +304,22 @@ preflight( ApplyFlags flags, beast::Journal j) { - PreflightContext const pfctx(app, tx, rules, flags, j); + bool const isDelegated = + rules.enabled(featureAccountPermission) && tx[~sfOnBehalfOf]; + AccountID const account = isDelegated ? *tx[~sfOnBehalfOf] : tx[sfAccount]; + PreflightContext const pfctx(app, tx, rules, flags, account, j); try { - return {pfctx, invoke_preflight(pfctx)}; + // if AccountPermission is not enabled, do not use OnBehalfOf field. + if (!rules.enabled(featureAccountPermission) && tx[~sfOnBehalfOf]) + throw std::runtime_error("invalid field"); + + return {pfctx, isDelegated, account, invoke_preflight(pfctx)}; } catch (std::exception const& e) { JLOG(j.fatal()) << "apply: " << e.what(); - return {pfctx, {tefEXCEPTION, TxConsequences{tx}}}; + return {pfctx, false, AccountID(0), {tefEXCEPTION, TxConsequences{tx}}}; } } @@ -327,6 +344,8 @@ preclaim( secondFlight.ter, secondFlight.tx, secondFlight.flags, + secondFlight.isDelegated, + secondFlight.account, secondFlight.j); } else @@ -337,18 +356,26 @@ preclaim( preflightResult.ter, preflightResult.tx, preflightResult.flags, + preflightResult.isDelegated, + preflightResult.account, preflightResult.j); } try { if (ctx->preflightResult != tesSUCCESS) - return {*ctx, ctx->preflightResult}; - return {*ctx, invoke_preclaim(*ctx)}; + return { + *ctx, + ctx->preflightResult, + std::unordered_set{}}; + + auto const& res = invoke_preclaim(*ctx); + return {*ctx, res.first, res.second}; } catch (std::exception const& e) { JLOG(ctx->j.fatal()) << "apply: " << e.what(); - return {*ctx, tefEXCEPTION}; + return { + *ctx, tefEXCEPTION, std::unordered_set{}}; } } @@ -384,6 +411,9 @@ doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view) preclaimResult.ter, calculateBaseFee(view, preclaimResult.tx), preclaimResult.flags, + preclaimResult.isDelegated, + preclaimResult.account, + preclaimResult.gpSet, preclaimResult.j); return invoke_apply(ctx); } diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index af204eaedf7..4e0592869a0 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -931,9 +931,10 @@ chooseLedgerEntryType(Json::Value const& params) std::pair result{RPC::Status::OK, ltANY}; if (params.isMember(jss::type)) { - static constexpr std::array, 25> + static constexpr std::array, 26> types{ {{jss::account, ltACCOUNT_ROOT}, + {jss::account_permission, ltACCOUNT_PERMISSION}, {jss::amendments, ltAMENDMENTS}, {jss::amm, ltAMM}, {jss::bridge, ltBRIDGE}, diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 5d03bbb189d..7f52582b73b 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -807,6 +807,10 @@ doLedgerEntry(RPC::JsonContext& context) } } } + else if (context.params.isMember(jss::account_permission)) + { + // to be implemented + } else { if (context.params.isMember("params") && From 372cc5cb6ea6f1ba2e85510228914503b2cdf6d8 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Tue, 19 Nov 2024 09:54:22 -0500 Subject: [PATCH 2/5] update granular permissions --- .../app/tx/detail/MPTokenIssuanceSet.cpp | 1 + src/xrpld/app/tx/detail/Payment.cpp | 8 +++- src/xrpld/app/tx/detail/SetAccount.cpp | 37 +++++++++++++++++++ src/xrpld/app/tx/detail/SetTrust.cpp | 26 ++++++++++++- 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp index a810ee30b92..1cf0a6cc67c 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp @@ -106,6 +106,7 @@ MPTokenIssuanceSet::doApply() bool bUnlock = txFlags & tfMPTUnlock; if (ctx_.isDelegated && !ctx_.gpSet.empty()) { + // if gpSet is not empty, granular delegation is happening. if (bLock && ctx_.gpSet.find(gpMPTokenIssuanceLock) == ctx_.gpSet.end()) return terNO_AUTH; if (bUnlock && diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 44a77558e3c..39e68f891e1 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -344,15 +344,19 @@ Payment::doApply() if (ctx_.isDelegated && !ctx_.gpSet.empty()) { + // If gpSet is not empty, granular delegation is happening. + // Currently we only support PaymentMint and PaymentBurn granular + // permission. PaymentMint means the sender is the issuer. PaymentBurn + // means the destination is the issuer. bool authorized = false; auto const amountIssue = dstAmount.issue(); if (isXRP(amountIssue)) return tecNO_AUTH; if (amountIssue.account == account_ && - ctx_.gpSet.find(gpPaymentMint) == ctx_.gpSet.end()) + ctx_.gpSet.find(gpPaymentMint) != ctx_.gpSet.end()) authorized = true; if (amountIssue.account == dstAccountID && - ctx_.gpSet.find(gpPaymentBurn) == ctx_.gpSet.end()) + ctx_.gpSet.find(gpPaymentBurn) != ctx_.gpSet.end()) authorized = true; if (!authorized) diff --git a/src/xrpld/app/tx/detail/SetAccount.cpp b/src/xrpld/app/tx/detail/SetAccount.cpp index c0e115c2497..d1d2496747b 100644 --- a/src/xrpld/app/tx/detail/SetAccount.cpp +++ b/src/xrpld/app/tx/detail/SetAccount.cpp @@ -268,6 +268,20 @@ SetAccount::doApply() // legacy AccountSet flags std::uint32_t const uTxFlags{tx.getFlags()}; + + bool granularDelegated = false; + if (ctx_.isDelegated && !ctx_.gpSet.empty()) + { + // if gpSet is not empty, granular delegation is happening. + granularDelegated = true; + + // We don't support any flag based granular permission under AccountSet + // transaction. If any delegated account is trying to update the flag + // onbehalf of another account, it is not authorized. + if (uSetFlag != 0 || uClearFlag != 0 || uTxFlags != 0) + return tecNO_AUTH; + } + bool const bSetRequireDest{ (uTxFlags & tfRequireDestTag) || (uSetFlag == asfRequireDest)}; bool const bClearRequireDest{ @@ -450,6 +464,10 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfEmailHash)) { + if (granularDelegated && + ctx_.gpSet.find(gpAccountEmailHashSet) == ctx_.gpSet.end()) + return tecNO_AUTH; + uint128 const uHash = tx.getFieldH128(sfEmailHash); if (!uHash) @@ -469,6 +487,9 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfWalletLocator)) { + if (granularDelegated) + return tecNO_AUTH; + uint256 const uHash = tx.getFieldH256(sfWalletLocator); if (!uHash) @@ -488,6 +509,10 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfMessageKey)) { + if (granularDelegated && + ctx_.gpSet.find(gpAccountMessageKeySet) == ctx_.gpSet.end()) + return tecNO_AUTH; + Blob const messageKey = tx.getFieldVL(sfMessageKey); if (messageKey.empty()) @@ -507,6 +532,10 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfDomain)) { + if (granularDelegated && + ctx_.gpSet.find(gpAccountDomainSet) == ctx_.gpSet.end()) + return tecNO_AUTH; + Blob const domain = tx.getFieldVL(sfDomain); if (domain.empty()) @@ -526,6 +555,10 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfTransferRate)) { + if (granularDelegated && + ctx_.gpSet.find(gpAccountTransferRateSet) == ctx_.gpSet.end()) + return tecNO_AUTH; + std::uint32_t uRate = tx.getFieldU32(sfTransferRate); if (uRate == 0 || uRate == QUALITY_ONE) @@ -545,6 +578,10 @@ SetAccount::doApply() // if (tx.isFieldPresent(sfTickSize)) { + if (granularDelegated && + ctx_.gpSet.find(gpAccountTickSizeSet) == ctx_.gpSet.end()) + return tecNO_AUTH; + auto uTickSize = tx[sfTickSize]; if ((uTickSize == 0) || (uTickSize == Quality::maxTickSize)) { diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 44976c1d705..ba45fbefb15 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -243,9 +243,14 @@ SetTrust::doApply() bool const bSetFreeze = (uTxFlags & tfSetFreeze); bool const bClearFreeze = (uTxFlags & tfClearFreeze); + bool granularDelegated = false; if (ctx_.isDelegated && !ctx_.gpSet.empty()) { - if (bSetNoRipple || bClearNoRipple) + granularDelegated = true; + // If granular permission is delegated under the TrustSet transaction. + // Currently we only support TrustlineAuthorize, TrustlineFreeze and + // TrustlineUnfreeze granular permission. + if (bSetNoRipple || bClearNoRipple || bQualityIn || bQualityOut) return terNO_AUTH; if (bSetAuth && ctx_.gpSet.find(gpTrustlineAuthorize) == ctx_.gpSet.end()) @@ -315,6 +320,18 @@ SetTrust::doApply() // // Limits // + if (granularDelegated) + { + // Currently we only support TrustlineAuthorize, TrustlineFreeze and + // TrustlineUnfreeze granular permission. So updating the + // LimitAmount is not allowed unless the delegated account has full + // transaction level permission. + auto const curLimit = bHigh + ? sleRippleState->getFieldAmount(sfHighLimit) + : sleRippleState->getFieldAmount(sfLowLimit); + if (curLimit != saLimitAllow) + return tecNO_AUTH; + } sleRippleState->setFieldAmount( !bHigh ? sfLowLimit : sfHighLimit, saLimitAllow); @@ -551,6 +568,13 @@ SetTrust::doApply() } else { + if (granularDelegated) + // currently we only allow TrustlineAuthorize, TrustlineFreeze and + // TrustlineUnfreeze granular permission delegation, a delegated + // account can not create a new trust line if it is not fully + // delegated with the whole TrustSet transaction based permission. + return tecNO_PERMISSION; + // Zero balance in currency. STAmount saBalance(Issue{currency, noAccount()}); From 9310eaec9f3e45878baa1684710c617ea3a892a0 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Thu, 5 Dec 2024 18:18:48 -0500 Subject: [PATCH 3/5] Add STTx Wrapper --- include/xrpl/protocol/Permissions.h | 28 ++- include/xrpl/protocol/STTxWr.h | 210 ++++++++++++++++++ include/xrpl/protocol/jss.h | 1 + src/libxrpl/protocol/Indexes.cpp | 2 +- src/libxrpl/protocol/Permissions.cpp | 48 ++-- src/test/app/AMM_test.cpp | 9 +- src/test/app/Escrow_test.cpp | 6 +- src/test/app/NFTokenBurn_test.cpp | 8 +- src/test/app/TxQ_test.cpp | 6 +- src/test/ledger/Invariants_test.cpp | 4 +- src/xrpld/app/ledger/AcceptedLedgerTx.cpp | 4 +- src/xrpld/app/ledger/detail/LedgerToJson.cpp | 4 +- src/xrpld/app/ledger/detail/LocalTxs.cpp | 5 +- src/xrpld/app/misc/CredentialHelpers.cpp | 2 +- src/xrpld/app/misc/NetworkOPs.cpp | 4 +- src/xrpld/app/misc/detail/TxQ.cpp | 5 +- src/xrpld/app/tx/apply.h | 1 + src/xrpld/app/tx/applySteps.h | 27 +-- src/xrpld/app/tx/detail/AMMBid.cpp | 3 +- src/xrpld/app/tx/detail/AMMClawback.cpp | 10 +- src/xrpld/app/tx/detail/AMMCreate.cpp | 2 +- src/xrpld/app/tx/detail/AMMDeposit.cpp | 2 +- src/xrpld/app/tx/detail/AMMVote.cpp | 2 +- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 8 +- .../app/tx/detail/AccountPermissionSet.cpp | 7 +- src/xrpld/app/tx/detail/ApplyContext.cpp | 12 +- src/xrpld/app/tx/detail/ApplyContext.h | 14 +- src/xrpld/app/tx/detail/CancelCheck.cpp | 2 +- src/xrpld/app/tx/detail/CancelOffer.cpp | 2 +- src/xrpld/app/tx/detail/CashCheck.cpp | 4 +- src/xrpld/app/tx/detail/Change.cpp | 16 +- src/xrpld/app/tx/detail/Clawback.cpp | 10 +- src/xrpld/app/tx/detail/CreateCheck.cpp | 4 +- src/xrpld/app/tx/detail/CreateOffer.cpp | 4 +- src/xrpld/app/tx/detail/CreateTicket.cpp | 4 +- src/xrpld/app/tx/detail/Credentials.cpp | 7 +- src/xrpld/app/tx/detail/DeleteAccount.cpp | 4 +- src/xrpld/app/tx/detail/DeleteOracle.cpp | 8 +- src/xrpld/app/tx/detail/DepositPreauth.cpp | 4 +- src/xrpld/app/tx/detail/Escrow.cpp | 17 +- src/xrpld/app/tx/detail/InvariantCheck.cpp | 28 +-- src/xrpld/app/tx/detail/InvariantCheck.h | 32 +-- src/xrpld/app/tx/detail/MPTokenAuthorize.cpp | 4 +- .../app/tx/detail/MPTokenIssuanceDestroy.cpp | 2 +- .../app/tx/detail/MPTokenIssuanceSet.cpp | 14 +- .../app/tx/detail/NFTokenAcceptOffer.cpp | 28 ++- src/xrpld/app/tx/detail/NFTokenBurn.cpp | 6 +- .../app/tx/detail/NFTokenCancelOffer.cpp | 2 +- .../app/tx/detail/NFTokenCreateOffer.cpp | 8 +- src/xrpld/app/tx/detail/NFTokenMint.cpp | 13 +- src/xrpld/app/tx/detail/PayChan.cpp | 39 ++-- src/xrpld/app/tx/detail/Payment.cpp | 14 +- src/xrpld/app/tx/detail/SetAccount.cpp | 22 +- src/xrpld/app/tx/detail/SetOracle.cpp | 12 +- src/xrpld/app/tx/detail/SetRegularKey.cpp | 2 +- src/xrpld/app/tx/detail/SetSignerList.cpp | 6 +- src/xrpld/app/tx/detail/SetTrust.cpp | 13 +- src/xrpld/app/tx/detail/Transactor.cpp | 46 ++-- src/xrpld/app/tx/detail/Transactor.h | 20 +- src/xrpld/app/tx/detail/XChainBridge.cpp | 69 +++--- src/xrpld/app/tx/detail/apply.cpp | 6 +- src/xrpld/app/tx/detail/applySteps.cpp | 53 ++--- src/xrpld/rpc/handlers/LedgerEntry.cpp | 32 ++- 63 files changed, 613 insertions(+), 378 deletions(-) create mode 100644 include/xrpl/protocol/STTxWr.h diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h index cddbcc64f20..bb62af85e29 100644 --- a/include/xrpl/protocol/Permissions.h +++ b/include/xrpl/protocol/Permissions.h @@ -34,29 +34,29 @@ namespace ripple { */ enum GranularPermissionType : std::uint32_t { - gpTrustlineAuthorize = 65537, + TrustlineAuthorize = 65537, - gpTrustlineFreeze = 65538, + TrustlineFreeze = 65538, - gpTrustlineUnfreeze = 65539, + TrustlineUnfreeze = 65539, - gpAccountDomainSet = 65540, + AccountDomainSet = 65540, - gpAccountEmailHashSet = 65541, + AccountEmailHashSet = 65541, - gpAccountMessageKeySet = 65542, + AccountMessageKeySet = 65542, - gpAccountTransferRateSet = 65543, + AccountTransferRateSet = 65543, - gpAccountTickSizeSet = 65544, + AccountTickSizeSet = 65544, - gpPaymentMint = 65545, + PaymentMint = 65545, - gpPaymentBurn = 65546, + PaymentBurn = 65546, - gpMPTokenIssuanceLock = 65547, + MPTokenIssuanceLock = 65547, - gpMPTokenIssuanceUnlock = 65548, + MPTokenIssuanceUnlock = 65548, }; class Permission @@ -73,6 +73,10 @@ class Permission static Permission const& getInstance(); + Permission(const Permission&) = delete; + Permission& + operator=(const Permission&) = delete; + std::optional getGranularValue(std::string const& name) const; diff --git a/include/xrpl/protocol/STTxWr.h b/include/xrpl/protocol/STTxWr.h new file mode 100644 index 00000000000..5bd4354ec09 --- /dev/null +++ b/include/xrpl/protocol/STTxWr.h @@ -0,0 +1,210 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_STTX_WRAPPER_H_INCLUDED +#define RIPPLE_PROTOCOL_STTX_WRAPPER_H_INCLUDED + +#include +#include +#include + +namespace ripple { + +// This class is a wrapper to deal with delegating in AccountPermission +// amendment. It wraps STTx, and delegates to STTx methods. The key change is +// getAccountID and operator[]. We need to first check if the transaction is +// delegated by another account by checking if the sfOnBehalfOf field is +// present. If it is present, we need to return the sfOnBehalfOf field as the +// account when calling getAccountID(sfAccount) and tx[sfAccount]. +class STTxWr +{ +private: + STTx tx_; // Wrap an instance of STTx + bool isDelegated_; // if the transaction is delegated by another account + +public: + explicit STTxWr(STTx tx, bool isDelegated) + : tx_(tx), isDelegated_(isDelegated) + { + } + + STTx + getTx() const + { + return tx_; + } + + bool + isDelegated() const + { + return isDelegated_; + } + + AccountID + getAccountID(SField const& field) const + { + if (field == sfAccount) + return tx_.isFieldPresent(sfOnBehalfOf) ? *tx_[~sfOnBehalfOf] + : tx_[sfAccount]; + return tx_.getAccountID(field); + } + + template + typename std::enable_if< + !std::is_same, SF_ACCOUNT>::value, + typename T::value_type>::type + operator[](TypedField const& f) const + { + return tx_[f]; + } + + // When Type is SF_ACCOUNT and also field name is sfAccount, we need to + // check if the transaction is delegated by another account. If it is, + // return sfOnBehalfOf field instead. + template + typename std::enable_if< + std::is_same, SF_ACCOUNT>::value, + AccountID>::type + operator[](TypedField const& f) const + { + if (f == sfAccount) + return tx_.isFieldPresent(sfOnBehalfOf) ? *tx_[~sfOnBehalfOf] + : tx_[sfAccount]; + return tx_[f]; + } + + template + std::optional> + operator[](OptionaledField const& of) const + { + return tx_[of]; + } + + uint256 + getTransactionID() const + { + return tx_.getTransactionID(); + } + + TxType + getTxnType() const + { + return tx_.getTxnType(); + } + + std::uint32_t + getFlags() const + { + return tx_.getFlags(); + } + + bool + isFieldPresent(SField const& field) const + { + return tx_.isFieldPresent(field); + } + + Json::Value + getJson(JsonOptions options) const + { + return tx_.getJson(options); + } + + void + add(Serializer& s) const + { + tx_.add(s); + } + + unsigned char + getFieldU8(SField const& field) const + { + return tx_.getFieldU8(field); + } + + std::uint32_t + getFieldU32(SField const& field) const + { + return tx_.getFieldU32(field); + } + + uint256 + getFieldH256(SField const& field) const + { + return tx_.getFieldH256(field); + } + + Blob + getFieldVL(SField const& field) const + { + return tx_.getFieldVL(field); + } + + STAmount const& + getFieldAmount(SField const& field) const + { + return tx_.getFieldAmount(field); + } + + STPathSet const& + getFieldPathSet(SField const& field) const + { + return tx_.getFieldPathSet(field); + } + + const STVector256& + getFieldV256(SField const& field) const + { + return tx_.getFieldV256(field); + } + + const STArray& + getFieldArray(SField const& field) const + { + return tx_.getFieldArray(field); + } + + Blob + getSigningPubKey() const + { + return tx_.getSigningPubKey(); + } + + Blob + getSignature() const + { + return tx_.getSignature(); + } + + bool + isFlag(std::uint32_t f) const + { + return tx_.isFlag(f); + } + + SeqProxy + getSeqProxy() const + { + return tx_.getSeqProxy(); + } +}; + +} // namespace ripple + +#endif diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index fc1a2e68e2b..ce9827f8186 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -170,6 +170,7 @@ JSS(attestations); JSS(attestation_reward_account); JSS(auction_slot); // out: amm_info JSS(authorized); // out: AccountLines +JSS(authorize); // out: account_permission JSS(authorized_credentials); // in: ledger_entry DepositPreauth JSS(auth_accounts); // out: amm_info JSS(auth_change); // out: AccountInfo diff --git a/src/libxrpl/protocol/Indexes.cpp b/src/libxrpl/protocol/Indexes.cpp index 80ca58a3897..d63dff10fb8 100644 --- a/src/libxrpl/protocol/Indexes.cpp +++ b/src/libxrpl/protocol/Indexes.cpp @@ -77,7 +77,7 @@ enum class LedgerNameSpace : std::uint16_t { MPTOKEN_ISSUANCE = '~', MPTOKEN = 't', CREDENTIAL = 'D', - ACCOUNT_PERMISSION = 'P', + ACCOUNT_PERMISSION = 'E', // No longer used or supported. Left here to reserve the space // to avoid accidental reuse. diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index 12ea4b12242..92f26acafdb 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -27,32 +27,32 @@ namespace ripple { Permission::Permission() { granularPermissionMap = { - {"TrustlineAuthorize", gpTrustlineAuthorize}, - {"TrustlineFreeze", gpTrustlineFreeze}, - {"TrustlineUnfreeze", gpTrustlineUnfreeze}, - {"AccountDomainSet", gpAccountDomainSet}, - {"AccountEmailHashSet", gpAccountEmailHashSet}, - {"AccountMessageKeySet", gpAccountMessageKeySet}, - {"AccountTransferRateSet", gpAccountTransferRateSet}, - {"AccountTickSizeSet", gpAccountTickSizeSet}, - {"PaymentMint", gpPaymentMint}, - {"PaymentBurn", gpPaymentBurn}, - {"MPTokenIssuanceLock", gpMPTokenIssuanceLock}, - {"MPTokenIssuanceUnlock", gpMPTokenIssuanceUnlock}}; + {"TrustlineAuthorize", TrustlineAuthorize}, + {"TrustlineFreeze", TrustlineFreeze}, + {"TrustlineUnfreeze", TrustlineUnfreeze}, + {"AccountDomainSet", AccountDomainSet}, + {"AccountEmailHashSet", AccountEmailHashSet}, + {"AccountMessageKeySet", AccountMessageKeySet}, + {"AccountTransferRateSet", AccountTransferRateSet}, + {"AccountTickSizeSet", AccountTickSizeSet}, + {"PaymentMint", PaymentMint}, + {"PaymentBurn", PaymentBurn}, + {"MPTokenIssuanceLock", MPTokenIssuanceLock}, + {"MPTokenIssuanceUnlock", MPTokenIssuanceUnlock}}; granularTxTypeMap = { - {gpTrustlineAuthorize, ttTRUST_SET}, - {gpTrustlineFreeze, ttTRUST_SET}, - {gpTrustlineUnfreeze, ttTRUST_SET}, - {gpAccountDomainSet, ttACCOUNT_SET}, - {gpAccountEmailHashSet, ttACCOUNT_SET}, - {gpAccountMessageKeySet, ttACCOUNT_SET}, - {gpAccountTransferRateSet, ttACCOUNT_SET}, - {gpAccountTickSizeSet, ttACCOUNT_SET}, - {gpPaymentMint, ttPAYMENT}, - {gpPaymentBurn, ttPAYMENT}, - {gpMPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET}, - {gpMPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET}}; + {TrustlineAuthorize, ttTRUST_SET}, + {TrustlineFreeze, ttTRUST_SET}, + {TrustlineUnfreeze, ttTRUST_SET}, + {AccountDomainSet, ttACCOUNT_SET}, + {AccountEmailHashSet, ttACCOUNT_SET}, + {AccountMessageKeySet, ttACCOUNT_SET}, + {AccountTransferRateSet, ttACCOUNT_SET}, + {AccountTickSizeSet, ttACCOUNT_SET}, + {PaymentMint, ttPAYMENT}, + {PaymentBurn, ttPAYMENT}, + {MPTokenIssuanceLock, ttMPTOKEN_ISSUANCE_SET}, + {MPTokenIssuanceUnlock, ttMPTOKEN_ISSUANCE_SET}}; } Permission const& diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index a6232417aa0..c32127a4531 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -3304,10 +3304,9 @@ struct AMM_test : public jtx::AMMTest env.app().config().features.erase(featureAMM); PreflightContext pfctx( env.app(), - *jtx.stx, + STTxWr(*jtx.stx, false), env.current()->rules(), tapNONE, - AccountID(0), env.journal); auto pf = AMMBid::preflight(pfctx); BEAST_EXPECT(pf == temDISABLED); @@ -3320,10 +3319,9 @@ struct AMM_test : public jtx::AMMTest jtx.stx = env.ust(jtx); PreflightContext pfctx( env.app(), - *jtx.stx, + STTxWr(*jtx.stx, false), env.current()->rules(), tapNONE, - AccountID(0), env.journal); auto pf = AMMBid::preflight(pfctx); BEAST_EXPECT(pf != tesSUCCESS); @@ -3336,10 +3334,9 @@ struct AMM_test : public jtx::AMMTest jtx.stx = env.ust(jtx); PreflightContext pfctx( env.app(), - *jtx.stx, + STTxWr(*jtx.stx, false), env.current()->rules(), tapNONE, - AccountID(0), env.journal); auto pf = AMMBid::preflight(pfctx); BEAST_EXPECT(pf == temBAD_AMM_TOKENS); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 714fc7734d9..2af6be94cd8 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1331,7 +1331,7 @@ struct Escrow_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - *jtx.stx, + STTxWr(*jtx.stx, false), tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -1345,7 +1345,7 @@ struct Escrow_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - *jtx.stx, + STTxWr(*jtx.stx, false), tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -1359,7 +1359,7 @@ struct Escrow_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - *jtx.stx, + STTxWr(*jtx.stx, false), tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index f7c8a80f332..3fceb822d21 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -807,12 +807,10 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite ApplyContext ac{ env.app(), ov, - tx, + STTxWr(tx, false), tesSUCCESS, env.current()->fees().base, tapNONE, - false, - AccountID(0), std::unordered_set{}, jlog}; @@ -854,12 +852,10 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite ApplyContext ac{ env.app(), ov, - tx, + STTxWr(tx, false), tesSUCCESS, env.current()->fees().base, tapNONE, - false, - AccountID(0), std::unordered_set{}, jlog}; diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index e7b70203c91..3fb5eeb1d79 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -2491,7 +2491,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - *jtx.stx, + STTxWr(*jtx.stx, false), tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -2508,7 +2508,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - *jtx.stx, + STTxWr(*jtx.stx, false), tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -2522,7 +2522,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - *jtx.stx, + STTxWr(*jtx.stx, false), tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index fb12e2d24fe..94f906dff7f 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -94,12 +94,10 @@ class Invariants_test : public beast::unit_test::suite ApplyContext ac{ env.app(), ov, - tx, + STTxWr(tx, false), tesSUCCESS, env.current()->fees().base, tapNONE, - false, - AccountID(0), std::unordered_set{}, jlog}; diff --git a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp index e1ad68dff37..1f4fe41951e 100644 --- a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp +++ b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp @@ -56,7 +56,9 @@ AcceptedLedgerTx::AcceptedLedgerTx( if (mTxn->getTxnType() == ttOFFER_CREATE) { - auto const& account = mTxn->getAccountID(sfAccount); + auto const& account = mTxn->isFieldPresent(sfOnBehalfOf) + ? mTxn->getAccountID(sfOnBehalfOf) + : mTxn->getAccountID(sfAccount); auto const amount = mTxn->getFieldAmount(sfTakerGets); // If the offer create is not self funded then add the owner balance diff --git a/src/xrpld/app/ledger/detail/LedgerToJson.cpp b/src/xrpld/app/ledger/detail/LedgerToJson.cpp index 3f6869df1d8..eda1cfc0b22 100644 --- a/src/xrpld/app/ledger/detail/LedgerToJson.cpp +++ b/src/xrpld/app/ledger/detail/LedgerToJson.cpp @@ -206,7 +206,9 @@ fillJsonTx( if ((fill.options & LedgerFill::ownerFunds) && txn->getTxnType() == ttOFFER_CREATE) { - auto const account = txn->getAccountID(sfAccount); + auto const& account = txn->isFieldPresent(sfOnBehalfOf) + ? txn->getAccountID(sfOnBehalfOf) + : txn->getAccountID(sfAccount); auto const amount = txn->getFieldAmount(sfTakerGets); // If the offer create is not self funded then add the diff --git a/src/xrpld/app/ledger/detail/LocalTxs.cpp b/src/xrpld/app/ledger/detail/LocalTxs.cpp index a6eb7721a3e..aaeb6245415 100644 --- a/src/xrpld/app/ledger/detail/LocalTxs.cpp +++ b/src/xrpld/app/ledger/detail/LocalTxs.cpp @@ -62,7 +62,10 @@ class LocalTx : m_txn(txn) , m_expire(index + holdLedgers) , m_id(txn->getTransactionID()) - , m_account(txn->getAccountID(sfAccount)) + , m_account( + txn->isFieldPresent(sfOnBehalfOf) + ? txn->getAccountID(sfOnBehalfOf) + : txn->getAccountID(sfAccount)) , m_seqProxy(txn->getSeqProxy()) { if (txn->isFieldPresent(sfLastLedgerSequence)) diff --git a/src/xrpld/app/misc/CredentialHelpers.cpp b/src/xrpld/app/misc/CredentialHelpers.cpp index 08b5d804d4b..032f8eb94ac 100644 --- a/src/xrpld/app/misc/CredentialHelpers.cpp +++ b/src/xrpld/app/misc/CredentialHelpers.cpp @@ -243,7 +243,7 @@ verifyDepositPreauth( bool const credentialsPresent = ctx.tx.isFieldPresent(sfCredentialIDs); if (credentialsPresent && - credentials::removeExpired(ctx.view(), ctx.tx, ctx.journal)) + credentials::removeExpired(ctx.view(), ctx.tx.getTx(), ctx.journal)) return tecEXPIRED; if (sleDst && (sleDst->getFlags() & lsfDepositAuth)) diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index d647df91f1e..6c598343988 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -2993,7 +2993,9 @@ NetworkOPsImp::transJson( if (transaction->getTxnType() == ttOFFER_CREATE) { - auto const account = transaction->getAccountID(sfAccount); + auto const& account = transaction->isFieldPresent(sfOnBehalfOf) + ? transaction->getAccountID(sfOnBehalfOf) + : transaction->getAccountID(sfAccount); auto const amount = transaction->getFieldAmount(sfTakerGets); // If the offer create is not self funded then add the owner balance diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index a4e62b382a7..e1b28caf449 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -736,7 +736,10 @@ TxQ::apply( // See if the transaction is valid, properly formed, // etc. before doing potentially expensive queue // replace and multi-transaction operations. - auto const pfresult = preflight(app, view.rules(), *tx, flags, j); + bool const isDelegated = view.rules().enabled(featureAccountPermission) && + tx->isFieldPresent(sfOnBehalfOf); + STTxWr txWr(*tx, isDelegated); + auto const pfresult = preflight(app, view.rules(), txWr, flags, j); if (pfresult.ter != tesSUCCESS) return {pfresult.ter, false}; diff --git a/src/xrpld/app/tx/apply.h b/src/xrpld/app/tx/apply.h index 6a0401e2b55..73093e8db30 100644 --- a/src/xrpld/app/tx/apply.h +++ b/src/xrpld/app/tx/apply.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/tx/applySteps.h b/src/xrpld/app/tx/applySteps.h index cc23b8aca39..07c2918e73a 100644 --- a/src/xrpld/app/tx/applySteps.h +++ b/src/xrpld/app/tx/applySteps.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace ripple { @@ -152,7 +153,7 @@ struct PreflightResult { public: /// From the input - the transaction - STTx const& tx; + STTxWr const& tx; /// From the input - the rules Rules const rules; /// Consequences of the transaction @@ -161,10 +162,6 @@ struct PreflightResult ApplyFlags const flags; /// From the input - the journal beast::Journal const j; - /// If the transaction is a delegated transaction - bool const isDelegated; - /// the account that the transaction is operated on - AccountID const account; /// Intermediate transaction result NotTEC const ter; @@ -172,16 +169,12 @@ struct PreflightResult template PreflightResult( Context const& ctx_, - bool const isDelegated, - AccountID const account, std::pair const& result) : tx(ctx_.tx) , rules(ctx_.rules) , consequences(result.second) , flags(ctx_.flags) , j(ctx_.j) - , isDelegated(isDelegated) - , account(account) , ter(result.first) { } @@ -204,21 +197,17 @@ struct PreclaimResult /// From the input - the ledger view ReadView const& view; /// From the input - the transaction - STTx const& tx; + STTxWr const& tx; /// From the input - the flags ApplyFlags const flags; /// From the input - the journal beast::Journal const j; - /// If the transaction is a delegated transaction - bool const isDelegated; - /// the account that the transaction is operated on - AccountID const account; /// Intermediate transaction result TER const ter; /// granular permissions enabled for the transaction. If empty and /// isDelegated=true, then the entire transaction is authorized. - std::unordered_set gpSet; + std::unordered_set permissions; /// Success flag - whether the transaction is likely to /// claim a fee bool const likelyToClaimFee; @@ -228,15 +217,13 @@ struct PreclaimResult PreclaimResult( Context const& ctx_, TER ter_, - std::unordered_set gpSet_) + std::unordered_set permissions_) : view(ctx_.view) , tx(ctx_.tx) , flags(ctx_.flags) , j(ctx_.j) - , isDelegated(ctx_.isDelegated) - , account(ctx_.account) , ter(ter_) - , gpSet(std::move(gpSet_)) + , permissions(std::move(permissions_)) , likelyToClaimFee(ter == tesSUCCESS || isTecClaimHardFail(ter, flags)) { } @@ -267,7 +254,7 @@ PreflightResult preflight( Application& app, Rules const& rules, - STTx const& tx, + STTxWr const& tx, ApplyFlags flags, beast::Journal j); diff --git a/src/xrpld/app/tx/detail/AMMBid.cpp b/src/xrpld/app/tx/detail/AMMBid.cpp index 66496b4dc1e..9de3762d2e3 100644 --- a/src/xrpld/app/tx/detail/AMMBid.cpp +++ b/src/xrpld/app/tx/detail/AMMBid.cpp @@ -110,7 +110,8 @@ AMMBid::preclaim(PreclaimContext const& ctx) } } - auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.account, ctx.j); + auto const lpTokens = + ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); // Not LP if (lpTokens == beast::zero) { diff --git a/src/xrpld/app/tx/detail/AMMClawback.cpp b/src/xrpld/app/tx/detail/AMMClawback.cpp index d3f4c94b1cf..64150ded6da 100644 --- a/src/xrpld/app/tx/detail/AMMClawback.cpp +++ b/src/xrpld/app/tx/detail/AMMClawback.cpp @@ -45,7 +45,7 @@ AMMClawback::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfAMMClawbackMask) return temINVALID_FLAG; - AccountID const issuer = ctx.account; + AccountID const issuer = ctx.tx[sfAccount]; AccountID const holder = ctx.tx[sfHolder]; if (issuer == holder) @@ -86,7 +86,7 @@ AMMClawback::preclaim(PreclaimContext const& ctx) { auto const asset = ctx.tx[sfAsset]; auto const asset2 = ctx.tx[sfAsset2]; - auto const sleIssuer = ctx.view.read(keylet::account(ctx.account)); + auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfAccount])); if (!sleIssuer) return terNO_ACCOUNT; // LCOV_EXCL_LINE @@ -136,6 +136,7 @@ TER AMMClawback::applyGuts(Sandbox& sb) { std::optional const clawAmount = ctx_.tx[~sfAmount]; + AccountID const issuer = ctx_.tx[sfAccount]; AccountID const holder = ctx_.tx[sfHolder]; Issue const asset = ctx_.tx[sfAsset]; Issue const asset2 = ctx_.tx[sfAsset2]; @@ -215,8 +216,7 @@ AMMClawback::applyGuts(Sandbox& sb) << to_string(newLPTokenBalance.iou()) << " old balance: " << to_string(lptAMMBalance.iou()); - auto const ter = - rippleCredit(sb, holder, account_, amountWithdraw, true, j_); + auto const ter = rippleCredit(sb, holder, issuer, amountWithdraw, true, j_); if (ter != tesSUCCESS) return ter; // LCOV_EXCL_LINE @@ -229,7 +229,7 @@ AMMClawback::applyGuts(Sandbox& sb) auto const flags = ctx_.tx.getFlags(); if (flags & tfClawTwoAssets) - return rippleCredit(sb, holder, account_, *amount2Withdraw, true, j_); + return rippleCredit(sb, holder, issuer, *amount2Withdraw, true, j_); return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index 5aeb630445e..31773166d4a 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -88,7 +88,7 @@ AMMCreate::calculateBaseFee(ReadView const& view, STTx const& tx) TER AMMCreate::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.account; + auto const accountID = ctx.tx[sfAccount]; auto const amount = ctx.tx[sfAmount]; auto const amount2 = ctx.tx[sfAmount2]; diff --git a/src/xrpld/app/tx/detail/AMMDeposit.cpp b/src/xrpld/app/tx/detail/AMMDeposit.cpp index 334a34a7909..3448401eb79 100644 --- a/src/xrpld/app/tx/detail/AMMDeposit.cpp +++ b/src/xrpld/app/tx/detail/AMMDeposit.cpp @@ -168,7 +168,7 @@ AMMDeposit::preflight(PreflightContext const& ctx) TER AMMDeposit::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.account; + auto const accountID = ctx.tx[sfAccount]; auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); diff --git a/src/xrpld/app/tx/detail/AMMVote.cpp b/src/xrpld/app/tx/detail/AMMVote.cpp index 70a24789940..c4b6c612c63 100644 --- a/src/xrpld/app/tx/detail/AMMVote.cpp +++ b/src/xrpld/app/tx/detail/AMMVote.cpp @@ -72,7 +72,7 @@ AMMVote::preclaim(PreclaimContext const& ctx) else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero) return tecAMM_EMPTY; else if (auto const lpTokensNew = - ammLPHolds(ctx.view, *ammSle, ctx.account, ctx.j); + ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j); lpTokensNew == beast::zero) { JLOG(ctx.j.debug()) << "AMM Vote: account is not LP."; diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index f53d64ee062..a6372e5c148 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -170,7 +170,7 @@ tokensWithdraw( TER AMMWithdraw::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.account; + auto const accountID = ctx.tx[sfAccount]; auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2])); @@ -304,7 +304,7 @@ AMMWithdraw::applyGuts(Sandbox& sb) if (!accountSle) return {tecINTERNAL, false}; // LCOV_EXCL_LINE auto const lpTokens = - ammLPHolds(ctx_.view(), *ammSle, account_, ctx_.journal); + ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal); auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags()); @@ -470,7 +470,7 @@ AMMWithdraw::withdraw( lpTokensWithdraw, tfee, FreezeHandling::fhZERO_IF_FROZEN, - isWithdrawAll(ctx_.tx), + isWithdrawAll(ctx_.tx.getTx()), mPriorBalance, j_); return {ter, newLPTokenBalance}; @@ -707,7 +707,7 @@ AMMWithdraw::equalWithdrawTokens( lpTokensWithdraw, tfee, FreezeHandling::fhZERO_IF_FROZEN, - isWithdrawAll(ctx_.tx), + isWithdrawAll(ctx_.tx.getTx()), mPriorBalance, ctx_.journal); return {ter, newLPTokenBalance}; diff --git a/src/xrpld/app/tx/detail/AccountPermissionSet.cpp b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp index 1d31b9f1e57..33bb0f533f7 100644 --- a/src/xrpld/app/tx/detail/AccountPermissionSet.cpp +++ b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp @@ -46,11 +46,6 @@ AccountPermissionSet::preflight(PreflightContext const& ctx) for (auto const& permission : permissions) { - auto permissionObj = dynamic_cast(&permission); - - if (!permissionObj || (permissionObj->getFName() != sfPermission)) - return temMALFORMED; - auto const permissionValue = permission[sfPermissionValue]; if (permissionSet.find(permissionValue) != permissionSet.end()) @@ -65,7 +60,7 @@ AccountPermissionSet::preflight(PreflightContext const& ctx) TER AccountPermissionSet::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.view.read(keylet::account(ctx.account)); + auto const account = ctx.view.read(keylet::account(ctx.tx[sfAccount])); if (!account) return terNO_ACCOUNT; // LCOV_EXCL_LINE diff --git a/src/xrpld/app/tx/detail/ApplyContext.cpp b/src/xrpld/app/tx/detail/ApplyContext.cpp index ac73bc499f5..54ccdfde2db 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.cpp +++ b/src/xrpld/app/tx/detail/ApplyContext.cpp @@ -31,21 +31,17 @@ namespace ripple { ApplyContext::ApplyContext( Application& app_, OpenView& base, - STTx const& tx_, + STTxWr const& tx_, TER preclaimResult_, XRPAmount baseFee_, ApplyFlags flags, - bool isDelegated, - AccountID const account, - std::unordered_set const gpSet, + std::unordered_set const permissions, beast::Journal journal_) : app(app_) , tx(tx_) , preclaimResult(preclaimResult_) , baseFee(baseFee_) - , isDelegated(isDelegated) - , account(account) - , gpSet(std::move(gpSet)) + , permissions(std::move(permissions)) , journal(journal_) , base_(base) , flags_(flags) @@ -62,7 +58,7 @@ ApplyContext::discard() void ApplyContext::apply(TER ter) { - view_->apply(base_, tx, ter, journal); + view_->apply(base_, tx.getTx(), ter, journal); } std::size_t diff --git a/src/xrpld/app/tx/detail/ApplyContext.h b/src/xrpld/app/tx/detail/ApplyContext.h index 5ee3f1f092b..2f8a287acf7 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.h +++ b/src/xrpld/app/tx/detail/ApplyContext.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -39,22 +39,18 @@ class ApplyContext explicit ApplyContext( Application& app, OpenView& base, - STTx const& tx, + STTxWr const& tx, TER preclaimResult, XRPAmount baseFee, ApplyFlags flags, - bool isDelegated, - AccountID const account, - std::unordered_set const gpSet, + std::unordered_set const permissions, beast::Journal = beast::Journal{beast::Journal::getNullSink()}); Application& app; - STTx const& tx; + STTxWr const& tx; TER const preclaimResult; XRPAmount const baseFee; - bool isDelegated; - AccountID const account; - std::unordered_set gpSet; + std::unordered_set permissions; beast::Journal const journal; ApplyView& diff --git a/src/xrpld/app/tx/detail/CancelCheck.cpp b/src/xrpld/app/tx/detail/CancelCheck.cpp index 39231e70e54..7954e86cf3b 100644 --- a/src/xrpld/app/tx/detail/CancelCheck.cpp +++ b/src/xrpld/app/tx/detail/CancelCheck.cpp @@ -73,7 +73,7 @@ CancelCheck::preclaim(PreclaimContext const& ctx) { // If the check is not yet expired, then only the creator or the // destination may cancel the check. - AccountID const acctId{ctx.account}; + AccountID const acctId{ctx.tx[sfAccount]}; if (acctId != (*sleCheck)[sfAccount] && acctId != (*sleCheck)[sfDestination]) { diff --git a/src/xrpld/app/tx/detail/CancelOffer.cpp b/src/xrpld/app/tx/detail/CancelOffer.cpp index f693f4cb394..30e955a8282 100644 --- a/src/xrpld/app/tx/detail/CancelOffer.cpp +++ b/src/xrpld/app/tx/detail/CancelOffer.cpp @@ -53,7 +53,7 @@ CancelOffer::preflight(PreflightContext const& ctx) TER CancelOffer::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.account; + auto const id = ctx.tx[sfAccount]; auto const offerSequence = ctx.tx[sfOfferSequence]; auto const sle = ctx.view.read(keylet::account(id)); diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index 776396b3e94..cbf30803abd 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -91,7 +91,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) // Only cash a check with this account as the destination. AccountID const dstId = sleCheck->at(sfDestination); - if (ctx.account != dstId) + if (ctx.tx[sfAccount] != dstId) { JLOG(ctx.j.warn()) << "Cashing a check with wrong Destination."; return tecNO_PERMISSION; @@ -138,7 +138,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) STAmount const value{[](STTx const& tx) { auto const optAmount = tx[~sfAmount]; return optAmount ? *optAmount : tx[sfDeliverMin]; - }(ctx.tx)}; + }(ctx.tx.getTx())}; STAmount const sendMax = sleCheck->at(sfSendMax); Currency const currency{value.getCurrency()}; diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index a924fbe0c96..0b466999e04 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -38,7 +38,7 @@ Change::preflight(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; - auto account = ctx.account; + auto account = ctx.tx.getAccountID(sfAccount); if (account != beast::zero) { JLOG(ctx.j.warn()) << "Change: Bad source id"; @@ -358,9 +358,9 @@ Change::applyFee() }; if (view().rules().enabled(featureXRPFees)) { - set(feeObject, ctx_.tx, sfBaseFeeDrops); - set(feeObject, ctx_.tx, sfReserveBaseDrops); - set(feeObject, ctx_.tx, sfReserveIncrementDrops); + set(feeObject, ctx_.tx.getTx(), sfBaseFeeDrops); + set(feeObject, ctx_.tx.getTx(), sfReserveBaseDrops); + set(feeObject, ctx_.tx.getTx(), sfReserveIncrementDrops); // Ensure the old fields are removed feeObject->makeFieldAbsent(sfBaseFee); feeObject->makeFieldAbsent(sfReferenceFeeUnits); @@ -369,10 +369,10 @@ Change::applyFee() } else { - set(feeObject, ctx_.tx, sfBaseFee); - set(feeObject, ctx_.tx, sfReferenceFeeUnits); - set(feeObject, ctx_.tx, sfReserveBase); - set(feeObject, ctx_.tx, sfReserveIncrement); + set(feeObject, ctx_.tx.getTx(), sfBaseFee); + set(feeObject, ctx_.tx.getTx(), sfReferenceFeeUnits); + set(feeObject, ctx_.tx.getTx(), sfReserveBase); + set(feeObject, ctx_.tx.getTx(), sfReserveIncrement); } view().update(feeObject); diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index 332d039932c..f1040790a42 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -39,7 +39,7 @@ preflightHelper(PreflightContext const& ctx) if (ctx.tx.isFieldPresent(sfHolder)) return temMALFORMED; - AccountID const issuer = ctx.account; + AccountID const issuer = ctx.tx[sfAccount]; STAmount const clawAmount = ctx.tx[sfAmount]; // The issuer field is used for the token holder instead @@ -65,7 +65,7 @@ preflightHelper(PreflightContext const& ctx) return temMALFORMED; // issuer is the same as holder - if (ctx.account == *mptHolder) + if (ctx.tx[sfAccount] == *mptHolder) return temMALFORMED; if (clawAmount.mpt() > MPTAmount{maxMPTokenAmount} || @@ -197,7 +197,7 @@ preclaimHelper( TER Clawback::preclaim(PreclaimContext const& ctx) { - AccountID const issuer = ctx.account; + AccountID const issuer = ctx.tx[sfAccount]; auto const clawAmount = ctx.tx[sfAmount]; AccountID const holder = clawAmount.holds() ? clawAmount.getIssuer() : ctx.tx[sfHolder]; @@ -226,7 +226,7 @@ template <> TER applyHelper(ApplyContext& ctx) { - AccountID const issuer = ctx.account; + AccountID const issuer = ctx.tx[sfAccount]; STAmount clawAmount = ctx.tx[sfAmount]; AccountID const holder = clawAmount.getIssuer(); // cannot be reference @@ -257,7 +257,7 @@ template <> TER applyHelper(ApplyContext& ctx) { - AccountID const issuer = ctx.account; + AccountID const issuer = ctx.tx[sfAccount]; auto clawAmount = ctx.tx[sfAmount]; AccountID const holder = ctx.tx[sfHolder]; diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index bf3c97953fc..3a278eed738 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -44,7 +44,7 @@ CreateCheck::preflight(PreflightContext const& ctx) JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } - if (ctx.account == ctx.tx[sfDestination]) + if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) { // They wrote a check to themselves. JLOG(ctx.j.warn()) << "Malformed transaction: Check to self."; @@ -125,7 +125,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) // // Note that we DO allow create check for a currency that the // account does not yet have a trustline to. - AccountID const srcId{ctx.account}; + AccountID const srcId{ctx.tx.getAccountID(sfAccount)}; if (issuerId != srcId) { // Check if the issuer froze the line diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index 65028b65de1..712303ddd00 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -36,7 +36,7 @@ CreateOffer::makeTxConsequences(PreflightContext const& ctx) return amount.native() ? amount.xrp() : beast::zero; }; - return TxConsequences{ctx.tx, calculateMaxXRPSpend(ctx.tx)}; + return TxConsequences{ctx.tx.getTx(), calculateMaxXRPSpend(ctx.tx.getTx())}; } NotTEC @@ -128,7 +128,7 @@ CreateOffer::preflight(PreflightContext const& ctx) TER CreateOffer::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.account; + auto const id = ctx.tx[sfAccount]; auto saTakerPays = ctx.tx[sfTakerPays]; auto saTakerGets = ctx.tx[sfTakerGets]; diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index b045498b183..ab6b277d7ce 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -31,7 +31,7 @@ TxConsequences CreateTicket::makeTxConsequences(PreflightContext const& ctx) { // Create TxConsequences identifying the number of sequences consumed. - return TxConsequences{ctx.tx, ctx.tx[sfTicketCount]}; + return TxConsequences{ctx.tx.getTx(), ctx.tx[sfTicketCount]}; } NotTEC @@ -56,7 +56,7 @@ CreateTicket::preflight(PreflightContext const& ctx) TER CreateTicket::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.account; + auto const id = ctx.tx[sfAccount]; auto const sleAccountRoot = ctx.view.read(keylet::account(id)); if (!sleAccountRoot) return terNO_ACCOUNT; diff --git a/src/xrpld/app/tx/detail/Credentials.cpp b/src/xrpld/app/tx/detail/Credentials.cpp index 7de89bd783f..4da875f8d7c 100644 --- a/src/xrpld/app/tx/detail/Credentials.cpp +++ b/src/xrpld/app/tx/detail/Credentials.cpp @@ -101,7 +101,8 @@ CredentialCreate::preclaim(PreclaimContext const& ctx) return tecNO_TARGET; } - if (ctx.view.exists(keylet::credential(subject, ctx.account, credType))) + if (ctx.view.exists( + keylet::credential(subject, ctx.tx[sfAccount], credType))) { JLOG(ctx.j.trace()) << "Credential already exists."; return tecDUPLICATE; @@ -241,7 +242,7 @@ CredentialDelete::preflight(PreflightContext const& ctx) TER CredentialDelete::preclaim(PreclaimContext const& ctx) { - AccountID const account{ctx.account}; + AccountID const account{ctx.tx[sfAccount]}; auto const subject = ctx.tx[~sfSubject].value_or(account); auto const issuer = ctx.tx[~sfIssuer].value_or(account); auto const credType(ctx.tx[sfCredentialType]); @@ -308,7 +309,7 @@ CredentialAccept::preflight(PreflightContext const& ctx) TER CredentialAccept::preclaim(PreclaimContext const& ctx) { - AccountID const subject = ctx.account; + AccountID const subject = ctx.tx[sfAccount]; AccountID const issuer = ctx.tx[sfIssuer]; auto const credType(ctx.tx[sfCredentialType]); diff --git a/src/xrpld/app/tx/detail/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 90fca354215..68f5bd20794 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -53,7 +53,7 @@ DeleteAccount::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - if (ctx.account == ctx.tx[sfDestination]) + if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) // An account cannot be deleted and give itself the resulting XRP. return temDST_IS_SRC; @@ -229,7 +229,7 @@ nonObligationDeleter(LedgerEntryType t) TER DeleteAccount::preclaim(PreclaimContext const& ctx) { - AccountID const account{ctx.account}; + AccountID const account{ctx.tx[sfAccount]}; AccountID const dst{ctx.tx[sfDestination]}; auto sleDst = ctx.view.read(keylet::account(dst)); diff --git a/src/xrpld/app/tx/detail/DeleteOracle.cpp b/src/xrpld/app/tx/detail/DeleteOracle.cpp index 0b9b25103bb..807e52ee6b4 100644 --- a/src/xrpld/app/tx/detail/DeleteOracle.cpp +++ b/src/xrpld/app/tx/detail/DeleteOracle.cpp @@ -47,17 +47,17 @@ DeleteOracle::preflight(PreflightContext const& ctx) TER DeleteOracle::preclaim(PreclaimContext const& ctx) { - if (!ctx.view.exists(keylet::account(ctx.account))) + if (!ctx.view.exists(keylet::account(ctx.tx.getAccountID(sfAccount)))) return terNO_ACCOUNT; // LCOV_EXCL_LINE - if (auto const sle = ctx.view.read( - keylet::oracle(ctx.account, ctx.tx[sfOracleDocumentID])); + if (auto const sle = ctx.view.read(keylet::oracle( + ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); !sle) { JLOG(ctx.j.debug()) << "Oracle Delete: Oracle does not exist."; return tecNO_ENTRY; } - else if (ctx.account != sle->getAccountID(sfOwner)) + else if (ctx.tx.getAccountID(sfAccount) != sle->getAccountID(sfOwner)) { // this can't happen because of the above check // LCOV_EXCL_START diff --git a/src/xrpld/app/tx/detail/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index 57bef6db4dd..73cd19e4120 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -85,7 +85,7 @@ DepositPreauth::preflight(PreflightContext const& ctx) } // An account may not preauthorize itself. - if (optAuth && (target == ctx.account)) + if (optAuth && (target == ctx.tx[sfAccount])) { JLOG(ctx.j.trace()) << "Malformed transaction: Attempting to DepositPreauth self."; @@ -141,7 +141,7 @@ DepositPreauth::preflight(PreflightContext const& ctx) TER DepositPreauth::preclaim(PreclaimContext const& ctx) { - AccountID const account(ctx.account); + AccountID const account(ctx.tx[sfAccount]); // Determine which operation we're performing: authorizing or unauthorizing. if (ctx.tx.isFieldPresent(sfAuthorize)) diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index f855188fd08..2f942e352c9 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -94,7 +94,7 @@ after(NetClock::time_point now, std::uint32_t mark) TxConsequences EscrowCreate::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx.getTx(), ctx.tx[sfAmount].xrp()}; } NotTEC @@ -206,7 +206,8 @@ EscrowCreate::doApply() } } - auto const sle = ctx_.view().peek(keylet::account(account_)); + auto const account = ctx_.tx[sfAccount]; + auto const sle = ctx_.view().peek(keylet::account(account)); if (!sle) return tefINTERNAL; @@ -243,10 +244,10 @@ EscrowCreate::doApply() // Create escrow in ledger. Note that we we use the value from the // sequence or ticket. For more explanation see comments in SeqProxy.h. Keylet const escrowKeylet = - keylet::escrow(account_, ctx_.tx.getSeqProxy().value()); + keylet::escrow(account, ctx_.tx.getSeqProxy().value()); auto const slep = std::make_shared(escrowKeylet); (*slep)[sfAmount] = ctx_.tx[sfAmount]; - (*slep)[sfAccount] = account_; + (*slep)[sfAccount] = account; (*slep)[~sfCondition] = ctx_.tx[~sfCondition]; (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; (*slep)[sfDestination] = ctx_.tx[sfDestination]; @@ -259,16 +260,14 @@ EscrowCreate::doApply() // Add escrow to sender's owner directory { auto page = ctx_.view().dirInsert( - keylet::ownerDir(account_), - escrowKeylet, - describeOwnerDir(account_)); + keylet::ownerDir(account), escrowKeylet, describeOwnerDir(account)); if (!page) return tecDIR_FULL; (*slep)[sfOwnerNode] = *page; } // If it's not a self-send, add escrow to recipient's owner directory. - if (auto const dest = ctx_.tx[sfDestination]; dest != account_) + if (auto const dest = ctx_.tx[sfDestination]; dest != ctx_.tx[sfAccount]) { auto page = ctx_.view().dirInsert( keylet::ownerDir(dest), escrowKeylet, describeOwnerDir(dest)); @@ -378,7 +377,7 @@ EscrowFinish::preclaim(PreclaimContext const& ctx) if (!ctx.view.rules().enabled(featureCredentials)) return Transactor::preclaim(ctx); - if (auto const err = credentials::valid(ctx, ctx.account); + if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); !isTesSuccess(err)) return err; diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index bc982bb12b0..3757481c4af 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -43,7 +43,7 @@ TransactionFeeCheck::visitEntry( bool TransactionFeeCheck::finalize( - STTx const& tx, + STTxWr const& tx, TER const, XRPAmount const fee, ReadView const&, @@ -137,7 +137,7 @@ XRPNotCreated::visitEntry( bool XRPNotCreated::finalize( - STTx const& tx, + STTxWr const& tx, TER const, XRPAmount const fee, ReadView const&, @@ -198,7 +198,7 @@ XRPBalanceChecks::visitEntry( bool XRPBalanceChecks::finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -242,7 +242,7 @@ NoBadOffers::visitEntry( bool NoBadOffers::finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -287,7 +287,7 @@ NoZeroEscrow::visitEntry( bool NoZeroEscrow::finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -316,7 +316,7 @@ AccountRootsNotDeleted::visitEntry( bool AccountRootsNotDeleted::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const, ReadView const&, @@ -371,7 +371,7 @@ AccountRootsDeletedClean::visitEntry( bool AccountRootsDeletedClean::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -493,7 +493,7 @@ LedgerEntryTypesMatch::visitEntry( bool LedgerEntryTypesMatch::finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -536,7 +536,7 @@ NoXRPTrustLines::visitEntry( bool NoXRPTrustLines::finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -566,7 +566,7 @@ ValidNewAccountRoot::visitEntry( bool ValidNewAccountRoot::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -717,7 +717,7 @@ ValidNFTokenPage::visitEntry( bool ValidNFTokenPage::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -793,7 +793,7 @@ NFTokenCountTracking::visitEntry( bool NFTokenCountTracking::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -894,7 +894,7 @@ ValidClawback::visitEntry( bool ValidClawback::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -982,7 +982,7 @@ ValidMPTIssuance::visitEntry( bool ValidMPTIssuance::finalize( - STTx const& tx, + STTxWr const& tx, TER const result, XRPAmount const _fee, ReadView const& _view, diff --git a/src/xrpld/app/tx/detail/InvariantCheck.h b/src/xrpld/app/tx/detail/InvariantCheck.h index 23ec8005556..563b6c5aaad 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.h +++ b/src/xrpld/app/tx/detail/InvariantCheck.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -76,7 +76,7 @@ class InvariantChecker_PROTOTYPE */ bool finalize( - STTx const& tx, + STTxWr const& tx, TER const tec, XRPAmount const fee, ReadView const& view, @@ -101,7 +101,7 @@ class TransactionFeeCheck bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -129,7 +129,7 @@ class XRPNotCreated bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -157,7 +157,7 @@ class AccountRootsNotDeleted bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -187,7 +187,7 @@ class AccountRootsDeletedClean bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -214,7 +214,7 @@ class XRPBalanceChecks bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -239,7 +239,7 @@ class LedgerEntryTypesMatch bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -265,7 +265,7 @@ class NoXRPTrustLines bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -292,7 +292,7 @@ class NoBadOffers bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -316,7 +316,7 @@ class NoZeroEscrow bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -342,7 +342,7 @@ class ValidNewAccountRoot bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -379,7 +379,7 @@ class ValidNFTokenPage bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -415,7 +415,7 @@ class NFTokenCountTracking bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -444,7 +444,7 @@ class ValidClawback bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, @@ -468,7 +468,7 @@ class ValidMPTIssuance bool finalize( - STTx const&, + STTxWr const&, TER const, XRPAmount const, ReadView const&, diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp index a7f96d6f028..8042c9c6982 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp @@ -37,7 +37,7 @@ MPTokenAuthorize::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfMPTokenAuthorizeMask) return temINVALID_FLAG; - if (ctx.account == ctx.tx[~sfHolder]) + if (ctx.tx[sfAccount] == ctx.tx[~sfHolder]) return temMALFORMED; return preflight2(ctx); @@ -46,7 +46,7 @@ MPTokenAuthorize::preflight(PreflightContext const& ctx) TER MPTokenAuthorize::preclaim(PreclaimContext const& ctx) { - auto const accountID = ctx.account; + auto const accountID = ctx.tx[sfAccount]; auto const holderID = ctx.tx[~sfHolder]; // if non-issuer account submits this tx, then they are trying either: diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp index 49d3a2f1f80..a0f0b9d8602 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceDestroy.cpp @@ -52,7 +52,7 @@ MPTokenIssuanceDestroy::preclaim(PreclaimContext const& ctx) return tecOBJECT_NOT_FOUND; // ensure it is issued by the tx submitter - if ((*sleMPT)[sfIssuer] != ctx.account) + if ((*sleMPT)[sfIssuer] != ctx.tx[sfAccount]) return tecNO_PERMISSION; // ensure it has no outstanding balances diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp index 1cf0a6cc67c..93bd66c329d 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp @@ -43,7 +43,7 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) else if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock)) return temINVALID_FLAG; - auto const accountID = ctx.account; + auto const accountID = ctx.tx[sfAccount]; auto const holderID = ctx.tx[~sfHolder]; if (holderID && accountID == holderID) return temMALFORMED; @@ -65,7 +65,7 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; // ensure it is issued by the tx submitter - if ((*sleMptIssuance)[sfIssuer] != ctx.account) + if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount]) return tecNO_PERMISSION; if (auto const holderID = ctx.tx[~sfHolder]) @@ -104,13 +104,13 @@ MPTokenIssuanceSet::doApply() bool bLock = txFlags & tfMPTLock; bool bUnlock = txFlags & tfMPTUnlock; - if (ctx_.isDelegated && !ctx_.gpSet.empty()) + + if (ctx_.tx.isDelegated() && !ctx_.permissions.empty()) { - // if gpSet is not empty, granular delegation is happening. - if (bLock && ctx_.gpSet.find(gpMPTokenIssuanceLock) == ctx_.gpSet.end()) + // if permissions is not empty, granular delegation is happening. + if (bLock && !ctx_.permissions.contains(MPTokenIssuanceLock)) return terNO_AUTH; - if (bUnlock && - ctx_.gpSet.find(gpMPTokenIssuanceUnlock) == ctx_.gpSet.end()) + if (bUnlock && !ctx_.permissions.contains(MPTokenIssuanceUnlock)) return terNO_AUTH; } diff --git a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp index c22411348ad..b884a791e78 100644 --- a/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenAcceptOffer.cpp @@ -125,10 +125,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // a broker. After, it must be whoever is submitting the tx. if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2)) { - if (*dest != ctx.account) + if (*dest != ctx.tx[sfAccount]) return tecNO_PERMISSION; } - else if (*dest != so->at(sfOwner) && *dest != ctx.account) + else if (*dest != so->at(sfOwner) && *dest != ctx.tx[sfAccount]) return tecNFTOKEN_BUY_SELL_MISMATCH; } @@ -139,10 +139,10 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // a broker. After, it must be whoever is submitting the tx. if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2)) { - if (*dest != ctx.account) + if (*dest != ctx.tx[sfAccount]) return tecNO_PERMISSION; } - else if (*dest != bo->at(sfOwner) && *dest != ctx.account) + else if (*dest != bo->at(sfOwner) && *dest != ctx.tx[sfAccount]) return tecNFTOKEN_BUY_SELL_MISMATCH; } @@ -169,11 +169,12 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) return tecNFTOKEN_OFFER_TYPE_MISMATCH; // An account can't accept an offer it placed: - if ((*bo)[sfOwner] == ctx.account) + if ((*bo)[sfOwner] == ctx.tx[sfAccount]) return tecCANT_ACCEPT_OWN_NFTOKEN_OFFER; // If not in bridged mode, the account must own the token: - if (!so && !nft::findToken(ctx.view, ctx.account, (*bo)[sfNFTokenID])) + if (!so && + !nft::findToken(ctx.view, ctx.tx[sfAccount], (*bo)[sfNFTokenID])) return tecNO_PERMISSION; // If not in bridged mode... @@ -182,7 +183,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // If the offer has a Destination field, the acceptor must be the // Destination. if (auto const dest = bo->at(~sfDestination); - dest.has_value() && *dest != ctx.account) + dest.has_value() && *dest != ctx.tx[sfAccount]) return tecNO_PERMISSION; } @@ -215,7 +216,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) return tecNFTOKEN_OFFER_TYPE_MISMATCH; // An account can't accept an offer it placed: - if ((*so)[sfOwner] == ctx.account) + if ((*so)[sfOwner] == ctx.tx[sfAccount]) return tecCANT_ACCEPT_OWN_NFTOKEN_OFFER; // The seller must own the token. @@ -228,7 +229,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // If the offer has a Destination field, the acceptor must be the // Destination. if (auto const dest = so->at(~sfDestination); - dest.has_value() && *dest != ctx.account) + dest.has_value() && *dest != ctx.tx[sfAccount]) return tecNO_PERMISSION; } @@ -238,7 +239,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) { if (accountHolds( ctx.view, - ctx.account, + ctx.tx[sfAccount], needed.getCurrency(), needed.getIssuer(), fhZERO_IF_FROZEN, @@ -260,8 +261,11 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx) // cover what the buyer will pay, which doesn't make sense, causes // an unnecessary tec, and is also resolved with this amendment. if (accountFunds( - ctx.view, ctx.account, needed, fhZERO_IF_FROZEN, ctx.j) < - needed) + ctx.view, + ctx.tx[sfAccount], + needed, + fhZERO_IF_FROZEN, + ctx.j) < needed) return tecINSUFFICIENT_FUNDS; } } diff --git a/src/xrpld/app/tx/detail/NFTokenBurn.cpp b/src/xrpld/app/tx/detail/NFTokenBurn.cpp index e5d795a8e87..725e35791f9 100644 --- a/src/xrpld/app/tx/detail/NFTokenBurn.cpp +++ b/src/xrpld/app/tx/detail/NFTokenBurn.cpp @@ -51,7 +51,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx) if (ctx.tx.isFieldPresent(sfOwner)) return ctx.tx.getAccountID(sfOwner); - return ctx.account; + return ctx.tx[sfAccount]; }(); if (!nft::findToken(ctx.view, owner, ctx.tx[sfNFTokenID])) @@ -59,7 +59,7 @@ NFTokenBurn::preclaim(PreclaimContext const& ctx) // The owner of a token can always burn it, but the issuer can only // do so if the token is marked as burnable. - if (auto const account = ctx.account; owner != account) + if (auto const account = ctx.tx[sfAccount]; owner != account) { if (!(nft::getFlags(ctx.tx[sfNFTokenID]) & nft::flagBurnable)) return tecNO_PERMISSION; @@ -93,7 +93,7 @@ NFTokenBurn::doApply() auto const ret = nft::removeToken( view(), ctx_.tx.isFieldPresent(sfOwner) ? ctx_.tx.getAccountID(sfOwner) - : account_, + : ctx_.tx.getAccountID(sfAccount), ctx_.tx[sfNFTokenID]); // Should never happen since preclaim() verified the token is present. diff --git a/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp index 85b2e7f6188..ef66ceecd0c 100644 --- a/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCancelOffer.cpp @@ -56,7 +56,7 @@ NFTokenCancelOffer::preflight(PreflightContext const& ctx) TER NFTokenCancelOffer::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.account; + auto const account = ctx.tx[sfAccount]; auto const& ids = ctx.tx[sfNFTokenOffers]; diff --git a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp index 860c2c0a3ec..43178d31b4a 100644 --- a/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp +++ b/src/xrpld/app/tx/detail/NFTokenCreateOffer.cpp @@ -45,7 +45,7 @@ NFTokenCreateOffer::preflight(PreflightContext const& ctx) // Use implementation shared with NFTokenMint if (NotTEC notTec = nft::tokenOfferCreatePreflight( - ctx.account, + ctx.tx[sfAccount], ctx.tx[sfAmount], ctx.tx[~sfDestination], ctx.tx[~sfExpiration], @@ -70,14 +70,14 @@ NFTokenCreateOffer::preclaim(PreclaimContext const& ctx) if (!nft::findToken( ctx.view, - (txFlags & tfSellNFToken) ? ctx.account : ctx.tx[sfOwner], + ctx.tx[(txFlags & tfSellNFToken) ? sfAccount : sfOwner], nftokenID)) return tecNO_ENTRY; // Use implementation shared with NFTokenMint return nft::tokenOfferCreatePreclaim( ctx.view, - ctx.account, + ctx.tx[sfAccount], nft::getIssuer(nftokenID), ctx.tx[sfAmount], ctx.tx[~sfDestination], @@ -94,7 +94,7 @@ NFTokenCreateOffer::doApply() // Use implementation shared with NFTokenMint return nft::tokenOfferCreateApply( view(), - account_, + ctx_.tx[sfAccount], ctx_.tx[sfAmount], ctx_.tx[~sfDestination], ctx_.tx[~sfExpiration], diff --git a/src/xrpld/app/tx/detail/NFTokenMint.cpp b/src/xrpld/app/tx/detail/NFTokenMint.cpp index a7c952290ae..d5c3a8707c2 100644 --- a/src/xrpld/app/tx/detail/NFTokenMint.cpp +++ b/src/xrpld/app/tx/detail/NFTokenMint.cpp @@ -84,7 +84,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) } // An issuer must only be set if the tx is executed by the minter - if (auto iss = ctx.tx[~sfIssuer]; iss == ctx.account) + if (auto iss = ctx.tx[~sfIssuer]; iss == ctx.tx[sfAccount]) return temMALFORMED; if (auto uri = ctx.tx[~sfURI]) @@ -104,7 +104,7 @@ NFTokenMint::preflight(PreflightContext const& ctx) // do the validation. We pass tfSellNFToken as the transaction flags // because a Mint is only allowed to create a sell offer. if (NotTEC notTec = nft::tokenOfferCreatePreflight( - ctx.account, + ctx.tx[sfAccount], ctx.tx[sfAmount], ctx.tx[~sfDestination], ctx.tx[~sfExpiration], @@ -177,7 +177,8 @@ NFTokenMint::preclaim(PreclaimContext const& ctx) if (!sle) return tecNO_ISSUER; - if (auto const minter = (*sle)[~sfNFTokenMinter]; minter != ctx.account) + if (auto const minter = (*sle)[~sfNFTokenMinter]; + minter != ctx.tx[sfAccount]) return tecNO_PERMISSION; } @@ -192,8 +193,8 @@ NFTokenMint::preclaim(PreclaimContext const& ctx) // because a Mint is only allowed to create a sell offer. if (TER const ter = nft::tokenOfferCreatePreclaim( ctx.view, - ctx.account, - ctx.tx[~sfIssuer].value_or(ctx.account), + ctx.tx[sfAccount], + ctx.tx[~sfIssuer].value_or(ctx.tx[sfAccount]), ctx.tx[sfAmount], ctx.tx[~sfDestination], extractNFTokenFlagsFromTxFlags(ctx.tx.getFlags()), @@ -321,7 +322,7 @@ NFTokenMint::doApply() // because a Mint is only allowed to create a sell offer. if (TER const ter = nft::tokenOfferCreateApply( view(), - account_, + ctx_.tx[sfAccount], ctx_.tx[sfAmount], ctx_.tx[~sfDestination], ctx_.tx[~sfExpiration], diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index 8dcb74094e0..9177aa04a75 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -166,7 +166,7 @@ closeChannel( TxConsequences PayChanCreate::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx.getTx(), ctx.tx[sfAmount].xrp()}; } NotTEC @@ -181,7 +181,7 @@ PayChanCreate::preflight(PreflightContext const& ctx) if (!isXRP(ctx.tx[sfAmount]) || (ctx.tx[sfAmount] <= beast::zero)) return temBAD_AMOUNT; - if (ctx.account == ctx.tx[sfDestination]) + if (ctx.tx[sfAccount] == ctx.tx[sfDestination]) return temDST_IS_SRC; if (!publicKeyType(ctx.tx[sfPublicKey])) @@ -193,7 +193,7 @@ PayChanCreate::preflight(PreflightContext const& ctx) TER PayChanCreate::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.account; + auto const account = ctx.tx[sfAccount]; auto const sle = ctx.view.read(keylet::account(account)); if (!sle) return terNO_ACCOUNT; @@ -245,7 +245,8 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) TER PayChanCreate::doApply() { - auto const sle = ctx_.view().peek(keylet::account(account_)); + auto const account = ctx_.tx[sfAccount]; + auto const sle = ctx_.view().peek(keylet::account(account)); if (!sle) return tefINTERNAL; @@ -256,14 +257,14 @@ PayChanCreate::doApply() // Note that we we use the value from the sequence or ticket as the // payChan sequence. For more explanation see comments in SeqProxy.h. Keylet const payChanKeylet = - keylet::payChan(account_, dst, ctx_.tx.getSeqProxy().value()); + keylet::payChan(account, dst, ctx_.tx.getSeqProxy().value()); auto const slep = std::make_shared(payChanKeylet); // Funds held in this channel (*slep)[sfAmount] = ctx_.tx[sfAmount]; // Amount channel has already paid (*slep)[sfBalance] = ctx_.tx[sfAmount].zeroed(); - (*slep)[sfAccount] = account_; + (*slep)[sfAccount] = account; (*slep)[sfDestination] = dst; (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey]; @@ -276,9 +277,9 @@ PayChanCreate::doApply() // Add PayChan to owner directory { auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account_), + keylet::ownerDir(account), payChanKeylet, - describeOwnerDir(account_)); + describeOwnerDir(account)); if (!page) return tecDIR_FULL; (*slep)[sfOwnerNode] = *page; @@ -307,7 +308,7 @@ PayChanCreate::doApply() TxConsequences PayChanFund::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx.getTx(), ctx.tx[sfAmount].xrp()}; } NotTEC @@ -334,6 +335,7 @@ PayChanFund::doApply() return tecNO_ENTRY; AccountID const src = (*slep)[sfAccount]; + auto const txAccount = ctx_.tx[sfAccount]; auto const expiration = (*slep)[~sfExpiration]; { @@ -346,7 +348,7 @@ PayChanFund::doApply() slep, ctx_.view(), k.key, ctx_.app.journal("View")); } - if (src != account_) + if (src != txAccount) // only the owner can add funds or extend return tecNO_PERMISSION; @@ -364,7 +366,7 @@ PayChanFund::doApply() ctx_.view().update(slep); } - auto const sle = ctx_.view().peek(keylet::account(account_)); + auto const sle = ctx_.view().peek(keylet::account(txAccount)); if (!sle) return tefINTERNAL; @@ -468,7 +470,7 @@ PayChanClaim::preclaim(PreclaimContext const& ctx) if (!ctx.view.rules().enabled(featureCredentials)) return Transactor::preclaim(ctx); - if (auto const err = credentials::valid(ctx, ctx.account); + if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); !isTesSuccess(err)) return err; @@ -485,6 +487,7 @@ PayChanClaim::doApply() AccountID const src = (*slep)[sfAccount]; AccountID const dst = (*slep)[sfDestination]; + AccountID const txAccount = ctx_.tx[sfAccount]; auto const curExpiration = (*slep)[~sfExpiration]; { @@ -497,7 +500,7 @@ PayChanClaim::doApply() slep, ctx_.view(), k.key, ctx_.app.journal("View")); } - if (account_ != src && account_ != dst) + if (txAccount != src && txAccount != dst) return tecNO_PERMISSION; if (ctx_.tx[~sfBalance]) @@ -506,7 +509,7 @@ PayChanClaim::doApply() auto const chanFunds = slep->getFieldAmount(sfAmount).xrp(); auto const reqBalance = ctx_.tx[sfBalance].xrp(); - if (account_ == dst && !ctx_.tx[~sfSignature]) + if (txAccount == dst && !ctx_.tx[~sfSignature]) return temBAD_SIGNATURE; if (ctx_.tx[~sfSignature]) @@ -531,12 +534,12 @@ PayChanClaim::doApply() // featureDepositAuth to remove the bug. bool const depositAuth{ctx_.view().rules().enabled(featureDepositAuth)}; if (!depositAuth && - (account_ == src && (sled->getFlags() & lsfDisallowXRP))) + (txAccount == src && (sled->getFlags() & lsfDisallowXRP))) return tecNO_TARGET; if (depositAuth) { - if (auto err = verifyDepositPreauth(ctx_, account_, dst, sled); + if (auto err = verifyDepositPreauth(ctx_, txAccount, dst, sled); !isTesSuccess(err)) return err; } @@ -551,7 +554,7 @@ PayChanClaim::doApply() if (ctx_.tx.getFlags() & tfRenew) { - if (src != account_) + if (src != txAccount) return tecNO_PERMISSION; (*slep)[~sfExpiration] = std::nullopt; ctx_.view().update(slep); @@ -560,7 +563,7 @@ PayChanClaim::doApply() if (ctx_.tx.getFlags() & tfClose) { // Channel will close immediately if dry or the receiver closes - if (dst == account_ || (*slep)[sfBalance] == (*slep)[sfAmount]) + if (dst == txAccount || (*slep)[sfBalance] == (*slep)[sfAmount]) return closeChannel( slep, ctx_.view(), k.key, ctx_.app.journal("View")); diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 39e68f891e1..8ee08ada82d 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -42,7 +42,7 @@ Payment::makeTxConsequences(PreflightContext const& ctx) return maxAmount.native() ? maxAmount.xrp() : beast::zero; }; - return TxConsequences{ctx.tx, calculateMaxXRPSpend(ctx.tx)}; + return TxConsequences{ctx.tx.getTx(), calculateMaxXRPSpend(ctx.tx.getTx())}; } STAmount @@ -105,7 +105,7 @@ Payment::preflight(PreflightContext const& ctx) auto const account = tx.getAccountID(sfAccount); STAmount const maxSourceAmount = - getMaxSourceAmount(ctx.account, dstAmount, tx[~sfSendMax]); + getMaxSourceAmount(account, dstAmount, tx[~sfSendMax]); if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) || (!mptDirect && maxSourceAmount.holds())) @@ -319,7 +319,7 @@ Payment::preclaim(PreclaimContext const& ctx) } } - if (auto const err = credentials::valid(ctx, ctx.account); + if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); !isTesSuccess(err)) return err; @@ -342,9 +342,9 @@ Payment::doApply() AccountID const dstAccountID(ctx_.tx.getAccountID(sfDestination)); STAmount const dstAmount(ctx_.tx.getFieldAmount(sfAmount)); - if (ctx_.isDelegated && !ctx_.gpSet.empty()) + if (ctx_.tx.isDelegated() && !ctx_.permissions.empty()) { - // If gpSet is not empty, granular delegation is happening. + // If permissions is not empty, granular delegation is happening. // Currently we only support PaymentMint and PaymentBurn granular // permission. PaymentMint means the sender is the issuer. PaymentBurn // means the destination is the issuer. @@ -353,10 +353,10 @@ Payment::doApply() if (isXRP(amountIssue)) return tecNO_AUTH; if (amountIssue.account == account_ && - ctx_.gpSet.find(gpPaymentMint) != ctx_.gpSet.end()) + ctx_.permissions.contains(PaymentMint)) authorized = true; if (amountIssue.account == dstAccountID && - ctx_.gpSet.find(gpPaymentBurn) != ctx_.gpSet.end()) + ctx_.permissions.contains(PaymentBurn)) authorized = true; if (!authorized) diff --git a/src/xrpld/app/tx/detail/SetAccount.cpp b/src/xrpld/app/tx/detail/SetAccount.cpp index d1d2496747b..5acf0221c20 100644 --- a/src/xrpld/app/tx/detail/SetAccount.cpp +++ b/src/xrpld/app/tx/detail/SetAccount.cpp @@ -52,7 +52,8 @@ SetAccount::makeTxConsequences(PreflightContext const& ctx) return TxConsequences::normal; }; - return TxConsequences{ctx.tx, getTxConsequencesCategory(ctx.tx)}; + return TxConsequences{ + ctx.tx.getTx(), getTxConsequencesCategory(ctx.tx.getTx())}; } NotTEC @@ -262,7 +263,7 @@ SetAccount::doApply() std::uint32_t const uFlagsIn = sle->getFieldU32(sfFlags); std::uint32_t uFlagsOut = uFlagsIn; - STTx const& tx{ctx_.tx}; + STTx const& tx{ctx_.tx.getTx()}; std::uint32_t const uSetFlag{tx.getFieldU32(sfSetFlag)}; std::uint32_t const uClearFlag{tx.getFieldU32(sfClearFlag)}; @@ -270,9 +271,9 @@ SetAccount::doApply() std::uint32_t const uTxFlags{tx.getFlags()}; bool granularDelegated = false; - if (ctx_.isDelegated && !ctx_.gpSet.empty()) + if (ctx_.tx.isDelegated() && !ctx_.permissions.empty()) { - // if gpSet is not empty, granular delegation is happening. + // if permissions is not empty, granular delegation is happening. granularDelegated = true; // We don't support any flag based granular permission under AccountSet @@ -465,7 +466,8 @@ SetAccount::doApply() if (tx.isFieldPresent(sfEmailHash)) { if (granularDelegated && - ctx_.gpSet.find(gpAccountEmailHashSet) == ctx_.gpSet.end()) + ctx_.permissions.find(AccountEmailHashSet) == + ctx_.permissions.end()) return tecNO_AUTH; uint128 const uHash = tx.getFieldH128(sfEmailHash); @@ -510,7 +512,8 @@ SetAccount::doApply() if (tx.isFieldPresent(sfMessageKey)) { if (granularDelegated && - ctx_.gpSet.find(gpAccountMessageKeySet) == ctx_.gpSet.end()) + ctx_.permissions.find(AccountMessageKeySet) == + ctx_.permissions.end()) return tecNO_AUTH; Blob const messageKey = tx.getFieldVL(sfMessageKey); @@ -533,7 +536,7 @@ SetAccount::doApply() if (tx.isFieldPresent(sfDomain)) { if (granularDelegated && - ctx_.gpSet.find(gpAccountDomainSet) == ctx_.gpSet.end()) + ctx_.permissions.find(AccountDomainSet) == ctx_.permissions.end()) return tecNO_AUTH; Blob const domain = tx.getFieldVL(sfDomain); @@ -556,7 +559,8 @@ SetAccount::doApply() if (tx.isFieldPresent(sfTransferRate)) { if (granularDelegated && - ctx_.gpSet.find(gpAccountTransferRateSet) == ctx_.gpSet.end()) + ctx_.permissions.find(AccountTransferRateSet) == + ctx_.permissions.end()) return tecNO_AUTH; std::uint32_t uRate = tx.getFieldU32(sfTransferRate); @@ -579,7 +583,7 @@ SetAccount::doApply() if (tx.isFieldPresent(sfTickSize)) { if (granularDelegated && - ctx_.gpSet.find(gpAccountTickSizeSet) == ctx_.gpSet.end()) + ctx_.permissions.find(AccountTickSizeSet) == ctx_.permissions.end()) return tecNO_AUTH; auto uTickSize = tx[sfTickSize]; diff --git a/src/xrpld/app/tx/detail/SetOracle.cpp b/src/xrpld/app/tx/detail/SetOracle.cpp index 9d62a8b7c55..055143cc6fd 100644 --- a/src/xrpld/app/tx/detail/SetOracle.cpp +++ b/src/xrpld/app/tx/detail/SetOracle.cpp @@ -70,7 +70,8 @@ SetOracle::preflight(PreflightContext const& ctx) TER SetOracle::preclaim(PreclaimContext const& ctx) { - auto const sleSetter = ctx.view.read(keylet::account(ctx.account)); + auto const sleSetter = + ctx.view.read(keylet::account(ctx.tx.getAccountID(sfAccount))); if (!sleSetter) return terNO_ACCOUNT; // LCOV_EXCL_LINE @@ -91,8 +92,8 @@ SetOracle::preclaim(PreclaimContext const& ctx) lastUpdateTimeEpoch > (closeTime + maxLastUpdateTimeDelta)) return tecINVALID_UPDATE_TIME; - auto const sle = - ctx.view.read(keylet::oracle(ctx.account, ctx.tx[sfOracleDocumentID])); + auto const sle = ctx.view.read(keylet::oracle( + ctx.tx.getAccountID(sfAccount), ctx.tx[sfOracleDocumentID])); // token pairs to add/update std::set> pairs; @@ -184,7 +185,8 @@ SetOracle::preclaim(PreclaimContext const& ctx) static bool adjustOwnerCount(ApplyContext& ctx, int count) { - if (auto const sleAccount = ctx.view().peek(keylet::account(ctx.account))) + if (auto const sleAccount = + ctx.view().peek(keylet::account(ctx.tx[sfAccount]))) { adjustOwnerCount(ctx.view(), sleAccount, count, ctx.journal); return true; @@ -279,7 +281,7 @@ SetOracle::doApply() // create sle = std::make_shared(oracleID); - sle->setAccountID(sfOwner, account_); + sle->setAccountID(sfOwner, ctx_.tx.getAccountID(sfAccount)); sle->setFieldVL(sfProvider, ctx_.tx[sfProvider]); if (ctx_.tx.isFieldPresent(sfURI)) sle->setFieldVL(sfURI, ctx_.tx[sfURI]); diff --git a/src/xrpld/app/tx/detail/SetRegularKey.cpp b/src/xrpld/app/tx/detail/SetRegularKey.cpp index 39a556fb0e2..9f165612f3a 100644 --- a/src/xrpld/app/tx/detail/SetRegularKey.cpp +++ b/src/xrpld/app/tx/detail/SetRegularKey.cpp @@ -64,7 +64,7 @@ SetRegularKey::preflight(PreflightContext const& ctx) if (ctx.rules.enabled(fixMasterKeyAsRegularKey) && ctx.tx.isFieldPresent(sfRegularKey) && - (ctx.tx.getAccountID(sfRegularKey) == ctx.account)) + (ctx.tx.getAccountID(sfRegularKey) == ctx.tx.getAccountID(sfAccount))) { return temBAD_REGKEY; } diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index 6093c798392..740390c1c4c 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -81,7 +81,7 @@ SetSignerList::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j); + auto const result = determineOperation(ctx.tx.getTx(), ctx.flags, ctx.j); if (std::get<0>(result) != tesSUCCESS) return std::get<0>(result); @@ -97,7 +97,7 @@ SetSignerList::preflight(PreflightContext const& ctx) if (std::get<3>(result) == set) { // Validate our settings. - auto const account = ctx.account; + auto const account = ctx.tx.getAccountID(sfAccount); NotTEC const ter = validateQuorumAndSignerEntries( std::get<1>(result), std::get<2>(result), @@ -136,7 +136,7 @@ void SetSignerList::preCompute() { // Get the quorum and operation info. - auto result = determineOperation(ctx_.tx, view().flags(), j_); + auto result = determineOperation(ctx_.tx.getTx(), view().flags(), j_); assert(std::get<0>(result) == tesSUCCESS); assert(std::get<3>(result) != unknown); diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index ba45fbefb15..6d29efc3bcd 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -84,7 +84,7 @@ SetTrust::preflight(PreflightContext const& ctx) TER SetTrust::preclaim(PreclaimContext const& ctx) { - auto const id = ctx.account; + auto const id = ctx.tx[sfAccount]; auto const sle = ctx.view.read(keylet::account(id)); if (!sle) @@ -244,7 +244,7 @@ SetTrust::doApply() bool const bClearFreeze = (uTxFlags & tfClearFreeze); bool granularDelegated = false; - if (ctx_.isDelegated && !ctx_.gpSet.empty()) + if (ctx_.tx.isDelegated() && !ctx_.permissions.empty()) { granularDelegated = true; // If granular permission is delegated under the TrustSet transaction. @@ -252,14 +252,11 @@ SetTrust::doApply() // TrustlineUnfreeze granular permission. if (bSetNoRipple || bClearNoRipple || bQualityIn || bQualityOut) return terNO_AUTH; - if (bSetAuth && - ctx_.gpSet.find(gpTrustlineAuthorize) == ctx_.gpSet.end()) + if (bSetAuth && !ctx_.permissions.contains(TrustlineAuthorize)) return terNO_AUTH; - if (bSetFreeze && - ctx_.gpSet.find(gpTrustlineFreeze) == ctx_.gpSet.end()) + if (bSetFreeze && !ctx_.permissions.contains(TrustlineFreeze)) return terNO_AUTH; - if (bClearFreeze && - ctx_.gpSet.find(gpTrustlineUnfreeze) == ctx_.gpSet.end()) + if (bClearFreeze && !ctx_.permissions.contains(TrustlineUnfreeze)) return terNO_AUTH; } diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 3c0cd359b3e..c674dd7898f 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -41,7 +41,7 @@ namespace ripple { NotTEC preflight0(PreflightContext const& ctx) { - if (!isPseudoTx(ctx.tx) || ctx.tx.isFieldPresent(sfNetworkID)) + if (!isPseudoTx(ctx.tx.getTx()) || ctx.tx.isFieldPresent(sfNetworkID)) { uint32_t nodeNID = ctx.app.config().NETWORK_ID; std::optional txNID = ctx.tx[~sfNetworkID]; @@ -81,6 +81,10 @@ preflight0(PreflightContext const& ctx) NotTEC preflight1(PreflightContext const& ctx) { + if (!ctx.rules.enabled(featureAccountPermission) && + ctx.tx.isFieldPresent(sfOnBehalfOf)) + return temDISABLED; + // This is inappropriate in preflight0, because only Change transactions // skip this function, and those do not allow an sfTicketSequence field. if (ctx.tx.isFieldPresent(sfTicketSequence) && @@ -126,6 +130,10 @@ preflight1(PreflightContext const& ctx) ctx.tx.isFieldPresent(sfAccountTxnID)) return temINVALID; + if (!ctx.rules.enabled(featureAccountPermission) && + ctx.tx.isFieldPresent(sfOnBehalfOf)) + return temDISABLED; + return tesSUCCESS; } @@ -134,7 +142,7 @@ NotTEC preflight2(PreflightContext const& ctx) { auto const sigValid = checkValidity( - ctx.app.getHashRouter(), ctx.tx, ctx.rules, ctx.app.config()); + ctx.app.getHashRouter(), ctx.tx.getTx(), ctx.rules, ctx.app.config()); if (sigValid.first == Validity::SigBad) { JLOG(ctx.j.debug()) << "preflight2: bad signature. " << sigValid.second; @@ -147,19 +155,18 @@ preflight2(PreflightContext const& ctx) PreflightContext::PreflightContext( Application& app_, - STTx const& tx_, + STTxWr const& tx_, Rules const& rules_, ApplyFlags flags_, - AccountID const account_, beast::Journal j_) - : app(app_), tx(tx_), rules(rules_), flags(flags_), account(account_), j(j_) + : app(app_), tx(tx_), rules(rules_), flags(flags_), j(j_) { } //------------------------------------------------------------------------------ Transactor::Transactor(ApplyContext& ctx) - : ctx_(ctx), j_(ctx.journal), account_(ctx.account) + : ctx_(ctx), j_(ctx.journal), account_(ctx.tx.getAccountID(sfAccount)) { } @@ -182,13 +189,12 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) } TER -Transactor::checkAuthorization( +Transactor::checkPermissions( ReadView const& view, STTx const& tx, - std::unordered_set& gpSet) + std::unordered_set& permissions) { - auto const onBehalfOfAccount = view.read(keylet::account(tx[sfOnBehalfOf])); - if (!onBehalfOfAccount) + if (!view.read(keylet::account(tx[sfOnBehalfOf]))) return terNO_ACCOUNT; auto const accountPermissionKey = @@ -197,17 +203,17 @@ Transactor::checkAuthorization( if (!sle) return temMALFORMED; // todo: change error code - auto const permissions = sle->getFieldArray(sfPermissions); + auto const permissionArray = sle->getFieldArray(sfPermissions); auto const transactionType = tx.getTxnType(); - for (auto const& permission : permissions) + for (auto const& permission : permissionArray) { auto const permissionValue = permission[sfPermissionValue]; if (permissionValue == transactionType + 1) { // if the transaction permission is authorized, do not need to check // granular permission. - gpSet.clear(); + permissions.clear(); return tesSUCCESS; } @@ -215,15 +221,15 @@ Transactor::checkAuthorization( static_cast(permissionValue); auto const& type = Permission::getInstance().getGranularTxType(gpType); if (type && *type == transactionType) - gpSet.insert(gpType); + permissions.insert(gpType); } - if (gpSet.empty()) + if (permissions.empty()) return terNO_AUTH; // When the code reaches here, the transaction permission is not authorized. // But one or more of its granular permission under this transaction type is - // authorized. And the granular types are stored in gpSet. + // authorized. And the granular types are stored in permissions. return tesSUCCESS; } @@ -294,11 +300,11 @@ TER Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - if (ctx_.isDelegated) + if (ctx_.tx.isDelegated()) { // if the transaction is being delegated to another account, // the sender account will pay the fee. - auto const sender = ctx_.tx.getAccountID(sfAccount); + auto const sender = ctx_.tx.getTx().getAccountID(sfAccount); auto const sleSender = view().peek(keylet::account(sender)); if (!sleSender) return tefINTERNAL; @@ -564,7 +570,7 @@ Transactor::checkSingleSign(PreclaimContext const& ctx) // Look up the account. auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); - auto const idAccount = ctx.tx.getAccountID(sfAccount); + auto const idAccount = ctx.tx.getTx().getAccountID(sfAccount); auto const sleAccount = ctx.view.read(keylet::account(idAccount)); if (!sleAccount) return terNO_ACCOUNT; @@ -928,7 +934,7 @@ Transactor::operator()() SerialIter sit(ser.slice()); STTx s2(sit); - if (!s2.isEquivalent(ctx_.tx)) + if (!s2.isEquivalent(ctx_.tx.getTx())) { JLOG(j_.fatal()) << "Transaction serdes mismatch"; JLOG(j_.info()) << to_string(ctx_.tx.getJson(JsonOptions::none)); diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index b6718ef16e1..23368da3f47 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -33,18 +33,16 @@ struct PreflightContext { public: Application& app; - STTx const& tx; + STTxWr const& tx; Rules const rules; ApplyFlags flags; - AccountID const account; beast::Journal const j; PreflightContext( Application& app_, - STTx const& tx_, + STTxWr const& tx_, Rules const& rules_, ApplyFlags flags_, - AccountID const account_, beast::Journal j_); PreflightContext& @@ -58,28 +56,22 @@ struct PreclaimContext Application& app; ReadView const& view; TER preflightResult; - STTx const& tx; + STTxWr const& tx; ApplyFlags flags; - bool isDelegated; - AccountID const account; beast::Journal const j; PreclaimContext( Application& app_, ReadView const& view_, TER preflightResult_, - STTx const& tx_, + STTxWr const& tx_, ApplyFlags flags_, - bool isDelegated, - AccountID const account, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) : app(app_) , view(view_) , preflightResult(preflightResult_) , tx(tx_) , flags(flags_) - , isDelegated(isDelegated) - , account(account) , j(j_) { } @@ -151,10 +143,10 @@ class Transactor calculateBaseFee(ReadView const& view, STTx const& tx); static TER - checkAuthorization( + checkPermissions( ReadView const& view, STTx const& tx, - std::unordered_set& gpSet); + std::unordered_set& permissions); static TER preclaim(PreclaimContext const& ctx) diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index e94941cd583..69c5c89c52a 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1220,7 +1220,7 @@ attestationPreflight(PreflightContext const& ctx) if (!publicKeyType(ctx.tx[sfPublicKey])) return temMALFORMED; - auto const att = toClaim(ctx.tx); + auto const att = toClaim(ctx.tx.getTx()); if (!att) return temMALFORMED; @@ -1244,7 +1244,7 @@ template TER attestationPreclaim(PreclaimContext const& ctx) { - auto const att = toClaim(ctx.tx); + auto const att = toClaim(ctx.tx.getTx()); if (!att) return tecINTERNAL; // checked in preflight @@ -1274,7 +1274,7 @@ template TER attestationDoApply(ApplyContext& ctx) { - auto const att = toClaim(ctx.tx); + auto const att = toClaim(ctx.tx.getTx()); if (!att) // Should already be checked in preflight return tecINTERNAL; @@ -1384,7 +1384,7 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; - auto const account = ctx.account; + auto const account = ctx.tx[sfAccount]; auto const reward = ctx.tx[sfSignatureReward]; auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; @@ -1459,7 +1459,7 @@ XChainCreateBridge::preflight(PreflightContext const& ctx) TER XChainCreateBridge::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.account; + auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; STXChainBridge::ChainType const chainType = STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); @@ -1510,21 +1510,22 @@ XChainCreateBridge::preclaim(PreclaimContext const& ctx) TER XChainCreateBridge::doApply() { + auto const account = ctx_.tx[sfAccount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[sfSignatureReward]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; - auto const sleAcct = ctx_.view().peek(keylet::account(account_)); + auto const sleAcct = ctx_.view().peek(keylet::account(account)); if (!sleAcct) return tecINTERNAL; STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(account_ == bridgeSpec.lockingChainDoor()); + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); Keylet const bridgeKeylet = keylet::bridge(bridgeSpec, chainType); auto const sleBridge = std::make_shared(bridgeKeylet); - (*sleBridge)[sfAccount] = account_; + (*sleBridge)[sfAccount] = account; (*sleBridge)[sfSignatureReward] = reward; if (minAccountCreate) (*sleBridge)[sfMinAccountCreateAmount] = *minAccountCreate; @@ -1536,9 +1537,7 @@ XChainCreateBridge::doApply() // Add to owner directory { auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account_), - bridgeKeylet, - describeOwnerDir(account_)); + keylet::ownerDir(account), bridgeKeylet, describeOwnerDir(account)); if (!page) return tecDIR_FULL; (*sleBridge)[sfOwnerNode] = *page; @@ -1566,7 +1565,7 @@ BridgeModify::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfBridgeModifyMask) return temINVALID_FLAG; - auto const account = ctx.account; + auto const account = ctx.tx[sfAccount]; auto const reward = ctx.tx[~sfSignatureReward]; auto const minAccountCreate = ctx.tx[~sfMinAccountCreateAmount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; @@ -1610,10 +1609,11 @@ BridgeModify::preflight(PreflightContext const& ctx) TER BridgeModify::preclaim(PreclaimContext const& ctx) { + auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(ctx.account == bridgeSpec.lockingChainDoor()); + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); if (!ctx.view.read(keylet::bridge(bridgeSpec, chainType))) { @@ -1626,18 +1626,19 @@ BridgeModify::preclaim(PreclaimContext const& ctx) TER BridgeModify::doApply() { + auto const account = ctx_.tx[sfAccount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[~sfSignatureReward]; auto const minAccountCreate = ctx_.tx[~sfMinAccountCreateAmount]; bool const clearAccountCreate = ctx_.tx.getFlags() & tfClearAccountCreateAmount; - auto const sleAcct = ctx_.view().peek(keylet::account(account_)); + auto const sleAcct = ctx_.view().peek(keylet::account(account)); if (!sleAcct) return tecINTERNAL; STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(account_ == bridgeSpec.lockingChainDoor()); + STXChainBridge::srcChain(account == bridgeSpec.lockingChainDoor()); auto const sleBridge = ctx_.view().peek(keylet::bridge(bridgeSpec, chainType)); @@ -1690,6 +1691,7 @@ XChainClaim::preflight(PreflightContext const& ctx) TER XChainClaim::preclaim(PreclaimContext const& ctx) { + AccountID const account = ctx.tx[sfAccount]; STXChainBridge const bridgeSpec = ctx.tx[sfXChainBridge]; STAmount const& thisChainAmount = ctx.tx[sfAmount]; auto const claimID = ctx.tx[sfXChainClaimID]; @@ -1759,7 +1761,7 @@ XChainClaim::preclaim(PreclaimContext const& ctx) return tecXCHAIN_NO_CLAIM_ID; } - if ((*sleClaimID)[sfAccount] != ctx.account) + if ((*sleClaimID)[sfAccount] != account) { // Sequence number isn't owned by the sender of this transaction return tecXCHAIN_BAD_CLAIM_ID; @@ -1775,6 +1777,7 @@ XChainClaim::doApply() { PaymentSandbox psb(&ctx_.view()); + AccountID const account = ctx_.tx[sfAccount]; auto const dst = ctx_.tx[sfDestination]; STXChainBridge const bridgeSpec = ctx_.tx[sfXChainBridge]; STAmount const& thisChainAmount = ctx_.tx[sfAmount]; @@ -1796,7 +1799,7 @@ XChainClaim::doApply() // `finalizeClaimHelper`. Since `finalizeClaimHelper` can create child // views, it's important that the sle's lifetime doesn't overlap. - auto const sleAcct = psb.peek(keylet::account(account_)); + auto const sleAcct = psb.peek(keylet::account(account)); auto const sleBridge = peekBridge(psb, bridgeSpec); auto const sleClaimID = psb.peek(claimIDKeylet); @@ -1865,7 +1868,7 @@ XChainClaim::doApply() bridgeSpec, dst, dstTag, - /*claimOwner*/ account_, + /*claimOwner*/ account, sendingAmount, rewardPoolSrc, signatureReward, @@ -1895,7 +1898,7 @@ XChainCommit::makeTxConsequences(PreflightContext const& ctx) return XRPAmount{beast::zero}; }(); - return TxConsequences{ctx.tx, maxSpend}; + return TxConsequences{ctx.tx.getTx(), maxSpend}; } NotTEC @@ -1936,8 +1939,9 @@ XChainCommit::preclaim(PreclaimContext const& ctx) } AccountID const thisDoor = (*sleBridge)[sfAccount]; + AccountID const account = ctx.tx[sfAccount]; - if (thisDoor == ctx.account) + if (thisDoor == account) { // Door account can't lock funds onto itself return tecXCHAIN_SELF_COMMIT; @@ -1972,10 +1976,11 @@ XChainCommit::doApply() { PaymentSandbox psb(&ctx_.view()); + auto const account = ctx_.tx[sfAccount]; auto const amount = ctx_.tx[sfAmount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; - if (!psb.read(keylet::account(account_))) + if (!psb.read(keylet::account(account))) return tecINTERNAL; auto const sleBridge = readBridge(psb, bridgeSpec); @@ -1990,7 +1995,7 @@ XChainCommit::doApply() auto const thTer = transferHelper( psb, - account_, + account, dst, /*dstTag*/ std::nullopt, /*claimOwner*/ std::nullopt, @@ -2033,6 +2038,7 @@ XChainCreateClaimID::preflight(PreflightContext const& ctx) TER XChainCreateClaimID::preclaim(PreclaimContext const& ctx) { + auto const account = ctx.tx[sfAccount]; auto const bridgeSpec = ctx.tx[sfXChainBridge]; auto const sleBridge = readBridge(ctx.view, bridgeSpec); @@ -2051,7 +2057,7 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) { // Check reserve - auto const sleAcc = ctx.view.read(keylet::account(ctx.account)); + auto const sleAcc = ctx.view.read(keylet::account(account)); if (!sleAcc) return terNO_ACCOUNT; @@ -2069,11 +2075,12 @@ XChainCreateClaimID::preclaim(PreclaimContext const& ctx) TER XChainCreateClaimID::doApply() { + auto const account = ctx_.tx[sfAccount]; auto const bridgeSpec = ctx_.tx[sfXChainBridge]; auto const reward = ctx_.tx[sfSignatureReward]; auto const otherChainSrc = ctx_.tx[sfOtherChainSource]; - auto const sleAcct = ctx_.view().peek(keylet::account(account_)); + auto const sleAcct = ctx_.view().peek(keylet::account(account)); if (!sleAcct) return tecINTERNAL; @@ -2093,7 +2100,7 @@ XChainCreateClaimID::doApply() auto const sleClaimID = std::make_shared(claimIDKeylet); - (*sleClaimID)[sfAccount] = account_; + (*sleClaimID)[sfAccount] = account; (*sleClaimID)[sfXChainBridge] = bridgeSpec; (*sleClaimID)[sfXChainClaimID] = claimID; (*sleClaimID)[sfOtherChainSource] = otherChainSrc; @@ -2104,9 +2111,9 @@ XChainCreateClaimID::doApply() // Add to owner directory { auto const page = ctx_.view().dirInsert( - keylet::ownerDir(account_), + keylet::ownerDir(account), claimIDKeylet, - describeOwnerDir(account_)); + describeOwnerDir(account)); if (!page) return tecDIR_FULL; (*sleClaimID)[sfOwnerNode] = *page; @@ -2221,7 +2228,8 @@ XChainCreateAccountCommit::preclaim(PreclaimContext const& ctx) return tecXCHAIN_BAD_TRANSFER_ISSUE; AccountID const thisDoor = (*sleBridge)[sfAccount]; - if (thisDoor == ctx.account) + AccountID const account = ctx.tx[sfAccount]; + if (thisDoor == account) { // Door account can't lock funds onto itself return tecXCHAIN_SELF_COMMIT; @@ -2253,11 +2261,12 @@ XChainCreateAccountCommit::doApply() { PaymentSandbox psb(&ctx_.view()); + AccountID const account = ctx_.tx[sfAccount]; STAmount const amount = ctx_.tx[sfAmount]; STAmount const reward = ctx_.tx[sfSignatureReward]; STXChainBridge const bridge = ctx_.tx[sfXChainBridge]; - auto const sle = psb.peek(keylet::account(account_)); + auto const sle = psb.peek(keylet::account(account)); if (!sle) return tecINTERNAL; @@ -2273,7 +2282,7 @@ XChainCreateAccountCommit::doApply() STAmount const toTransfer = amount + reward; auto const thTer = transferHelper( psb, - account_, + account, dst, /*dstTag*/ std::nullopt, /*claimOwner*/ std::nullopt, diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/xrpld/app/tx/detail/apply.cpp index 103ec041074..bad97f9bc85 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/xrpld/app/tx/detail/apply.cpp @@ -116,7 +116,11 @@ apply( STAmountSO stAmountSO{view.rules().enabled(fixSTAmountCanonicalize)}; NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; - auto pfresult = preflight(app, view.rules(), tx, flags, j); + bool const isDelegated = view.rules().enabled(featureAccountPermission) && + tx.isFieldPresent(sfOnBehalfOf); + STTxWr txWr(tx, isDelegated); + + auto pfresult = preflight(app, view.rules(), txWr, flags, j); auto pcresult = preclaim(pfresult, app, view); return doApply(pcresult, app, view); } diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 17f6c2b658c..340df3155d5 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -118,7 +118,7 @@ requires(T::ConsequencesFactory == Transactor::Normal) TxConsequences consequences_helper(PreflightContext const& ctx) { - return TxConsequences(ctx.tx); + return TxConsequences(ctx.tx.getTx()); }; // For Transactor::Blocker @@ -127,7 +127,7 @@ requires(T::ConsequencesFactory == Transactor::Blocker) TxConsequences consequences_helper(PreflightContext const& ctx) { - return TxConsequences(ctx.tx, TxConsequences::blocker); + return TxConsequences(ctx.tx.getTx(), TxConsequences::blocker); }; // For Transactor::Custom @@ -175,40 +175,42 @@ invoke_preclaim(PreclaimContext const& ctx) // doesn't list one, preflight will have already a flagged a // failure. auto const id = ctx.tx.getAccountID(sfAccount); - std::unordered_set gpSet; + std::unordered_set permissions; if (id != beast::zero) { - TER result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j); + TER result = T::checkSeqProxy(ctx.view, ctx.tx.getTx(), ctx.j); if (result != tesSUCCESS) - return std::make_pair(result, gpSet); + return std::make_pair(result, permissions); result = T::checkPriorTxAndLastLedger(ctx); if (result != tesSUCCESS) - return std::make_pair(result, gpSet); + return std::make_pair(result, permissions); - result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); + result = T::checkFee( + ctx, calculateBaseFee(ctx.view, ctx.tx.getTx())); if (result != tesSUCCESS) - return std::make_pair(result, gpSet); + return std::make_pair(result, permissions); result = T::checkSign(ctx); if (result != tesSUCCESS) - return std::make_pair(result, gpSet); + return std::make_pair(result, permissions); - if (ctx.isDelegated) + if (ctx.tx.isDelegated()) // if this is a delegated transaction, check if the account // has authorization. - result = T::checkAuthorization(ctx.view, ctx.tx, gpSet); + result = T::checkPermissions( + ctx.view, ctx.tx.getTx(), permissions); if (result != tesSUCCESS) - return std::make_pair(result, gpSet); + return std::make_pair(result, permissions); } - return std::make_pair(T::preclaim(ctx), gpSet); + return std::make_pair(T::preclaim(ctx), permissions); }); } catch (UnknownTxnType const& e) @@ -300,26 +302,19 @@ PreflightResult preflight( Application& app, Rules const& rules, - STTx const& tx, + STTxWr const& tx, ApplyFlags flags, beast::Journal j) { - bool const isDelegated = - rules.enabled(featureAccountPermission) && tx[~sfOnBehalfOf]; - AccountID const account = isDelegated ? *tx[~sfOnBehalfOf] : tx[sfAccount]; - PreflightContext const pfctx(app, tx, rules, flags, account, j); + PreflightContext const pfctx(app, tx, rules, flags, j); try { - // if AccountPermission is not enabled, do not use OnBehalfOf field. - if (!rules.enabled(featureAccountPermission) && tx[~sfOnBehalfOf]) - throw std::runtime_error("invalid field"); - - return {pfctx, isDelegated, account, invoke_preflight(pfctx)}; + return {pfctx, invoke_preflight(pfctx)}; } catch (std::exception const& e) { JLOG(j.fatal()) << "apply: " << e.what(); - return {pfctx, false, AccountID(0), {tefEXCEPTION, TxConsequences{tx}}}; + return {pfctx, {tefEXCEPTION, TxConsequences{tx.getTx()}}}; } } @@ -344,8 +339,6 @@ preclaim( secondFlight.ter, secondFlight.tx, secondFlight.flags, - secondFlight.isDelegated, - secondFlight.account, secondFlight.j); } else @@ -356,8 +349,6 @@ preclaim( preflightResult.ter, preflightResult.tx, preflightResult.flags, - preflightResult.isDelegated, - preflightResult.account, preflightResult.j); } try @@ -409,11 +400,9 @@ doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view) view, preclaimResult.tx, preclaimResult.ter, - calculateBaseFee(view, preclaimResult.tx), + calculateBaseFee(view, preclaimResult.tx.getTx()), preclaimResult.flags, - preclaimResult.isDelegated, - preclaimResult.account, - preclaimResult.gpSet, + preclaimResult.permissions, preclaimResult.j); return invoke_apply(ctx); } diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 7f52582b73b..4ef9660bb59 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -809,7 +809,37 @@ doLedgerEntry(RPC::JsonContext& context) } else if (context.params.isMember(jss::account_permission)) { - // to be implemented + expectedType = ltACCOUNT_PERMISSION; + auto const& permission = context.params[jss::account_permission]; + + if (!permission.isObject()) + { + if (!uNodeIndex.parseHex(permission.asString())) + { + uNodeIndex = beast::zero; + jvResult[jss::error] = "malformedRequest"; + } + } + else if ( + !permission.isMember(jss::account) || + !permission.isMember(jss::authorize)) + { + jvResult[jss::error] = "malformedRequest"; + } + else + { + auto const account = + parseBase58(permission[jss::account].asString()); + auto const authorize = parseBase58( + permission[jss::authorize].asString()); + if (!account) + jvResult[jss::error] = "malformedAccount"; + else if (!authorize) + jvResult[jss::error] = "malformedAuthorize"; + else + uNodeIndex = + keylet::accountPermission(*account, *authorize).key; + } } else { From 15506e5f8f53b2e5a912cbe93c2348c3a05e7567 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Fri, 6 Dec 2024 01:23:33 -0500 Subject: [PATCH 4/5] Modify STTxWr --- include/xrpl/protocol/STTxWr.h | 4 ++-- src/xrpld/app/tx/detail/Transactor.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/xrpl/protocol/STTxWr.h b/include/xrpl/protocol/STTxWr.h index 5bd4354ec09..5affdbbc724 100644 --- a/include/xrpl/protocol/STTxWr.h +++ b/include/xrpl/protocol/STTxWr.h @@ -35,11 +35,11 @@ namespace ripple { class STTxWr { private: - STTx tx_; // Wrap an instance of STTx + const STTx& tx_; // Wrap an instance of STTx bool isDelegated_; // if the transaction is delegated by another account public: - explicit STTxWr(STTx tx, bool isDelegated) + explicit STTxWr(STTx const& tx, bool isDelegated) : tx_(tx), isDelegated_(isDelegated) { } diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 7a0d65aa8e0..404242adbae 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -97,7 +97,7 @@ preflight1(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; - auto const id = ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getTx().getAccountID(sfAccount); if (id == beast::zero) { JLOG(ctx.j.warn()) << "preflight1: bad account id"; @@ -271,7 +271,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - auto const id = ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getTx().getAccountID(sfAccount); auto const sle = ctx.view.read(keylet::account(id)); if (!sle) return terNO_ACCOUNT; @@ -406,7 +406,7 @@ Transactor::checkSeqProxy( NotTEC Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx) { - auto const id = ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getTx().getAccountID(sfAccount); auto const sle = ctx.view.read(keylet::account(id)); @@ -639,7 +639,7 @@ Transactor::checkSingleSign(PreclaimContext const& ctx) NotTEC Transactor::checkMultiSign(PreclaimContext const& ctx) { - auto const id = ctx.tx.getAccountID(sfAccount); + auto const id = ctx.tx.getTx().getAccountID(sfAccount); // Get mTxnAccountID's SignerList and Quorum. std::shared_ptr sleAccountSigners = ctx.view.read(keylet::signers(id)); From 1e15774a93222138c71e2d79c42a09325a1c9ae7 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Tue, 7 Jan 2025 16:55:19 -0500 Subject: [PATCH 5/5] addressing comments --- include/xrpl/protocol/STTx.h | 3 + .../protocol/{STTxWr.h => STTxDelegated.h} | 18 +++-- src/libxrpl/protocol/Permissions.cpp | 2 - src/libxrpl/protocol/STTx.cpp | 7 ++ src/test/app/AMM_test.cpp | 6 +- src/test/app/Escrow_test.cpp | 6 +- src/test/app/NFTokenBurn_test.cpp | 4 +- src/test/app/TxQ_test.cpp | 6 +- src/test/ledger/Invariants_test.cpp | 2 +- src/xrpld/app/ledger/AcceptedLedgerTx.cpp | 4 +- src/xrpld/app/ledger/detail/LedgerToJson.cpp | 4 +- src/xrpld/app/ledger/detail/LocalTxs.cpp | 5 +- src/xrpld/app/misc/CredentialHelpers.cpp | 2 +- src/xrpld/app/misc/detail/TxQ.cpp | 9 +-- src/xrpld/app/tx/apply.h | 2 +- src/xrpld/app/tx/applySteps.h | 8 +-- src/xrpld/app/tx/detail/AMMWithdraw.cpp | 4 +- .../app/tx/detail/AccountPermissionSet.cpp | 14 ++-- src/xrpld/app/tx/detail/ApplyContext.cpp | 6 +- src/xrpld/app/tx/detail/ApplyContext.h | 6 +- src/xrpld/app/tx/detail/CashCheck.cpp | 2 +- src/xrpld/app/tx/detail/Change.cpp | 14 ++-- src/xrpld/app/tx/detail/CreateOffer.cpp | 3 +- src/xrpld/app/tx/detail/CreateTicket.cpp | 2 +- src/xrpld/app/tx/detail/Escrow.cpp | 2 +- src/xrpld/app/tx/detail/InvariantCheck.cpp | 28 ++++---- src/xrpld/app/tx/detail/InvariantCheck.h | 32 ++++----- .../app/tx/detail/MPTokenIssuanceSet.cpp | 12 ++-- src/xrpld/app/tx/detail/PayChan.cpp | 4 +- src/xrpld/app/tx/detail/Payment.cpp | 3 +- src/xrpld/app/tx/detail/SetAccount.cpp | 4 +- src/xrpld/app/tx/detail/SetSignerList.cpp | 4 +- src/xrpld/app/tx/detail/Transactor.cpp | 68 ++++++++----------- src/xrpld/app/tx/detail/Transactor.h | 10 +-- src/xrpld/app/tx/detail/XChainBridge.cpp | 8 +-- src/xrpld/app/tx/detail/apply.cpp | 6 +- src/xrpld/app/tx/detail/applySteps.cpp | 25 +++---- src/xrpld/rpc/handlers/LedgerEntry.cpp | 2 +- 38 files changed, 167 insertions(+), 180 deletions(-) rename include/xrpl/protocol/{STTxWr.h => STTxDelegated.h} (93%) diff --git a/include/xrpl/protocol/STTx.h b/include/xrpl/protocol/STTx.h index 08b9a1bad10..60abbababce 100644 --- a/include/xrpl/protocol/STTx.h +++ b/include/xrpl/protocol/STTx.h @@ -139,6 +139,9 @@ class STTx final : public STObject, public CountedObject char status, std::string const& escapedMetaData) const; + AccountID + getEffectiveAccountID() const; + private: Expected checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const; diff --git a/include/xrpl/protocol/STTxWr.h b/include/xrpl/protocol/STTxDelegated.h similarity index 93% rename from include/xrpl/protocol/STTxWr.h rename to include/xrpl/protocol/STTxDelegated.h index 5affdbbc724..7acfba19a82 100644 --- a/include/xrpl/protocol/STTxWr.h +++ b/include/xrpl/protocol/STTxDelegated.h @@ -32,20 +32,20 @@ namespace ripple { // delegated by another account by checking if the sfOnBehalfOf field is // present. If it is present, we need to return the sfOnBehalfOf field as the // account when calling getAccountID(sfAccount) and tx[sfAccount]. -class STTxWr +class STTxDelegated { private: const STTx& tx_; // Wrap an instance of STTx bool isDelegated_; // if the transaction is delegated by another account public: - explicit STTxWr(STTx const& tx, bool isDelegated) + explicit STTxDelegated(STTx const& tx, bool isDelegated) : tx_(tx), isDelegated_(isDelegated) { } - STTx - getTx() const + const STTx& + getSTTx() const { return tx_; } @@ -66,9 +66,8 @@ class STTxWr } template - typename std::enable_if< - !std::is_same, SF_ACCOUNT>::value, - typename T::value_type>::type + requires(!std::is_same, SF_ACCOUNT>::value) + typename T::value_type operator[](TypedField const& f) const { return tx_[f]; @@ -78,9 +77,8 @@ class STTxWr // check if the transaction is delegated by another account. If it is, // return sfOnBehalfOf field instead. template - typename std::enable_if< - std::is_same, SF_ACCOUNT>::value, - AccountID>::type + requires std::is_same, SF_ACCOUNT>::value + AccountID operator[](TypedField const& f) const { if (f == sfAccount) diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index 92f26acafdb..8b1eb2e3431 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -18,8 +18,6 @@ //============================================================================== #include -#include -#include #include namespace ripple { diff --git a/src/libxrpl/protocol/STTx.cpp b/src/libxrpl/protocol/STTx.cpp index b530271080d..276bab2fe15 100644 --- a/src/libxrpl/protocol/STTx.cpp +++ b/src/libxrpl/protocol/STTx.cpp @@ -439,6 +439,13 @@ STTx::checkMultiSign( return {}; } +AccountID +STTx::getEffectiveAccountID() const +{ + return isFieldPresent(sfOnBehalfOf) ? getAccountID(sfOnBehalfOf) + : getAccountID(sfAccount); +} + //------------------------------------------------------------------------------ static bool diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index c32127a4531..f1e81132c5e 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -3304,7 +3304,7 @@ struct AMM_test : public jtx::AMMTest env.app().config().features.erase(featureAMM); PreflightContext pfctx( env.app(), - STTxWr(*jtx.stx, false), + *jtx.stx, env.current()->rules(), tapNONE, env.journal); @@ -3319,7 +3319,7 @@ struct AMM_test : public jtx::AMMTest jtx.stx = env.ust(jtx); PreflightContext pfctx( env.app(), - STTxWr(*jtx.stx, false), + *jtx.stx, env.current()->rules(), tapNONE, env.journal); @@ -3334,7 +3334,7 @@ struct AMM_test : public jtx::AMMTest jtx.stx = env.ust(jtx); PreflightContext pfctx( env.app(), - STTxWr(*jtx.stx, false), + *jtx.stx, env.current()->rules(), tapNONE, env.journal); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 2af6be94cd8..714fc7734d9 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -1331,7 +1331,7 @@ struct Escrow_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - STTxWr(*jtx.stx, false), + *jtx.stx, tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -1345,7 +1345,7 @@ struct Escrow_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - STTxWr(*jtx.stx, false), + *jtx.stx, tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -1359,7 +1359,7 @@ struct Escrow_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - STTxWr(*jtx.stx, false), + *jtx.stx, tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); diff --git a/src/test/app/NFTokenBurn_test.cpp b/src/test/app/NFTokenBurn_test.cpp index 3fceb822d21..f3765b8de4a 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -807,7 +807,7 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite ApplyContext ac{ env.app(), ov, - STTxWr(tx, false), + tx, tesSUCCESS, env.current()->fees().base, tapNONE, @@ -852,7 +852,7 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite ApplyContext ac{ env.app(), ov, - STTxWr(tx, false), + tx, tesSUCCESS, env.current()->fees().base, tapNONE, diff --git a/src/test/app/TxQ_test.cpp b/src/test/app/TxQ_test.cpp index 3fb5eeb1d79..e7b70203c91 100644 --- a/src/test/app/TxQ_test.cpp +++ b/src/test/app/TxQ_test.cpp @@ -2491,7 +2491,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - STTxWr(*jtx.stx, false), + *jtx.stx, tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -2508,7 +2508,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - STTxWr(*jtx.stx, false), + *jtx.stx, tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); @@ -2522,7 +2522,7 @@ class TxQPosNegFlows_test : public beast::unit_test::suite auto const pf = preflight( env.app(), env.current()->rules(), - STTxWr(*jtx.stx, false), + *jtx.stx, tapNONE, env.journal); BEAST_EXPECT(pf.ter == tesSUCCESS); diff --git a/src/test/ledger/Invariants_test.cpp b/src/test/ledger/Invariants_test.cpp index 94f906dff7f..8007a496149 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -94,7 +94,7 @@ class Invariants_test : public beast::unit_test::suite ApplyContext ac{ env.app(), ov, - STTxWr(tx, false), + tx, tesSUCCESS, env.current()->fees().base, tapNONE, diff --git a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp index 585c6c11517..35a203512c9 100644 --- a/src/xrpld/app/ledger/AcceptedLedgerTx.cpp +++ b/src/xrpld/app/ledger/AcceptedLedgerTx.cpp @@ -58,9 +58,7 @@ AcceptedLedgerTx::AcceptedLedgerTx( if (mTxn->getTxnType() == ttOFFER_CREATE) { - auto const& account = mTxn->isFieldPresent(sfOnBehalfOf) - ? mTxn->getAccountID(sfOnBehalfOf) - : mTxn->getAccountID(sfAccount); + auto const& account = mTxn->getEffectiveAccountID(); auto const amount = mTxn->getFieldAmount(sfTakerGets); // If the offer create is not self funded then add the owner balance diff --git a/src/xrpld/app/ledger/detail/LedgerToJson.cpp b/src/xrpld/app/ledger/detail/LedgerToJson.cpp index eda1cfc0b22..3977f5a6028 100644 --- a/src/xrpld/app/ledger/detail/LedgerToJson.cpp +++ b/src/xrpld/app/ledger/detail/LedgerToJson.cpp @@ -206,9 +206,7 @@ fillJsonTx( if ((fill.options & LedgerFill::ownerFunds) && txn->getTxnType() == ttOFFER_CREATE) { - auto const& account = txn->isFieldPresent(sfOnBehalfOf) - ? txn->getAccountID(sfOnBehalfOf) - : txn->getAccountID(sfAccount); + auto const& account = txn->getEffectiveAccountID(); auto const amount = txn->getFieldAmount(sfTakerGets); // If the offer create is not self funded then add the diff --git a/src/xrpld/app/ledger/detail/LocalTxs.cpp b/src/xrpld/app/ledger/detail/LocalTxs.cpp index aaeb6245415..57739b50076 100644 --- a/src/xrpld/app/ledger/detail/LocalTxs.cpp +++ b/src/xrpld/app/ledger/detail/LocalTxs.cpp @@ -62,10 +62,7 @@ class LocalTx : m_txn(txn) , m_expire(index + holdLedgers) , m_id(txn->getTransactionID()) - , m_account( - txn->isFieldPresent(sfOnBehalfOf) - ? txn->getAccountID(sfOnBehalfOf) - : txn->getAccountID(sfAccount)) + , m_account(txn->getEffectiveAccountID()) , m_seqProxy(txn->getSeqProxy()) { if (txn->isFieldPresent(sfLastLedgerSequence)) diff --git a/src/xrpld/app/misc/CredentialHelpers.cpp b/src/xrpld/app/misc/CredentialHelpers.cpp index 032f8eb94ac..91c5ac5e78f 100644 --- a/src/xrpld/app/misc/CredentialHelpers.cpp +++ b/src/xrpld/app/misc/CredentialHelpers.cpp @@ -243,7 +243,7 @@ verifyDepositPreauth( bool const credentialsPresent = ctx.tx.isFieldPresent(sfCredentialIDs); if (credentialsPresent && - credentials::removeExpired(ctx.view(), ctx.tx.getTx(), ctx.journal)) + credentials::removeExpired(ctx.view(), ctx.tx.getSTTx(), ctx.journal)) return tecEXPIRED; if (sleDst && (sleDst->getFlags() & lsfDepositAuth)) diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 6a2b9171d26..4eec1735b12 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -309,8 +309,8 @@ TxQ::MaybeTx::apply(Application& app, OpenView& view, beast::Journal j) << " rules or flags have changed. Flags from " << pfresult->flags << " to " << flags; - pfresult.emplace( - preflight(app, view.rules(), pfresult->tx, flags, pfresult->j)); + pfresult.emplace(preflight( + app, view.rules(), pfresult->tx.getSTTx(), flags, pfresult->j)); } auto pcresult = preclaim(*pfresult, app, view); @@ -751,10 +751,7 @@ TxQ::apply( // See if the transaction is valid, properly formed, // etc. before doing potentially expensive queue // replace and multi-transaction operations. - bool const isDelegated = view.rules().enabled(featureAccountPermission) && - tx->isFieldPresent(sfOnBehalfOf); - STTxWr txWr(*tx, isDelegated); - auto const pfresult = preflight(app, view.rules(), txWr, flags, j); + auto const pfresult = preflight(app, view.rules(), *tx, flags, j); if (pfresult.ter != tesSUCCESS) return {pfresult.ter, false}; diff --git a/src/xrpld/app/tx/apply.h b/src/xrpld/app/tx/apply.h index 73093e8db30..00faa8609c8 100644 --- a/src/xrpld/app/tx/apply.h +++ b/src/xrpld/app/tx/apply.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/xrpld/app/tx/applySteps.h b/src/xrpld/app/tx/applySteps.h index 07c2918e73a..b1a7badefaa 100644 --- a/src/xrpld/app/tx/applySteps.h +++ b/src/xrpld/app/tx/applySteps.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace ripple { @@ -153,7 +153,7 @@ struct PreflightResult { public: /// From the input - the transaction - STTxWr const& tx; + STTxDelegated const tx; /// From the input - the rules Rules const rules; /// Consequences of the transaction @@ -197,7 +197,7 @@ struct PreclaimResult /// From the input - the ledger view ReadView const& view; /// From the input - the transaction - STTxWr const& tx; + STTxDelegated const tx; /// From the input - the flags ApplyFlags const flags; /// From the input - the journal @@ -254,7 +254,7 @@ PreflightResult preflight( Application& app, Rules const& rules, - STTxWr const& tx, + STTx const& tx, ApplyFlags flags, beast::Journal j); diff --git a/src/xrpld/app/tx/detail/AMMWithdraw.cpp b/src/xrpld/app/tx/detail/AMMWithdraw.cpp index e7f065eb612..86003c55c4e 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -470,7 +470,7 @@ AMMWithdraw::withdraw( lpTokensWithdraw, tfee, FreezeHandling::fhZERO_IF_FROZEN, - isWithdrawAll(ctx_.tx.getTx()), + isWithdrawAll(ctx_.tx.getSTTx()), mPriorBalance, j_); return {ter, newLPTokenBalance}; @@ -707,7 +707,7 @@ AMMWithdraw::equalWithdrawTokens( lpTokensWithdraw, tfee, FreezeHandling::fhZERO_IF_FROZEN, - isWithdrawAll(ctx_.tx.getTx()), + isWithdrawAll(ctx_.tx.getSTTx()), mPriorBalance, ctx_.journal); return {ter, newLPTokenBalance}; diff --git a/src/xrpld/app/tx/detail/AccountPermissionSet.cpp b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp index 33bb0f533f7..bdfc73f525c 100644 --- a/src/xrpld/app/tx/detail/AccountPermissionSet.cpp +++ b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp @@ -60,13 +60,10 @@ AccountPermissionSet::preflight(PreflightContext const& ctx) TER AccountPermissionSet::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.view.read(keylet::account(ctx.tx[sfAccount])); - if (!account) + if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount]))) return terNO_ACCOUNT; // LCOV_EXCL_LINE - auto const authAccount = - ctx.view.read(keylet::account(ctx.tx[sfAuthorize])); - if (!authAccount) + if (!ctx.view.exists(keylet::account(ctx.tx[sfAuthorize]))) return terNO_ACCOUNT; return tesSUCCESS; @@ -86,6 +83,13 @@ AccountPermissionSet::doApply() if (sle) { auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); + if (permissions.empty()) + { + // if permissions array is empty, delete the ledger object. + ctx_.view().erase(sle); + return tesSUCCESS; + } + sle->setFieldArray(sfPermissions, permissions); ctx_.view().update(sle); return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/ApplyContext.cpp b/src/xrpld/app/tx/detail/ApplyContext.cpp index cc91ac457aa..f13d71b0f0e 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.cpp +++ b/src/xrpld/app/tx/detail/ApplyContext.cpp @@ -31,14 +31,14 @@ namespace ripple { ApplyContext::ApplyContext( Application& app_, OpenView& base, - STTxWr const& tx_, + STTx const& tx_, TER preclaimResult_, XRPAmount baseFee_, ApplyFlags flags, std::unordered_set const permissions, beast::Journal journal_) : app(app_) - , tx(tx_) + , tx(STTxDelegated(tx_, tx_.isFieldPresent(sfOnBehalfOf))) , preclaimResult(preclaimResult_) , baseFee(baseFee_) , permissions(std::move(permissions)) @@ -58,7 +58,7 @@ ApplyContext::discard() void ApplyContext::apply(TER ter) { - view_->apply(base_, tx.getTx(), ter, journal); + view_->apply(base_, tx.getSTTx(), ter, journal); } std::size_t diff --git a/src/xrpld/app/tx/detail/ApplyContext.h b/src/xrpld/app/tx/detail/ApplyContext.h index 2f8a287acf7..ff891491bb5 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.h +++ b/src/xrpld/app/tx/detail/ApplyContext.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -39,7 +39,7 @@ class ApplyContext explicit ApplyContext( Application& app, OpenView& base, - STTxWr const& tx, + STTx const& tx, TER preclaimResult, XRPAmount baseFee, ApplyFlags flags, @@ -47,7 +47,7 @@ class ApplyContext beast::Journal = beast::Journal{beast::Journal::getNullSink()}); Application& app; - STTxWr const& tx; + STTxDelegated const tx; TER const preclaimResult; XRPAmount const baseFee; std::unordered_set permissions; diff --git a/src/xrpld/app/tx/detail/CashCheck.cpp b/src/xrpld/app/tx/detail/CashCheck.cpp index cbf30803abd..c471cfca14d 100644 --- a/src/xrpld/app/tx/detail/CashCheck.cpp +++ b/src/xrpld/app/tx/detail/CashCheck.cpp @@ -138,7 +138,7 @@ CashCheck::preclaim(PreclaimContext const& ctx) STAmount const value{[](STTx const& tx) { auto const optAmount = tx[~sfAmount]; return optAmount ? *optAmount : tx[sfDeliverMin]; - }(ctx.tx.getTx())}; + }(ctx.tx.getSTTx())}; STAmount const sendMax = sleCheck->at(sfSendMax); Currency const currency{value.getCurrency()}; diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index e5a5eaf2efd..992fb05ade2 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -359,9 +359,9 @@ Change::applyFee() }; if (view().rules().enabled(featureXRPFees)) { - set(feeObject, ctx_.tx.getTx(), sfBaseFeeDrops); - set(feeObject, ctx_.tx.getTx(), sfReserveBaseDrops); - set(feeObject, ctx_.tx.getTx(), sfReserveIncrementDrops); + set(feeObject, ctx_.tx.getSTTx(), sfBaseFeeDrops); + set(feeObject, ctx_.tx.getSTTx(), sfReserveBaseDrops); + set(feeObject, ctx_.tx.getSTTx(), sfReserveIncrementDrops); // Ensure the old fields are removed feeObject->makeFieldAbsent(sfBaseFee); feeObject->makeFieldAbsent(sfReferenceFeeUnits); @@ -370,10 +370,10 @@ Change::applyFee() } else { - set(feeObject, ctx_.tx.getTx(), sfBaseFee); - set(feeObject, ctx_.tx.getTx(), sfReferenceFeeUnits); - set(feeObject, ctx_.tx.getTx(), sfReserveBase); - set(feeObject, ctx_.tx.getTx(), sfReserveIncrement); + set(feeObject, ctx_.tx.getSTTx(), sfBaseFee); + set(feeObject, ctx_.tx.getSTTx(), sfReferenceFeeUnits); + set(feeObject, ctx_.tx.getSTTx(), sfReserveBase); + set(feeObject, ctx_.tx.getSTTx(), sfReserveIncrement); } view().update(feeObject); diff --git a/src/xrpld/app/tx/detail/CreateOffer.cpp b/src/xrpld/app/tx/detail/CreateOffer.cpp index 58a511e2bca..c330ee88584 100644 --- a/src/xrpld/app/tx/detail/CreateOffer.cpp +++ b/src/xrpld/app/tx/detail/CreateOffer.cpp @@ -36,7 +36,8 @@ CreateOffer::makeTxConsequences(PreflightContext const& ctx) return amount.native() ? amount.xrp() : beast::zero; }; - return TxConsequences{ctx.tx.getTx(), calculateMaxXRPSpend(ctx.tx.getTx())}; + return TxConsequences{ + ctx.tx.getSTTx(), calculateMaxXRPSpend(ctx.tx.getSTTx())}; } NotTEC diff --git a/src/xrpld/app/tx/detail/CreateTicket.cpp b/src/xrpld/app/tx/detail/CreateTicket.cpp index ab6b277d7ce..b808b2115ad 100644 --- a/src/xrpld/app/tx/detail/CreateTicket.cpp +++ b/src/xrpld/app/tx/detail/CreateTicket.cpp @@ -31,7 +31,7 @@ TxConsequences CreateTicket::makeTxConsequences(PreflightContext const& ctx) { // Create TxConsequences identifying the number of sequences consumed. - return TxConsequences{ctx.tx.getTx(), ctx.tx[sfTicketCount]}; + return TxConsequences{ctx.tx.getSTTx(), ctx.tx[sfTicketCount]}; } NotTEC diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 2f942e352c9..fc4b1c1d666 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -94,7 +94,7 @@ after(NetClock::time_point now, std::uint32_t mark) TxConsequences EscrowCreate::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx.getTx(), ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx.getSTTx(), ctx.tx[sfAmount].xrp()}; } NotTEC diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index 1d7bbca3444..4ad3a402591 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -43,7 +43,7 @@ TransactionFeeCheck::visitEntry( bool TransactionFeeCheck::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const, XRPAmount const fee, ReadView const&, @@ -137,7 +137,7 @@ XRPNotCreated::visitEntry( bool XRPNotCreated::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const, XRPAmount const fee, ReadView const&, @@ -198,7 +198,7 @@ XRPBalanceChecks::visitEntry( bool XRPBalanceChecks::finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -242,7 +242,7 @@ NoBadOffers::visitEntry( bool NoBadOffers::finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -287,7 +287,7 @@ NoZeroEscrow::visitEntry( bool NoZeroEscrow::finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -316,7 +316,7 @@ AccountRootsNotDeleted::visitEntry( bool AccountRootsNotDeleted::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const, ReadView const&, @@ -371,7 +371,7 @@ AccountRootsDeletedClean::visitEntry( bool AccountRootsDeletedClean::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -496,7 +496,7 @@ LedgerEntryTypesMatch::visitEntry( bool LedgerEntryTypesMatch::finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -539,7 +539,7 @@ NoXRPTrustLines::visitEntry( bool NoXRPTrustLines::finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -569,7 +569,7 @@ ValidNewAccountRoot::visitEntry( bool ValidNewAccountRoot::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -720,7 +720,7 @@ ValidNFTokenPage::visitEntry( bool ValidNFTokenPage::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -796,7 +796,7 @@ NFTokenCountTracking::visitEntry( bool NFTokenCountTracking::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -897,7 +897,7 @@ ValidClawback::visitEntry( bool ValidClawback::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const, ReadView const& view, @@ -985,7 +985,7 @@ ValidMPTIssuance::visitEntry( bool ValidMPTIssuance::finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const result, XRPAmount const _fee, ReadView const& _view, diff --git a/src/xrpld/app/tx/detail/InvariantCheck.h b/src/xrpld/app/tx/detail/InvariantCheck.h index 563b6c5aaad..5230d43d012 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.h +++ b/src/xrpld/app/tx/detail/InvariantCheck.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -76,7 +76,7 @@ class InvariantChecker_PROTOTYPE */ bool finalize( - STTxWr const& tx, + STTxDelegated const& tx, TER const tec, XRPAmount const fee, ReadView const& view, @@ -101,7 +101,7 @@ class TransactionFeeCheck bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -129,7 +129,7 @@ class XRPNotCreated bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -157,7 +157,7 @@ class AccountRootsNotDeleted bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -187,7 +187,7 @@ class AccountRootsDeletedClean bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -214,7 +214,7 @@ class XRPBalanceChecks bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -239,7 +239,7 @@ class LedgerEntryTypesMatch bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -265,7 +265,7 @@ class NoXRPTrustLines bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -292,7 +292,7 @@ class NoBadOffers bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -316,7 +316,7 @@ class NoZeroEscrow bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -342,7 +342,7 @@ class ValidNewAccountRoot bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -379,7 +379,7 @@ class ValidNFTokenPage bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -415,7 +415,7 @@ class NFTokenCountTracking bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -444,7 +444,7 @@ class ValidClawback bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, @@ -468,7 +468,7 @@ class ValidMPTIssuance bool finalize( - STTxWr const&, + STTxDelegated const&, TER const, XRPAmount const, ReadView const&, diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp index 93bd66c329d..2f561dca3f6 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp @@ -102,21 +102,21 @@ MPTokenIssuanceSet::doApply() std::uint32_t const flagsIn = sle->getFieldU32(sfFlags); std::uint32_t flagsOut = flagsIn; - bool bLock = txFlags & tfMPTLock; - bool bUnlock = txFlags & tfMPTUnlock; + bool lock = txFlags & tfMPTLock; + bool unlock = txFlags & tfMPTUnlock; if (ctx_.tx.isDelegated() && !ctx_.permissions.empty()) { // if permissions is not empty, granular delegation is happening. - if (bLock && !ctx_.permissions.contains(MPTokenIssuanceLock)) + if (lock && !ctx_.permissions.contains(MPTokenIssuanceLock)) return terNO_AUTH; - if (bUnlock && !ctx_.permissions.contains(MPTokenIssuanceUnlock)) + if (unlock && !ctx_.permissions.contains(MPTokenIssuanceUnlock)) return terNO_AUTH; } - if (bLock) + if (lock) flagsOut |= lsfMPTLocked; - else if (bUnlock) + else if (unlock) flagsOut &= ~lsfMPTLocked; if (flagsIn != flagsOut) diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index 5a9e87f2b8b..fb2c61e64e2 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -168,7 +168,7 @@ closeChannel( TxConsequences PayChanCreate::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx.getTx(), ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx.getSTTx(), ctx.tx[sfAmount].xrp()}; } NotTEC @@ -310,7 +310,7 @@ PayChanCreate::doApply() TxConsequences PayChanFund::makeTxConsequences(PreflightContext const& ctx) { - return TxConsequences{ctx.tx.getTx(), ctx.tx[sfAmount].xrp()}; + return TxConsequences{ctx.tx.getSTTx(), ctx.tx[sfAmount].xrp()}; } NotTEC diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index dfd4e918cb3..80eefd3d9f9 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -42,7 +42,8 @@ Payment::makeTxConsequences(PreflightContext const& ctx) return maxAmount.native() ? maxAmount.xrp() : beast::zero; }; - return TxConsequences{ctx.tx.getTx(), calculateMaxXRPSpend(ctx.tx.getTx())}; + return TxConsequences{ + ctx.tx.getSTTx(), calculateMaxXRPSpend(ctx.tx.getSTTx())}; } STAmount diff --git a/src/xrpld/app/tx/detail/SetAccount.cpp b/src/xrpld/app/tx/detail/SetAccount.cpp index 5acf0221c20..f69c4b17222 100644 --- a/src/xrpld/app/tx/detail/SetAccount.cpp +++ b/src/xrpld/app/tx/detail/SetAccount.cpp @@ -53,7 +53,7 @@ SetAccount::makeTxConsequences(PreflightContext const& ctx) }; return TxConsequences{ - ctx.tx.getTx(), getTxConsequencesCategory(ctx.tx.getTx())}; + ctx.tx.getSTTx(), getTxConsequencesCategory(ctx.tx.getSTTx())}; } NotTEC @@ -263,7 +263,7 @@ SetAccount::doApply() std::uint32_t const uFlagsIn = sle->getFieldU32(sfFlags); std::uint32_t uFlagsOut = uFlagsIn; - STTx const& tx{ctx_.tx.getTx()}; + STTx const& tx{ctx_.tx.getSTTx()}; std::uint32_t const uSetFlag{tx.getFieldU32(sfSetFlag)}; std::uint32_t const uClearFlag{tx.getFieldU32(sfClearFlag)}; diff --git a/src/xrpld/app/tx/detail/SetSignerList.cpp b/src/xrpld/app/tx/detail/SetSignerList.cpp index 7916602c8b7..79cb9cb8887 100644 --- a/src/xrpld/app/tx/detail/SetSignerList.cpp +++ b/src/xrpld/app/tx/detail/SetSignerList.cpp @@ -81,7 +81,7 @@ SetSignerList::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; - auto const result = determineOperation(ctx.tx.getTx(), ctx.flags, ctx.j); + auto const result = determineOperation(ctx.tx.getSTTx(), ctx.flags, ctx.j); if (std::get<0>(result) != tesSUCCESS) return std::get<0>(result); @@ -136,7 +136,7 @@ void SetSignerList::preCompute() { // Get the quorum and operation info. - auto result = determineOperation(ctx_.tx.getTx(), view().flags(), j_); + auto result = determineOperation(ctx_.tx.getSTTx(), view().flags(), j_); ASSERT( std::get<0>(result) == tesSUCCESS, "ripple::SetSignerList::preCompute : result is tesSUCCESS"); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 404242adbae..9c37978fccc 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -41,7 +41,7 @@ namespace ripple { NotTEC preflight0(PreflightContext const& ctx) { - if (!isPseudoTx(ctx.tx.getTx()) || ctx.tx.isFieldPresent(sfNetworkID)) + if (!isPseudoTx(ctx.tx.getSTTx()) || ctx.tx.isFieldPresent(sfNetworkID)) { uint32_t nodeNID = ctx.app.config().NETWORK_ID; std::optional txNID = ctx.tx[~sfNetworkID]; @@ -97,7 +97,7 @@ preflight1(PreflightContext const& ctx) if (!isTesSuccess(ret)) return ret; - auto const id = ctx.tx.getTx().getAccountID(sfAccount); + auto const id = ctx.tx.getSTTx().getAccountID(sfAccount); if (id == beast::zero) { JLOG(ctx.j.warn()) << "preflight1: bad account id"; @@ -130,10 +130,6 @@ preflight1(PreflightContext const& ctx) ctx.tx.isFieldPresent(sfAccountTxnID)) return temINVALID; - if (!ctx.rules.enabled(featureAccountPermission) && - ctx.tx.isFieldPresent(sfOnBehalfOf)) - return temDISABLED; - return tesSUCCESS; } @@ -142,7 +138,7 @@ NotTEC preflight2(PreflightContext const& ctx) { auto const sigValid = checkValidity( - ctx.app.getHashRouter(), ctx.tx.getTx(), ctx.rules, ctx.app.config()); + ctx.app.getHashRouter(), ctx.tx.getSTTx(), ctx.rules, ctx.app.config()); if (sigValid.first == Validity::SigBad) { JLOG(ctx.j.debug()) << "preflight2: bad signature. " << sigValid.second; @@ -155,11 +151,15 @@ preflight2(PreflightContext const& ctx) PreflightContext::PreflightContext( Application& app_, - STTxWr const& tx_, + STTx const& tx_, Rules const& rules_, ApplyFlags flags_, beast::Journal j_) - : app(app_), tx(tx_), rules(rules_), flags(flags_), j(j_) + : app(app_) + , tx(STTxDelegated(tx_, tx_.isFieldPresent(sfOnBehalfOf))) + , rules(rules_) + , flags(flags_) + , j(j_) { } @@ -194,14 +194,15 @@ Transactor::checkPermissions( STTx const& tx, std::unordered_set& permissions) { - if (!view.read(keylet::account(tx[sfOnBehalfOf]))) + if (!tx.isFieldPresent(sfOnBehalfOf) || + !view.exists(keylet::account(tx[sfOnBehalfOf]))) return terNO_ACCOUNT; auto const accountPermissionKey = keylet::accountPermission(tx[sfOnBehalfOf], tx[sfAccount]); auto const sle = view.read(accountPermissionKey); if (!sle) - return temMALFORMED; // todo: change error code + return tecNO_PERMISSION; auto const permissionArray = sle->getFieldArray(sfPermissions); auto const transactionType = tx.getTxnType(); @@ -271,7 +272,7 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (feePaid == beast::zero) return tesSUCCESS; - auto const id = ctx.tx.getTx().getAccountID(sfAccount); + auto const id = ctx.tx.getSTTx().getAccountID(sfAccount); auto const sle = ctx.view.read(keylet::account(id)); if (!sle) return terNO_ACCOUNT; @@ -300,33 +301,20 @@ TER Transactor::payFee() { auto const feePaid = ctx_.tx[sfFee].xrp(); - if (ctx_.tx.isDelegated()) - { - // if the transaction is being delegated to another account, - // the sender account will pay the fee. - auto const sender = ctx_.tx.getTx().getAccountID(sfAccount); - auto const sleSender = view().peek(keylet::account(sender)); - if (!sleSender) - return tefINTERNAL; - - auto senderBalance = STAmount{(*sleSender)[sfBalance]}.xrp(); - senderBalance -= feePaid; - sleSender->setFieldAmount(sfBalance, senderBalance); - view().update(sleSender); - return tesSUCCESS; - } - - auto const sle = view().peek(keylet::account(account_)); - if (!sle) + // whether the transaction is being delegated to another account or not, + // the sender account will pay the fee. + auto const sender = ctx_.tx.getSTTx().getAccountID(sfAccount); + auto const sleSender = view().peek(keylet::account(sender)); + if (!sleSender) return tefINTERNAL; - // Deduct the fee, so it's not available during the transaction. - // Will only write the account back if the transaction succeeds. - - mSourceBalance -= feePaid; - sle->setFieldAmount(sfBalance, mSourceBalance); + auto senderBalance = STAmount{(*sleSender)[sfBalance]}.xrp(); + senderBalance -= feePaid; + sleSender->setFieldAmount(sfBalance, senderBalance); + view().update(sleSender); - // VFALCO Should we call view().rawDestroyXRP() here as well? + if (!ctx_.tx.isDelegated()) + mSourceBalance -= feePaid; return tesSUCCESS; } @@ -406,7 +394,7 @@ Transactor::checkSeqProxy( NotTEC Transactor::checkPriorTxAndLastLedger(PreclaimContext const& ctx) { - auto const id = ctx.tx.getTx().getAccountID(sfAccount); + auto const id = ctx.tx.getSTTx().getAccountID(sfAccount); auto const sle = ctx.view.read(keylet::account(id)); @@ -576,7 +564,7 @@ Transactor::checkSingleSign(PreclaimContext const& ctx) // Look up the account. auto const idSigner = calcAccountID(PublicKey(makeSlice(pkSigner))); - auto const idAccount = ctx.tx.getTx().getAccountID(sfAccount); + auto const idAccount = ctx.tx.getSTTx().getAccountID(sfAccount); auto const sleAccount = ctx.view.read(keylet::account(idAccount)); if (!sleAccount) return terNO_ACCOUNT; @@ -639,7 +627,7 @@ Transactor::checkSingleSign(PreclaimContext const& ctx) NotTEC Transactor::checkMultiSign(PreclaimContext const& ctx) { - auto const id = ctx.tx.getTx().getAccountID(sfAccount); + auto const id = ctx.tx.getSTTx().getAccountID(sfAccount); // Get mTxnAccountID's SignerList and Quorum. std::shared_ptr sleAccountSigners = ctx.view.read(keylet::signers(id)); @@ -947,7 +935,7 @@ Transactor::operator()() SerialIter sit(ser.slice()); STTx s2(sit); - if (!s2.isEquivalent(ctx_.tx.getTx())) + if (!s2.isEquivalent(ctx_.tx.getSTTx())) { JLOG(j_.fatal()) << "Transaction serdes mismatch"; JLOG(j_.info()) << to_string(ctx_.tx.getJson(JsonOptions::none)); diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index 23368da3f47..42fb5a0356f 100644 --- a/src/xrpld/app/tx/detail/Transactor.h +++ b/src/xrpld/app/tx/detail/Transactor.h @@ -33,14 +33,14 @@ struct PreflightContext { public: Application& app; - STTxWr const& tx; + STTxDelegated const tx; Rules const rules; ApplyFlags flags; beast::Journal const j; PreflightContext( Application& app_, - STTxWr const& tx_, + STTx const& tx_, Rules const& rules_, ApplyFlags flags_, beast::Journal j_); @@ -56,7 +56,7 @@ struct PreclaimContext Application& app; ReadView const& view; TER preflightResult; - STTxWr const& tx; + STTxDelegated const tx; ApplyFlags flags; beast::Journal const j; @@ -64,13 +64,13 @@ struct PreclaimContext Application& app_, ReadView const& view_, TER preflightResult_, - STTxWr const& tx_, + STTx const& tx_, ApplyFlags flags_, beast::Journal j_ = beast::Journal{beast::Journal::getNullSink()}) : app(app_) , view(view_) , preflightResult(preflightResult_) - , tx(tx_) + , tx(STTxDelegated(tx_, tx_.isFieldPresent(sfOnBehalfOf))) , flags(flags_) , j(j_) { diff --git a/src/xrpld/app/tx/detail/XChainBridge.cpp b/src/xrpld/app/tx/detail/XChainBridge.cpp index 1d6e05f4af9..600d2e4ab4e 100644 --- a/src/xrpld/app/tx/detail/XChainBridge.cpp +++ b/src/xrpld/app/tx/detail/XChainBridge.cpp @@ -1225,7 +1225,7 @@ attestationPreflight(PreflightContext const& ctx) if (!publicKeyType(ctx.tx[sfPublicKey])) return temMALFORMED; - auto const att = toClaim(ctx.tx.getTx()); + auto const att = toClaim(ctx.tx.getSTTx()); if (!att) return temMALFORMED; @@ -1249,7 +1249,7 @@ template TER attestationPreclaim(PreclaimContext const& ctx) { - auto const att = toClaim(ctx.tx.getTx()); + auto const att = toClaim(ctx.tx.getSTTx()); if (!att) return tecINTERNAL; // checked in preflight @@ -1279,7 +1279,7 @@ template TER attestationDoApply(ApplyContext& ctx) { - auto const att = toClaim(ctx.tx.getTx()); + auto const att = toClaim(ctx.tx.getSTTx()); if (!att) // Should already be checked in preflight return tecINTERNAL; @@ -1903,7 +1903,7 @@ XChainCommit::makeTxConsequences(PreflightContext const& ctx) return XRPAmount{beast::zero}; }(); - return TxConsequences{ctx.tx.getTx(), maxSpend}; + return TxConsequences{ctx.tx.getSTTx(), maxSpend}; } NotTEC diff --git a/src/xrpld/app/tx/detail/apply.cpp b/src/xrpld/app/tx/detail/apply.cpp index bad97f9bc85..103ec041074 100644 --- a/src/xrpld/app/tx/detail/apply.cpp +++ b/src/xrpld/app/tx/detail/apply.cpp @@ -116,11 +116,7 @@ apply( STAmountSO stAmountSO{view.rules().enabled(fixSTAmountCanonicalize)}; NumberSO stNumberSO{view.rules().enabled(fixUniversalNumber)}; - bool const isDelegated = view.rules().enabled(featureAccountPermission) && - tx.isFieldPresent(sfOnBehalfOf); - STTxWr txWr(tx, isDelegated); - - auto pfresult = preflight(app, view.rules(), txWr, flags, j); + auto pfresult = preflight(app, view.rules(), tx, flags, j); auto pcresult = preclaim(pfresult, app, view); return doApply(pcresult, app, view); } diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 2445de915cd..fd9e092b3e6 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -118,7 +118,7 @@ requires(T::ConsequencesFactory == Transactor::Normal) TxConsequences consequences_helper(PreflightContext const& ctx) { - return TxConsequences(ctx.tx.getTx()); + return TxConsequences(ctx.tx.getSTTx()); }; // For Transactor::Blocker @@ -127,7 +127,7 @@ requires(T::ConsequencesFactory == Transactor::Blocker) TxConsequences consequences_helper(PreflightContext const& ctx) { - return TxConsequences(ctx.tx.getTx(), TxConsequences::blocker); + return TxConsequences(ctx.tx.getSTTx(), TxConsequences::blocker); }; // For Transactor::Custom @@ -179,7 +179,8 @@ invoke_preclaim(PreclaimContext const& ctx) if (id != beast::zero) { - TER result = T::checkSeqProxy(ctx.view, ctx.tx.getTx(), ctx.j); + TER result = + T::checkSeqProxy(ctx.view, ctx.tx.getSTTx(), ctx.j); if (result != tesSUCCESS) return std::make_pair(result, permissions); @@ -190,7 +191,7 @@ invoke_preclaim(PreclaimContext const& ctx) return std::make_pair(result, permissions); result = T::checkFee( - ctx, calculateBaseFee(ctx.view, ctx.tx.getTx())); + ctx, calculateBaseFee(ctx.view, ctx.tx.getSTTx())); if (result != tesSUCCESS) return std::make_pair(result, permissions); @@ -204,7 +205,7 @@ invoke_preclaim(PreclaimContext const& ctx) // if this is a delegated transaction, check if the account // has authorization. result = T::checkPermissions( - ctx.view, ctx.tx.getTx(), permissions); + ctx.view, ctx.tx.getSTTx(), permissions); if (result != tesSUCCESS) return std::make_pair(result, permissions); @@ -305,7 +306,7 @@ PreflightResult preflight( Application& app, Rules const& rules, - STTxWr const& tx, + STTx const& tx, ApplyFlags flags, beast::Journal j) { @@ -317,7 +318,7 @@ preflight( catch (std::exception const& e) { JLOG(j.fatal()) << "apply: " << e.what(); - return {pfctx, {tefEXCEPTION, TxConsequences{tx.getTx()}}}; + return {pfctx, {tefEXCEPTION, TxConsequences{tx}}}; } } @@ -333,14 +334,14 @@ preclaim( auto secondFlight = preflight( app, view.rules(), - preflightResult.tx, + preflightResult.tx.getSTTx(), preflightResult.flags, preflightResult.j); ctx.emplace( app, view, secondFlight.ter, - secondFlight.tx, + secondFlight.tx.getSTTx(), secondFlight.flags, secondFlight.j); } @@ -350,7 +351,7 @@ preclaim( app, view, preflightResult.ter, - preflightResult.tx, + preflightResult.tx.getSTTx(), preflightResult.flags, preflightResult.j); } @@ -401,9 +402,9 @@ doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view) ApplyContext ctx( app, view, - preclaimResult.tx, + preclaimResult.tx.getSTTx(), preclaimResult.ter, - calculateBaseFee(view, preclaimResult.tx.getTx()), + calculateBaseFee(view, preclaimResult.tx.getSTTx()), preclaimResult.flags, preclaimResult.permissions, preclaimResult.j); diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index d09555d70e0..00ca5a0f680 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -869,7 +869,7 @@ doLedgerEntry(RPC::JsonContext& context) {jss::index, parseIndex, ltANY}, {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, // TODO: add amendments - {jss::amm, parseAccountPermission, ltACCOUNT_PERMISSION}, + {jss::account_permission, parseAccountPermission, ltACCOUNT_PERMISSION}, {jss::amm, parseAMM, ltAMM}, {jss::bridge, parseBridge, ltBRIDGE}, {jss::check, parseCheck, ltCHECK},