diff --git a/src/ripple/app/tx/impl/Escrow.cpp b/src/ripple/app/tx/impl/Escrow.cpp index 4918004fa..c9a438ff4 100644 --- a/src/ripple/app/tx/impl/Escrow.cpp +++ b/src/ripple/app/tx/impl/Escrow.cpp @@ -118,13 +118,6 @@ EscrowCreate::preflight(PreflightContext const& ctx) if (isFakeXRP(amount)) return temBAD_CURRENCY; - - if (ctx.tx[sfAccount] == amount.getIssuer()) - { - JLOG(ctx.j.trace()) - << "Malformed transaction: Cannot escrow own tokens to self."; - return temBAD_SRC_ACCOUNT; - } } if (ctx.tx[sfAmount] <= beast::zero) @@ -215,15 +208,15 @@ EscrowCreate::doApply() auto const account = ctx_.tx[sfAccount]; auto const sle = ctx_.view().peek(keylet::account(account)); if (!sle) - return temDISABLED; + return tefINTERNAL; STAmount const amount{ctx_.tx[sfAmount]}; std::shared_ptr sleLine; auto const balance = STAmount((*sle)[sfBalance]).xrp(); - auto const reserve = - ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); + auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); + bool isIssuer = amount.getIssuer() == account; if (balance < reserve) return tecINSUFFICIENT_RESERVE; @@ -244,37 +237,42 @@ EscrowCreate::doApply() // check if the escrow is capable of being // finished before we allow it to be created + if (!sleLine && amount.getIssuer() != account) + return tecNO_LINE; + + TER result = trustTransferAllowed( + ctx_.view(), + {account, ctx_.tx[sfDestination]}, + amount.issue(), + ctx_.journal); + + JLOG(ctx_.journal.trace()) + << "EscrowCreate::doApply trustTransferAllowed result=" + << result; + + if (!isTesSuccess(result)) + return result; + + // issuer does not need to lock anything + if (!isIssuer) { - TER result = trustTransferAllowed( - ctx_.view(), - {account, ctx_.tx[sfDestination]}, - amount.issue(), - ctx_.journal); + // perform the lock as a dry run before + // we modify anything on-ledger + sleLine = ctx_.view().peek( + keylet::line(account, amount.getIssuer(), amount.getCurrency())); - JLOG(ctx_.journal.trace()) - << "EscrowCreate::doApply trustTransferAllowed result=" - << result; + { + TER result = trustAdjustLockedBalance( + ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun); - if (!isTesSuccess(result)) - return result; - } + JLOG(ctx_.journal.trace()) + << "EscrowCreate::doApply trustAdjustLockedBalance (dry) " + "result=" + << result; - // perform the lock as a dry run before - // we modify anything on-ledger - sleLine = ctx_.view().peek( - keylet::line(account, amount.getIssuer(), amount.getCurrency())); - - { - TER result = trustAdjustLockedBalance( - ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun); - - JLOG(ctx_.journal.trace()) - << "EscrowCreate::doApply trustAdjustLockedBalance (dry) " - "result=" - << result; - - if (!isTesSuccess(result)) - return result; + if (!isTesSuccess(result)) + return result; + } } } @@ -335,20 +333,26 @@ EscrowCreate::doApply() (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; else { - if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens) || - !sleLine) + if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; - // do the lock-up for real now - TER result = trustAdjustLockedBalance( - ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); + if (!sleLine && amount.getIssuer() != account) + return tecNO_LINE; - JLOG(ctx_.journal.trace()) - << "EscrowCreate::doApply trustAdjustLockedBalance (wet) result=" - << result; + // issuer does not need to lock anything + if (!isIssuer) + { + // do the lock-up for real now + TER result = trustAdjustLockedBalance( + ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); - if (!isTesSuccess(result)) - return result; + JLOG(ctx_.journal.trace()) + << "EscrowCreate::doApply trustAdjustLockedBalance (wet) result=" + << result; + + if (!isTesSuccess(result)) + return result; + } } adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); @@ -682,6 +686,7 @@ EscrowCancel::doApply() AccountID const account = (*slep)[sfAccount]; auto const sle = ctx_.view().peek(keylet::account(account)); auto amount = slep->getFieldAmount(sfAmount); + bool isIssuer = amount.getIssuer() == account; std::shared_ptr sleLine; @@ -690,14 +695,15 @@ EscrowCancel::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; - sleLine = ctx_.view().peek( - keylet::line(account, amount.getIssuer(), amount.getCurrency())); + // issuer does not need to lock anything + if (!isIssuer) + { + sleLine = ctx_.view().peek(keylet::line(account, amount.getIssuer(), amount.getCurrency())); - // dry run before we make any changes to ledger - if (TER result = trustAdjustLockedBalance( - ctx_.view(), sleLine, -amount, -1, ctx_.journal, DryRun); - result != tesSUCCESS) - return result; + // dry run before we make any changes to ledger + if (TER result = trustAdjustLockedBalance(ctx_.view(), sleLine, -amount, -1, ctx_.journal, DryRun); result != tesSUCCESS) + return result; + } } // Remove escrow from owner directory @@ -733,16 +739,20 @@ EscrowCancel::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; - // unlock previously locked tokens from source line - TER result = trustAdjustLockedBalance( - ctx_.view(), sleLine, -amount, -1, ctx_.journal, WetRun); + // issuer does not need to lock anything + if (!isIssuer) + { + // unlock previously locked tokens from source line + TER result = trustAdjustLockedBalance( + ctx_.view(), sleLine, -amount, -1, ctx_.journal, WetRun); - JLOG(ctx_.journal.trace()) - << "EscrowCancel::doApply trustAdjustLockedBalance (wet) result=" - << result; + JLOG(ctx_.journal.trace()) + << "EscrowCancel::doApply trustAdjustLockedBalance (wet) result=" + << result; - if (!isTesSuccess(result)) - return result; + if (!isTesSuccess(result)) + return result; + } } // Decrement owner count diff --git a/src/ripple/app/tx/impl/PayChan.cpp b/src/ripple/app/tx/impl/PayChan.cpp index ce021d343..f97c95f5e 100644 --- a/src/ripple/app/tx/impl/PayChan.cpp +++ b/src/ripple/app/tx/impl/PayChan.cpp @@ -225,13 +225,6 @@ PayChanCreate::preflight(PreflightContext const& ctx) if (isFakeXRP(amount)) return temBAD_CURRENCY; - - if (ctx.tx[sfAccount] == amount.getIssuer()) - { - JLOG(ctx.j.trace()) - << "Malformed transaction: Cannot paychan own tokens to self."; - return temBAD_SRC_ACCOUNT; - } } if (ctx.tx[sfAmount] <= beast::zero) @@ -264,6 +257,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) return tecINSUFFICIENT_RESERVE; auto const dst = ctx.tx[sfDestination]; + bool isIssuer = amount.getIssuer() == account; // Check reserve and funds availability if (isXRP(amount) && balance < reserve + amount) @@ -287,20 +281,16 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) if (!isTesSuccess(result)) return result; } - // check if the amount can be locked { - auto sleLine = ctx.view.read(keylet::line( - account, amount.getIssuer(), amount.getCurrency())); - TER result = trustAdjustLockedBalance( - ctx.view, sleLine, amount, 1, ctx.j, DryRun); - - JLOG(ctx.j.trace()) << "PayChanCreate::preclaim " - "trustAdjustLockedBalance(dry) result=" - << result; - - if (!isTesSuccess(result)) - return result; + if (!isIssuer) + { + auto sleLine = ctx.view.read(keylet::line(account, amount.getIssuer(), amount.getCurrency())); + TER result = trustAdjustLockedBalance(ctx.view, sleLine, amount, 1, ctx.j, DryRun); + JLOG(ctx.j.trace()) << "PayChanCreate::preclaim trustAdjustLockedBalance(dry) result=" << result; + if (!isTesSuccess(result)) + return result; + } } } @@ -334,6 +324,7 @@ PayChanCreate::doApply() auto const dst = ctx_.tx[sfDestination]; STAmount const amount{ctx_.tx[sfAmount]}; + bool isIssuer = amount.getIssuer() == account; // Create PayChan in ledger. // @@ -386,21 +377,22 @@ PayChanCreate::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; - auto sleLine = ctx_.view().peek( - keylet::line(account, amount.getIssuer(), amount.getCurrency())); - - if (!sleLine) + auto sleLine = ctx_.view().peek(keylet::line(account, amount.getIssuer(), amount.getCurrency())); + if (!sleLine && !isIssuer) return tecNO_LINE; - - TER result = trustAdjustLockedBalance( + + if (!isIssuer) + { + TER result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); - JLOG(ctx_.journal.trace()) - << "PayChanCreate::doApply trustAdjustLockedBalance(wet) result=" - << result; + JLOG(ctx_.journal.trace()) + << "PayChanCreate::doApply trustAdjustLockedBalance(wet) result=" + << result; - if (!isTesSuccess(result)) - return result; + if (!isTesSuccess(result)) + return result; + } } adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); @@ -438,13 +430,6 @@ PayChanFund::preflight(PreflightContext const& ctx) if (isFakeXRP(amount)) return temBAD_CURRENCY; - - if (ctx.tx[sfAccount] == amount.getIssuer()) - { - JLOG(ctx.j.trace()) - << "Malformed transaction: Cannot escrow own tokens to self."; - return temDST_IS_SRC; - } } if (ctx.tx[sfAmount] <= beast::zero) @@ -466,27 +451,30 @@ PayChanFund::doApply() std::shared_ptr sleLine; // if XRP or featurePaychanAndEscrowForTokens // not enabled this remains null + AccountID const src = (*slep)[sfAccount]; + auto const txAccount = ctx_.tx[sfAccount]; + auto const expiration = (*slep)[~sfExpiration]; + bool isIssuer = amount.getIssuer() == txAccount; // if this is a Fund operation on an IOU then perform a dry run here if (!isXRP(amount) && ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) { - sleLine = ctx_.view().peek(keylet::line( - (*slep)[sfAccount], amount.getIssuer(), amount.getCurrency())); + // issuer does not need to lock anything + if (!isIssuer) + { + sleLine = ctx_.view().peek(keylet::line((*slep)[sfAccount], amount.getIssuer(), amount.getCurrency())); - TER result = trustAdjustLockedBalance( - ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun); + TER result = trustAdjustLockedBalance( + ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun); - JLOG(ctx_.journal.trace()) - << "PayChanFund::doApply trustAdjustLockedBalance(dry) result=" - << result; + JLOG(ctx_.journal.trace()) + << "PayChanFund::doApply trustAdjustLockedBalance(dry) result=" + << result; - if (!isTesSuccess(result)) - return result; + if (!isTesSuccess(result)) + return result; + } } - - AccountID const src = (*slep)[sfAccount]; - auto const txAccount = ctx_.tx[sfAccount]; - auto const expiration = (*slep)[~sfExpiration]; { auto const cancelAfter = (*slep)[~sfCancelAfter]; auto const closeTime = @@ -547,15 +535,19 @@ PayChanFund::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; - TER result = trustAdjustLockedBalance( + // issuer does not need to lock anything + if (!isIssuer) + { + TER result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); - JLOG(ctx_.journal.trace()) - << "PayChanFund::doApply trustAdjustLockedBalance(wet) result=" - << result; + JLOG(ctx_.journal.trace()) + << "PayChanFund::doApply trustAdjustLockedBalance(wet) result=" + << result; - if (!isTesSuccess(result)) - return result; + if (!isTesSuccess(result)) + return result; + } } (*slep)[sfAmount] = (*slep)[sfAmount] + ctx_.tx[sfAmount]; diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h index d910d0390..413d51143 100644 --- a/src/ripple/ledger/View.h +++ b/src/ripple/ledger/View.h @@ -20,12 +20,14 @@ #ifndef RIPPLE_LEDGER_VIEW_H_INCLUDED #define RIPPLE_LEDGER_VIEW_H_INCLUDED +#include #include #include #include #include #include #include +#include #include #include #include @@ -33,13 +35,11 @@ #include #include #include -#include -#include #include #include #include -#include #include +#include #include namespace ripple { @@ -353,7 +353,6 @@ trustDelete( AccountID const& uHighAccountID, beast::Journal j); - /** Delete an offer. Requirements: @@ -422,7 +421,7 @@ transferXRP( // Trustline Locking and Transfer (PaychanAndEscrowForTokens) // -// In functions white require a `RunType` +// In functions white require a `RunType` // pass DryRun (don't apply changes) or WetRun (do apply changes) // to allow compile time evaluation of which types and calls to use @@ -435,15 +434,16 @@ transferXRP( template struct RunType { - //see: - //http://alumni.media.mit.edu/~rahimi/compile-time-flags/ - constexpr operator T() const - { + // see: + // http://alumni.media.mit.edu/~rahimi/compile-time-flags/ + constexpr operator T() const + { static_assert(std::is_same::value); return V; } - constexpr T operator!() const + constexpr T + operator!() const { static_assert(std::is_same::value); return !(V); @@ -452,7 +452,8 @@ struct RunType // allow party lists to be logged easily template -std::ostream& operator<< (std::ostream& lhs, std::vector const& rhs) +std::ostream& +operator<<(std::ostream& lhs, std::vector const& rhs) { lhs << "{"; for (int i = 0; i < rhs.size(); ++i) @@ -461,7 +462,8 @@ std::ostream& operator<< (std::ostream& lhs, std::vector const& rhs) return lhs; } // Return true iff the acc side of line is in default state -bool isTrustDefault( +bool +isTrustDefault( std::shared_ptr const& acc, // side to check std::shared_ptr const& line); // line to check @@ -469,17 +471,17 @@ bool isTrustDefault( If positive deltaAmt lock the amount. If negative deltaAmt unlock the amount. */ -template +template [[nodiscard]] TER trustAdjustLockedBalance( V& view, S& sleLine, STAmount const& deltaAmt, - int deltaLockCount, // if +1 lockCount is increased, -1 is decreased, 0 unchanged + int deltaLockCount, // if +1 lockCount is increased, -1 is decreased, 0 + // unchanged beast::Journal const& j, R dryRun) { - static_assert( (std::is_same::value && std::is_same>::value) || @@ -498,27 +500,23 @@ trustAdjustLockedBalance( return tecNO_LINE; // auto const currency = deltaAmt.getCurrency(); - auto const issuer = deltaAmt.getIssuer(); + auto const issuer = deltaAmt.getIssuer(); - STAmount lowLimit = sleLine->getFieldAmount(sfLowLimit); + STAmount lowLimit = sleLine->getFieldAmount(sfLowLimit); // the account which is modifying the LockedBalance is always // the side that isn't the issuer, so if the low side is the // issuer then the high side is the account. - bool high = lowLimit.getIssuer() == issuer; + bool high = lowLimit.getIssuer() == issuer; - std::vector parties - {high ? sleLine->getFieldAmount(sfHighLimit).getIssuer(): lowLimit.getIssuer()}; + std::vector parties{ + high ? sleLine->getFieldAmount(sfHighLimit).getIssuer() + : lowLimit.getIssuer()}; // check for freezes & auth { - TER result = - trustTransferAllowed( - view, - parties, - deltaAmt.issue(), - j); - + TER result = trustTransferAllowed(view, parties, deltaAmt.issue(), j); + JLOG(j.trace()) << "trustAdjustLockedBalance: trustTransferAllowed result=" << result; @@ -528,8 +526,7 @@ trustAdjustLockedBalance( } // pull the TL balance from the account's perspective - STAmount balance = - high ? -(*sleLine)[sfBalance] : (*sleLine)[sfBalance]; + STAmount balance = high ? -(*sleLine)[sfBalance] : (*sleLine)[sfBalance]; // this would mean somehow the issuer is trying to lock balance if (balance < beast::zero) @@ -541,14 +538,13 @@ trustAdjustLockedBalance( // can't lock or unlock a zero balance if (balance == beast::zero) { - JLOG(j.trace()) - << "trustAdjustLockedBalance failed, zero balance"; + JLOG(j.trace()) << "trustAdjustLockedBalance failed, zero balance"; return tecUNFUNDED_PAYMENT; } - STAmount priorLockedBalance {sfLockedBalance, deltaAmt.issue()}; + STAmount priorLockedBalance{sfLockedBalance, deltaAmt.issue()}; if (sleLine->isFieldPresent(sfLockedBalance)) - priorLockedBalance = + priorLockedBalance = high ? -(*sleLine)[sfLockedBalance] : (*sleLine)[sfLockedBalance]; uint32_t priorLockCount = 0; @@ -560,19 +556,15 @@ trustAdjustLockedBalance( if (finalLockedBalance > balance) { - JLOG(j.trace()) - << "trustAdjustLockedBalance: " - << "lockedBalance(" - << finalLockedBalance - << ") > balance(" - << balance - << ") = true\n"; + JLOG(j.trace()) << "trustAdjustLockedBalance: " + << "lockedBalance(" << finalLockedBalance + << ") > balance(" << balance << ") = true\n"; return tecUNFUNDED_PAYMENT; } if (finalLockedBalance < beast::zero) return tecINTERNAL; - + // check if there is significant precision loss if (!isAddable(balance, deltaAmt) || !isAddable(priorLockedBalance, deltaAmt) || @@ -589,7 +581,9 @@ trustAdjustLockedBalance( if (dryRun) return tesSUCCESS; - if constexpr(std::is_same::value && std::is_same>::value) + if constexpr ( + std::is_same::value && + std::is_same>::value) { if (finalLockedBalance == beast::zero || finalLockCount == 0) { @@ -598,18 +592,18 @@ trustAdjustLockedBalance( } else { - sleLine-> - setFieldAmount(sfLockedBalance, high ? -finalLockedBalance : finalLockedBalance); + sleLine->setFieldAmount( + sfLockedBalance, + high ? -finalLockedBalance : finalLockedBalance); sleLine->setFieldU32(sfLockCount, finalLockCount); } - + view.update(sleLine); } return tesSUCCESS; } - /** Check if a set of accounts can freely exchange the specified token. Read only, does not change any ledger object. May be called with ApplyView or ReadView. @@ -618,8 +612,8 @@ trustAdjustLockedBalance( If parties contains more than 1 entry then any party with noRipple on issuer side is a bar to xfer. */ -template -[[nodiscard]]TER +template +[[nodiscard]] TER trustTransferAllowed( V& view, std::vector const& parties, @@ -629,13 +623,12 @@ trustTransferAllowed( static_assert( std::is_same::value || std::is_same::value); - + typedef typename std::conditional< std::is_same::value, std::shared_ptr, std::shared_ptr>::type SLEPtr; - if (isFakeXRP(issue.currency)) return tecNO_PERMISSION; @@ -656,12 +649,13 @@ trustTransferAllowed( bool requireAuth = issuerFlags & lsfRequireAuth; - for (AccountID const& p: parties) + for (AccountID const& p : parties) { if (p == issue.account) continue; - auto const line = view.read(keylet::line(p, issue.account, issue.currency)); + auto const line = + view.read(keylet::line(p, issue.account, issue.currency)); if (!line) { if (requireAuth) @@ -686,16 +680,16 @@ trustTransferAllowed( // these "strange" old lines, if they even exist anymore are // always a bar to xfer if (line->getFieldAmount(sfLowLimit).getIssuer() == - line->getFieldAmount(sfHighLimit).getIssuer()) + line->getFieldAmount(sfHighLimit).getIssuer()) return tecINTERNAL; if (line->isFieldPresent(sfLockedBalance)) { if (!lockedBalanceAllowed) { - JLOG(j.warn()) - << "trustTransferAllowed: " - << "sfLockedBalance found on line when amendment not enabled"; + JLOG(j.warn()) << "trustTransferAllowed: " + << "sfLockedBalance found on line when " + "amendment not enabled"; return tecINTERNAL; } @@ -704,9 +698,9 @@ trustTransferAllowed( if (lockedBalance.getCurrency() != balance.getCurrency()) { - JLOG(j.warn()) - << "trustTansferAllowed: " - << "lockedBalance currency did not match balance currency"; + JLOG(j.warn()) << "trustTansferAllowed: " + << "lockedBalance currency did not match " + "balance currency"; return tecINTERNAL; } } @@ -719,19 +713,19 @@ trustTransferAllowed( { bool pHigh = p > issue.account; - auto const flagIssuerNoRipple { pHigh ? lsfLowNoRipple : lsfHighNoRipple }; - auto const flagIssuerFreeze { pHigh ? lsfLowFreeze : lsfHighFreeze }; - auto const flagIssuerAuth { pHigh ? lsfLowAuth : lsfHighAuth }; + auto const flagIssuerNoRipple{ + pHigh ? lsfLowNoRipple : lsfHighNoRipple}; + auto const flagIssuerFreeze{pHigh ? lsfLowFreeze : lsfHighFreeze}; + auto const flagIssuerAuth{pHigh ? lsfLowAuth : lsfHighAuth}; uint32_t flags = line->getFieldU32(sfFlags); if (flags & flagIssuerFreeze) { - JLOG(j.trace()) - << "trustTransferAllowed: " - << "parties=[" << parties << "], " - << "issuer: " << issue.account << " " - << "has freeze on party: " << p; + JLOG(j.trace()) << "trustTransferAllowed: " + << "parties=[" << parties << "], " + << "issuer: " << issue.account << " " + << "has freeze on party: " << p; return tecFROZEN; } @@ -740,11 +734,10 @@ trustTransferAllowed( // blocks any possible xfer if (parties.size() > 1 && (flags & flagIssuerNoRipple)) { - JLOG(j.trace()) - << "trustTransferAllowed: " - << "parties=[" << parties << "], " - << "issuer: " << issue.account << " " - << "has noRipple on party: " << p; + JLOG(j.trace()) << "trustTransferAllowed: " + << "parties=[" << parties << "], " + << "issuer: " << issue.account << " " + << "has noRipple on party: " << p; return tecPATH_DRY; } @@ -752,13 +745,12 @@ trustTransferAllowed( // the issuer has specified lsfRequireAuth if (requireAuth && !(flags & flagIssuerAuth)) { - JLOG(j.trace()) - << "trustTransferAllowed: " - << "parties=[" << parties << "], " - << "issuer: " << issue.account << " " - << "requires TL auth which " - << "party: " << p << " " - << "does not possess."; + JLOG(j.trace()) << "trustTransferAllowed: " + << "parties=[" << parties << "], " + << "issuer: " << issue.account << " " + << "requires TL auth which " + << "party: " << p << " " + << "does not possess."; return tecNO_AUTH; } } @@ -775,22 +767,20 @@ template [[nodiscard]] TER trustTransferLockedBalance( V& view, - AccountID const& actingAccID, // the account whose tx is actioning xfer + AccountID const& actingAccID, // the account whose tx is actioning xfer S& sleSrcAcc, S& sleDstAcc, - STAmount const& amount, // issuer, currency are in this field - int deltaLockCount, // -1 decrement, +1 increment, 0 unchanged + STAmount const& amount, // issuer, currency are in this field + int deltaLockCount, // -1 decrement, +1 increment, 0 unchanged beast::Journal const& j, R dryRun) { - typedef typename std::conditional< std::is_same::value && !dryRun, std::shared_ptr, std::shared_ptr>::type SLEPtr; - auto peek = [&](Keylet& k) - { + auto peek = [&](Keylet& k) { if constexpr (std::is_same::value && !dryRun) return const_cast(view).peek(k); else @@ -804,15 +794,13 @@ trustTransferLockedBalance( if (!sleSrcAcc || !sleDstAcc) { - JLOG(j.warn()) - << "trustTransferLockedBalance without sleSrc/sleDst"; + JLOG(j.warn()) << "trustTransferLockedBalance without sleSrc/sleDst"; return tecINTERNAL; } if (amount <= beast::zero) { - JLOG(j.warn()) - << "trustTransferLockedBalance with non-positive amount"; + JLOG(j.warn()) << "trustTransferLockedBalance with non-positive amount"; return tecINTERNAL; } @@ -823,15 +811,12 @@ trustTransferLockedBalance( bool srcHigh = srcAccID > issuerAccID; bool dstHigh = dstAccID > issuerAccID; + bool isIssuer = issuerAccID == srcAccID; // check for freezing, auth, no ripple and TL sanity { - TER result = - trustTransferAllowed( - view, - {srcAccID, dstAccID}, - {currency, issuerAccID}, - j); + TER result = trustTransferAllowed( + view, {srcAccID, dstAccID}, {currency, issuerAccID}, j); JLOG(j.trace()) << "trustTransferLockedBalance: trustTransferAlowed result=" @@ -841,85 +826,94 @@ trustTransferLockedBalance( } // ensure source line exists - Keylet klSrcLine { keylet::line(srcAccID, issuerAccID, currency)}; + Keylet klSrcLine{keylet::line(srcAccID, issuerAccID, currency)}; SLEPtr sleSrcLine = peek(klSrcLine); - if (!sleSrcLine) - return tecNO_LINE; - - // can't transfer a locked balance that does not exist - if (!sleSrcLine->isFieldPresent(sfLockedBalance) || !sleSrcLine->isFieldPresent(sfLockCount)) + // source account IS issuer + if (!isIssuer) { - JLOG(j.trace()) - << "trustTransferLockedBalance could not find sfLockedBalance/sfLockCount on source line"; - return tecUNFUNDED_PAYMENT; - } + if (!sleSrcLine) + return tecNO_LINE; - - // decrement source balance - { - STAmount priorBalance = - srcHigh ? -((*sleSrcLine)[sfBalance]) : (*sleSrcLine)[sfBalance]; - - STAmount priorLockedBalance = - srcHigh ? -((*sleSrcLine)[sfLockedBalance]) : (*sleSrcLine)[sfLockedBalance]; - - uint32_t priorLockCount = (*sleSrcLine)[sfLockCount]; - - AccountID srcIssuerAccID = - sleSrcLine->getFieldAmount(srcHigh ? sfLowLimit : sfHighLimit).getIssuer(); - - // check they have sufficient funds - if (amount > priorLockedBalance) + // can't transfer a locked balance that does not exist + if (!sleSrcLine->isFieldPresent(sfLockedBalance) || + !sleSrcLine->isFieldPresent(sfLockCount)) { - JLOG(j.trace()) - << "trustTransferLockedBalance amount > lockedBalance: " - << "amount=" << amount << " lockedBalance=" - << priorLockedBalance; + JLOG(j.trace()) << "trustTransferLockedBalance could not find " + "sfLockedBalance/sfLockCount on source line"; return tecUNFUNDED_PAYMENT; } - STAmount finalBalance = priorBalance - amount; - - STAmount finalLockedBalance = priorLockedBalance - amount; - - uint32_t finalLockCount = priorLockCount + deltaLockCount; - - // check if there is significant precision loss - if (!isAddable(priorBalance, amount) || - !isAddable(priorLockedBalance, amount)) - return tecPRECISION_LOSS; - - // sanity check possible overflows on the lock counter - if ((deltaLockCount > 0 && priorLockCount > finalLockCount) || - (deltaLockCount < 0 && priorLockCount < finalLockCount) || - (deltaLockCount == 0 && priorLockCount != finalLockCount)) - return tecOVERSIZE; - - // this should never happen but defensively check it here before updating sle - if (finalBalance < beast::zero || finalLockedBalance < beast::zero) + // decrement source balance { - JLOG(j.warn()) - << "trustTransferLockedBalance results in a negative balance on source line"; - return tecINTERNAL; - } + STAmount priorBalance = srcHigh ? -((*sleSrcLine)[sfBalance]) + : (*sleSrcLine)[sfBalance]; - if constexpr(!dryRun) - { - sleSrcLine->setFieldAmount(sfBalance, srcHigh ? -finalBalance : finalBalance); + STAmount priorLockedBalance = srcHigh + ? -((*sleSrcLine)[sfLockedBalance]) + : (*sleSrcLine)[sfLockedBalance]; - if (finalLockedBalance == beast::zero || finalLockCount == 0) + uint32_t priorLockCount = (*sleSrcLine)[sfLockCount]; + + AccountID srcIssuerAccID = + sleSrcLine->getFieldAmount(srcHigh ? sfLowLimit : sfHighLimit) + .getIssuer(); + + // check they have sufficient funds + if (amount > priorLockedBalance) { - sleSrcLine->makeFieldAbsent(sfLockedBalance); - sleSrcLine->makeFieldAbsent(sfLockCount); + JLOG(j.trace()) + << "trustTransferLockedBalance amount > lockedBalance: " + << "amount=" << amount + << " lockedBalance=" << priorLockedBalance; + return tecUNFUNDED_PAYMENT; } - else + + STAmount finalBalance = priorBalance - amount; + + STAmount finalLockedBalance = priorLockedBalance - amount; + + uint32_t finalLockCount = priorLockCount + deltaLockCount; + + // check if there is significant precision loss + if (!isAddable(priorBalance, amount) || + !isAddable(priorLockedBalance, amount)) + return tecPRECISION_LOSS; + + // sanity check possible overflows on the lock counter + if ((deltaLockCount > 0 && priorLockCount > finalLockCount) || + (deltaLockCount < 0 && priorLockCount < finalLockCount) || + (deltaLockCount == 0 && priorLockCount != finalLockCount)) + return tecOVERSIZE; + + // this should never happen but defensively check it here before + // updating sle + if (finalBalance < beast::zero || finalLockedBalance < beast::zero) { - sleSrcLine->setFieldAmount(sfLockedBalance, srcHigh ? -finalLockedBalance : finalLockedBalance); - sleSrcLine->setFieldU32(sfLockCount, finalLockCount); + JLOG(j.warn()) << "trustTransferLockedBalance results in a " + "negative balance on source line"; + return tecINTERNAL; + } + + if constexpr (!dryRun) + { + sleSrcLine->setFieldAmount( + sfBalance, srcHigh ? -finalBalance : finalBalance); + + if (finalLockedBalance == beast::zero || finalLockCount == 0) + { + sleSrcLine->makeFieldAbsent(sfLockedBalance); + sleSrcLine->makeFieldAbsent(sfLockCount); + } + else + { + sleSrcLine->setFieldAmount( + sfLockedBalance, + srcHigh ? -finalLockedBalance : finalLockedBalance); + sleSrcLine->setFieldU32(sfLockCount, finalLockCount); + } } } - } // dstLow XNOR srcLow tells us if we need to flip the balance amount @@ -931,10 +925,9 @@ trustTransferLockedBalance( // the destination will sometimes get less depending on xfer rate // with any difference in tokens burned - auto dstAmt = - xferRate == parityRate - ? amount - : multiplyRound(amount, xferRate, amount.issue(), true); + auto dstAmt = xferRate == parityRate + ? amount + : multiplyRound(amount, xferRate, amount.issue(), true); // check for a destination line Keylet klDstLine = keylet::line(dstAccID, issuerAccID, currency); @@ -953,9 +946,12 @@ trustTransferLockedBalance( dstBalanceDrops < view.fees().accountReserve(ownerCount + 1)) return tecNO_LINE_INSUF_RESERVE; - // yes we can... we will + // yes we can... we will - if constexpr(!dryRun) + auto const finalDstAmt = isIssuer ? dstAmt + : flipDstAmt ? -dstAmt + : dstAmt; + if constexpr (!dryRun) { // clang-format off if (TER const ter = trustCreate( @@ -968,7 +964,7 @@ trustTransferLockedBalance( false, // authorize account (sleDstAcc->getFlags() & lsfDefaultRipple) == 0, false, // freeze trust line - flipDstAmt ? -dstAmt : dstAmt, // initial balance + finalDstAmt, // initial balance Issue(currency, dstAccID), // limit of zero 0, // quality in 0, // quality out @@ -996,45 +992,52 @@ trustTransferLockedBalance( if (finalBalance < priorBalance) { - JLOG(j.warn()) - << "trustTransferLockedBalance resulted in a lower/equal final balance on dest line"; + JLOG(j.warn()) << "trustTransferLockedBalance resulted in a " + "lower/equal final balance on dest line"; return tecINTERNAL; } if (finalBalance > dstLimit && actingAccID != dstAccID) { - JLOG(j.trace()) - << "trustTransferLockedBalance would increase dest line above limit without permission"; + JLOG(j.trace()) << "trustTransferLockedBalance would increase dest " + "line above limit without permission"; return tecPATH_DRY; } - + // check if there is significant precision loss if (!isAddable(priorBalance, dstAmt)) return tecPRECISION_LOSS; - if constexpr(!dryRun) - sleDstLine->setFieldAmount(sfBalance, dstHigh ? -finalBalance : finalBalance); + finalBalance = isIssuer ? -finalBalance + : dstHigh + ? -finalBalance + : finalBalance; + if constexpr (!dryRun) + sleDstLine->setFieldAmount(sfBalance, finalBalance); } if constexpr (!dryRun) { static_assert(std::is_same::value); - // check if source line ended up in default state and adjust owner count if it did - if (isTrustDefault(sleSrcAcc, sleSrcLine)) + // check if source line ended up in default state and adjust owner count + // if it did + if (!isIssuer) { - uint32_t flags = sleSrcLine->getFieldU32(sfFlags); - uint32_t fReserve { srcHigh ? lsfHighReserve : lsfLowReserve }; - if (flags & fReserve) + if (isTrustDefault(sleSrcAcc, sleSrcLine)) { - sleSrcLine->setFieldU32(sfFlags, flags & ~fReserve); - adjustOwnerCount(view, sleSrcAcc, -1, j); - view.update(sleSrcAcc); + uint32_t flags = sleSrcLine->getFieldU32(sfFlags); + uint32_t fReserve{srcHigh ? lsfHighReserve : lsfLowReserve}; + if (flags & fReserve) + { + sleSrcLine->setFieldU32(sfFlags, flags & ~fReserve); + adjustOwnerCount(view, sleSrcAcc, -1, j); + view.update(sleSrcAcc); + } } + view.update(sleSrcLine); } - view.update(sleSrcLine); - // a destination line already existed and was updated if (sleDstLine) view.update(sleDstLine); diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index a109abc82..c8448f259 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -3483,6 +3483,53 @@ struct Escrow_test : public beast::unit_test::suite } } + void + testICGW(FeatureBitset features) + { + testcase("IC Gateway"); + using namespace test::jtx; + using namespace std::literals; + + auto const alice = Account("alice"); + auto const gw = Account{"gateway"}; + auto const USD = gw["USD"]; + { + // test create escrow from issuer with ic + // test with dest tl + // test finish from destination account + Env env(*this, features); + env.fund(XRP(5000), alice, gw); + env.close(); + env.trust(USD(10000), alice); + env.close(); + env(pay(gw, alice, USD(5000))); + env.close(); + auto const seq1 = env.seq(gw); + auto const preAlice = env.balance(alice, USD.issue()); + env(escrow(gw, alice, USD(1000)), condition(cb1), finish_time(env.now() + 1s), fee(1500)); + env.close(); + env(finish(alice, gw, seq1), condition(cb1), fulfillment(fb1), fee(1500)); + env.close(); + BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + USD(1000)); + } + { + // setup env + Env env(*this, features); + env.fund(XRP(5000), alice, gw); + env.close(); + + // test create escrow from issuer with ic no dest tl + auto const seq1 = env.seq(gw); + auto const preAlice = env.balance(alice, USD.issue()); + env(escrow(gw, alice, USD(1000)), condition(cb1), finish_time(env.now() + 1s), fee(1500)); + env.close(); + // test finish from dest account + env(finish(alice, gw, seq1), condition(cb1), fulfillment(fb1), fee(1500)); + env.close(); + BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + USD(1000)); + } + } + void testWithFeats(FeatureBitset features) { @@ -3508,6 +3555,7 @@ struct Escrow_test : public beast::unit_test::suite testICMetaAndOwnership(features); testICConsequences(features); testICEscrowWithTickets(features); + testICGW(features); } public: diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index 7b176fad2..d2d00ea3a 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -122,6 +122,17 @@ struct PayChan_test : public beast::unit_test::suite return STAmount(iou, 0); } + static STAmount + limitAmount( + jtx::Env const& env, + jtx::Account const& account, + jtx::Account const& gw, + jtx::IOU const& iou) + { + auto const sle = env.le(keylet::line(account, gw, iou.currency)); + return (*sle)[sfHighLimit]; + } + static std::optional channelExpiration(ReadView const& view, uint256 const& chan) { @@ -4540,9 +4551,130 @@ struct PayChan_test : public beast::unit_test::suite } void - testICAbuseTL(FeatureBitset features) + testICGateway(FeatureBitset features) { - testcase("IC Abuse Trust Line"); + testcase("IC Gateway"); + using namespace test::jtx; + using namespace std::literals; + + auto const alice = Account("alice"); + auto const gw = Account{"gateway"}; + auto const USD = gw["USD"]; + { + // test create paychan from issuer with ic + // test where dest has no tl + // test claim from issuer account + Env env(*this, features); + env.fund(XRP(10000), alice, gw); + env.close(); + auto const pk = gw.pk(); + auto const settleDelay = 100s; + auto const chan = channel(gw, alice, env.seq(gw)); + env(create(gw, alice, USD(1000), settleDelay, pk)); + env.close(); + + // gw can not claim + auto const preAlice = env.balance(alice, USD.issue()); + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + auto const delta = USD(500); + auto const reqBal = chanBal + delta; + auto const authAmt = reqBal + USD(100); + env(claim(gw, chan, reqBal, authAmt), ter(tecNO_LINE)); + } + { + // test create paychan from issuer with ic + // test where dest has no tl + // test claim from destination account + Env env(*this, features); + env.fund(XRP(10000), alice, gw); + env.close(); + auto const pk = gw.pk(); + auto const settleDelay = 100s; + auto const chan = channel(gw, alice, env.seq(gw)); + env(create(gw, alice, USD(1000), settleDelay, pk)); + env.close(); + + // alice can claim + auto const preAlice = env.balance(alice, USD.issue()); + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + auto const delta = USD(500); + auto const reqBal = chanBal + delta; + auto const authAmt = reqBal + USD(100); + auto const sig = signClaimICAuth(gw.pk(), gw.sk(), chan, authAmt); + env(claim(alice, chan, reqBal, authAmt, Slice(sig), gw.pk())); + env.close(); + BEAST_EXPECT(preAlice == USD(0)); + BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta); + } + { + // test create paychan from issuer with ic + // test where dest has tl + // test claim from issuer/source account + Env env(*this, features); + env.fund(XRP(10000), alice, gw); + env.close(); + env.trust(USD(100000), alice); + env.close(); + env(pay(gw, alice, USD(10000))); + env.close(); + auto const pk = gw.pk(); + auto const settleDelay = 100s; + auto const chan = channel(gw, alice, env.seq(gw)); + env(create(gw, alice, USD(1000), settleDelay, pk)); + env.close(); + + // gw can claim + auto const preAlice = env.balance(alice, USD.issue()); + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + auto const delta = USD(500); + auto const reqBal = chanBal + delta; + auto const authAmt = reqBal + USD(100); + env(claim(gw, chan, reqBal, authAmt)); + env.close(); + BEAST_EXPECT(preAlice == USD(10000)); + BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta); + } + { + // test create paychan from issuer with ic + // test where dest has tl + // test fund paychan from issuer with ic + // test claim from issuer/source account + Env env(*this, features); + env.fund(XRP(10000), alice, gw); + env.close(); + env.trust(USD(100000), alice); + env.close(); + env(pay(gw, alice, USD(10000))); + env.close(); + auto const pk = gw.pk(); + auto const settleDelay = 100s; + auto const chan = channel(gw, alice, env.seq(gw)); + env(create(gw, alice, USD(1000), settleDelay, pk)); + env.close(); + + env(fund(gw, chan, USD(1000))); + + // gw can claim + auto const preAlice = env.balance(alice, USD.issue()); + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + auto const delta = USD(500); + auto const reqBal = chanBal + delta; + auto const authAmt = reqBal + USD(100); + env(claim(gw, chan, reqBal, authAmt)); + env.close(); + BEAST_EXPECT(preAlice == USD(10000)); + BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta); + } + } + + void + testICTLFeatures(FeatureBitset features) + { + testcase("IC Trustline Features"); using namespace test::jtx; using namespace std::literals; @@ -4550,8 +4682,213 @@ struct PayChan_test : public beast::unit_test::suite auto const bob = Account("bob"); auto const carol = Account("carol"); auto const gw = Account{"gateway"}; - auto const USD = gw["USD"]; + auto const USD = gw["USD"]; + auto const aliceUSD = alice["USD"]; + auto const bobUSD = bob["USD"]; + + // test LimitAmount + { + Env env(*this, features); + env.fund(XRP(10000), alice, bob, gw); + env.close(); + env.trust(USD(1000), alice); + env.trust(USD(1000), bob); + env.close(); + env(pay(gw, alice, USD(1000))); + env(pay(gw, bob, USD(1000))); + env.close(); + + auto const pk = alice.pk(); + auto const settleDelay = 100s; + auto const chan = channel(alice, bob, env.seq(alice)); + env(create(alice, bob, USD(1000), settleDelay, pk)); + env.close(); + BEAST_EXPECT(channelBalance(*env.current(), chan) == USD(0)); + BEAST_EXPECT(channelAmount(*env.current(), chan) == USD(1000)); + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + BEAST_EXPECT(chanBal == USD(0)); + BEAST_EXPECT(chanAmt == USD(1000)); + auto preBob = env.balance(bob); + auto const delta = USD(50); + auto reqBal = chanBal + delta; + auto authAmt = reqBal + USD(100); + assert(reqBal <= chanAmt); + auto const preLocked = lockedAmount(env, alice, gw, USD); + BEAST_EXPECT(preLocked == USD(1000)); + // alice cannot claim because bobs amount would be > than limit + env(claim(alice, chan, reqBal, authAmt), ter(tecPATH_DRY)); + + // bob can claim, increasing the limit amount + auto const preBobLimit = limitAmount(env, bob, gw, USD); + auto const sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt); + env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); + env.close(); + auto const postBobLimit = limitAmount(env, bob, gw, USD); + // BEAST_EXPECT(postBobLimit == preBobLimit + delta); + } + // test asfRequireAuth + { + Env env(*this, features); + env.fund(XRP(1000), alice, bob, gw); + env(fset(gw, asfRequireAuth)); + env.close(); + env(trust(gw, aliceUSD(10000)), txflags(tfSetfAuth)); + env(trust(alice, USD(10000))); + env.close(); + env(pay(gw, alice, USD(1000))); + env.close(); + + auto const gwLimit = limitAmount(env, gw, alice, aliceUSD); + auto const aliceLimit = limitAmount(env, alice, gw, USD); + + auto const pk = alice.pk(); + auto const settleDelay = 100s; + auto chan = channel(alice, bob, env.seq(alice)); + // alice cannot create because bob's trustline is not authorized + // all parties must be authorized + env(create(alice, bob, USD(100), settleDelay, pk), ter(tecNO_AUTH)); + env.close(); + + env(trust(gw, bobUSD(10000)), txflags(tfSetfAuth)); + env(trust(bob, USD(10000))); + env.close(); + env(pay(gw, bob, USD(1000))); + env.close(); + + // alice can now create because bob's trustline is authorized + chan = channel(alice, bob, env.seq(alice)); + env(create(alice, bob, USD(100), settleDelay, pk)); + env.close(); + + auto const delta = USD(50); + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + auto reqBal = chanBal + delta; + auto authAmt = reqBal + USD(100); + // alice can claim + env(claim(alice, chan, reqBal, authAmt)); + env.close(); + // bob can claim + // auto const sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt); + // env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); + // env.close(); + } + // test TransferRate + { + Env env(*this, features); + env.fund(XRP(10000), alice, bob, gw); + env(rate(gw, 1.25)); + env.close(); + env.trust(USD(100000), alice); + env.trust(USD(100000), bob); + env.close(); + env(pay(gw, alice, USD(10000))); + env(pay(gw, bob, USD(10000))); + env.close(); + + // env(pay(alice, bob, USD(100)), sendmax(USD(125))); + // env(pay(alice, bob, USD(100)), txflags(tfPartialPayment)); + // env.close(); + // env.require( + // balance(alice, xrpMinusFee(env, 10000 - 50)), + // balance(bob, USD(2.5)), // owner pays transfer fee + // balance(carol, USD(50))); + + auto const pk = alice.pk(); + auto const settleDelay = 100s; + auto const chan = channel(alice, bob, env.seq(alice)); + env(create(alice, bob, USD(1000), settleDelay, pk)); + env.close(); + + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + auto const delta = USD(100); + auto reqBal = chanBal + delta; + auto authAmt = reqBal + USD(200); + // alice can claim + env(claim(alice, chan, reqBal, authAmt)); + env.close(); + + // bob can claim, increasing the limit amount + // auto const preBobLimit = limitAmount(env, bob, gw, USD); + // auto const sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt); + // env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); + // env.close(); + + auto const postLocked = lockedAmount(env, alice, gw, USD); + auto const aliceLimit = limitAmount(env, alice, gw, USD); + auto const bobLimit = limitAmount(env, bob, gw, USD); + // std::cout << "ALICE AMOUNT: " << env.balance(alice, USD.issue()) << "\n"; + // std::cout << "BOB AMOUNT: " << env.balance(bob, USD.issue()) << "\n"; + // std::cout << "ALICE LIMIT: " << aliceLimit << "\n"; + // std::cout << "BOB LIMIT: " << bobLimit << "\n"; + // std::cout << "POST LOCKED: " << postLocked << "\n"; + // std::cout << "CHAN BAL: " << channelBalance(*env.current(), chan) << "\n"; + // std::cout << "CHAN AUTH: " << channelAmount(*env.current(), chan) << "\n"; + } + // test Global Freeze + { + Env env(*this, features); + env.fund(XRP(10000), alice, bob, gw); + env.close(); + env.trust(USD(100000), alice); + env.trust(USD(100000), bob); + env.close(); + env(pay(gw, alice, USD(10000))); + env(pay(gw, bob, USD(10000))); + env.close(); + + env(fset(gw, asfGlobalFreeze)); + env.close(); + + auto const pk = alice.pk(); + auto const settleDelay = 100s; + auto chan = channel(alice, bob, env.seq(alice)); + env(create(alice, bob, USD(1000), settleDelay, pk), ter(tecFROZEN)); + env.close(); + + env(fclear(gw, asfGlobalFreeze)); + env.close(); + + chan = channel(alice, bob, env.seq(alice)); + env(create(alice, bob, USD(1000), settleDelay, pk)); + env.close(); + + env(fset(gw, asfGlobalFreeze)); + env.close(); + + auto chanBal = channelBalance(*env.current(), chan); + auto chanAmt = channelAmount(*env.current(), chan); + + auto const delta = USD(10); + auto reqBal = chanBal + delta; + auto authAmt = reqBal + USD(100); + // alice cannot claim - tl global freeze + env(claim(alice, chan, reqBal, authAmt), ter(tecFROZEN)); + // bob cannot claim - tl global freeze + auto sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt); + env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ter(tecFROZEN)); + env.close(); + + env(fclear(gw, asfGlobalFreeze)); + env.close(); + + // alice can claim + env(claim(alice, chan, reqBal, authAmt)); + env.close(); + + // update channel values for claim + chanBal = channelBalance(*env.current(), chan); + chanAmt = channelAmount(*env.current(), chan); + reqBal = chanBal + delta; + authAmt = reqBal + USD(100); + // bob can claim + sig = signClaimICAuth(alice.pk(), alice.sk(), chan, authAmt); + env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); + env.close(); + } { // test pay more than locked amount // ie. has 10000, lock 1000 then try to pay 10000 @@ -4617,21 +4954,6 @@ struct PayChan_test : public beast::unit_test::suite env(create(alice, bob, USD(10000), settleDelay, pk), ter(tecUNFUNDED_PAYMENT)); } - { - // test create paychan from issuer with ic - Env env(*this, features); - env.fund(XRP(10000), alice, gw); - env.close(); - env.trust(USD(100000), alice); - env.close(); - env(pay(gw, alice, USD(10000))); - env.close(); - // Create a channel from gw to alice - auto const pk = alice.pk(); - auto const settleDelay = 100s; - env(create(gw, alice, USD(1000), settleDelay, pk), - ter(temBAD_SRC_ACCOUNT)); - } } void @@ -4676,7 +4998,8 @@ struct PayChan_test : public beast::unit_test::suite testICAccountDelete(features); testICUsingTickets(features); testICAutoTL(features); - testICAbuseTL(features); + testICGateway(features); + testICTLFeatures(features); } public: