From 298aaac456ee121a6ed4abc122b80bf9c059439c Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Mon, 28 Apr 2025 17:41:40 +0100 Subject: [PATCH] Check for pseudo-account in other transaction types --- src/xrpld/app/tx/detail/Clawback.cpp | 9 +++++---- src/xrpld/app/tx/detail/CreateCheck.cpp | 2 +- src/xrpld/app/tx/detail/Escrow.cpp | 7 ++++++- src/xrpld/app/tx/detail/PayChan.cpp | 8 +++++++- src/xrpld/app/tx/detail/Payment.cpp | 9 ++++++--- src/xrpld/app/tx/detail/SetTrust.cpp | 11 +++++++++++ 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/xrpld/app/tx/detail/Clawback.cpp b/src/xrpld/app/tx/detail/Clawback.cpp index 6f9262743d..41ab1256fb 100644 --- a/src/xrpld/app/tx/detail/Clawback.cpp +++ b/src/xrpld/app/tx/detail/Clawback.cpp @@ -207,12 +207,13 @@ Clawback::preclaim(PreclaimContext const& ctx) if (!sleIssuer || !sleHolder) return terNO_ACCOUNT; - if (sleHolder->isFieldPresent(sfAMMID)) - return tecAMM_ACCOUNT; - + // Note the order of checks - when SAV is active, this check here will make + // the one which follows `sleHolder->isFieldPresent(sfAMMID)` redundant. if (ctx.view.rules().enabled(featureSingleAssetVault) && - sleHolder->isFieldPresent(sfVaultID)) + isPseudoAccount(sleHolder)) return tecPSEUDO_ACCOUNT; + else if (sleHolder->isFieldPresent(sfAMMID)) + return tecAMM_ACCOUNT; return std::visit( [&](T const&) { diff --git a/src/xrpld/app/tx/detail/CreateCheck.cpp b/src/xrpld/app/tx/detail/CreateCheck.cpp index 4a835eaeee..9baceef944 100644 --- a/src/xrpld/app/tx/detail/CreateCheck.cpp +++ b/src/xrpld/app/tx/detail/CreateCheck.cpp @@ -97,7 +97,7 @@ CreateCheck::preclaim(PreclaimContext const& ctx) (flags & lsfDisallowIncomingCheck)) return tecNO_PERMISSION; - // Pseudo-accounts cannot cash check. Note, this is not amendment-gated + // Pseudo-accounts cannot cash checks. Note, this is not amendment-gated // because all writes to pseudo-account discriminator fields **are** // amendment gated, hence the behaviour of this check will always match the // currently active amendments. diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index ff46a68e69..0b58957fcf 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -148,7 +148,12 @@ EscrowCreate::preclaim(PreclaimContext const& ctx) auto const sled = ctx.view.read(keylet::account(ctx.tx[sfDestination])); if (!sled) return tecNO_DST; - if (sled->isFieldPresent(sfAMMID)) + + // Pseudo-accounts cannot receive escrow. Note, this is not amendment-gated + // because all writes to pseudo-account discriminator fields **are** + // amendment gated, hence the behaviour of this check will always match the + // currently active amendments. + if (isPseudoAccount(sled)) return tecNO_PERMISSION; return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/PayChan.cpp b/src/xrpld/app/tx/detail/PayChan.cpp index 376f1e9750..a42902f6ac 100644 --- a/src/xrpld/app/tx/detail/PayChan.cpp +++ b/src/xrpld/app/tx/detail/PayChan.cpp @@ -237,7 +237,13 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) (flags & lsfDisallowXRP)) return tecNO_TARGET; - if (sled->isFieldPresent(sfAMMID)) + // Pseudo-accounts cannot receive payment channels, other than native + // to their underlying ledger object - implemented in their respective + // transaction types. Note, this is not amendment-gated because all + // writes to pseudo-account discriminator fields **are** amendment + // gated, hence the behaviour of this check will always match the + // currently active amendments. + if (isPseudoAccount(sled)) return tecNO_PERMISSION; } diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 5420283438..fe10fec7fe 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -556,9 +556,12 @@ Payment::doApply() return tecUNFUNDED_PAYMENT; } - // AMMs can never receive an XRP payment. - // Must use AMMDeposit transaction instead. - if (sleDst->isFieldPresent(sfAMMID)) + // Pseudo-accounts cannot receive payments, other than these native to + // their underlying ledger object - implemented in their respective + // transaction types. Note, this is not amendment-gated because all writes + // to pseudo-account discriminator fields **are** amendment gated, hence the + // behaviour of this check will always match the active amendments. + if (isPseudoAccount(sleDst)) return tecNO_PERMISSION; // The source account does have enough money. Make sure the diff --git a/src/xrpld/app/tx/detail/SetTrust.cpp b/src/xrpld/app/tx/detail/SetTrust.cpp index 93abcdc4c4..f0340040bc 100644 --- a/src/xrpld/app/tx/detail/SetTrust.cpp +++ b/src/xrpld/app/tx/detail/SetTrust.cpp @@ -228,6 +228,17 @@ SetTrust::preclaim(PreclaimContext const& ctx) } } + // Pseudo-accounts cannot receive trustlines, other than these native to + // their underlying ledger object - implemented in their respective + // transaction types. Note, this is not amendment-gated because all writes + // to pseudo-account discriminator fields **are** amendment gated, hence the + // behaviour of this check will always match the currently active + // amendments. + // The AMM destination is handled above, so exclude it from check here + // since AMM does allow trustline in certain conditions. + if (sleDst && !sleDst->isFieldPresent(sfAMMID) && isPseudoAccount(sleDst)) + return tecPSEUDO_ACCOUNT; + // Checking all freeze/deep freeze flag invariants. if (ctx.view.rules().enabled(featureDeepFreeze)) {