diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 141533e1e52..32c3bfae4c9 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -94,7 +94,7 @@ XRPL_FIX (1513, Supported::yes, VoteBehavior::DefaultYe XRPL_FEATURE(FlowCross, Supported::yes, VoteBehavior::DefaultYes) XRPL_FEATURE(Flow, Supported::yes, VoteBehavior::DefaultYes) XRPL_FEATURE(OwnerPaysFee, Supported::no, VoteBehavior::DefaultNo) -XRPL_FEATURE(AccountPermission, Supported::yes, VoteBehavior::DefaultYes) +XRPL_FEATURE(AccountPermission, Supported::yes, VoteBehavior::DefaultNo) // The following amendments are obsolete, but must remain supported // because they could potentially get enabled. diff --git a/src/test/app/AMM_test.cpp b/src/test/app/AMM_test.cpp index ceddc019504..3c59603ea6e 100644 --- a/src/test/app/AMM_test.cpp +++ b/src/test/app/AMM_test.cpp @@ -3234,6 +3234,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); @@ -3249,6 +3250,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); @@ -3264,6 +3266,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 a84ac63da9d..010caf2245d 100644 --- a/src/test/app/NFTokenBurn_test.cpp +++ b/src/test/app/NFTokenBurn_test.cpp @@ -821,6 +821,8 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite tesSUCCESS, env.current()->fees().base, tapNONE, + false, + AccountID(0), jlog}; // Verify that the last page is present and contains one NFT. @@ -865,6 +867,8 @@ class NFTokenBurnBaseUtil_test : public beast::unit_test::suite tesSUCCESS, env.current()->fees().base, tapNONE, + false, + AccountID(0), 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..f54d0954c1c 100644 --- a/src/test/ledger/Invariants_test.cpp +++ b/src/test/ledger/Invariants_test.cpp @@ -98,6 +98,8 @@ class Invariants_test : public beast::unit_test::suite tesSUCCESS, env.current()->fees().base, tapNONE, + false, + AccountID(0), 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..ef4fbd4649c 100644 --- a/src/xrpld/app/tx/applySteps.h +++ b/src/xrpld/app/tx/applySteps.h @@ -160,7 +160,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 +171,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,6 +208,10 @@ struct PreclaimResult ApplyFlags const flags; /// From the input - the journal beast::Journal const j; + /// If the transaction is a delegated transaction + bool const isDelegated; // todo: may remove + /// the account that the transaction is operated on + AccountID const account; /// Intermediate transaction result TER const ter; @@ -215,6 +226,8 @@ struct PreclaimResult , tx(ctx_.tx) , flags(ctx_.flags) , j(ctx_.j) + , isDelegated(ctx_.isDelegated) + , account(ctx_.account) , ter(ter_) , 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/AMMCreate.cpp b/src/xrpld/app/tx/detail/AMMCreate.cpp index fa0c7fefaef..1c44ae71424 100644 --- a/src/xrpld/app/tx/detail/AMMCreate.cpp +++ b/src/xrpld/app/tx/detail/AMMCreate.cpp @@ -88,50 +88,7 @@ AMMCreate::calculateBaseFee(ReadView const& view, STTx const& tx) TER AMMCreate::preclaim(PreclaimContext const& ctx) { - // if (ctx.rules.enabled(featureAccountPermission)) - // { - auto const onBehalfOfAccount = - ctx.view.read(keylet::account(ctx.tx[sfOnBehalfOf])); - if (!onBehalfOfAccount) - return terNO_ACCOUNT; - - auto const accountPermissionKey = - keylet::accountPermission(ctx.tx[sfAccount], ctx.tx[sfOnBehalfOf]); - auto const sle = ctx.view.read(accountPermissionKey); - if (!sle) - return temMALFORMED; // todo: change error code - - auto const permissions = sle->getFieldArray(sfPermissions); - bool grantedPermission = false; - for (auto const& permissionObj : permissions) - { - // auto permissionObj = dynamic_cast(&permission); - auto const permissionValue = - permissionObj.getFieldU32(sfPermissionValue); - std::cout << "perimssionValue = " << permissionValue << std::endl; - auto const transactionType = ctx.tx.getTxnType(); - std::cout << "transactionType = " << transactionType << std::endl; - if (permissionValue == transactionType + 1) - grantedPermission = true; - // for (auto const& permissionElement : *permissionObj) - // { - // auto permissionStr = permissionElement.getText(); - // auto const& name = permissionElement.getFName(); - - // auto const permissionValue = - // permissionElement.getFieldU32(sfPermissionValue); auto const - // transactionType = ctx.tx.getTxnType(); if (permissionValue == - // transactionType + 1) - // grantedPermission = true; - // } - } - - if (!grantedPermission) - return terNO_AUTH; - - //} - - 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 9bbf5b4a60a..0bbf08ca272 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 51b512aba0a..95f79bc3855 100644 --- a/src/xrpld/app/tx/detail/AMMWithdraw.cpp +++ b/src/xrpld/app/tx/detail/AMMWithdraw.cpp @@ -171,7 +171,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])); @@ -251,8 +251,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()); @@ -306,7 +305,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 index bacb2c96878..1d31b9f1e57 100644 --- a/src/xrpld/app/tx/detail/AccountPermissionSet.cpp +++ b/src/xrpld/app/tx/detail/AccountPermissionSet.cpp @@ -65,7 +65,7 @@ AccountPermissionSet::preflight(PreflightContext const& ctx) TER AccountPermissionSet::preclaim(PreclaimContext const& ctx) { - auto const account = ctx.view.read(keylet::account(ctx.tx[sfAccount])); + auto const account = ctx.view.read(keylet::account(ctx.account)); 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 969af7960eb..ea182e30553 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.cpp +++ b/src/xrpld/app/tx/detail/ApplyContext.cpp @@ -35,11 +35,15 @@ ApplyContext::ApplyContext( TER preclaimResult_, XRPAmount baseFee_, ApplyFlags flags, + bool isDelegated, // todo: may remove + AccountID const account, beast::Journal journal_) : app(app_) , tx(tx_) , preclaimResult(preclaimResult_) , baseFee(baseFee_) + , isDelegated(isDelegated) + , account(account) , 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..5d75bbdd215 100644 --- a/src/xrpld/app/tx/detail/ApplyContext.h +++ b/src/xrpld/app/tx/detail/ApplyContext.h @@ -42,12 +42,16 @@ class ApplyContext TER preclaimResult, XRPAmount baseFee, ApplyFlags flags, + bool isDelegated, + AccountID const account, beast::Journal = beast::Journal{beast::Journal::getNullSink()}); Application& app; STTx const& tx; TER const preclaimResult; XRPAmount const baseFee; + bool isDelegated; + AccountID const account; 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/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/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index 15d76526094..2101510215f 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -39,7 +39,7 @@ Clawback::preflight(PreflightContext const& ctx) if (ctx.tx.getFlags() & tfClawbackMask) return temINVALID_FLAG; - 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 @@ -54,7 +54,7 @@ Clawback::preflight(PreflightContext const& ctx) TER Clawback::preclaim(PreclaimContext const& ctx) { - AccountID const issuer = ctx.tx[sfAccount]; + AccountID const issuer = ctx.account; STAmount const clawAmount = ctx.tx[sfAmount]; AccountID const& holder = clawAmount.getIssuer(); 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/DeleteAccount.cpp b/src/xrpld/app/tx/detail/DeleteAccount.cpp index 50e59e6537d..2fa8febccb8 100644 --- a/src/xrpld/app/tx/detail/DeleteAccount.cpp +++ b/src/xrpld/app/tx/detail/DeleteAccount.cpp @@ -48,7 +48,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; @@ -207,7 +207,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/DepositPreauth.cpp b/src/xrpld/app/tx/detail/DepositPreauth.cpp index b60fd3e0eae..b3e5bba2b18 100644 --- a/src/xrpld/app/tx/detail/DepositPreauth.cpp +++ b/src/xrpld/app/tx/detail/DepositPreauth.cpp @@ -67,7 +67,7 @@ DepositPreauth::preflight(PreflightContext const& ctx) } // An account may not preauthorize itself. - if (optAuth && (target == ctx.tx[sfAccount])) + if (optAuth && (target == ctx.account)) { JLOG(j.trace()) << "Malformed transaction: Attempting to DepositPreauth self."; @@ -90,14 +90,14 @@ DepositPreauth::preclaim(PreclaimContext const& ctx) // Verify that the Preauth entry they asked to add is not already // in the ledger. - if (ctx.view.exists(keylet::depositPreauth(ctx.tx[sfAccount], auth))) + if (ctx.view.exists(keylet::depositPreauth(ctx.account, auth))) return tecDUPLICATE; } else { // Verify that the Preauth entry they asked to remove is in the ledger. AccountID const unauth{ctx.tx[sfUnauthorize]}; - if (!ctx.view.exists(keylet::depositPreauth(ctx.tx[sfAccount], unauth))) + if (!ctx.view.exists(keylet::depositPreauth(ctx.account, unauth))) return tecNO_ENTRY; } return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index e34b675998d..a2070ad7fa6 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -205,8 +205,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; @@ -243,10 +242,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,14 +258,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)); 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 d17736c4738..fe599abe929 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -180,7 +180,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])) @@ -192,7 +192,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; @@ -244,8 +244,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; @@ -256,14 +255,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 +275,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; @@ -334,7 +333,6 @@ PayChanFund::doApply() return tecNO_ENTRY; AccountID const src = (*slep)[sfAccount]; - auto const txAccount = ctx_.tx[sfAccount]; auto const expiration = (*slep)[~sfExpiration]; { @@ -347,7 +345,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; @@ -365,7 +363,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; @@ -466,7 +464,6 @@ PayChanClaim::doApply() AccountID const src = (*slep)[sfAccount]; AccountID const dst = (*slep)[sfDestination]; - AccountID const txAccount = ctx_.tx[sfAccount]; auto const curExpiration = (*slep)[~sfExpiration]; { @@ -479,7 +476,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]) @@ -488,7 +485,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]) @@ -513,7 +510,7 @@ 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; // Check whether the destination account requires deposit authorization. @@ -523,9 +520,9 @@ PayChanClaim::doApply() // ways to get a Payment Channel Claim into the account: // 1. If Account == Destination, or // 2. If Account is deposit preauthorized by destination. - if (txAccount != dst) + if (account_ != dst) { - if (!view().exists(keylet::depositPreauth(dst, txAccount))) + if (!view().exists(keylet::depositPreauth(dst, account_))) return tecNO_PERMISSION; } } @@ -540,7 +537,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); @@ -549,7 +546,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/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 3a7fe9cca0d..d94ef83f917 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) diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index 7ea024ee6dc..2503dc46ea6 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -149,15 +149,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) { } @@ -179,6 +180,32 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) return baseFee + (signerCount * baseFee); } +TER +Transactor::checkAuthorization(ReadView const& view, STTx const& tx) +{ + 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(); + if (std::find_if( + permissions.begin(), + permissions.end(), + [&](STObject const& o) -> bool { + return o[sfPermissionValue] == transactionType + 1; + }) == permissions.end()) + return terNO_AUTH; + + return tesSUCCESS; +} + XRPAmount Transactor::minimumFee( Application& app, @@ -246,16 +273,32 @@ 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); + } + else + { + auto const sle = view().peek(keylet::account(account_)); + if (!sle) + return tefINTERNAL; - auto const sle = view().peek(keylet::account(account_)); - if (!sle) - return tefINTERNAL; - - // Deduct the fee, so it's not available during the transaction. - // Will only write the account back if the transaction succeeds. + // 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); + mSourceBalance -= feePaid; + sle->setFieldAmount(sfBalance, mSourceBalance); + } // VFALCO Should we call view().rawDestroyXRP() here as well? diff --git a/src/xrpld/app/tx/detail/Transactor.h b/src/xrpld/app/tx/detail/Transactor.h index c587e5e1994..ea3b78732c6 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,9 @@ class Transactor static XRPAmount calculateBaseFee(ReadView const& view, STTx const& tx); + static TER + checkAuthorization(ReadView const& view, STTx const& tx); + 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 5d83d3db4db..35c196f8a3d 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -191,6 +191,14 @@ invoke_preclaim(PreclaimContext const& ctx) if (result != tesSUCCESS) return result; + + if (ctx.isDelegated) + // if this is a delegated transaction, check if the account + // has authorization. + result = T::checkAuthorization(ctx.view, ctx.tx); + + if (result != tesSUCCESS) + return result; } return T::preclaim(ctx); @@ -289,15 +297,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}}}; } } @@ -322,6 +337,8 @@ preclaim( secondFlight.ter, secondFlight.tx, secondFlight.flags, + secondFlight.isDelegated, + secondFlight.account, secondFlight.j); } else @@ -332,6 +349,8 @@ preclaim( preflightResult.ter, preflightResult.tx, preflightResult.flags, + preflightResult.isDelegated, + preflightResult.account, preflightResult.j); } try @@ -379,6 +398,8 @@ doApply(PreclaimResult const& preclaimResult, Application& app, OpenView& view) preclaimResult.ter, calculateBaseFee(view, preclaimResult.tx), preclaimResult.flags, + preclaimResult.isDelegated, + preclaimResult.account, preclaimResult.j); return invoke_apply(ctx); }