diff --git a/src/ripple/app/tx/impl/Escrow.cpp b/src/ripple/app/tx/impl/Escrow.cpp index 9482250dd..992c0cb44 100644 --- a/src/ripple/app/tx/impl/Escrow.cpp +++ b/src/ripple/app/tx/impl/Escrow.cpp @@ -218,7 +218,7 @@ EscrowCreate::doApply() auto const balance = STAmount((*sle)[sfBalance]).xrp(); auto const reserve = ctx_.view().fees().accountReserve((*sle)[sfOwnerCount] + 1); - bool isIssuer = amount.getIssuer() == account; + bool const isIssuer = amount.getIssuer() == account; if (balance < reserve) return tecINSUFFICIENT_RESERVE; @@ -237,7 +237,7 @@ EscrowCreate::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; - TER result = trustTransferAllowed( + TER const result = trustTransferAllowed( ctx_.view(), {account, ctx_.tx[sfDestination]}, amount.issue(), @@ -263,7 +263,7 @@ EscrowCreate::doApply() return tecNO_LINE; { - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun); JLOG(ctx_.journal.trace()) @@ -296,13 +296,11 @@ 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. - auto xferRate = transferRate(view(), amount.getIssuer()); Keylet const escrowKeylet = keylet::escrow(account, seqID(ctx_)); auto const slep = std::make_shared(escrowKeylet); (*slep)[sfAmount] = ctx_.tx[sfAmount]; (*slep)[sfAccount] = account; - (*slep)[sfTransferRate] = xferRate.value; (*slep)[~sfCondition] = ctx_.tx[~sfCondition]; (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; (*slep)[sfDestination] = ctx_.tx[sfDestination]; @@ -310,6 +308,12 @@ EscrowCreate::doApply() (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter]; (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + if (ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) + { + auto const xferRate = transferRate(view(), amount.getIssuer()); + (*slep)[~sfTransferRate] = xferRate.value; + } + ctx_.view().insert(slep); // Add escrow to sender's owner directory @@ -346,7 +350,7 @@ EscrowCreate::doApply() return tecNO_LINE; // do the lock-up for real now - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); JLOG(ctx_.journal.trace()) @@ -469,7 +473,7 @@ EscrowFinish::doApply() AccountID const account = (*slep)[sfAccount]; auto const sle = ctx_.view().peek(keylet::account(account)); - auto amount = slep->getFieldAmount(sfAmount); + auto const amount = slep->getFieldAmount(sfAmount); // If a cancel time is present, a finish operation should only succeed prior // to that time. fix1571 corrects a logic error in the check that would make @@ -576,8 +580,11 @@ EscrowFinish::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; + if (!slep->isFieldPresent(sfTransferRate)) + return tecINTERNAL; + Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate)); - auto issuerAccID = amount.getIssuer(); + auto const issuerAccID = amount.getIssuer(); auto const xferRate = transferRate(view(), issuerAccID); // update if issuer rate is less than locked rate if (xferRate < lockedRate) @@ -585,7 +592,7 @@ EscrowFinish::doApply() // perform a dry run of the transfer before we // change anything on-ledger - TER result = trustTransferLockedBalance( + TER const result = trustTransferLockedBalance( ctx_.view(), account_, // txn signing account sle, // src account @@ -632,8 +639,11 @@ EscrowFinish::doApply() else { // compute transfer fee, if any + if (!slep->isFieldPresent(sfTransferRate)) + return tecINTERNAL; + Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate)); - auto issuerAccID = amount.getIssuer(); + auto const issuerAccID = amount.getIssuer(); auto const xferRate = transferRate(view(), issuerAccID); // update if issuer rate is less than locked rate if (xferRate < lockedRate) @@ -642,7 +652,7 @@ EscrowFinish::doApply() // all the significant complexity of checking the validity of this // transfer and ensuring the lines exist etc is hidden away in this // function, all we need to do is call it and return if unsuccessful. - TER result = trustTransferLockedBalance( + TER const result = trustTransferLockedBalance( ctx_.view(), account_, // txn signing account sle, // src account @@ -733,8 +743,8 @@ 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; + auto const amount = slep->getFieldAmount(sfAmount); + bool const isIssuer = amount.getIssuer() == account; std::shared_ptr sleLine; @@ -750,7 +760,7 @@ EscrowCancel::doApply() account, amount.getIssuer(), amount.getCurrency())); // dry run before we make any changes to ledger - if (TER result = trustAdjustLockedBalance( + if (TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, -amount, -1, ctx_.journal, DryRun); result != tesSUCCESS) return result; @@ -794,7 +804,7 @@ EscrowCancel::doApply() if (!isIssuer) { // unlock previously locked tokens from source line - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, -amount, -1, ctx_.journal, WetRun); JLOG(ctx_.journal.trace()) diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index 7df95850b..c666a2b35 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -361,7 +361,7 @@ NoZeroEscrow::finalize( if (bad_ && rv.rules().enabled(featurePaychanAndEscrowForTokens) && txn.isFieldPresent(sfTransactionType)) { - uint16_t tt = txn.getFieldU16(sfTransactionType); + uint16_t const tt = txn.getFieldU16(sfTransactionType); if (tt == ttESCROW_CANCEL || tt == ttESCROW_FINISH) return true; diff --git a/src/ripple/app/tx/impl/PayChan.cpp b/src/ripple/app/tx/impl/PayChan.cpp index 827cd9d05..b24f2d1b8 100644 --- a/src/ripple/app/tx/impl/PayChan.cpp +++ b/src/ripple/app/tx/impl/PayChan.cpp @@ -133,7 +133,7 @@ closeChannel( keylet::line(src, amount.getIssuer(), amount.getCurrency())); // dry run - TER result = + TER const result = trustAdjustLockedBalance(view, sleLine, -amount, -1, j, DryRun); JLOG(j.trace()) << "closeChannel: trustAdjustLockedBalance(dry) result=" @@ -178,7 +178,7 @@ closeChannel( (*sle)[sfBalance] = (*sle)[sfBalance] + amount; else { - TER result = + TER const result = trustAdjustLockedBalance(view, sleLine, -amount, -1, j, WetRun); JLOG(j.trace()) << "closeChannel: trustAdjustLockedBalance(wet) result=" @@ -257,7 +257,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) return tecINSUFFICIENT_RESERVE; auto const dst = ctx.tx[sfDestination]; - bool isIssuer = amount.getIssuer() == account; + bool const isIssuer = amount.getIssuer() == account; // Check reserve and funds availability if (isXRP(amount) && balance < reserve + amount) @@ -272,7 +272,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) // check for any possible bars to a channel existing // between these accounts for this asset { - TER result = trustTransferAllowed( + TER const result = trustTransferAllowed( ctx.view, {account, dst}, amount.issue(), ctx.j); JLOG(ctx.j.trace()) << "PayChanCreate::preclaim trustTransferAllowed result=" @@ -287,7 +287,7 @@ PayChanCreate::preclaim(PreclaimContext const& ctx) { auto sleLine = ctx.view.read(keylet::line( account, amount.getIssuer(), amount.getCurrency())); - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx.view, sleLine, amount, 1, ctx.j, DryRun); JLOG(ctx.j.trace()) << "PayChanCreate::preclaim " "trustAdjustLockedBalance(dry) result=" @@ -334,8 +334,7 @@ PayChanCreate::doApply() auto const dst = ctx_.tx[sfDestination]; STAmount const amount{ctx_.tx[sfAmount]}; - bool isIssuer = amount.getIssuer() == account; - auto xferRate = transferRate(view(), amount.getIssuer()); + bool const isIssuer = amount.getIssuer() == account; // Create PayChan in ledger. // @@ -353,12 +352,17 @@ PayChanCreate::doApply() (*slep)[sfAccount] = account; (*slep)[sfDestination] = dst; (*slep)[sfSettleDelay] = ctx_.tx[sfSettleDelay]; - (*slep)[sfTransferRate] = xferRate.value; (*slep)[sfPublicKey] = ctx_.tx[sfPublicKey]; (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; (*slep)[~sfSourceTag] = ctx_.tx[~sfSourceTag]; (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + if (ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) + { + auto xferRate = transferRate(view(), amount.getIssuer()); + (*slep)[~sfTransferRate] = xferRate.value; + } + ctx_.view().insert(slep); // Add PayChan to owner directory @@ -398,7 +402,7 @@ PayChanCreate::doApply() if (!sleLine) return tecNO_LINE; - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); JLOG(ctx_.journal.trace()) @@ -470,24 +474,28 @@ PayChanFund::doApply() AccountID const src = (*slep)[sfAccount]; auto const txAccount = ctx_.tx[sfAccount]; auto const expiration = (*slep)[~sfExpiration]; - bool isIssuer = amount.getIssuer() == txAccount; - // auto const chanFunds = (*slep)[sfAmount]; - - // adjust transfer rate - Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate)); - auto issuerAccID = amount.getIssuer(); - auto const xferRate = transferRate(view(), issuerAccID); - // update if issuer rate less than locked rate - if (xferRate < lockedRate) - (*slep)[sfTransferRate] = xferRate.value; - // throw if issuer rate greater than locked rate - if (xferRate > lockedRate) - return temBAD_TRANSFER_RATE; + bool const 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)) { + // adjust transfer rate + if (!slep->isFieldPresent(sfTransferRate)) + return tecINTERNAL; + + Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate)); + auto const issuerAccID = amount.getIssuer(); + auto const xferRate = transferRate(view(), issuerAccID); + + // update if issuer rate less than locked rate + if (xferRate < lockedRate) + (*slep)[~sfTransferRate] = xferRate.value; + + // throw if issuer rate greater than locked rate + if (xferRate > lockedRate) + return temBAD_TRANSFER_RATE; + // issuer does not need to lock anything if (!isIssuer) { @@ -497,7 +505,7 @@ PayChanFund::doApply() sleLine = ctx_.view().peek(keylet::line( (*slep)[sfAccount], amount.getIssuer(), amount.getCurrency())); - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, DryRun); JLOG(ctx_.journal.trace()) @@ -571,7 +579,7 @@ PayChanFund::doApply() // issuer does not need to lock anything if (!isIssuer) { - TER result = trustAdjustLockedBalance( + TER const result = trustAdjustLockedBalance( ctx_.view(), sleLine, amount, 1, ctx_.journal, WetRun); JLOG(ctx_.journal.trace()) @@ -715,11 +723,11 @@ PayChanClaim::doApply() } if (reqBalance > chanFunds) - return tecINSUFFICIENT_FUNDS; + return tecUNFUNDED_PAYMENT; if (reqBalance <= chanBalance) // nothing requested - return tecINSUFFICIENT_FUNDS; + return tecUNFUNDED_PAYMENT; auto sled = ctx_.view().peek(keylet::account(dst)); if (!sled) @@ -746,17 +754,6 @@ PayChanClaim::doApply() } } - // compute transfer fee, if any - Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate)); - auto issuerAccID = chanFunds.getIssuer(); - auto const xferRate = transferRate(view(), issuerAccID); - // update if issuer rate is less than locked rate - if (xferRate < lockedRate) - { - (*slep)[sfTransferRate] = xferRate.value; - lockedRate = xferRate; - } - (*slep)[sfBalance] = ctx_.tx[sfBalance]; STAmount const reqDelta = reqBalance - chanBalance; assert(reqDelta >= beast::zero); @@ -770,8 +767,22 @@ PayChanClaim::doApply() if (!ctx_.view().rules().enabled(featurePaychanAndEscrowForTokens)) return temDISABLED; + // compute transfer fee, if any + if (!slep->isFieldPresent(sfTransferRate)) + return tecINTERNAL; + + Rate lockedRate = ripple::Rate(slep->getFieldU32(sfTransferRate)); + auto const issuerAccID = chanFunds.getIssuer(); + auto const xferRate = transferRate(view(), issuerAccID); + // update if issuer rate is less than locked rate + if (xferRate < lockedRate) + { + (*slep)[~sfTransferRate] = xferRate.value; + lockedRate = xferRate; + } + auto sleSrcAcc = ctx_.view().peek(keylet::account(src)); - TER result = trustTransferLockedBalance( + TER const result = trustTransferLockedBalance( ctx_.view(), txAccount, sleSrcAcc, diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h index 58771c706..cfc1cef18 100644 --- a/src/ripple/ledger/View.h +++ b/src/ripple/ledger/View.h @@ -506,12 +506,12 @@ trustAdjustLockedBalance( // auto const currency = deltaAmt.getCurrency(); auto const issuer = deltaAmt.getIssuer(); - STAmount lowLimit = sleLine->getFieldAmount(sfLowLimit); + STAmount const 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 const high = lowLimit.getIssuer() == issuer; std::vector parties{ high ? sleLine->getFieldAmount(sfHighLimit).getIssuer() @@ -519,7 +519,8 @@ trustAdjustLockedBalance( // check for freezes & auth { - TER result = trustTransferAllowed(view, parties, deltaAmt.issue(), j); + TER const result = + trustTransferAllowed(view, parties, deltaAmt.issue(), j); JLOG(j.trace()) << "trustAdjustLockedBalance: trustTransferAllowed result=" @@ -810,19 +811,19 @@ trustTransferLockedBalance( return tecINTERNAL; } - auto issuerAccID = amount.getIssuer(); - auto currency = amount.getCurrency(); - auto srcAccID = sleSrcAcc->getAccountID(sfAccount); - auto dstAccID = sleDstAcc->getAccountID(sfAccount); + auto const issuerAccID = amount.getIssuer(); + auto const currency = amount.getCurrency(); + auto const srcAccID = sleSrcAcc->getAccountID(sfAccount); + auto const dstAccID = sleDstAcc->getAccountID(sfAccount); - bool srcHigh = srcAccID > issuerAccID; - bool dstHigh = dstAccID > issuerAccID; - bool srcIssuer = issuerAccID == srcAccID; - bool dstIssuer = issuerAccID == dstAccID; + bool const srcHigh = srcAccID > issuerAccID; + bool const dstHigh = dstAccID > issuerAccID; + bool const srcIssuer = issuerAccID == srcAccID; + bool const dstIssuer = issuerAccID == dstAccID; // check for freezing, auth, no ripple and TL sanity { - TER result = trustTransferAllowed( + TER const result = trustTransferAllowed( view, {srcAccID, dstAccID}, {currency, issuerAccID}, j); JLOG(j.trace()) @@ -876,10 +877,6 @@ trustTransferLockedBalance( uint32_t priorLockCount = (*sleSrcLine)[sfLockCount]; - AccountID srcIssuerAccID = - sleSrcLine->getFieldAmount(srcHigh ? sfLowLimit : sfHighLimit) - .getIssuer(); - // check they have sufficient funds if (amount > priorLockedBalance) { diff --git a/src/ripple/ledger/impl/View.cpp b/src/ripple/ledger/impl/View.cpp index 5234b1e25..42bd3fe6a 100644 --- a/src/ripple/ledger/impl/View.cpp +++ b/src/ripple/ledger/impl/View.cpp @@ -260,8 +260,8 @@ accountHolds( if (view.rules().enabled(featurePaychanAndEscrowForTokens) && sle->isFieldPresent(sfLockedBalance)) { - STAmount lockedBalance = sle->getFieldAmount(sfLockedBalance); - STAmount spendableBalance = + STAmount const lockedBalance = sle->getFieldAmount(sfLockedBalance); + STAmount const spendableBalance = amount - (account > issuer ? -lockedBalance : lockedBalance); // RH NOTE: this is defensively programmed, it should never fire @@ -919,18 +919,19 @@ isTrustDefault( { assert(acc && line); - uint32_t tlFlags = line->getFieldU32(sfFlags); + uint32_t const tlFlags = line->getFieldU32(sfFlags); - AccountID highAccID = line->getFieldAmount(sfHighLimit).issue().account; - AccountID lowAccID = line->getFieldAmount(sfLowLimit).issue().account; + AccountID const highAccID = + line->getFieldAmount(sfHighLimit).issue().account; + AccountID const lowAccID = line->getFieldAmount(sfLowLimit).issue().account; - AccountID accID = acc->getAccountID(sfAccount); + AccountID const accID = acc->getAccountID(sfAccount); assert(accID == highAccID || accID == lowAccID); - bool high = accID == highAccID; + bool const high = accID == highAccID; - uint32_t acFlags = line->getFieldU32(sfFlags); + uint32_t const acFlags = line->getFieldU32(sfFlags); const auto fNoRipple{high ? lsfHighNoRipple : lsfLowNoRipple}; const auto fFreeze{high ? lsfHighFreeze : lsfLowFreeze}; @@ -950,9 +951,9 @@ isTrustDefault( if (line->getFieldAmount(high ? sfHighLimit : sfLowLimit) != beast::zero) return false; - uint32_t qualityIn = + uint32_t const qualityIn = line->getFieldU32(high ? sfHighQualityIn : sfLowQualityIn); - uint32_t qualityOut = + uint32_t const qualityOut = line->getFieldU32(high ? sfHighQualityOut : sfLowQualityOut); if (qualityIn && qualityIn != QUALITY_ONE) diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index b3f81ef4a..5665d6e19 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -127,7 +127,7 @@ LedgerFormats::LedgerFormats() {sfAccount, soeREQUIRED}, {sfDestination, soeREQUIRED}, {sfAmount, soeREQUIRED}, - {sfTransferRate, soeREQUIRED}, + {sfTransferRate, soeOPTIONAL}, {sfCondition, soeOPTIONAL}, {sfCancelAfter, soeOPTIONAL}, {sfFinishAfter, soeOPTIONAL}, @@ -250,7 +250,7 @@ LedgerFormats::LedgerFormats() {sfBalance, soeREQUIRED}, {sfPublicKey, soeREQUIRED}, {sfSettleDelay, soeREQUIRED}, - {sfTransferRate, soeREQUIRED}, + {sfTransferRate, soeOPTIONAL}, {sfExpiration, soeOPTIONAL}, {sfCancelAfter, soeOPTIONAL}, {sfSourceTag, soeOPTIONAL}, diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 92a90b653..aafb25b42 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -214,6 +214,20 @@ struct Escrow_test : public beast::unit_test::suite return Rate{0}; } + static STAmount + limitAmount( + jtx::Env const& env, + jtx::Account const& account, + jtx::Account const& gw, + jtx::IOU const& iou) + { + auto const aHigh = account.id() > gw.id(); + auto const sle = env.le(keylet::line(account, gw, iou.currency)); + if (sle && sle->isFieldPresent(aHigh ? sfLowLimit : sfHighLimit)) + return (*sle)[aHigh ? sfLowLimit : sfHighLimit]; + return STAmount(iou, 0); + } + static STAmount lineBalance( jtx::Env const& env, @@ -1674,9 +1688,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenEnablement(FeatureBitset features) + testIOUEnablement(FeatureBitset features) { - testcase("Token Enablement"); + testcase("IOU Enablement"); using namespace jtx; using namespace std::chrono; @@ -1721,13 +1735,13 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenTiming(FeatureBitset features) + testIOUTiming(FeatureBitset features) { using namespace jtx; using namespace std::chrono; { - testcase("Timing: Token Finish Only"); + testcase("Timing: IOU Finish Only"); Env env{*this, features}; auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -1756,7 +1770,7 @@ struct Escrow_test : public beast::unit_test::suite } { - testcase("Timing: Token Cancel Only"); + testcase("Timing: IOU Cancel Only"); Env env{*this, features}; auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -1797,7 +1811,7 @@ struct Escrow_test : public beast::unit_test::suite } { - testcase("Timing: Token Finish and Cancel -> Finish"); + testcase("Timing: IOU Finish and Cancel -> Finish"); Env env{*this, features}; auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -1840,7 +1854,7 @@ struct Escrow_test : public beast::unit_test::suite } { - testcase("Timing: Token Finish and Cancel -> Cancel"); + testcase("Timing: IOU Finish and Cancel -> Cancel"); Env env{*this, features}; auto const alice = Account("alice"); auto const bob = Account("bob"); @@ -1892,9 +1906,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenTags(FeatureBitset features) + testIOUTags(FeatureBitset features) { - testcase("Token Tags"); + testcase("IOU Tags"); using namespace jtx; using namespace std::chrono; @@ -1934,9 +1948,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenDisallowXRP(FeatureBitset features) + testIOUDisallowXRP(FeatureBitset features) { - testcase("Token Disallow XRP"); + testcase("IOU Disallow XRP"); using namespace jtx; using namespace std::chrono; @@ -1976,13 +1990,13 @@ struct Escrow_test : public beast::unit_test::suite } void - testToken1571(FeatureBitset features) + testIOU1571(FeatureBitset features) { using namespace jtx; using namespace std::chrono; { - testcase("Token Implied Finish Time (without fix1571)"); + testcase("IOU Implied Finish Time (without fix1571)"); Env env(*this, supported_amendments() - fix1571); auto const alice = Account("alice"); @@ -2026,7 +2040,7 @@ struct Escrow_test : public beast::unit_test::suite } { - testcase("Token Implied Finish Time (with fix1571)"); + testcase("IOU Implied Finish Time (with fix1571)"); Env env{*this, features}; auto const alice = Account("alice"); @@ -2066,9 +2080,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenFails(FeatureBitset features) + testIOUFails(FeatureBitset features) { - testcase("Token Failure Cases"); + testcase("IOU Failure Cases"); using namespace jtx; using namespace std::chrono; @@ -2222,9 +2236,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenLockup(FeatureBitset features) + testIOULockup(FeatureBitset features) { - testcase("Token Lockup"); + testcase("IOU Lockup"); using namespace jtx; using namespace std::chrono; @@ -2593,9 +2607,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenEscrowConditions(FeatureBitset features) + testIOUEscrowConditions(FeatureBitset features) { - testcase("Token Escrow with CryptoConditions"); + testcase("IOU Escrow with CryptoConditions"); using namespace jtx; using namespace std::chrono; @@ -3089,7 +3103,7 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenMetaAndOwnership(FeatureBitset features) + testIOUMetaAndOwnership(FeatureBitset features) { using namespace jtx; using namespace std::chrono; @@ -3100,7 +3114,7 @@ struct Escrow_test : public beast::unit_test::suite auto const gw = Account{"gateway"}; auto const USD = gw["USD"]; { - testcase("Token Metadata to self"); + testcase("IOU Metadata to self"); Env env{*this, features}; env.fund(XRP(5000), alice, bob, carol, gw); @@ -3181,7 +3195,7 @@ struct Escrow_test : public beast::unit_test::suite } } { - testcase("Token Metadata to other"); + testcase("IOU Metadata to other"); Env env{*this, features}; env.fund(XRP(5000), alice, bob, carol, gw); @@ -3280,9 +3294,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenConsequences(FeatureBitset features) + testIOUConsequences(FeatureBitset features) { - testcase("Token Consequences"); + testcase("IOU Consequences"); using namespace jtx; using namespace std::chrono; @@ -3354,9 +3368,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenEscrowWithTickets(FeatureBitset features) + testIOUEscrowWithTickets(FeatureBitset features) { - testcase("Token Escrow with tickets"); + testcase("IOU Escrow with tickets"); using namespace jtx; using namespace std::chrono; @@ -3504,671 +3518,223 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenRippleState(FeatureBitset features) + testIOURippleState(FeatureBitset features) { - testcase("Token RippleState"); + testcase("IOU RippleState"); using namespace test::jtx; using namespace std::literals; - // src > dst - // src > issuer - // dest no trustline - // negative locked/tl balance + struct TestAccountData { - auto const src = Account("alice2"); - auto const dst = Account("bob0"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; + Account src; + Account dst; + Account gw; + bool hasTrustline; + bool negative; + }; + std::array tests = {{ + // src > dst && src > issuer && dst no trustline + {Account("alice2"), Account("bob0"), Account{"gw0"}, false, true}, + // src < dst && src < issuer && dst no trustline + {Account("carol0"), Account("dan1"), Account{"gw1"}, false, false}, + // // dst > src && dst > issuer && dst no trustline + {Account("dan1"), Account("alice2"), Account{"gw0"}, false, true}, + // // dst < src && dst < issuer && dst no trustline + {Account("bob0"), Account("carol0"), Account{"gw1"}, false, false}, + // // src > dst && src > issuer && dst has trustline + {Account("alice2"), Account("bob0"), Account{"gw0"}, true, true}, + // // src < dst && src < issuer && dst has trustline + {Account("carol0"), Account("dan1"), Account{"gw1"}, true, false}, + // // dst > src && dst > issuer && dst has trustline + {Account("dan1"), Account("alice2"), Account{"gw0"}, true, true}, + // // dst < src && dst < issuer && dst has trustline + {Account("bob0"), Account("carol0"), Account{"gw1"}, true, false}, + }}; + + for (auto const& t : tests) + { Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); + auto const USD = t.gw["USD"]; + env.fund(XRP(5000), t.src, t.dst, t.gw); env.close(); - env.trust(USD(100000), src); + + if (t.hasTrustline) + env.trust(USD(100000), t.src, t.dst); + else + env.trust(USD(100000), t.src); env.close(); - env(pay(gw, src, USD(10000))); + + env(pay(t.gw, t.src, USD(10000))); + if (t.hasTrustline) + env(pay(t.gw, t.dst, USD(10000))); env.close(); // src can create escrow - auto const seq1 = env.seq(src); + auto const seq1 = env.seq(t.src); auto const delta = USD(1000); - env(escrow(src, dst, delta), + env(escrow(t.src, t.dst, delta), condition(cb1), finish_time(env.now() + 1s), fee(1500)); env.close(); - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -delta); + auto const preLocked = lockedAmount(env, t.src, t.gw, USD); + BEAST_EXPECT(preLocked == (t.negative ? -delta : delta)); // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); + auto const preSrc = lineBalance(env, t.src, t.gw, USD); + auto const preDst = lineBalance(env, t.dst, t.gw, USD); - env(finish(dst, src, seq1), + env(finish(t.dst, t.src, seq1), condition(cb1), fulfillment(fb1), fee(1500)); env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); + auto postLocked = lockedAmount(env, t.src, t.gw, USD); BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() + delta); + lineBalance(env, t.src, t.gw, USD) == + (t.negative ? (preSrc + delta) : (preSrc - delta))); BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - } - // src < dst - // src < issuer - // dest no trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const dst = Account("dan1"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); + lineBalance(env, t.dst, t.gw, USD) == + (t.negative ? (preDst - delta) : (preDst + delta))); BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() - delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - } - // dst > src - // dst > issuer - // dest no trustline - // negative locked/tl balance - { - auto const src = Account("dan1"); - auto const dst = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() + delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - } - // dst < src - // dst < issuer - // dest no trustline - // positive locked/tl balance - { - auto const src = Account("bob0"); - auto const dst = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() - delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - } - // src > dst - // src > issuer - // dest has trustline - // negative locked/tl balance - { - auto const src = Account("alice2"); - auto const dst = Account("bob0"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() + delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - } - // src < dst - // src < issuer - // dest has trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const dst = Account("dan1"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() - delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - } - // dst > src - // dst > issuer - // dest has trustline - // negative locked/tl balance - { - auto const src = Account("dan1"); - auto const dst = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() + delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - } - // dst < src - // dst < issuer - // dest has trustline - // positive locked/tl balance - { - auto const src = Account("bob0"); - auto const dst = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create escrow - auto const seq1 = env.seq(src); - auto const delta = USD(1000); - env(escrow(src, dst, delta), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - auto const preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == delta); - - // dst can finish escrow - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - - env(finish(dst, src, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT( - lineBalance(env, src, gw, USD) == preSrc.value() - delta); - BEAST_EXPECT( - lineBalance(env, dst, gw, USD) == preDst.value() + delta); - BEAST_EXPECT(preLocked == postLocked + delta); + postLocked == + (t.negative ? (preLocked + delta) : (preLocked - delta))); } } void - testTokenGateway(FeatureBitset features) + testIOUGateway(FeatureBitset features) { - testcase("Token Gateway"); + testcase("IOU Gateway"); using namespace test::jtx; using namespace std::literals; - // test escrow with issuer - // src > issuer - // no dest trustline - // negative locked/tl balance + struct TestAccountData { - auto const src = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; + Account src; + Account dst; + bool hasTrustline; + bool negative; + }; + std::array gwSrcTests = {{ + // src > dst && src > issuer && dst no trustline + {Account("gw0"), Account{"alice2"}, false, true}, + // // src < dst && src < issuer && dst no trustline + {Account("gw1"), Account{"carol0"}, false, false}, + // // // // // dst > src && dst > issuer && dst no trustline + {Account("gw0"), Account{"dan1"}, false, true}, + // // // // // dst < src && dst < issuer && dst no trustline + {Account("gw1"), Account{"bob0"}, false, false}, + // // // // src > dst && src > issuer && dst has trustline + {Account("gw0"), Account{"alice2"}, true, true}, + // // // // src < dst && src < issuer && dst has trustline + {Account("gw1"), Account{"carol0"}, true, false}, + // // // // dst > src && dst > issuer && dst has trustline + {Account("gw0"), Account{"dan1"}, true, true}, + // // // // dst < src && dst < issuer && dst has trustline + {Account("gw1"), Account{"bob0"}, true, false}, + }}; + + for (auto const& t : gwSrcTests) + { Env env{*this, features}; - env.fund(XRP(5000), src, gw); + auto const USD = t.src["USD"]; + env.fund(XRP(5000), t.dst, t.src); + env.close(); + + if (t.hasTrustline) + env.trust(USD(100000), t.dst); + + env.close(); + + if (t.hasTrustline) + env(pay(t.src, t.dst, USD(10000))); + env.close(); // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), + auto const seq1 = env.seq(t.src); + auto const preDst = lineBalance(env, t.dst, t.src, USD); + env(escrow(t.src, t.dst, USD(1000)), condition(cb1), finish_time(env.now() + 1s), fee(1500)); env.close(); // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), + env(finish(t.dst, t.src, seq1), condition(cb1), fulfillment(fb1), fee(1500)); env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(1000)); + auto const preAmount = t.hasTrustline ? 10000 : 0; + BEAST_EXPECT( + preDst == (t.negative ? -USD(preAmount) : USD(preAmount))); + auto const postAmount = t.hasTrustline ? 11000 : 1000; + BEAST_EXPECT( + lineBalance(env, t.dst, t.src, USD) == + (t.negative ? -USD(postAmount) : USD(postAmount))); + BEAST_EXPECT(lineBalance(env, t.src, t.src, USD) == USD(0)); } - // test escrow with issuer - // src < issuer - // no dest trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; + std::array gwDstTests = {{ + // // // // src > dst && src > issuer && dst has trustline + {Account("alice2"), Account{"gw0"}, true, true}, + // // // // src < dst && src < issuer && dst has trustline + {Account("carol0"), Account{"gw1"}, true, false}, + // // // // dst > src && dst > issuer && dst has trustline + {Account("dan1"), Account{"gw0"}, true, true}, + // // // // dst < src && dst < issuer && dst has trustline + {Account("bob0"), Account{"gw1"}, true, false}, + }}; + + for (auto const& t : gwDstTests) + { Env env{*this, features}; - env.fund(XRP(5000), src, gw); + auto const USD = t.dst["USD"]; + env.fund(XRP(5000), t.dst, t.src); + env.close(); + + env.trust(USD(100000), t.src); + env.close(); + + env(pay(t.dst, t.src, USD(10000))); env.close(); // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), + auto const seq1 = env.seq(t.src); + auto const preSrc = lineBalance(env, t.src, t.dst, USD); + env(escrow(t.src, t.dst, USD(1000)), condition(cb1), finish_time(env.now() + 1s), fee(1500)); env.close(); // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), + env(finish(t.dst, t.src, seq1), condition(cb1), fulfillment(fb1), fee(1500)); env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(1000)); - } - // test escrow with issuer - // dst > issuer - // no dest trustline - // negative locked/tl balance - { - auto const src = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, gw); - env.close(); - - // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(1000)); - } - // test escrow with issuer, no dest trustline - // dst < issuer - // no dest trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, gw); - env.close(); - - // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(1000)); - } - // test escrow with issuer - // src > issuer - // dest has trustline - // negative locked/tl balance - { - auto const src = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - BEAST_EXPECT(preSrc == -USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(11000)); - } - // test escrow with issuer - // src < issuer - // dest has trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - BEAST_EXPECT(preSrc == USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(11000)); - } - // test escrow with issuer - // dst > issuer - // dest has trustline - // negative locked/tl balance - { - auto const src = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - BEAST_EXPECT(preSrc == -USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(11000)); - } - // test escrow with issuer, no dest trustline - // dst < issuer - // dest has trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create escrow - auto const seq1 = env.seq(gw); - auto const preSrc = lineBalance(env, src, gw, USD); - env(escrow(gw, src, USD(1000)), - condition(cb1), - finish_time(env.now() + 1s), - fee(1500)); - env.close(); - - // src can finish escrow, no dest trustline - env(finish(src, gw, seq1), - condition(cb1), - fulfillment(fb1), - fee(1500)); - env.close(); - BEAST_EXPECT(preSrc == USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(11000)); + auto const preAmount = 10000; + BEAST_EXPECT( + preSrc == (t.negative ? -USD(preAmount) : USD(preAmount))); + auto const postAmount = 9000; + BEAST_EXPECT( + lineBalance(env, t.src, t.dst, USD) == + (t.negative ? -USD(postAmount) : USD(postAmount))); + BEAST_EXPECT(lineBalance(env, t.dst, t.dst, USD) == USD(0)); } } void - testTokenLockedRate(FeatureBitset features) + testIOULockedRate(FeatureBitset features) { - testcase("Token Locked Rate"); + testcase("IOU Locked Rate"); using namespace test::jtx; using namespace std::literals; @@ -4324,7 +3890,7 @@ struct Escrow_test : public beast::unit_test::suite BEAST_EXPECT( transferRate.value == std::uint32_t(1000000000 * 1.25)); - // issuer can finish escrow - no rate charged + // alice can finish escrow - no rate charged env(finish(alice, gw, seq1), condition(cb1), fulfillment(fb1), @@ -4338,9 +3904,54 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenTLRequireAuth(FeatureBitset features) + testIOUTLLimitAmount(FeatureBitset features) { - testcase("Token Trustline Require Auth"); + testcase("IOU Trustline Require Auth"); + using namespace test::jtx; + using namespace std::literals; + + auto const alice = Account("alice"); + auto const bob = Account("bob"); + auto const gw = Account{"gateway"}; + auto const USD = gw["USD"]; + + // test LimitAmount + { + Env env{*this, features}; + env.fund(XRP(1000), alice, bob, gw); + env.close(); + env.trust(USD(10000), alice, bob); + env.close(); + env(pay(gw, alice, USD(1000))); + env(pay(gw, bob, USD(1000))); + env.close(); + + // alice can create escrow + auto seq1 = env.seq(alice); + auto const delta = USD(125); + env(escrow(alice, bob, delta), + condition(cb1), + finish_time(env.now() + 1s), + fee(1500)); + env.close(); + + // bob can finish + auto const preBobLimit = limitAmount(env, bob, gw, USD); + env(finish(bob, alice, seq1), + condition(cb1), + fulfillment(fb1), + fee(1500)); + env.close(); + auto const postBobLimit = limitAmount(env, bob, gw, USD); + // bobs limit is NOT changed + BEAST_EXPECT(postBobLimit == preBobLimit); + } + } + + void + testIOUTLRequireAuth(FeatureBitset features) + { + testcase("IOU Trustline Require Auth"); using namespace test::jtx; using namespace std::literals; @@ -4400,9 +4011,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenTLFreeze(FeatureBitset features) + testIOUTLFreeze(FeatureBitset features) { - testcase("Token Trustline Freeze"); + testcase("IOU Trustline Freeze"); using namespace test::jtx; using namespace std::literals; @@ -4539,9 +4150,9 @@ struct Escrow_test : public beast::unit_test::suite } } void - testTokenTLINSF(FeatureBitset features) + testIOUTLINSF(FeatureBitset features) { - testcase("Token Trustline Insuficient Funds"); + testcase("IOU Trustline Insuficient Funds"); using namespace test::jtx; using namespace std::literals; @@ -4604,9 +4215,9 @@ struct Escrow_test : public beast::unit_test::suite } void - testTokenPrecisionLoss(FeatureBitset features) + testIOUPrecisionLoss(FeatureBitset features) { - testcase("Token Precision Loss"); + testcase("IOU Precision Loss"); using namespace test::jtx; using namespace std::literals; @@ -4678,7 +4289,7 @@ struct Escrow_test : public beast::unit_test::suite env(pay(gw, bob, USD(1))); env.close(); - // alice cannot create escrow for 1/10 token - precision loss + // alice cannot create escrow for 1/10 iou - precision loss env(escrow(alice, bob, USD(1)), condition(cb1), finish_time(env.now() + 1s), @@ -4687,7 +4298,7 @@ struct Escrow_test : public beast::unit_test::suite env.close(); auto const seq1 = env.seq(alice); - // alice can create escrow for 1000 token + // alice can create escrow for 1000 iou env(escrow(alice, bob, USD(1000)), condition(cb1), finish_time(env.now() + 1s), @@ -4717,24 +4328,30 @@ struct Escrow_test : public beast::unit_test::suite testMetaAndOwnership(features); testConsequences(features); testEscrowWithTickets(features); - testTokenEnablement(features); - testTokenTiming(features); - testTokenTags(features); - testTokenDisallowXRP(features); - testToken1571(features); - testTokenFails(features); - testTokenLockup(features); - testTokenEscrowConditions(features); - testTokenMetaAndOwnership(features); - testTokenConsequences(features); - testTokenEscrowWithTickets(features); - testTokenRippleState(features); - testTokenGateway(features); - testTokenLockedRate(features); - testTokenTLRequireAuth(features); - testTokenTLFreeze(features); - testTokenTLINSF(features); - testTokenPrecisionLoss(features); + } + + void + testIOUWithFeats(FeatureBitset features) + { + testIOUEnablement(features); + testIOUTiming(features); + testIOUTags(features); + testIOUDisallowXRP(features); + testIOU1571(features); + testIOUFails(features); + testIOULockup(features); + testIOUEscrowConditions(features); + testIOUMetaAndOwnership(features); + testIOUConsequences(features); + testIOUEscrowWithTickets(features); + testIOURippleState(features); + testIOUGateway(features); + testIOULockedRate(features); + testIOUTLLimitAmount(features); + testIOUTLRequireAuth(features); + testIOUTLFreeze(features); + testIOUTLINSF(features); + testIOUPrecisionLoss(features); } public: @@ -4743,11 +4360,13 @@ public: { using namespace test::jtx; FeatureBitset const all{supported_amendments()}; + testWithFeats(all - featurePaychanAndEscrowForTokens); testWithFeats(all); + testIOUWithFeats(all); } }; BEAST_DEFINE_TESTSUITE(Escrow, app, ripple); } // namespace test -} // namespace ripple +} // namespace ripple \ No newline at end of file diff --git a/src/test/app/PayChan_test.cpp b/src/test/app/PayChan_test.cpp index e14b0d533..a7753727f 100644 --- a/src/test/app/PayChan_test.cpp +++ b/src/test/app/PayChan_test.cpp @@ -70,7 +70,7 @@ struct PayChan_test : public beast::unit_test::suite } static Buffer - signClaimTokenAuth( + signClaimIOUAuth( PublicKey const& pk, SecretKey const& sk, uint256 const& channel, @@ -114,10 +114,10 @@ struct PayChan_test : public beast::unit_test::suite static Rate channelRate(ReadView const& view, uint256 const& chan) { - auto const slep = view.read({ltPAYCHAN, chan}); - if (!slep) - return Rate{0}; - return ripple::Rate((*slep)[sfTransferRate]); + auto const sle = view.read({ltPAYCHAN, chan}); + if (sle->isFieldPresent(sfTransferRate)) + return ripple::Rate((*sle)[sfTransferRate]); + return Rate{0}; } static STAmount @@ -140,8 +140,11 @@ struct PayChan_test : public beast::unit_test::suite jtx::Account const& gw, jtx::IOU const& iou) { + auto const aHigh = account.id() > gw.id(); auto const sle = env.le(keylet::line(account, gw, iou.currency)); - return (*sle)[sfHighLimit]; + if (sle && sle->isFieldPresent(aHigh ? sfLowLimit : sfHighLimit)) + return (*sle)[aHigh ? sfLowLimit : sfHighLimit]; + return STAmount(iou, 0); } static STAmount @@ -339,7 +342,7 @@ struct PayChan_test : public beast::unit_test::suite // claim again preBob = env.balance(bob); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), - ter(tecINSUFFICIENT_FUNDS)); + ter(tecUNFUNDED_PAYMENT)); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); BEAST_EXPECT(env.balance(bob) == preBob - feeDrops); @@ -2121,7 +2124,7 @@ struct PayChan_test : public beast::unit_test::suite // A transaction that generates a tec still consumes its ticket. env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ticket::use(bobTicketSeq++), - ter(tecINSUFFICIENT_FUNDS)); + ter(tecUNFUNDED_PAYMENT)); env.require(tickets(bob, env.seq(bob) - bobTicketSeq)); BEAST_EXPECT(env.seq(bob) == bobSeq); @@ -2188,9 +2191,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenSimple(FeatureBitset features) + testIOUSimple(FeatureBitset features) { - testcase("token simple"); + testcase("iou simple"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -2259,11 +2262,11 @@ struct PayChan_test : public beast::unit_test::suite { // No signature claim with bad amounts (negative) - auto const negToken = USD(-100).value(); - auto const posToken = USD(100).value(); - env(claim(alice, chan, negToken, negToken), ter(temBAD_AMOUNT)); - env(claim(alice, chan, posToken, negToken), ter(temBAD_AMOUNT)); - env(claim(alice, chan, negToken, posToken), ter(temBAD_AMOUNT)); + auto const negIOU = USD(-100).value(); + auto const posIOU = USD(100).value(); + env(claim(alice, chan, negIOU, negIOU), ter(temBAD_AMOUNT)); + env(claim(alice, chan, posIOU, negIOU), ter(temBAD_AMOUNT)); + env(claim(alice, chan, negIOU, posIOU), ter(temBAD_AMOUNT)); } { // No signature claim more than authorized @@ -2300,7 +2303,7 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = reqBal + USD(100); assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); auto postLocked = -lockedAmount(env, alice, gw, USD); BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal); @@ -2315,7 +2318,7 @@ struct PayChan_test : public beast::unit_test::suite preBob = env.balance(bob, USD.issue()); preLocked = -lockedAmount(env, alice, gw, USD); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), - ter(tecINSUFFICIENT_FUNDS)); + ter(tecUNFUNDED_PAYMENT)); postLocked = -lockedAmount(env, alice, gw, USD); BEAST_EXPECT(channelBalance(*env.current(), chan) == chanBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); @@ -2330,7 +2333,7 @@ struct PayChan_test : public beast::unit_test::suite STAmount const reqAmt = authAmt + USD(1); assert(reqAmt <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()), ter(temBAD_AMOUNT)); auto const postLocked = -lockedAmount(env, alice, gw, USD); @@ -2348,7 +2351,7 @@ struct PayChan_test : public beast::unit_test::suite { // Wrong signing key auto const sig = - signClaimTokenAuth(bob.pk(), bob.sk(), chan, USD(1500)); + signClaimIOUAuth(bob.pk(), bob.sk(), chan, USD(1500)); env(claim( bob, chan, @@ -2363,7 +2366,7 @@ struct PayChan_test : public beast::unit_test::suite { // Bad signature auto const sig = - signClaimTokenAuth(bob.pk(), bob.sk(), chan, USD(1500)); + signClaimIOUAuth(bob.pk(), bob.sk(), chan, USD(1500)); env(claim( bob, chan, @@ -2397,9 +2400,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenCancelAfter(FeatureBitset features) + testIOUCancelAfter(FeatureBitset features) { - testcase("token cancel after"); + testcase("iou cancel after"); using namespace jtx; using namespace std::literals::chrono_literals; auto const alice = Account("alice"); @@ -2447,7 +2450,7 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = reqBal + USD(100); assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); auto const feeDrops = env.current()->fees().base; auto const postLocked = -lockedAmount(env, alice, gw, USD); @@ -2498,9 +2501,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenSettleDelay(FeatureBitset features) + testIOUSettleDelay(FeatureBitset features) { - testcase("token settle delay"); + testcase("iou settle delay"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -2542,7 +2545,7 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = reqBal + USD(100); assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal); BEAST_EXPECT(channelAmount(*env.current(), chan) == chanAmt); @@ -2569,7 +2572,7 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = reqBal + USD(100); assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); auto const feeDrops = env.current()->fees().base; auto const postLocked = -lockedAmount(env, alice, gw, USD); @@ -2583,9 +2586,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenExpiration(FeatureBitset features) + testIOUExpiration(FeatureBitset features) { - testcase("token expiration"); + testcase("iou expiration"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -2658,9 +2661,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenCloseDry(FeatureBitset features) + testIOUCloseDry(FeatureBitset features) { - testcase("token close dry"); + testcase("iou close dry"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -2705,10 +2708,10 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenDefaultAmount(FeatureBitset features) + testIOUDefaultAmount(FeatureBitset features) { // auth amount defaults to balance if not present - testcase("token default amount"); + testcase("iou default amount"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -2743,7 +2746,7 @@ struct PayChan_test : public beast::unit_test::suite auto const reqBal = chanBal + delta; assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, reqBal); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, reqBal); env(claim(bob, chan, reqBal, std::nullopt, Slice(sig), alice.pk())); auto const feeDrops = env.current()->fees().base; auto const postLocked = -lockedAmount(env, alice, gw, USD); @@ -2768,7 +2771,7 @@ struct PayChan_test : public beast::unit_test::suite auto const reqBal = chanBal + delta; assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, reqBal); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, reqBal); env(claim(bob, chan, reqBal, std::nullopt, Slice(sig), alice.pk())); auto const feeDrops = env.current()->fees().base; auto const postLocked = -lockedAmount(env, alice, gw, USD); @@ -2784,10 +2787,10 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenDisallowXRP(FeatureBitset features) + testIOUDisallowXRP(FeatureBitset features) { // auth amount defaults to balance if not present - testcase("Token Disallow XRP"); + testcase("IOU Disallow XRP"); using namespace jtx; using namespace std::literals::chrono_literals; @@ -2865,10 +2868,10 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenDstTag(FeatureBitset features) + testIOUDstTag(FeatureBitset features) { // auth amount defaults to balance if not present - testcase("Token Dst Tag"); + testcase("IOU Dst Tag"); using namespace jtx; using namespace std::literals::chrono_literals; // Create a channel where dst disallows XRP @@ -2905,9 +2908,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenDepositAuth(FeatureBitset features) + testIOUDepositAuth(FeatureBitset features) { - testcase("Token Deposit Authorization"); + testcase("IOU Deposit Authorization"); using namespace jtx; using namespace std::literals::chrono_literals; @@ -2954,7 +2957,7 @@ struct PayChan_test : public beast::unit_test::suite auto const preBobXrp = env.balance(bob); { auto const delta = USD(500).value(); - auto const sig = signClaimTokenAuth(pk, alice.sk(), chan, delta); + auto const sig = signClaimIOUAuth(pk, alice.sk(), chan, delta); // alice claims with signature. Fails since bob has // lsfDepositAuth flag set. @@ -2981,7 +2984,7 @@ struct PayChan_test : public beast::unit_test::suite { // Explore the limits of deposit preauthorization. auto const delta = USD(600).value(); - auto const sig = signClaimTokenAuth(pk, alice.sk(), chan, delta); + auto const sig = signClaimIOUAuth(pk, alice.sk(), chan, delta); // carol claims and fails. Only channel participants (bob or // alice) may claim. @@ -3042,10 +3045,10 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenMultiple(FeatureBitset features) + testIOUMultiple(FeatureBitset features) { // auth amount defaults to balance if not present - testcase("Token Multiple channels to the same account"); + testcase("IOU Multiple channels to the same account"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -3073,9 +3076,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenAccountChannelsRPC(FeatureBitset features) + testIOUAccountChannelsRPC(FeatureBitset features) { - testcase("Token AccountChannels RPC"); + testcase("IOU AccountChannels RPC"); using namespace jtx; using namespace std::literals::chrono_literals; @@ -3137,9 +3140,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenAccountChannelsRPCMarkers(FeatureBitset features) + testIOUAccountChannelsRPCMarkers(FeatureBitset features) { - testcase("Token Account channels RPC markers"); + testcase("IOU Account channels RPC markers"); using namespace test::jtx; using namespace std::literals; @@ -3264,11 +3267,11 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenAccountChannelsRPCSenderOnly(FeatureBitset features) + testIOUAccountChannelsRPCSenderOnly(FeatureBitset features) { // Check that the account_channels command only returns channels owned // by the account - testcase("Token Account channels RPC owner only"); + testcase("IOU Account channels RPC owner only"); using namespace test::jtx; using namespace std::literals; @@ -3309,9 +3312,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenAuthVerifyRPC(FeatureBitset features) + testIOUAuthVerifyRPC(FeatureBitset features) { - testcase("Token PayChan Auth/Verify RPC"); + testcase("IOU PayChan Auth/Verify RPC"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -3828,9 +3831,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenOptionalFields(FeatureBitset features) + testIOUOptionalFields(FeatureBitset features) { - testcase("Token Optional Fields"); + testcase("IOU Optional Fields"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -3889,9 +3892,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenMalformedPK(FeatureBitset features) + testIOUMalformedPK(FeatureBitset features) { - testcase("token malformed pk"); + testcase("iou malformed pk"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -3926,7 +3929,8 @@ struct PayChan_test : public beast::unit_test::suite env(jv); auto const authAmt = USD(100); - auto const sig = signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + auto const sig = + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); jv = claim( bob, chan, @@ -3966,9 +3970,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenMetaAndOwnership(FeatureBitset features) + testIOUMetaAndOwnership(FeatureBitset features) { - testcase("Token Metadata & Ownership"); + testcase("IOU Metadata & Ownership"); using namespace jtx; using namespace std::literals::chrono_literals; @@ -4098,9 +4102,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenAccountDelete(FeatureBitset features) + testIOUAccountDelete(FeatureBitset features) { - testcase("Token Account Delete"); + testcase("IOU Account Delete"); using namespace test::jtx; using namespace std::literals::chrono_literals; auto rmAccount = [this]( @@ -4355,7 +4359,7 @@ struct PayChan_test : public beast::unit_test::suite reqBal = chanBal + delta; authAmt = reqBal + USD(100); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); auto const postLocked = -lockedAmount(env, alice, gw, USD); BEAST_EXPECT(channelBalance(*env.current(), chan) == reqBal); @@ -4399,9 +4403,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenUsingTickets(FeatureBitset features) + testIOUUsingTickets(FeatureBitset features) { - testcase("token using tickets"); + testcase("iou using tickets"); using namespace jtx; using namespace std::literals::chrono_literals; Env env{*this, features}; @@ -4489,7 +4493,7 @@ struct PayChan_test : public beast::unit_test::suite auto const authAmt = reqBal + USD(100); assert(reqBal <= chanAmt); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ticket::use(bobTicketSeq++)); @@ -4510,7 +4514,7 @@ struct PayChan_test : public beast::unit_test::suite // A transaction that generates a tec still consumes its ticket. env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ticket::use(bobTicketSeq++), - ter(tecINSUFFICIENT_FUNDS)); + ter(tecUNFUNDED_PAYMENT)); env.require(tickets(bob, env.seq(bob) - bobTicketSeq)); BEAST_EXPECT(env.seq(bob) == bobSeq); @@ -4529,7 +4533,7 @@ struct PayChan_test : public beast::unit_test::suite // Note that since claim() returns a tem (neither tec nor tes), // the ticket is not consumed. So we don't increment bobTicket. auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqAmt, authAmt, Slice(sig), alice.pk()), ticket::use(bobTicketSeq), ter(temBAD_AMOUNT)); @@ -4582,9 +4586,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenAutoTL(FeatureBitset features) + testIOUAutoTL(FeatureBitset features) { - testcase("Token Auto Trust Line"); + testcase("IOU Auto Trust Line"); using namespace test::jtx; using namespace std::literals; @@ -4642,7 +4646,7 @@ struct PayChan_test : public beast::unit_test::suite auto const preBob = env.balance(bob, USD.issue()); auto const preBobXrp = env.balance(bob); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); env.close(); BEAST_EXPECT(preBob.value() == USD(0)); @@ -4656,9 +4660,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenRippleState(FeatureBitset features) + testIOURippleState(FeatureBitset features) { - testcase("Token RippleState"); + testcase("IOU RippleState"); using namespace test::jtx; using namespace std::literals; @@ -4667,741 +4671,229 @@ struct PayChan_test : public beast::unit_test::suite // I did this to check the exact sign "-/+" // - // src > dst - // src > issuer - // dest no trustline - // negative locked/tl balance + struct TestAccountData { - auto const src = Account("alice2"); - auto const dst = Account("bob0"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; + Account src; + Account dst; + Account gw; + bool hasTrustline; + bool negative; + }; + std::array tests = {{ + // src > dst && src > issuer && dst no trustline + {Account("alice2"), Account("bob0"), Account{"gw0"}, false, true}, + // src < dst && src < issuer && dst no trustline + {Account("carol0"), Account("dan1"), Account{"gw1"}, false, false}, + // // dst > src && dst > issuer && dst no trustline + {Account("dan1"), Account("alice2"), Account{"gw0"}, false, true}, + // // dst < src && dst < issuer && dst no trustline + {Account("bob0"), Account("carol0"), Account{"gw1"}, false, false}, + // // src > dst && src > issuer && dst has trustline + {Account("alice2"), Account("bob0"), Account{"gw0"}, true, true}, + // // src < dst && src < issuer && dst has trustline + {Account("carol0"), Account("dan1"), Account{"gw1"}, true, false}, + // // dst > src && dst > issuer && dst has trustline + {Account("dan1"), Account("alice2"), Account{"gw0"}, true, true}, + // // dst < src && dst < issuer && dst has trustline + {Account("bob0"), Account("carol0"), Account{"gw1"}, true, false}, + }}; + + for (auto const& t : tests) + { Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); + auto const USD = t.gw["USD"]; + env.fund(XRP(5000), t.src, t.dst, t.gw); env.close(); - env.trust(USD(100000), src); + + if (t.hasTrustline) + env.trust(USD(100000), t.src, t.dst); + else + env.trust(USD(100000), t.src); env.close(); - env(pay(gw, src, USD(10000))); + + env(pay(t.gw, t.src, USD(10000))); + if (t.hasTrustline) + env(pay(t.gw, t.dst, USD(10000))); env.close(); // src can create paychan - auto const pk = src.pk(); + auto const pk = t.src.pk(); auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = lockedAmount(env, src, gw, USD); + auto const chan = channel(t.src, t.dst, env.seq(t.src)); + auto preLocked = lockedAmount(env, t.src, t.gw, USD); BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); + env(create(t.src, t.dst, USD(1000), settleDelay, pk)); env.close(); - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -USD(1000)); + preLocked = lockedAmount(env, t.src, t.gw, USD); + BEAST_EXPECT(preLocked == (t.negative ? -USD(1000) : USD(1000))); // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); + auto const preSrc = lineBalance(env, t.src, t.gw, USD); + auto const preDst = lineBalance(env, t.dst, t.gw, USD); 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); + auto const sig = + signClaimIOUAuth(t.src.pk(), t.src.sk(), chan, authAmt); + env(claim(t.dst, chan, reqBal, authAmt, Slice(sig), t.src.pk())); env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc + delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst - delta); - BEAST_EXPECT(preLocked == postLocked - delta); + auto postLocked = lockedAmount(env, t.src, t.gw, USD); + BEAST_EXPECT( + lineBalance(env, t.src, t.gw, USD) == + (t.negative ? (preSrc + delta) : (preSrc - delta))); + BEAST_EXPECT( + lineBalance(env, t.dst, t.gw, USD) == + (t.negative ? (preDst - delta) : (preDst + delta))); + BEAST_EXPECT( + postLocked == + (t.negative ? (preLocked + delta) : (preLocked - delta))); // src claim fails because trust limit is 0 - env(claim(src, chan, authAmt, authAmt), ter(tecPATH_DRY)); - } - // src < dst - // src < issuer - // dest no trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const dst = Account("dan1"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc - delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - // src claim fails because trust limit is 0 - env(claim(src, chan, authAmt, authAmt), ter(tecPATH_DRY)); - } - // dst > src - // dst > issuer - // dest no trustline - // negative locked/tl balance - { - auto const src = Account("dan1"); - auto const dst = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc + delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - // src claim fails because trust limit is 0 - env(claim(src, chan, authAmt, authAmt), ter(tecPATH_DRY)); - } - // dst < src - // dst < issuer - // dest no trustline - // positive locked/tl balance - { - auto const src = Account("bob0"); - auto const dst = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(5000), src, dst, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = -lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc - delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - // src claim fails because trust limit is 0 - env(claim(src, chan, authAmt, authAmt), ter(tecPATH_DRY)); - } - // src > dst - // src > issuer - // dest trustline - // negative locked/tl balance - { - auto const src = Account("alice2"); - auto const dst = Account("bob0"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = -lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc + delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - env(claim(src, chan, authAmt, authAmt)); - env.close(); - postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc + authAmt); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst - authAmt); - BEAST_EXPECT(preLocked == postLocked - authAmt); - } - // src < dst - // src < issuer - // dest trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const dst = Account("dan1"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc - delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - env(claim(src, chan, authAmt, authAmt)); - env.close(); - postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc - authAmt); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst + authAmt); - BEAST_EXPECT(preLocked == postLocked + authAmt); - } - // dst > src - // dst > issuer - // dest trustline - // negative locked/tl balance - { - auto const src = Account("dan1"); - auto const dst = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == -USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc + delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst - delta); - BEAST_EXPECT(preLocked == postLocked - delta); - env(claim(src, chan, authAmt, authAmt)); - env.close(); - postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc + authAmt); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst - authAmt); - BEAST_EXPECT(preLocked == postLocked - authAmt); - } - // dst < src - // dst < issuer - // dest trustline - // positive locked/tl balance - { - auto const src = Account("bob0"); - auto const dst = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, dst, gw); - env.close(); - env.trust(USD(100000), src, dst); - env.close(); - env(pay(gw, src, USD(10000))); - env(pay(gw, dst, USD(10000))); - env.close(); - - // src can create paychan - auto const pk = src.pk(); - auto const settleDelay = 100s; - auto const chan = channel(src, dst, env.seq(src)); - auto preLocked = -lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(0)); - env(create(src, dst, USD(1000), settleDelay, pk)); - env.close(); - - preLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(preLocked == USD(1000)); - - // dst can claim paychan - auto const preSrc = lineBalance(env, src, gw, USD); - auto const preDst = lineBalance(env, dst, gw, USD); - 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 = USD(1000); - auto const sig = signClaimTokenAuth(src.pk(), src.sk(), chan, authAmt); - env(claim(dst, chan, reqBal, authAmt, Slice(sig), src.pk())); - env.close(); - auto postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc - delta); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst + delta); - BEAST_EXPECT(preLocked == postLocked + delta); - env(claim(src, chan, authAmt, authAmt)); - env.close(); - postLocked = lockedAmount(env, src, gw, USD); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == preSrc - authAmt); - BEAST_EXPECT(lineBalance(env, dst, gw, USD) == preDst + authAmt); - BEAST_EXPECT(preLocked == postLocked + authAmt); + auto const testResult = + t.hasTrustline ? ter(tesSUCCESS) : ter(tecPATH_DRY); + env(claim(t.src, chan, authAmt, authAmt), testResult); } } void - testTokenGateway(FeatureBitset features) + testIOUGateway(FeatureBitset features) { - testcase("Token Gateway"); + testcase("IOU Gateway"); using namespace test::jtx; using namespace std::literals; - // issuer -> src - // src > issuer - // dest no trustline - // negative locked/tl balance + struct TestAccountData { - auto const src = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; + Account src; + Account dst; + bool hasTrustline; + bool negative; + }; + std::array gwSrcTests = {{ + // src > dst && src > issuer && dst no trustline + {Account("gw0"), Account{"alice2"}, false, true}, + // // src < dst && src < issuer && dst no trustline + {Account("gw1"), Account{"carol0"}, false, false}, + // // // // dst > src && dst > issuer && dst no trustline + {Account("gw0"), Account{"dan1"}, false, true}, + // // // // dst < src && dst < issuer && dst no trustline + {Account("gw1"), Account{"bob0"}, false, false}, + // // // src > dst && src > issuer && dst has trustline + {Account("gw0"), Account{"alice2"}, true, true}, + // // // src < dst && src < issuer && dst has trustline + {Account("gw1"), Account{"carol0"}, true, false}, + // // // dst > src && dst > issuer && dst has trustline + {Account("gw0"), Account{"dan1"}, true, true}, + // // // dst < src && dst < issuer && dst has trustline + {Account("gw1"), Account{"bob0"}, true, false}, + }}; + + for (auto const& t : gwSrcTests) + { Env env{*this, features}; - env.fund(XRP(10000), src, gw); + auto const USD = t.src["USD"]; + env.fund(XRP(5000), t.dst, t.src); + env.close(); + + if (t.hasTrustline) + env.trust(USD(100000), t.dst); + + env.close(); + + if (t.hasTrustline) + env(pay(t.src, t.dst, USD(10000))); + env.close(); // issuer can create paychan - auto const pk = gw.pk(); + auto const pk = t.src.pk(); auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); + auto const chan = channel(t.src, t.dst, env.seq(t.src)); + env(create(t.src, t.dst, USD(1000), settleDelay, pk)); env.close(); // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(500)); - } - // issuer -> src - // src < issuer - // dest no trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(500)); - } - // issuer -> src - // dst > issuer - // dest no trustline - // negative locked/tl balance - { - auto const src = Account("dan1"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(500)); - } - // issuer -> src - // dst < issuer - // dest no trustline - // positive locked/tl balance - { - auto const src = Account("bob0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == USD(0)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(500)); - } - // issuer -> src - // src > issuer - // dest has trustline - // negative locked/tl balance - { - auto const src = Account("alice2"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == -USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(10500)); - } - // issuer -> src - // src < issuer - // dest has trustline - // positive locked/tl balance - { - auto const src = Account("carol0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(10500)); - } - // issuer -> src - // dst > issuer - // dest has trustline - // negative locked/tl balance - { - auto const src = Account("dan1"); - auto const gw = Account{"gw0"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == -USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == -USD(10500)); - } - // issuer -> src - // dst < issuer - // dest has trustline - // positive locked/tl balance - { - auto const src = Account("bob0"); - auto const gw = Account{"gw1"}; - auto const USD = gw["USD"]; - - Env env{*this, features}; - env.fund(XRP(10000), src, gw); - env.close(); - env.trust(USD(100000), src); - env.close(); - env(pay(gw, src, USD(10000))); - env.close(); - - // issuer can create paychan - auto const pk = gw.pk(); - auto const settleDelay = 100s; - auto const chan = channel(gw, src, env.seq(gw)); - env(create(gw, src, USD(1000), settleDelay, pk)); - env.close(); - - // src can claim paychan without trustline - auto const preSrc = lineBalance(env, src, gw, USD); - 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 = signClaimTokenAuth(gw.pk(), gw.sk(), chan, authAmt); - env(claim(src, chan, reqBal, authAmt, Slice(sig), gw.pk())); - env.close(); - BEAST_EXPECT(preSrc == USD(10000)); - BEAST_EXPECT(lineBalance(env, src, gw, USD) == USD(10500)); - } - // alice -> issuer - { - Env env{*this, features}; - auto const alice = Account("alice"); - auto const bob = Account("bob"); - auto const gw = Account{"gateway"}; - auto const USD = gw["USD"]; - - env.fund(XRP(10000), alice, gw); - env.close(); - env.trust(USD(100000), alice); - env.close(); - env(pay(gw, alice, USD(10000))); - env.close(); - - // alice can create paychan - auto const pk = alice.pk(); - auto const settleDelay = 100s; - auto const chan = channel(alice, gw, env.seq(alice)); - env(create(alice, gw, USD(1000), settleDelay, pk)); - env.close(); - - // issuer can claim paychan - auto const preAlice = env.balance(alice, USD.issue()); + auto const preDst = lineBalance(env, t.dst, t.src, USD); 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 = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); - env(claim(gw, chan, reqBal, authAmt, Slice(sig), alice.pk())); + signClaimIOUAuth(t.src.pk(), t.src.sk(), chan, authAmt); + env(claim(t.dst, chan, reqBal, authAmt, Slice(sig), t.src.pk())); env.close(); - BEAST_EXPECT(preAlice == USD(10000)); - BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice - delta); + auto const preAmount = t.hasTrustline ? 10000 : 0; + BEAST_EXPECT( + preDst == (t.negative ? -USD(preAmount) : USD(preAmount))); + auto const postAmount = t.hasTrustline ? 10500 : 500; + BEAST_EXPECT( + lineBalance(env, t.dst, t.src, USD) == + (t.negative ? -USD(postAmount) : USD(postAmount))); + BEAST_EXPECT(lineBalance(env, t.src, t.src, USD) == USD(0)); + } + + std::array gwDstTests = {{ + // // // src > dst && src > issuer && dst has trustline + {Account("alice2"), Account{"gw0"}, true, true}, + // // // src < dst && src < issuer && dst has trustline + {Account("carol0"), Account{"gw1"}, true, false}, + // // // // dst > src && dst > issuer && dst has trustline + {Account("dan1"), Account{"gw0"}, true, true}, + // // // // dst < src && dst < issuer && dst has trustline + {Account("bob0"), Account{"gw1"}, true, false}, + }}; + + for (auto const& t : gwDstTests) + { + Env env{*this, features}; + auto const USD = t.dst["USD"]; + env.fund(XRP(5000), t.src, t.dst); + env.close(); + + env.trust(USD(100000), t.src); + env.close(); + + env(pay(t.dst, t.src, USD(10000))); + env.close(); + + // src can create paychan to dst/issuer + auto const pk = t.src.pk(); + auto const settleDelay = 100s; + auto const chan = channel(t.src, t.dst, env.seq(t.src)); + env(create(t.src, t.dst, USD(1000), settleDelay, pk)); + env.close(); + + // dst/gw can claim paychan + auto const preSrc = lineBalance(env, t.src, t.dst, USD); + 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 = + signClaimIOUAuth(t.src.pk(), t.src.sk(), chan, authAmt); + env(claim(t.dst, chan, reqBal, authAmt, Slice(sig), t.src.pk())); + env.close(); + auto const preAmount = 10000; + BEAST_EXPECT( + preSrc == (t.negative ? -USD(preAmount) : USD(preAmount))); + auto const postAmount = 9500; + BEAST_EXPECT( + lineBalance(env, t.src, t.dst, USD) == + (t.negative ? -USD(postAmount) : USD(postAmount))); + BEAST_EXPECT(lineBalance(env, t.dst, t.dst, USD) == USD(0)); } } void - testTokenLockedRate(FeatureBitset features) + testIOULockedRate(FeatureBitset features) { - testcase("Token Locked Rate"); + testcase("IOU Locked Rate"); using namespace test::jtx; using namespace std::literals; @@ -5544,21 +5036,17 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenTLLimitAmount(FeatureBitset features) + testIOUTLLimitAmount(FeatureBitset features) { - testcase("Token Trustline Limit Amount"); + testcase("IOU Trustline Limit Amount"); using namespace test::jtx; using namespace std::literals; auto const alice = Account("alice"); auto const bob = Account("bob"); - auto const carol = Account("carol"); auto const gw = Account{"gateway"}; auto const USD = gw["USD"]; - auto const aliceUSD = alice["USD"]; - auto const bobUSD = bob["USD"]; - // test LimitAmount { Env env{*this, features}; @@ -5593,21 +5081,21 @@ struct PayChan_test : public beast::unit_test::suite 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 preBobLimit = limitAmount(env, bob, gw, USD); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + signClaimIOUAuth(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); + auto const postBobLimit = limitAmount(env, bob, gw, USD); // bobs limit is NOT changed - // BEAST_EXPECT(postBobLimit == preBobLimit); + BEAST_EXPECT(postBobLimit == preBobLimit); } } void - testTokenTLRequireAuth(FeatureBitset features) + testIOUTLRequireAuth(FeatureBitset features) { - testcase("Token Trustline Require Auth"); + testcase("IOU Trustline Require Auth"); using namespace test::jtx; using namespace std::literals; @@ -5632,9 +5120,6 @@ struct PayChan_test : public beast::unit_test::suite 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)); @@ -5670,7 +5155,7 @@ struct PayChan_test : public beast::unit_test::suite auto const newReqBal = chanBal + delta; auto const newAuthAmt = newReqBal + USD(100); auto const sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, newAuthAmt); + signClaimIOUAuth(alice.pk(), alice.sk(), chan, newAuthAmt); env(claim( bob, chan, newReqBal, newAuthAmt, Slice(sig), alice.pk())); env.close(); @@ -5678,9 +5163,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenTLFreeze(FeatureBitset features) + testIOUTLFreeze(FeatureBitset features) { - testcase("Token Trustline Freeze"); + testcase("IOU Trustline Freeze"); using namespace test::jtx; using namespace std::literals; @@ -5723,7 +5208,7 @@ struct PayChan_test : public beast::unit_test::suite auto reqBal = chanBal + delta; auto authAmt = reqBal + USD(100); env(claim(alice, chan, reqBal, authAmt), ter(tecFROZEN)); - auto sig = signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + auto sig = signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ter(tecFROZEN)); env.close(); @@ -5735,7 +5220,7 @@ struct PayChan_test : public beast::unit_test::suite chanAmt = channelAmount(*env.current(), chan); reqBal = chanBal + delta; authAmt = reqBal + USD(100); - sig = signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + sig = signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); env.close(); } @@ -5789,7 +5274,7 @@ struct PayChan_test : public beast::unit_test::suite env(claim(alice, chan, reqBal, authAmt), ter(tecFROZEN)); // bob claim paychan fails - frozen trustline - auto sig = signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + auto sig = signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ter(tecFROZEN)); env.close(); @@ -5811,16 +5296,16 @@ struct PayChan_test : public beast::unit_test::suite authAmt = reqBal + USD(100); // bob claim paychan success - sig = signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + sig = signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); env.close(); } } void - testTokenTLINSF(FeatureBitset features) + testIOUTLINSF(FeatureBitset features) { - testcase("Token Trustline Insuficient Funds"); + testcase("IOU Trustline Insuficient Funds"); using namespace test::jtx; using namespace std::literals; @@ -5896,9 +5381,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenMismatchFunding(FeatureBitset features) + testIOUMismatchFunding(FeatureBitset features) { - testcase("Token Mismatch Funding"); + testcase("IOU Mismatch Funding"); using namespace test::jtx; using namespace std::literals; @@ -5931,9 +5416,9 @@ struct PayChan_test : public beast::unit_test::suite } void - testTokenPrecisionLoss(FeatureBitset features) + testIOUPrecisionLoss(FeatureBitset features) { - testcase("Token Precision Loss"); + testcase("IOU Precision Loss"); using namespace test::jtx; using namespace std::literals; @@ -5994,22 +5479,22 @@ struct PayChan_test : public beast::unit_test::suite Env env(*this, features); env.fund(XRP(10000), alice, bob, gw); env.close(); - env.trust(USD(100000000000000000), alice); - env.trust(USD(100000000000000000), bob); + env.trust(USD(1000000000000000000), alice); + env.trust(USD(1000000000000000000), bob); env.close(); - env(pay(gw, alice, USD(10000000000000000))); + env(pay(gw, alice, USD(100000000000000000))); env(pay(gw, bob, USD(1))); env.close(); auto const pk = alice.pk(); auto const settleDelay = 100s; - // alice cannot create paychan for 1/10 token - precision loss + // alice cannot create paychan for 1/10 iou - precision loss env(create(alice, bob, USD(1), settleDelay, pk), ter(tecPRECISION_LOSS)); env.close(); - // alice can create paychan for 10000 token + // alice can create paychan for 100 iou auto const chan = channel(alice, bob, env.seq(alice)); - env(create(alice, bob, USD(1000), settleDelay, pk)); + env(create(alice, bob, USD(100), settleDelay, pk)); env.close(); auto const chanBal = channelBalance(*env.current(), chan); @@ -6017,14 +5502,13 @@ struct PayChan_test : public beast::unit_test::suite auto reqBal = USD(10); auto authAmt = reqBal + USD(10); - auto sig = - signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + auto sig = signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk()), ter(tecPRECISION_LOSS)); - reqBal = USD(1000); - authAmt = reqBal + USD(1000); - sig = signClaimTokenAuth(alice.pk(), alice.sk(), chan, authAmt); + reqBal = USD(100); + authAmt = reqBal + USD(100); + sig = signClaimIOUAuth(alice.pk(), alice.sk(), chan, authAmt); env(claim(bob, chan, reqBal, authAmt, Slice(sig), alice.pk())); } } @@ -6051,34 +5535,40 @@ struct PayChan_test : public beast::unit_test::suite testMetaAndOwnership(features); testAccountDelete(features); testUsingTickets(features); - testTokenSimple(features); - testTokenCancelAfter(features); - testTokenSettleDelay(features); - testTokenExpiration(features); - testTokenCloseDry(features); - testTokenDefaultAmount(features); - testTokenDisallowXRP(features); - testTokenDstTag(features); - testTokenDepositAuth(features); - testTokenMultiple(features); - testTokenAccountChannelsRPC(features); - testTokenAccountChannelsRPCMarkers(features); - testTokenAccountChannelsRPCSenderOnly(features); - testTokenAuthVerifyRPC(features); - testTokenOptionalFields(features); - testTokenMalformedPK(features); - testTokenMetaAndOwnership(features); - testTokenAccountDelete(features); - testTokenUsingTickets(features); - testTokenAutoTL(features); - testTokenRippleState(features); - testTokenGateway(features); - testTokenLockedRate(features); - testTokenTLRequireAuth(features); - testTokenTLFreeze(features); - testTokenTLINSF(features); - testTokenMismatchFunding(features); - testTokenPrecisionLoss(features); + } + + void + testIOUWithFeats(FeatureBitset features) + { + testIOUSimple(features); + testIOUCancelAfter(features); + testIOUSettleDelay(features); + testIOUExpiration(features); + testIOUCloseDry(features); + testIOUDefaultAmount(features); + testIOUDisallowXRP(features); + testIOUDstTag(features); + testIOUDepositAuth(features); + testIOUMultiple(features); + testIOUAccountChannelsRPC(features); + testIOUAccountChannelsRPCMarkers(features); + testIOUAccountChannelsRPCSenderOnly(features); + testIOUAuthVerifyRPC(features); + testIOUOptionalFields(features); + testIOUMalformedPK(features); + testIOUMetaAndOwnership(features); + testIOUAccountDelete(features); + testIOUUsingTickets(features); + testIOUAutoTL(features); + testIOURippleState(features); + testIOUGateway(features); + testIOULockedRate(features); + testIOUTLLimitAmount(features); + testIOUTLRequireAuth(features); + testIOUTLFreeze(features); + testIOUTLINSF(features); + testIOUMismatchFunding(features); + testIOUPrecisionLoss(features); } public: @@ -6087,11 +5577,15 @@ public: { using namespace test::jtx; FeatureBitset const all{supported_amendments()}; - // testWithFeats(all - disallowIncoming); + testWithFeats(all - disallowIncoming); + testWithFeats( + all - disallowIncoming - featurePaychanAndEscrowForTokens); testWithFeats(all); + testIOUWithFeats(all - disallowIncoming); + testIOUWithFeats(all); } }; BEAST_DEFINE_TESTSUITE(PayChan, app, ripple); } // namespace test -} // namespace ripple +} // namespace ripple \ No newline at end of file