diff --git a/src/ripple/app/tx/impl/SetTrust.cpp b/src/ripple/app/tx/impl/SetTrust.cpp index 7869cc7027d..00a5165221e 100644 --- a/src/ripple/app/tx/impl/SetTrust.cpp +++ b/src/ripple/app/tx/impl/SetTrust.cpp @@ -140,7 +140,20 @@ SetTrust::preclaim(PreclaimContext const& ctx) return tecNO_DST; if (sleDst->getFlags() & lsfDisallowIncomingTrustline) - return tecNO_PERMISSION; + { + // The original implementation of featureDisallowIncoming was + // too restrictive. If + // o fixDisallowIncomingV1 is enabled and + // o The trust line already exists + // Then allow the TrustSet. + if (ctx.view.rules().enabled(fixDisallowIncomingV1) && + ctx.view.exists(keylet::line(id, uDstAccountID, currency))) + { + // pass + } + else + return tecNO_PERMISSION; + } } // If destination is AMM and the trustline doesn't exist then only diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index d4d9e72a830..4c882f3fb8a 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -50,371 +50,240 @@ #include #include +#include + namespace ripple { -// Templates so preflight does the right thing with T::ConsequencesFactory. -// -// This could be done more easily using if constexpr, but Visual Studio -// 2017 doesn't handle if constexpr correctly. So once we're no longer -// building with Visual Studio 2017 we can consider replacing the four -// templates with a single template function that uses if constexpr. -// -// For Transactor::Normal -template < - class T, - std::enable_if_t = 0> -TxConsequences -consequences_helper(PreflightContext const& ctx) -{ - return TxConsequences(ctx.tx); -}; +namespace { -// For Transactor::Blocker -template < - class T, - std::enable_if_t = 0> -TxConsequences -consequences_helper(PreflightContext const& ctx) -{ - return TxConsequences(ctx.tx, TxConsequences::blocker); -}; - -// For Transactor::Custom -template < - class T, - std::enable_if_t = 0> -TxConsequences -consequences_helper(PreflightContext const& ctx) +struct UnknownTxnType : std::exception { - return T::makeTxConsequences(ctx); + TxType txnType; + UnknownTxnType(TxType t) : txnType{t} + { + } }; -template -std::pair -invoke_preflight_helper(PreflightContext const& ctx) +// Call a lambda with the concrete transaction type as a template parameter +// throw an "UnknownTxnType" exception on error +template +auto +with_txn_type(TxType txnType, F&& f) { - auto const tec = T::preflight(ctx); - return { - tec, - isTesSuccess(tec) ? consequences_helper(ctx) : TxConsequences{tec}}; -} - -static std::pair -invoke_preflight(PreflightContext const& ctx) -{ - switch (ctx.tx.getTxnType()) + switch (txnType) { case ttACCOUNT_DELETE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttACCOUNT_SET: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttCHECK_CANCEL: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttCHECK_CASH: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttCHECK_CREATE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttDEPOSIT_PREAUTH: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttOFFER_CANCEL: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttOFFER_CREATE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttESCROW_CREATE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttESCROW_FINISH: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttESCROW_CANCEL: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttPAYCHAN_CLAIM: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttPAYCHAN_CREATE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttPAYCHAN_FUND: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttPAYMENT: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttREGULAR_KEY_SET: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttSIGNER_LIST_SET: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttTICKET_CREATE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttTRUST_SET: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMENDMENT: case ttFEE: case ttUNL_MODIFY: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttNFTOKEN_MINT: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttNFTOKEN_BURN: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttNFTOKEN_CREATE_OFFER: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttNFTOKEN_CANCEL_OFFER: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttNFTOKEN_ACCEPT_OFFER: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttCLAWBACK: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMM_CREATE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMM_DEPOSIT: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMM_WITHDRAW: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMM_VOTE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMM_BID: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttAMM_DELETE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_CREATE_BRIDGE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_MODIFY_BRIDGE: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_CREATE_CLAIM_ID: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_COMMIT: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_CLAIM: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_ADD_CLAIM_ATTESTATION: - return invoke_preflight_helper(ctx); + return f.template operator()(); case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION: - return invoke_preflight_helper( - ctx); + return f.template operator()(); case ttXCHAIN_ACCOUNT_CREATE_COMMIT: - return invoke_preflight_helper(ctx); + return f.template operator()(); default: - assert(false); - return {temUNKNOWN, TxConsequences{temUNKNOWN}}; + throw UnknownTxnType(txnType); } } +} // namespace -/* invoke_preclaim uses name hiding to accomplish - compile-time polymorphism of (presumably) static - class functions for Transactor and derived classes. -*/ +// Templates so preflight does the right thing with T::ConsequencesFactory. +// +// This could be done more easily using if constexpr, but Visual Studio +// 2017 doesn't handle if constexpr correctly. So once we're no longer +// building with Visual Studio 2017 we can consider replacing the four +// templates with a single template function that uses if constexpr. +// +// For Transactor::Normal +// + +// clang-format off +// Current formatter for rippled is based on clang-10, which does not handle `requires` clauses +template +requires(T::ConsequencesFactory == Transactor::Normal) +TxConsequences + consequences_helper(PreflightContext const& ctx) +{ + return TxConsequences(ctx.tx); +}; + +// For Transactor::Blocker +template +requires(T::ConsequencesFactory == Transactor::Blocker) +TxConsequences + consequences_helper(PreflightContext const& ctx) +{ + return TxConsequences(ctx.tx, TxConsequences::blocker); +}; + +// For Transactor::Custom template +requires(T::ConsequencesFactory == Transactor::Custom) +TxConsequences + consequences_helper(PreflightContext const& ctx) +{ + return T::makeTxConsequences(ctx); +}; +// clang-format on + +static std::pair +invoke_preflight(PreflightContext const& ctx) +{ + try + { + return with_txn_type(ctx.tx.getTxnType(), [&]() { + auto const tec = T::preflight(ctx); + return std::make_pair( + tec, + isTesSuccess(tec) ? consequences_helper(ctx) + : TxConsequences{tec}); + }); + } + catch (UnknownTxnType const& e) + { + // Should never happen + JLOG(ctx.j.fatal()) + << "Unknown transaction type in preflight: " << e.txnType; + assert(false); + return {temUNKNOWN, TxConsequences{temUNKNOWN}}; + } +} + static TER invoke_preclaim(PreclaimContext const& ctx) { - // If the transactor requires a valid account and the transaction doesn't - // list one, preflight will have already a flagged a failure. - auto const id = ctx.tx.getAccountID(sfAccount); - - if (id != beast::zero) + try { - TER result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j); + // use name hiding to accomplish compile-time polymorphism of static + // class functions for Transactor and derived classes. + return with_txn_type(ctx.tx.getTxnType(), [&]() { + // If the transactor requires a valid account and the transaction + // doesn't list one, preflight will have already a flagged a + // failure. + auto const id = ctx.tx.getAccountID(sfAccount); - if (result != tesSUCCESS) - return result; + if (id != beast::zero) + { + TER result = T::checkSeqProxy(ctx.view, ctx.tx, ctx.j); - result = T::checkPriorTxAndLastLedger(ctx); + if (result != tesSUCCESS) + return result; - if (result != tesSUCCESS) - return result; + result = T::checkPriorTxAndLastLedger(ctx); - result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); + if (result != tesSUCCESS) + return result; - if (result != tesSUCCESS) - return result; + result = T::checkFee(ctx, calculateBaseFee(ctx.view, ctx.tx)); - result = T::checkSign(ctx); + if (result != tesSUCCESS) + return result; - if (result != tesSUCCESS) - return result; - } + result = T::checkSign(ctx); - return T::preclaim(ctx); -} + if (result != tesSUCCESS) + return result; + } -static TER -invoke_preclaim(PreclaimContext const& ctx) -{ - switch (ctx.tx.getTxnType()) + return T::preclaim(ctx); + }); + } + catch (UnknownTxnType const& e) { - case ttACCOUNT_DELETE: - return invoke_preclaim(ctx); - case ttACCOUNT_SET: - return invoke_preclaim(ctx); - case ttCHECK_CANCEL: - return invoke_preclaim(ctx); - case ttCHECK_CASH: - return invoke_preclaim(ctx); - case ttCHECK_CREATE: - return invoke_preclaim(ctx); - case ttDEPOSIT_PREAUTH: - return invoke_preclaim(ctx); - case ttOFFER_CANCEL: - return invoke_preclaim(ctx); - case ttOFFER_CREATE: - return invoke_preclaim(ctx); - case ttESCROW_CREATE: - return invoke_preclaim(ctx); - case ttESCROW_FINISH: - return invoke_preclaim(ctx); - case ttESCROW_CANCEL: - return invoke_preclaim(ctx); - case ttPAYCHAN_CLAIM: - return invoke_preclaim(ctx); - case ttPAYCHAN_CREATE: - return invoke_preclaim(ctx); - case ttPAYCHAN_FUND: - return invoke_preclaim(ctx); - case ttPAYMENT: - return invoke_preclaim(ctx); - case ttREGULAR_KEY_SET: - return invoke_preclaim(ctx); - case ttSIGNER_LIST_SET: - return invoke_preclaim(ctx); - case ttTICKET_CREATE: - return invoke_preclaim(ctx); - case ttTRUST_SET: - return invoke_preclaim(ctx); - case ttAMENDMENT: - case ttFEE: - case ttUNL_MODIFY: - return invoke_preclaim(ctx); - case ttNFTOKEN_MINT: - return invoke_preclaim(ctx); - case ttNFTOKEN_BURN: - return invoke_preclaim(ctx); - case ttNFTOKEN_CREATE_OFFER: - return invoke_preclaim(ctx); - case ttNFTOKEN_CANCEL_OFFER: - return invoke_preclaim(ctx); - case ttNFTOKEN_ACCEPT_OFFER: - return invoke_preclaim(ctx); - case ttCLAWBACK: - return invoke_preclaim(ctx); - case ttAMM_CREATE: - return invoke_preclaim(ctx); - case ttAMM_DEPOSIT: - return invoke_preclaim(ctx); - case ttAMM_WITHDRAW: - return invoke_preclaim(ctx); - case ttAMM_VOTE: - return invoke_preclaim(ctx); - case ttAMM_BID: - return invoke_preclaim(ctx); - case ttAMM_DELETE: - return invoke_preclaim(ctx); - case ttXCHAIN_CREATE_BRIDGE: - return invoke_preclaim(ctx); - case ttXCHAIN_MODIFY_BRIDGE: - return invoke_preclaim(ctx); - case ttXCHAIN_CREATE_CLAIM_ID: - return invoke_preclaim(ctx); - case ttXCHAIN_COMMIT: - return invoke_preclaim(ctx); - case ttXCHAIN_CLAIM: - return invoke_preclaim(ctx); - case ttXCHAIN_ACCOUNT_CREATE_COMMIT: - return invoke_preclaim(ctx); - case ttXCHAIN_ADD_CLAIM_ATTESTATION: - return invoke_preclaim(ctx); - case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION: - return invoke_preclaim(ctx); - default: - assert(false); - return temUNKNOWN; + // Should never happen + JLOG(ctx.j.fatal()) + << "Unknown transaction type in preclaim: " << e.txnType; + assert(false); + return temUNKNOWN; } } static XRPAmount invoke_calculateBaseFee(ReadView const& view, STTx const& tx) { - switch (tx.getTxnType()) + try { - case ttACCOUNT_DELETE: - return DeleteAccount::calculateBaseFee(view, tx); - case ttACCOUNT_SET: - return SetAccount::calculateBaseFee(view, tx); - case ttCHECK_CANCEL: - return CancelCheck::calculateBaseFee(view, tx); - case ttCHECK_CASH: - return CashCheck::calculateBaseFee(view, tx); - case ttCHECK_CREATE: - return CreateCheck::calculateBaseFee(view, tx); - case ttDEPOSIT_PREAUTH: - return DepositPreauth::calculateBaseFee(view, tx); - case ttOFFER_CANCEL: - return CancelOffer::calculateBaseFee(view, tx); - case ttOFFER_CREATE: - return CreateOffer::calculateBaseFee(view, tx); - case ttESCROW_CREATE: - return EscrowCreate::calculateBaseFee(view, tx); - case ttESCROW_FINISH: - return EscrowFinish::calculateBaseFee(view, tx); - case ttESCROW_CANCEL: - return EscrowCancel::calculateBaseFee(view, tx); - case ttPAYCHAN_CLAIM: - return PayChanClaim::calculateBaseFee(view, tx); - case ttPAYCHAN_CREATE: - return PayChanCreate::calculateBaseFee(view, tx); - case ttPAYCHAN_FUND: - return PayChanFund::calculateBaseFee(view, tx); - case ttPAYMENT: - return Payment::calculateBaseFee(view, tx); - case ttREGULAR_KEY_SET: - return SetRegularKey::calculateBaseFee(view, tx); - case ttSIGNER_LIST_SET: - return SetSignerList::calculateBaseFee(view, tx); - case ttTICKET_CREATE: - return CreateTicket::calculateBaseFee(view, tx); - case ttTRUST_SET: - return SetTrust::calculateBaseFee(view, tx); - case ttAMENDMENT: - case ttFEE: - case ttUNL_MODIFY: - return Change::calculateBaseFee(view, tx); - case ttNFTOKEN_MINT: - return NFTokenMint::calculateBaseFee(view, tx); - case ttNFTOKEN_BURN: - return NFTokenBurn::calculateBaseFee(view, tx); - case ttNFTOKEN_CREATE_OFFER: - return NFTokenCreateOffer::calculateBaseFee(view, tx); - case ttNFTOKEN_CANCEL_OFFER: - return NFTokenCancelOffer::calculateBaseFee(view, tx); - case ttNFTOKEN_ACCEPT_OFFER: - return NFTokenAcceptOffer::calculateBaseFee(view, tx); - case ttCLAWBACK: - return Clawback::calculateBaseFee(view, tx); - case ttAMM_CREATE: - return AMMCreate::calculateBaseFee(view, tx); - case ttAMM_DEPOSIT: - return AMMDeposit::calculateBaseFee(view, tx); - case ttAMM_WITHDRAW: - return AMMWithdraw::calculateBaseFee(view, tx); - case ttAMM_VOTE: - return AMMVote::calculateBaseFee(view, tx); - case ttAMM_BID: - return AMMBid::calculateBaseFee(view, tx); - case ttAMM_DELETE: - return AMMDelete::calculateBaseFee(view, tx); - case ttXCHAIN_CREATE_BRIDGE: - return XChainCreateBridge::calculateBaseFee(view, tx); - case ttXCHAIN_MODIFY_BRIDGE: - return BridgeModify::calculateBaseFee(view, tx); - case ttXCHAIN_CREATE_CLAIM_ID: - return XChainCreateClaimID::calculateBaseFee(view, tx); - case ttXCHAIN_COMMIT: - return XChainCommit::calculateBaseFee(view, tx); - case ttXCHAIN_CLAIM: - return XChainClaim::calculateBaseFee(view, tx); - case ttXCHAIN_ADD_CLAIM_ATTESTATION: - return XChainAddClaimAttestation::calculateBaseFee(view, tx); - case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION: - return XChainAddAccountCreateAttestation::calculateBaseFee( - view, tx); - case ttXCHAIN_ACCOUNT_CREATE_COMMIT: - return XChainCreateAccountCommit::calculateBaseFee(view, tx); - default: - assert(false); - return XRPAmount{0}; + return with_txn_type(tx.getTxnType(), [&]() { + return T::calculateBaseFee(view, tx); + }); + } + catch (UnknownTxnType const& e) + { + assert(false); + return XRPAmount{0}; } } @@ -460,173 +329,20 @@ TxConsequences::TxConsequences(STTx const& tx, std::uint32_t sequencesConsumed) static std::pair invoke_apply(ApplyContext& ctx) { - switch (ctx.tx.getTxnType()) + try { - case ttACCOUNT_DELETE: { - DeleteAccount p(ctx); - return p(); - } - case ttACCOUNT_SET: { - SetAccount p(ctx); - return p(); - } - case ttCHECK_CANCEL: { - CancelCheck p(ctx); - return p(); - } - case ttCHECK_CASH: { - CashCheck p(ctx); - return p(); - } - case ttCHECK_CREATE: { - CreateCheck p(ctx); - return p(); - } - case ttDEPOSIT_PREAUTH: { - DepositPreauth p(ctx); - return p(); - } - case ttOFFER_CANCEL: { - CancelOffer p(ctx); - return p(); - } - case ttOFFER_CREATE: { - CreateOffer p(ctx); - return p(); - } - case ttESCROW_CREATE: { - EscrowCreate p(ctx); - return p(); - } - case ttESCROW_FINISH: { - EscrowFinish p(ctx); - return p(); - } - case ttESCROW_CANCEL: { - EscrowCancel p(ctx); - return p(); - } - case ttPAYCHAN_CLAIM: { - PayChanClaim p(ctx); - return p(); - } - case ttPAYCHAN_CREATE: { - PayChanCreate p(ctx); - return p(); - } - case ttPAYCHAN_FUND: { - PayChanFund p(ctx); - return p(); - } - case ttPAYMENT: { - Payment p(ctx); - return p(); - } - case ttREGULAR_KEY_SET: { - SetRegularKey p(ctx); - return p(); - } - case ttSIGNER_LIST_SET: { - SetSignerList p(ctx); - return p(); - } - case ttTICKET_CREATE: { - CreateTicket p(ctx); - return p(); - } - case ttTRUST_SET: { - SetTrust p(ctx); + return with_txn_type(ctx.tx.getTxnType(), [&]() { + T p(ctx); return p(); - } - case ttAMENDMENT: - case ttFEE: - case ttUNL_MODIFY: { - Change p(ctx); - return p(); - } - case ttNFTOKEN_MINT: { - NFTokenMint p(ctx); - return p(); - } - case ttNFTOKEN_BURN: { - NFTokenBurn p(ctx); - return p(); - } - case ttNFTOKEN_CREATE_OFFER: { - NFTokenCreateOffer p(ctx); - return p(); - } - case ttNFTOKEN_CANCEL_OFFER: { - NFTokenCancelOffer p(ctx); - return p(); - } - case ttNFTOKEN_ACCEPT_OFFER: { - NFTokenAcceptOffer p(ctx); - return p(); - } - case ttCLAWBACK: { - Clawback p(ctx); - return p(); - } - case ttAMM_CREATE: { - AMMCreate p(ctx); - return p(); - } - case ttAMM_DEPOSIT: { - AMMDeposit p(ctx); - return p(); - } - case ttAMM_WITHDRAW: { - AMMWithdraw p(ctx); - return p(); - } - case ttAMM_VOTE: { - AMMVote p(ctx); - return p(); - } - case ttAMM_BID: { - AMMBid p(ctx); - return p(); - } - case ttAMM_DELETE: { - AMMDelete p(ctx); - return p(); - } - case ttXCHAIN_CREATE_BRIDGE: { - XChainCreateBridge p(ctx); - return p(); - } - case ttXCHAIN_MODIFY_BRIDGE: { - BridgeModify p(ctx); - return p(); - } - case ttXCHAIN_CREATE_CLAIM_ID: { - XChainCreateClaimID p(ctx); - return p(); - } - case ttXCHAIN_COMMIT: { - XChainCommit p(ctx); - return p(); - } - case ttXCHAIN_CLAIM: { - XChainClaim p(ctx); - return p(); - } - case ttXCHAIN_ADD_CLAIM_ATTESTATION: { - XChainAddClaimAttestation p(ctx); - return p(); - } - case ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION: { - XChainAddAccountCreateAttestation p(ctx); - return p(); - } - case ttXCHAIN_ACCOUNT_CREATE_COMMIT: { - XChainCreateAccountCommit p(ctx); - return p(); - } - default: - assert(false); - return {temUNKNOWN, false}; + }); + } + catch (UnknownTxnType const& e) + { + // Should never happen + JLOG(ctx.journal.fatal()) + << "Unknown transaction type in apply: " << e.txnType; + assert(false); + return {temUNKNOWN, false}; } } diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index df0570d3a52..17aca813f71 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -74,7 +74,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 = 62; +static constexpr std::size_t numFeatures = 63; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -349,6 +349,7 @@ extern uint256 const fixNFTokenRemint; extern uint256 const fixReducedOffersV1; extern uint256 const featureClawback; extern uint256 const featureXChainBridge; +extern uint256 const fixDisallowIncomingV1; } // namespace ripple diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index aa41b7d5b6e..6a3430f4f50 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -456,6 +456,7 @@ REGISTER_FIX (fixReducedOffersV1, Supported::yes, VoteBehavior::De REGISTER_FEATURE(Clawback, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(AMM, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(XChainBridge, Supported::yes, VoteBehavior::DefaultNo); +REGISTER_FIX(fixDisallowIncomingV1, 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/SetTrust_test.cpp b/src/test/app/SetTrust_test.cpp index fce9c4295c2..599ac1917f9 100644 --- a/src/test/app/SetTrust_test.cpp +++ b/src/test/app/SetTrust_test.cpp @@ -275,6 +275,48 @@ class SetTrust_test : public beast::unit_test::suite BEAST_EXPECT(!(flags & lsfDisallowIncomingTrustline)); } + // fixDisallowIncomingV1 + { + for (bool const withFix : {true, false}) + { + auto const amend = withFix + ? features | disallowIncoming + : (features | disallowIncoming) - fixDisallowIncomingV1; + + Env env{*this, amend}; + auto const dist = Account("dist"); + auto const gw = Account("gw"); + auto const USD = gw["USD"]; + auto const distUSD = dist["USD"]; + + env.fund(XRP(1000), gw, dist); + env.close(); + + env(fset(gw, asfRequireAuth)); + env.close(); + + env(fset(dist, asfDisallowIncomingTrustline)); + env.close(); + + env(trust(dist, USD(10000))); + env.close(); + + // withFix: can set trustline + // withOutFix: cannot set trustline + auto const trustResult = + withFix ? ter(tesSUCCESS) : ter(tecNO_PERMISSION); + env(trust(gw, distUSD(10000)), + txflags(tfSetfAuth), + trustResult); + env.close(); + + auto const txResult = + withFix ? ter(tesSUCCESS) : ter(tecPATH_DRY); + env(pay(gw, dist, USD(1000)), txResult); + env.close(); + } + } + Env env{*this, features | disallowIncoming}; auto const gw = Account{"gateway"};