diff --git a/src/ripple/app/paths/impl/DirectStep.cpp b/src/ripple/app/paths/impl/DirectStep.cpp index 57738b3ad0..c0a1fdce32 100644 --- a/src/ripple/app/paths/impl/DirectStep.cpp +++ b/src/ripple/app/paths/impl/DirectStep.cpp @@ -595,7 +595,6 @@ TER DirectStepI::check (StrandContext const& ctx) const return temBAD_PATH; } - auto sleLine = ctx.view.read (keylet::line (src_, dst_, currency_)); { auto sleSrc = ctx.view.read (keylet::account (src_)); if (!sleSrc) @@ -606,6 +605,8 @@ TER DirectStepI::check (StrandContext const& ctx) const return terNO_ACCOUNT; } + auto const sleLine = ctx.view.read (keylet::line (src_, dst_, currency_)); + if (!sleLine) { JLOG (j_.trace()) << "DirectStepI: No credit line. " << *this; @@ -623,39 +624,40 @@ TER DirectStepI::check (StrandContext const& ctx) const << " src: " << src_; return terNO_AUTH; } - } - // pure issue/redeem can't be frozen - if (! (ctx.isLast && ctx.isFirst)) - { - auto const ter = checkFreeze (ctx.view, src_, dst_, currency_); - if (ter != tesSUCCESS) - return ter; - } - - if (ctx.prevStep) - { - if (auto prevSrc = ctx.prevStep->directStepSrcAcct ()) + // pure issue/redeem can't be frozen + if (!(ctx.isLast && ctx.isFirst)) { - auto const ter = - checkNoRipple (ctx.view, *prevSrc, src_, dst_, currency_, j_); + auto const ter = checkFreeze(ctx.view, src_, dst_, currency_); if (ter != tesSUCCESS) return ter; } - if (fix1449(ctx.view.info().parentCloseTime)) + if (ctx.prevStep) { - if (ctx.prevStep->bookStepBook()) + if (auto prevSrc = ctx.prevStep->directStepSrcAcct()) { - auto const noRippleSrcToDst = - ((*sleLine)[sfFlags] & - ((src_ > dst_) ? lsfHighNoRipple : lsfLowNoRipple)); - if (noRippleSrcToDst) - return terNO_RIPPLE; + auto const ter = checkNoRipple( + ctx.view, *prevSrc, src_, dst_, currency_, j_); + if (ter != tesSUCCESS) + return ter; + } + + if (fix1449(ctx.view.info().parentCloseTime)) + { + if (ctx.prevStep->bookStepBook()) + { + auto const noRippleSrcToDst = + ((*sleLine)[sfFlags] & + ((src_ > dst_) ? lsfHighNoRipple : lsfLowNoRipple)); + if (noRippleSrcToDst) + return terNO_RIPPLE; + } } } } + { Issue const srcIssue{currency_, src_}; Issue const dstIssue{currency_, dst_}; diff --git a/src/test/app/Flow_test.cpp b/src/test/app/Flow_test.cpp index 57a2545c1d..344ccc83b0 100644 --- a/src/test/app/Flow_test.cpp +++ b/src/test/app/Flow_test.cpp @@ -1157,11 +1157,55 @@ struct Flow_test : public beast::unit_test::suite pass(); } + void + testRIPD1449(bool withFix) + { + testcase("ripd1449"); + + using namespace jtx; + Env env(*this, features(featureFlow)); + auto const timeDelta = env.closed ()->info ().closeTimeResolution; + auto const d = withFix ? timeDelta*100 : -timeDelta*100; + auto closeTime = fix1449Time() + d; + env.close(closeTime); + + // pay alice -> xrp -> USD/bob -> bob -> gw -> alice + // set no ripple on bob's side of the bob/gw trust line + // carol has the bob/USD and makes an offer, bob has USD/gw + + auto const alice = Account("alice"); + auto const bob = Account("bob"); + auto const carol = Account("carol"); + auto const gw = Account("gw"); + auto const USD = gw["USD"]; + + env.fund(XRP(100000000), alice, bob, carol, gw); + env.trust(USD(10000), alice, carol); + env(trust(bob, USD(10000), tfSetNoRipple)); + env.trust(USD(10000), bob); + env.trust(bob["USD"](10000), carol); + env.close(); + + env(pay(bob, carol, bob["USD"](1000))); + env(pay(gw, bob, USD(1000))); + env.close(); + + env(offer(carol, XRP(1), bob["USD"](1000))); + env.close(); + + env(pay(alice, alice, USD(1000)), path(~bob["USD"], bob, gw), + sendmax(XRP(1)), txflags(tfNoRippleDirect), + ter(withFix ? tecPATH_DRY : tesSUCCESS)); + env.close(); + } + void run() override { testLimitQuality(); testRIPD1443(true); testRIPD1443(false); + testRIPD1449(true); + testRIPD1449(false); auto testWithFeats = [this](auto&&... fs) {