diff --git a/hook/error.h b/hook/error.h index dbde702660d..2d7060ee122 100644 --- a/hook/error.h +++ b/hook/error.h @@ -45,5 +45,7 @@ #define INVALID_KEY -41 #define NOT_A_STRING -42 #define MEM_OVERLAP -43 +#define TOO_MANY_STATE_MODIFICATIONS -44 +#define TOO_MANY_NAMESPACES -45 #define HOOK_ERROR_CODES #endif //HOOK_ERROR_CODES \ No newline at end of file diff --git a/src/ripple/app/hook/Enum.h b/src/ripple/app/hook/Enum.h index 197d96ef35e..f28ca9c2c5f 100644 --- a/src/ripple/app/hook/Enum.h +++ b/src/ripple/app/hook/Enum.h @@ -54,6 +54,12 @@ maxHookChainLength(void) return 10; } +inline uint32_t +maxNamespaces(void) +{ + return 256; +} + enum TSHFlags : uint8_t { tshNONE = 0b000, tshROLLBACK = 0b001, @@ -313,6 +319,7 @@ enum hook_return_code : int64_t { MEM_OVERLAP = -43, // one or more specified buffers are the same memory TOO_MANY_STATE_MODIFICATIONS = -44, // more than 5000 modified state // entires in the combined hook chains + TOO_MANY_NAMESPACES = -45 }; enum ExitType : uint8_t { diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index dcb418a54c4..ade62b7923e 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -29,8 +29,9 @@ isEmittedTxn(ripple::STTx const& tx); // only upon tesSuccess for the otxn. class HookStateMap : public std::map< ripple::AccountID, // account that owns the state - std::pair< + std::tuple< int64_t, // remaining available ownercount + int64_t, // total namespace count std::map< ripple::uint256, // namespace std::map< diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index ff84efed6ca..67f97572fc5 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -71,6 +71,8 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv) return rv.read(keylet::nftoffer(*id)); }; + bool const fixV1 = rv.rules().enabled(fixXahauV1); + switch (tt) { case ttIMPORT: { @@ -97,19 +99,36 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv) // the burner is the issuer and not the owner of the token if (issuer == owner) - { - // pass, already a TSH - } - else if (*otxnAcc == owner) + break; + // pass, already a TSH + + // new logic + if (fixV1) { // the owner burns their token, and the issuer is a weak TSH - ADD_TSH(issuer, canRollback); - } - else - { + if (*otxnAcc == owner && rv.exists(keylet::account(issuer))) + ADD_TSH(issuer, false); // the issuer burns the owner's token, and the owner is a weak // TSH - ADD_TSH(owner, canRollback); + else if (rv.exists(keylet::account(owner))) + ADD_TSH(owner, false); + + break; + } + + // old logic + { + if (*otxnAcc == owner) + { + // the owner burns their token, and the issuer is a weak TSH + ADD_TSH(issuer, true); + } + else + { + // the issuer burns the owner's token, and the owner is a + // weak TSH + ADD_TSH(owner, true); + } } break; @@ -300,19 +319,59 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv) case ttESCROW_CANCEL: case ttESCROW_FINISH: { - if (!tx.isFieldPresent(sfOwner) || - !tx.isFieldPresent(sfOfferSequence)) - return {}; + // new logic + if (fixV1) + { + if (!tx.isFieldPresent(sfOwner)) + return {}; - auto escrow = rv.read(keylet::escrow( - tx.getAccountID(sfOwner), tx.getFieldU32(sfOfferSequence))); + AccountID const owner = tx.getAccountID(sfOwner); - if (!escrow) - return {}; + bool const hasSeq = tx.isFieldPresent(sfOfferSequence); + bool const hasID = tx.isFieldPresent(sfEscrowID); + if (!hasSeq && !hasID) + return {}; - ADD_TSH(escrow->getAccountID(sfAccount), true); - ADD_TSH(escrow->getAccountID(sfDestination), canRollback); - break; + Keylet kl = hasSeq + ? keylet::escrow(owner, tx.getFieldU32(sfOfferSequence)) + : Keylet(ltESCROW, tx.getFieldH256(sfEscrowID)); + + auto escrow = rv.read(kl); + + if (!escrow || + escrow->getFieldU16(sfLedgerEntryType) != ltESCROW) + return {}; + + // this should always be the same as owner, but defensively... + AccountID const src = escrow->getAccountID(sfAccount); + AccountID const dst = escrow->getAccountID(sfDestination); + + // the source account is a strong transacitonal stakeholder for + // fin and can + ADD_TSH(src, true); + + // the dest acc is a strong tsh for fin and weak for can + if (src != dst) + ADD_TSH(dst, tt == ttESCROW_FINISH); + + break; + } + // old logic + { + if (!tx.isFieldPresent(sfOwner) || + !tx.isFieldPresent(sfOfferSequence)) + return {}; + + auto escrow = rv.read(keylet::escrow( + tx.getAccountID(sfOwner), tx.getFieldU32(sfOfferSequence))); + + if (!escrow) + return {}; + + ADD_TSH(escrow->getAccountID(sfAccount), true); + ADD_TSH(escrow->getAccountID(sfDestination), canRollback); + break; + } } case ttPAYCHAN_FUND: @@ -1287,7 +1346,7 @@ lookup_state_cache( if (stateMap.find(acc) == stateMap.end()) return std::nullopt; - auto& stateMapAcc = stateMap[acc].second; + auto& stateMapAcc = std::get<2>(stateMap[acc]); if (stateMapAcc.find(ns) == stateMapAcc.end()) return std::nullopt; @@ -1312,18 +1371,22 @@ set_state_cache( bool modified) { auto& stateMap = hookCtx.result.stateMap; + auto& view = hookCtx.applyCtx.view(); if (modified && stateMap.modified_entry_count >= max_state_modifications) return TOO_MANY_STATE_MODIFICATIONS; + bool const createNamespace = view.rules().enabled(fixXahauV1) && + !view.exists(keylet::hookStateDir(acc, ns)); + if (stateMap.find(acc) == stateMap.end()) { // if this is the first time this account has been interacted with // we will compute how many available reserve positions there are auto const& fees = hookCtx.applyCtx.view().fees(); - auto const accSLE = - hookCtx.applyCtx.view().read(ripple::keylet::account(acc)); + auto const accSLE = view.read(ripple::keylet::account(acc)); + if (!accSLE) return DOESNT_EXIST; @@ -1342,15 +1405,32 @@ set_state_cache( if (availableForReserves < 1 && modified) return RESERVE_INSUFFICIENT; + int64_t namespaceCount = accSLE->isFieldPresent(sfHookNamespaces) + ? accSLE->getFieldV256(sfHookNamespaces).size() + : 0; + + if (createNamespace) + { + // overflow should never ever happen but check anyway + if (namespaceCount + 1 < namespaceCount) + return INTERNAL_ERROR; + + if (++namespaceCount > hook::maxNamespaces()) + return TOO_MANY_NAMESPACES; + } + stateMap.modified_entry_count++; stateMap[acc] = { - availableForReserves - 1, {{ns, {{key, {modified, data}}}}}}; + availableForReserves - 1, + namespaceCount, + {{ns, {{key, {modified, data}}}}}}; return 1; } - auto& stateMapAcc = stateMap[acc].second; - auto& availableForReserves = stateMap[acc].first; + auto& availableForReserves = std::get<0>(stateMap[acc]); + auto& namespaceCount = std::get<1>(stateMap[acc]); + auto& stateMapAcc = std::get<2>(stateMap[acc]); bool const canReserveNew = availableForReserves > 0; if (stateMapAcc.find(ns) == stateMapAcc.end()) @@ -1360,6 +1440,18 @@ set_state_cache( if (!canReserveNew) return RESERVE_INSUFFICIENT; + if (createNamespace) + { + // overflow should never ever happen but check anyway + if (namespaceCount + 1 < namespaceCount) + return INTERNAL_ERROR; + + if (namespaceCount + 1 > hook::maxNamespaces()) + return TOO_MANY_NAMESPACES; + + namespaceCount++; + } + availableForReserves--; stateMap.modified_entry_count++; } @@ -1488,6 +1580,13 @@ DEFINE_HOOK_FUNCTION( auto const key = make_state_key( std::string_view{(const char*)(memory + kread_ptr), (size_t)kread_len}); + if (view.rules().enabled(fixXahauV1)) + { + auto const sleAccount = view.peek(hookCtx.result.accountKeylet); + if (!sleAccount) + return tefINTERNAL; + } + if (!key) return INTERNAL_ERROR; @@ -1610,7 +1709,7 @@ hook::finalizeHookState( for (const auto& accEntry : stateMap) { const auto& acc = accEntry.first; - for (const auto& nsEntry : accEntry.second.second) + for (const auto& nsEntry : std::get<2>(accEntry.second)) { const auto& ns = nsEntry.first; for (const auto& cacheEntry : nsEntry.second) diff --git a/src/ripple/app/misc/impl/TxQ.cpp b/src/ripple/app/misc/impl/TxQ.cpp index daaca0dbea2..08157c2927d 100644 --- a/src/ripple/app/misc/impl/TxQ.cpp +++ b/src/ripple/app/misc/impl/TxQ.cpp @@ -2030,19 +2030,20 @@ TxQ::doRPC(Application& app, std::optional hookFeeUnits) const levels[jss::median_level] = to_string(metrics.medFeeLevel); levels[jss::open_ledger_level] = to_string(metrics.openLedgerFeeLevel); - auto const txFee = XRPAmount{hookFeeUnits->drops()}; - auto const baseFee = view->fees().base; + auto const baseFee = + hookFeeUnits ? XRPAmount{hookFeeUnits->drops()} : view->fees().base; // If the base fee is 0 drops, but escalation has kicked in, treat the // base fee as if it is 1 drop, which makes the rest of the math // work. - auto const effectiveBaseFee = [&txFee, &metrics]() { - if (!txFee && metrics.openLedgerFeeLevel != metrics.referenceFeeLevel) + auto const effectiveBaseFee = [&baseFee, &metrics]() { + if (!baseFee && metrics.openLedgerFeeLevel != metrics.referenceFeeLevel) return XRPAmount{1}; - return txFee; + return baseFee; }(); auto& drops = ret[jss::drops] = Json::Value(); + drops[jss::base_fee_no_hooks] = to_string(view->fees().base); drops[jss::base_fee] = to_string(baseFee); drops[jss::median_fee] = to_string(toDrops(metrics.medFeeLevel, baseFee)); drops[jss::minimum_fee] = to_string(toDrops( diff --git a/src/ripple/app/tx/impl/CancelOffer.cpp b/src/ripple/app/tx/impl/CancelOffer.cpp index f69fa66a64c..b9337d7dee8 100644 --- a/src/ripple/app/tx/impl/CancelOffer.cpp +++ b/src/ripple/app/tx/impl/CancelOffer.cpp @@ -39,10 +39,26 @@ CancelOffer::preflight(PreflightContext const& ctx) return temINVALID_FLAG; } - if (!ctx.tx[sfOfferSequence]) + if (ctx.rules.enabled(fixXahauV1)) { - JLOG(ctx.j.trace()) << "CancelOffer::preflight: missing sequence"; - return temBAD_SEQUENCE; + if ((!ctx.tx.isFieldPresent(sfOfferSequence) && + !ctx.tx.isFieldPresent(sfOfferID)) || + (ctx.tx.isFieldPresent(sfOfferSequence) && + ctx.tx.isFieldPresent(sfOfferID))) + { + JLOG(ctx.j.trace()) + << "CancelOffer::preflight: invalid sequence or offer id"; + return temBAD_SEQUENCE; + } + } + else + { + if (!ctx.tx.isFieldPresent(sfOfferSequence) || + ctx.tx[sfOfferSequence] == 0) + { + JLOG(ctx.j.trace()) << "CancelOffer::preflight: missing sequence"; + return temBAD_SEQUENCE; + } } return preflight2(ctx); @@ -54,29 +70,18 @@ TER CancelOffer::preclaim(PreclaimContext const& ctx) { auto const id = ctx.tx[sfAccount]; - auto const offerSequence = ctx.tx[sfOfferSequence]; auto const sle = ctx.view.read(keylet::account(id)); if (!sle) return terNO_ACCOUNT; - bool hooksEnabled = ctx.view.rules().enabled(featureHooks); - - auto const offerID = ctx.tx[~sfOfferID]; + auto const offerSequence = ctx.tx[~sfOfferSequence]; - if ((*sle)[sfSequence] <= offerSequence) + if (offerSequence && (*sle)[sfSequence] <= *offerSequence) { - if (hooksEnabled && offerID && offerSequence == 0) - { - // pass, here the txn provides offerID instead of offerSequence - } - else - { - JLOG(ctx.j.trace()) - << "Malformed transaction: " - << "Sequence " << offerSequence << " is invalid."; - return temBAD_SEQUENCE; - } + JLOG(ctx.j.trace()) << "Malformed transaction: " + << "Sequence " << *offerSequence << " is invalid."; + return temBAD_SEQUENCE; } return tesSUCCESS; @@ -87,36 +92,40 @@ CancelOffer::preclaim(PreclaimContext const& ctx) TER CancelOffer::doApply() { - auto const offerSequence = ctx_.tx[sfOfferSequence]; - auto const sle = view().read(keylet::account(account_)); if (!sle) return tefINTERNAL; - bool hooksEnabled = view().rules().enabled(featureHooks); - - std::optional offerID; + auto const offerSequence = ctx_.tx[~sfOfferSequence]; + auto const offerID = ctx_.tx[~sfOfferID]; - if (hooksEnabled) - offerID = ctx_.tx[~sfOfferID]; + bool hooksEnabled = view().rules().enabled(featureHooks); - Keylet cancel = hooksEnabled && offerID && offerSequence == 0 + Keylet cancel = hooksEnabled && offerID && !offerSequence ? Keylet(ltOFFER, *offerID) - : keylet::offer(account_, offerSequence); + : keylet::offer(account_, *offerSequence); if (auto sleOffer = view().peek(cancel)) { if (offerID) JLOG(j_.debug()) << "Trying to cancel offer :" << *offerID; else - JLOG(j_.debug()) << "Trying to cancel offer #" << offerSequence; + JLOG(j_.debug()) << "Trying to cancel offer #" << *offerSequence; + + bool const fixV1 = view().rules().enabled(fixXahauV1); + if (fixV1 && sleOffer->getFieldU16(sfLedgerEntryType) != ltOFFER) + { + JLOG(j_.debug()) << "OfferCancel specified non-offer ledger object"; + return tecINTERNAL; + } + return offerDelete(view(), sleOffer, ctx_.app.journal("View")); } if (offerID) JLOG(j_.debug()) << "Offer :" << *offerID << " can't be found."; else - JLOG(j_.debug()) << "Offer #" << offerSequence << " can't be found."; + JLOG(j_.debug()) << "Offer #" << *offerSequence << " can't be found."; return tesSUCCESS; } diff --git a/src/ripple/app/tx/impl/Escrow.cpp b/src/ripple/app/tx/impl/Escrow.cpp index a9c0424e7b9..0ea66498b08 100644 --- a/src/ripple/app/tx/impl/Escrow.cpp +++ b/src/ripple/app/tx/impl/Escrow.cpp @@ -431,6 +431,18 @@ EscrowFinish::preflight(PreflightContext const& ctx) } } + // sfOfferSequence was changed to optional, so ensure the behaviour is the + // same until amendment passes + if (!ctx.rules.enabled(fixXahauV1)) + { + if (!ctx.tx.isFieldPresent(sfOfferSequence)) + return temMALFORMED; + } + + if (!ctx.tx.isFieldPresent(sfEscrowID) && + !ctx.tx.isFieldPresent(sfOfferSequence)) + return temMALFORMED; + return tesSUCCESS; } @@ -456,18 +468,31 @@ EscrowFinish::doApply() return temDISABLED; std::optional escrowID = ctx_.tx[~sfEscrowID]; + std::optional offerSequence = ctx_.tx[~sfOfferSequence]; - if (escrowID && ctx_.tx[sfOfferSequence] != 0) - return temMALFORMED; + bool const fixV1 = view().rules().enabled(fixXahauV1); + + if (!fixV1) + { + if (escrowID && ctx_.tx[sfOfferSequence] != 0) + return temMALFORMED; + } + else + { + if (escrowID && offerSequence) + return temMALFORMED; + } - Keylet k = escrowID - ? Keylet(ltESCROW, *escrowID) - : keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); + Keylet k = escrowID ? Keylet(ltESCROW, *escrowID) + : keylet::escrow(ctx_.tx[sfOwner], *offerSequence); auto const slep = ctx_.view().peek(k); if (!slep) return tecNO_TARGET; + if (fixV1 && slep->getFieldU16(sfLedgerEntryType) != ltESCROW) + return tecINTERNAL; + AccountID const account = (*slep)[sfAccount]; auto const sle = ctx_.view().peek(keylet::account(account)); auto const amount = slep->getFieldAmount(sfAmount); @@ -692,6 +717,18 @@ EscrowCancel::preflight(PreflightContext const& ctx) if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + // sfOfferSequence was changed to optional, so ensure the behaviour is the + // same until amendment passes + if (!ctx.rules.enabled(fixXahauV1)) + { + if (!ctx.tx.isFieldPresent(sfOfferSequence)) + return temMALFORMED; + } + + if (!ctx.tx.isFieldPresent(sfEscrowID) && + !ctx.tx.isFieldPresent(sfOfferSequence)) + return temMALFORMED; + return preflight2(ctx); } @@ -704,18 +741,30 @@ EscrowCancel::doApply() return temDISABLED; std::optional escrowID = ctx_.tx[~sfEscrowID]; + std::optional offerSequence = ctx_.tx[~sfOfferSequence]; - if (escrowID && ctx_.tx[sfOfferSequence] != 0) - return temMALFORMED; + bool const fixV1 = view().rules().enabled(fixXahauV1); + if (!fixV1) + { + if (escrowID && ctx_.tx[sfOfferSequence] != 0) + return temMALFORMED; + } + else + { + if (escrowID && offerSequence) + return temMALFORMED; + } - Keylet k = escrowID - ? Keylet(ltESCROW, *escrowID) - : keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); + Keylet k = escrowID ? Keylet(ltESCROW, *escrowID) + : keylet::escrow(ctx_.tx[sfOwner], *offerSequence); auto const slep = ctx_.view().peek(k); if (!slep) return tecNO_TARGET; + if (fixV1 && slep->getFieldU16(sfLedgerEntryType) != ltESCROW) + return tecINTERNAL; + if (ctx_.view().rules().enabled(fix1571)) { auto const now = ctx_.view().info().parentCloseTime; diff --git a/src/ripple/app/tx/impl/GenesisMint.cpp b/src/ripple/app/tx/impl/GenesisMint.cpp index ef14212a635..dd2a1021463 100644 --- a/src/ripple/app/tx/impl/GenesisMint.cpp +++ b/src/ripple/app/tx/impl/GenesisMint.cpp @@ -74,6 +74,8 @@ GenesisMint::preflight(PreflightContext const& ctx) return temMALFORMED; } + bool const allowDuplicates = ctx.rules.enabled(fixXahauV1); + std::unordered_set alreadySeen; for (auto const& dest : dests) { @@ -137,6 +139,9 @@ GenesisMint::preflight(PreflightContext const& ctx) return temMALFORMED; } + if (allowDuplicates) + continue; + if (alreadySeen.find(accid) != alreadySeen.end()) { JLOG(ctx.j.warn()) << "GenesisMint: duplicate in destinations."; @@ -171,7 +176,114 @@ GenesisMint::doApply() { auto const& dests = ctx_.tx.getFieldArray(sfGenesisMints); + if (!view().rules().enabled(fixXahauV1)) + { + STAmount dropsAdded{0}; + for (auto const& dest : dests) + { + auto const amt = dest[~sfAmount]; + + if (amt && !isXRP(*amt)) + { + JLOG(ctx_.journal.warn()) << "GenesisMint: Non-xrp amount."; + return tecINTERNAL; + } + + auto const flags = dest[~sfGovernanceFlags]; + auto const marks = dest[~sfGovernanceMarks]; + + auto const id = dest.getAccountID(sfDestination); + auto const k = keylet::account(id); + auto sle = view().peek(k); + + bool const created = !sle; + + if (created) + { + // Create the account. + std::uint32_t const seqno{ + view().info().parentCloseTime.time_since_epoch().count()}; + + sle = std::make_shared(k); + sle->setAccountID(sfAccount, id); + + sle->setFieldU32(sfSequence, seqno); + + if (amt) + { + sle->setFieldAmount(sfBalance, *amt); + dropsAdded += *amt; + } + else // give them 2 XRP if the account didn't exist, same as + // ttIMPORT + { + XRPAmount const initialXrp = + Import::computeStartingBonus(ctx_.view()); + sle->setFieldAmount(sfBalance, initialXrp); + dropsAdded += initialXrp; + } + } + else if (amt) + { + // Credit the account + STAmount startBal = sle->getFieldAmount(sfBalance); + STAmount finalBal = startBal + *amt; + if (finalBal <= startBal) + { + JLOG(ctx_.journal.warn()) + << "GenesisMint: cannot credit " << dest + << " due to balance overflow"; + return tecINTERNAL; + } + + sle->setFieldAmount(sfBalance, finalBal); + dropsAdded += *amt; + } + + // set flags and marks as applicable + if (flags) + sle->setFieldH256(sfGovernanceFlags, *flags); + + if (marks) + sle->setFieldH256(sfGovernanceMarks, *marks); + + if (created) + view().insert(sle); + else + view().update(sle); + } + + // update ledger header + if (dropsAdded < beast::zero || + dropsAdded.xrp() + view().info().drops < view().info().drops) + { + JLOG(ctx_.journal.warn()) << "GenesisMint: dropsAdded overflowed\n"; + return tecINTERNAL; + } + + if (dropsAdded > beast::zero) + ctx_.rawView().rawDestroyXRP(-dropsAdded.xrp()); + + return tesSUCCESS; + } + + // RH NOTE: + // As of fixXahauV1, duplicate accounts are allowed + // so we first do a summation loop then an actioning loop + // if amendment is not active then there's exactly one entry per + + std::map< + AccountID, // account to credit + std::tuple< + XRPAmount, // amount to credit + std::optional, // gov flags + std::optional>> // gov marks + + mints; + STAmount dropsAdded{0}; + + // sum loop for (auto const& dest : dests) { auto const amt = dest[~sfAmount]; @@ -182,13 +294,53 @@ GenesisMint::doApply() return tecINTERNAL; } + XRPAmount toCredit = amt ? amt->xrp() : XRPAmount{0}; + auto const flags = dest[~sfGovernanceFlags]; auto const marks = dest[~sfGovernanceMarks]; - auto const id = dest.getAccountID(sfDestination); + + bool const firstOccurance = mints.find(id) == mints.end(); + + dropsAdded += toCredit; + + // if flags / marks appear more than once we just take the first + // appearance + if (firstOccurance) + mints[id] = {toCredit, flags, marks}; + else + { + // duplicated entries accumulate in the map + + auto& [accTotal, f, m] = mints[id]; + + // detect overflow + if (accTotal + toCredit < accTotal) + { + JLOG(ctx_.journal.warn()) << "GenesisMint: cannot credit " << id + << " due to balance overflow"; + return tecINTERNAL; + } + + accTotal += toCredit; + } + } + + // check for ledger header overflow + if (dropsAdded < beast::zero || + dropsAdded.xrp() + view().info().drops < view().info().drops) + { + JLOG(ctx_.journal.warn()) << "GenesisMint: dropsAdded overflowed\n"; + return tecINTERNAL; + } + + // action loop + for (auto const& [id, values] : mints) + { + auto const& [amt, flags, marks] = values; + auto const k = keylet::account(id); auto sle = view().peek(k); - bool const created = !sle; if (created) @@ -202,34 +354,21 @@ GenesisMint::doApply() sle->setFieldU32(sfSequence, seqno); - if (amt) - { - sle->setFieldAmount(sfBalance, *amt); - dropsAdded += *amt; - } - else // give them 2 XRP if the account didn't exist, same as - // ttIMPORT - { - XRPAmount const initialXrp = - Import::computeStartingBonus(ctx_.view()); - sle->setFieldAmount(sfBalance, initialXrp); - dropsAdded += initialXrp; - } + sle->setFieldAmount(sfBalance, amt); } - else if (amt) + else if (amt > beast::zero) { // Credit the account STAmount startBal = sle->getFieldAmount(sfBalance); - STAmount finalBal = startBal + *amt; + STAmount finalBal = startBal + amt; if (finalBal <= startBal) { - JLOG(ctx_.journal.warn()) << "GenesisMint: cannot credit " - << dest << " due to balance overflow"; + JLOG(ctx_.journal.warn()) << "GenesisMint: cannot credit " << id + << " due to balance overflow"; return tecINTERNAL; } sle->setFieldAmount(sfBalance, finalBal); - dropsAdded += *amt; } // set flags and marks as applicable @@ -245,14 +384,6 @@ GenesisMint::doApply() view().update(sle); } - // update ledger header - if (dropsAdded < beast::zero || - dropsAdded.xrp() + view().info().drops < view().info().drops) - { - JLOG(ctx_.journal.warn()) << "GenesisMint: dropsAdded overflowed\n"; - return tecINTERNAL; - } - if (dropsAdded > beast::zero) ctx_.rawView().rawDestroyXRP(-dropsAdded.xrp()); diff --git a/src/ripple/app/tx/impl/Import.cpp b/src/ripple/app/tx/impl/Import.cpp index 2eaeba0f931..6b4c335d4f1 100644 --- a/src/ripple/app/tx/impl/Import.cpp +++ b/src/ripple/app/tx/impl/Import.cpp @@ -710,7 +710,6 @@ Import::preflight(PreflightContext const& ctx) { auto const& data = (*xpop)[jss::validation][jss::data]; std::set used_key; - for (const auto& key : data.getMemberNames()) { auto nodepub = key; @@ -818,7 +817,17 @@ Import::preflight(PreflightContext const& ctx) << " validation count: " << validationCount; // check if the validation count is adequate - if (quorum >= validationCount) + auto hasInsufficientQuorum = [&ctx](int quorum, int validationCount) { + if (ctx.rules.enabled(fixXahauV1)) + { + return quorum > validationCount; + } + else + { + return quorum >= validationCount; + } + }; + if (hasInsufficientQuorum(quorum, validationCount)) { JLOG(ctx.j.warn()) << "Import: xpop did not contain an 80% quorum for " "the txn it purports to prove. " diff --git a/src/ripple/app/tx/impl/Invoke.cpp b/src/ripple/app/tx/impl/Invoke.cpp index c3c3c528832..08a14bf32c8 100644 --- a/src/ripple/app/tx/impl/Invoke.cpp +++ b/src/ripple/app/tx/impl/Invoke.cpp @@ -86,6 +86,27 @@ Invoke::calculateBaseFee(ReadView const& view, STTx const& tx) extraFee += XRPAmount{static_cast(tx.getFieldVL(sfBlob).size())}; + // old code (prior to fixXahauV1) + if (!view.rules().enabled(fixXahauV1)) + { + if (tx.isFieldPresent(sfHookParameters)) + { + uint64_t paramBytes = 0; + auto const& params = tx.getFieldArray(sfHookParameters); + for (auto const& param : params) + { + paramBytes += + (param.isFieldPresent(sfHookParameterName) + ? param.getFieldVL(sfHookParameterName).size() + : 0) + + (param.isFieldPresent(sfHookParameterValue) + ? param.getFieldVL(sfHookParameterValue).size() + : 0); + } + extraFee += XRPAmount{static_cast(paramBytes)}; + } + } + return Transactor::calculateBaseFee(view, tx) + extraFee; } diff --git a/src/ripple/app/tx/impl/SetHook.cpp b/src/ripple/app/tx/impl/SetHook.cpp index 7506ae8cd73..dec4570ec38 100644 --- a/src/ripple/app/tx/impl/SetHook.cpp +++ b/src/ripple/app/tx/impl/SetHook.cpp @@ -1856,6 +1856,10 @@ SetHook::setHook() else if (oldHookSLE && !newHooksEmpty) { // UPDATE ltHOOK + if (view().rules().enabled(fixXahauV1)) + { + (*newHookSLE)[sfOwnerNode] = (*oldHookSLE)[sfOwnerNode]; + } view().erase(oldHookSLE); view().insert(newHookSLE); } diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 9a254d9a2f1..fa0dc0be296 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -351,7 +351,7 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) XRPAmount accumulator = baseFee; if (view.rules().enabled(featureHooks) && - tx.isFieldPresent(sfHookParameters)) + view.rules().enabled(fixXahauV1) && tx.isFieldPresent(sfHookParameters)) { uint64_t paramBytes = 0; auto const& params = tx.getFieldArray(sfHookParameters); diff --git a/src/ripple/app/tx/impl/URIToken.cpp b/src/ripple/app/tx/impl/URIToken.cpp index c8284921444..13880684594 100644 --- a/src/ripple/app/tx/impl/URIToken.cpp +++ b/src/ripple/app/tx/impl/URIToken.cpp @@ -80,7 +80,7 @@ URIToken::preflight(PreflightContext const& ctx) // fix amendment to return temMALFORMED if sfDestination field is present // and sfAmount field is not present - if (ctx.rules.enabled(fixURITokenV1)) + if (ctx.rules.enabled(fixXahauV1)) { if (ctx.tx.isFieldPresent(sfDestination) && !ctx.tx.isFieldPresent(sfAmount)) @@ -283,7 +283,11 @@ URIToken::doApply() { auto j = ctx_.app.journal("View"); - auto const sle = view().peek(keylet::account(account_)); + Sandbox sb(&ctx_.view()); + + bool const fixV1 = sb.rules().enabled(fixXahauV1); + + auto const sle = sb.peek(keylet::account(account_)); if (!sle) return tefINTERNAL; @@ -292,7 +296,7 @@ URIToken::doApply() if (tt == ttURITOKEN_MINT || tt == ttURITOKEN_BUY) { STAmount const reserve{ - view().fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; + sb.fees().accountReserve(sle->getFieldU32(sfOwnerCount) + 1)}; STAmount const afterFee = mPriorBalance - ctx_.tx.getFieldAmount(sfFee).xrp(); @@ -314,7 +318,7 @@ URIToken::doApply() if (tt != ttURITOKEN_MINT) { kl = Keylet{ltURI_TOKEN, ctx_.tx.getFieldH256(sfURITokenID)}; - sleU = view().peek(*kl); + sleU = sb.peek(*kl); if (!sleU) return tecNO_ENTRY; @@ -330,7 +334,7 @@ URIToken::doApply() if (*owner == account_) sleOwner = sle; else - sleOwner = view().peek(keylet::account(*owner)); + sleOwner = sb.peek(keylet::account(*owner)); if (!sleOwner) { @@ -344,7 +348,7 @@ URIToken::doApply() { case ttURITOKEN_MINT: { kl = keylet::uritoken(account_, ctx_.tx.getFieldVL(sfURI)); - if (view().exists(*kl)) + if (sb.exists(*kl)) return tecDUPLICATE; sleU = std::make_shared(*kl); @@ -372,7 +376,7 @@ URIToken::doApply() if (flags & tfBurnable) sleU->setFlag(tfBurnable); - auto const page = view().dirInsert( + auto const page = sb.dirInsert( keylet::ownerDir(account_), *kl, describeOwnerDir(account_)); JLOG(j_.trace()) @@ -383,12 +387,15 @@ URIToken::doApply() return tecDIR_FULL; sleU->setFieldU64(sfOwnerNode, *page); - view().insert(sleU); + sb.insert(sleU); // ensure there is a deletion blocker against the issuer now sle->setFieldU32(sfFlags, sle->getFlags() | lsfURITokenIssuer); - adjustOwnerCount(view(), sle, 1, j); + adjustOwnerCount(sb, sle, 1, j); + + sb.update(sle); + sb.apply(ctx_.rawView()); return tesSUCCESS; } @@ -399,7 +406,8 @@ URIToken::doApply() if (sleU->isFieldPresent(sfDestination)) sleU->makeFieldAbsent(sfDestination); - view().update(sleU); + sb.update(sleU); + sb.apply(ctx_.rawView()); return tesSUCCESS; } @@ -410,17 +418,13 @@ URIToken::doApply() sleU->makeFieldAbsent(sfAmount); if (sleU->isFieldPresent(sfDestination)) sleU->makeFieldAbsent(sfDestination); - view().update(sleU); + sb.update(sleU); + sb.apply(ctx_.rawView()); return tesSUCCESS; } STAmount const purchaseAmount = ctx_.tx.getFieldAmount(sfAmount); - bool const sellerLow = purchaseAmount.getIssuer() > *owner; - bool const buyerLow = purchaseAmount.getIssuer() > account_; - bool sellerIssuer = purchaseAmount.getIssuer() == *owner; - bool buyerIssuer = purchaseAmount.getIssuer() == account_; - // check if the seller has listed it at all if (!saleAmount) return tecNO_PERMISSION; @@ -432,331 +436,484 @@ URIToken::doApply() if (purchaseAmount.issue() != saleAmount->issue()) return temBAD_CURRENCY; - std::optional initBuyerBal; - std::optional initSellerBal; - std::optional finBuyerBal; - std::optional finSellerBal; - std::optional dstAmt; - std::optional tlSeller; - std::shared_ptr sleDstLine; - std::shared_ptr sleSrcLine; - - // if it's an xrp sale/purchase then no trustline needed - if (purchaseAmount.native()) + if (fixV1) { + // this is the reworked version of the buy routine + if (purchaseAmount < saleAmount) return tecINSUFFICIENT_PAYMENT; - if (purchaseAmount > ((*sleOwner)[sfBalance] - ctx_.tx[sfFee])) - return tecINSUFFICIENT_FUNDS; - - dstAmt = purchaseAmount; - - initSellerBal = (*sleOwner)[sfBalance]; - initBuyerBal = (*sle)[sfBalance]; - - finSellerBal = *initSellerBal + purchaseAmount; - finBuyerBal = *initBuyerBal - purchaseAmount; - } - else - { - // IOU sale - STAmount availableFunds{accountFunds( - view(), account_, purchaseAmount, fhZERO_IF_FROZEN, j)}; - - // check for any possible bars to a buy transaction - // between these accounts for this asset - - if (buyerIssuer) + // if it's an xrp sale/purchase then no trustline needed + if (purchaseAmount.native()) { - // pass: issuer does not create own trustline + if (purchaseAmount > + ((*sleOwner)[sfBalance] - ctx_.tx[sfFee])) + return tecINSUFFICIENT_FUNDS; } else { - TER result = trustTransferAllowed( - view(), {account_, *owner}, purchaseAmount.issue(), j); - JLOG(j.trace()) - << "URIToken::doApply trustTransferAllowed result=" - << result; + // IOU sale + if (TER result = trustTransferAllowed( + sb, {account_, *owner}, purchaseAmount.issue(), j); + result != tesSUCCESS) + { + JLOG(j.trace()) + << "URIToken::doApply trustTransferAllowed result=" + << result; - if (!isTesSuccess(result)) return result; + } + + if (STAmount availableFunds{accountFunds( + sb, account_, purchaseAmount, fhZERO_IF_FROZEN, j)}; + purchaseAmount > availableFunds) + return tecINSUFFICIENT_FUNDS; } - if (purchaseAmount > availableFunds) - return tecINSUFFICIENT_FUNDS; + // execute the funds transfer, we'll check reserves last + if (TER result = accountSend( + sb, account_, *owner, purchaseAmount, j, false); + result != tesSUCCESS) + return result; + + // add token to new owner dir + auto const newPage = sb.dirInsert( + keylet::ownerDir(account_), + *kl, + describeOwnerDir(account_)); + + JLOG(j_.trace()) << "Adding URIToken to owner directory " + << to_string(kl->key) << ": " + << (newPage ? "success" : "failure"); + + if (!newPage) + return tecDIR_FULL; + + // remove from current owner directory + if (!sb.dirRemove( + keylet::ownerDir(*owner), + sleU->getFieldU64(sfOwnerNode), + kl->key, + true)) + { + JLOG(j.fatal()) + << "Could not remove URIToken from owner directory"; - // check if the seller has a line - tlSeller = keylet::line( - *owner, - purchaseAmount.getIssuer(), - purchaseAmount.getCurrency()); - Keylet tlBuyer = keylet::line( - account_, - purchaseAmount.getIssuer(), - purchaseAmount.getCurrency()); + return tefBAD_LEDGER; + } - sleDstLine = view().peek(*tlSeller); - sleSrcLine = view().peek(tlBuyer); + // adjust owner counts + adjustOwnerCount(sb, sleOwner, -1, j); + adjustOwnerCount(sb, sle, 1, j); - if (sellerIssuer) - { - // pass: issuer does not create own trustline - } - else if (!sleDstLine) - { - // they do not, so we can create one if they have sufficient - // reserve + // clean the offer off the object + sleU->makeFieldAbsent(sfAmount); + if (sleU->isFieldPresent(sfDestination)) + sleU->makeFieldAbsent(sfDestination); - if (std::uint32_t const ownerCount = {sleOwner->at( - sfOwnerCount)}; - (*sleOwner)[sfBalance] < - view().fees().accountReserve(ownerCount + 1)) - { - JLOG(j_.trace()) - << "Trust line does not exist. " - "Insufficent reserve to create line."; + // set the new owner of the object + sleU->setAccountID(sfOwner, account_); - return tecNO_LINE_INSUF_RESERVE; - } - } - if (buyerIssuer) - { - // pass: issuer does not adjust own trustline - initBuyerBal = purchaseAmount.zeroed(); - finBuyerBal = purchaseAmount.zeroed(); - } - else + // tell the ledger where to find it + sleU->setFieldU64(sfOwnerNode, *newPage); + + // check each side has sufficient balance remaining to cover the + // updated ownercounts + auto hasSufficientReserve = + [&](std::shared_ptr const& sle) -> bool { + std::uint32_t const uOwnerCount = + sle->getFieldU32(sfOwnerCount); + return sle->getFieldAmount(sfBalance) >= + sb.fees().accountReserve(uOwnerCount); + }; + + if (!hasSufficientReserve(sle)) { - // remove from buyer - initBuyerBal = buyerLow ? ((*sleSrcLine)[sfBalance]) - : -((*sleSrcLine)[sfBalance]); - finBuyerBal = *initBuyerBal - purchaseAmount; + JLOG(j.trace()) << "URIToken: buyer " << account_ + << " has insufficient reserve to buy"; + return tecINSUFFICIENT_RESERVE; } - dstAmt = purchaseAmount; - static Rate const parityRate(QUALITY_ONE); - auto xferRate = transferRate(view(), saleAmount->getIssuer()); - if (!sellerIssuer && !buyerIssuer && xferRate != parityRate) + // This should only happen if the owner burned their reserves + // below the needed amount via another transactor. If this + // happens they should top up their account before selling! + if (!hasSufficientReserve(sleOwner)) { - dstAmt = multiplyRound( - purchaseAmount, xferRate, purchaseAmount.issue(), true); + JLOG(j.warn()) + << "URIToken: seller " << *owner + << " has insufficient reserve to allow purchase!"; + return tecINSUF_RESERVE_SELLER; } - initSellerBal = !sleDstLine - ? purchaseAmount.zeroed() - : sellerLow ? ((*sleDstLine)[sfBalance]) - : -((*sleDstLine)[sfBalance]); - - finSellerBal = *initSellerBal + *dstAmt; + sb.update(sle); + sb.update(sleU); + sb.update(sleOwner); + sb.apply(ctx_.rawView()); + return tesSUCCESS; } - // sanity check balance mutations (xrp or iou, both are checked the - // same way now) - if (*finSellerBal < *initSellerBal) + // old logic { - JLOG(j.warn()) - << "URIToken txid=" << ctx_.tx.getTransactionID() << " " - << "finSellerBal < initSellerBal"; - return tecINTERNAL; - } + STAmount const purchaseAmount = + ctx_.tx.getFieldAmount(sfAmount); + + bool const sellerLow = purchaseAmount.getIssuer() > *owner; + bool const buyerLow = purchaseAmount.getIssuer() > account_; + bool sellerIssuer = purchaseAmount.getIssuer() == *owner; + bool buyerIssuer = purchaseAmount.getIssuer() == account_; + + // check if the seller has listed it at all + if (!saleAmount) + return tecNO_PERMISSION; + + // check if the seller has listed it for sale to a specific + // account + if (dest && *dest != account_) + return tecNO_PERMISSION; + + if (purchaseAmount.issue() != saleAmount->issue()) + return temBAD_CURRENCY; + + std::optional initBuyerBal; + std::optional initSellerBal; + std::optional finBuyerBal; + std::optional finSellerBal; + std::optional dstAmt; + std::optional tlSeller; + std::shared_ptr sleDstLine; + std::shared_ptr sleSrcLine; - if (*finBuyerBal > *initBuyerBal) - { - JLOG(j.warn()) - << "URIToken txid=" << ctx_.tx.getTransactionID() << " " - << "finBuyerBal > initBuyerBal"; - return tecINTERNAL; - } + // if it's an xrp sale/purchase then no trustline needed + if (purchaseAmount.native()) + { + if (purchaseAmount < saleAmount) + return tecINSUFFICIENT_PAYMENT; - if (*finBuyerBal < beast::zero) - { - JLOG(j.warn()) - << "URIToken txid=" << ctx_.tx.getTransactionID() << " " - << "finBuyerBal < 0"; - return tecINTERNAL; - } + if (purchaseAmount > + ((*sleOwner)[sfBalance] - ctx_.tx[sfFee])) + return tecINSUFFICIENT_FUNDS; - if (*finSellerBal < beast::zero) - { - JLOG(j.warn()) - << "URIToken txid=" << ctx_.tx.getTransactionID() << " " - << "finSellerBal < 0"; - return tecINTERNAL; - } + dstAmt = purchaseAmount; - // to this point no ledger changes have been made - // make them in a sensible order such that failure doesn't require - // cleanup + initSellerBal = (*sleOwner)[sfBalance]; + initBuyerBal = (*sle)[sfBalance]; - // add to new owner's directory first, this can fail if they have - // too many objects - auto const newPage = view().dirInsert( - keylet::ownerDir(account_), *kl, describeOwnerDir(account_)); + finSellerBal = *initSellerBal + purchaseAmount; + finBuyerBal = *initBuyerBal - purchaseAmount; + } + else + { + // IOU sale + STAmount availableFunds{accountFunds( + sb, account_, purchaseAmount, fhZERO_IF_FROZEN, j)}; - JLOG(j_.trace()) - << "Adding URIToken to owner directory " << to_string(kl->key) - << ": " << (newPage ? "success" : "failure"); + // check for any possible bars to a buy transaction + // between these accounts for this asset - if (!newPage) - { - // nothing has happened at all and there is nothing to clean up - // we can just leave with DIR_FULL - return tecDIR_FULL; - } + if (buyerIssuer) + { + // pass: issuer does not create own trustline + } + else + { + TER result = trustTransferAllowed( + sb, {account_, *owner}, purchaseAmount.issue(), j); + JLOG(j.trace()) + << "URIToken::doApply trustTransferAllowed result=" + << result; + + if (!isTesSuccess(result)) + return result; + } - // Next create destination trustline where applicable. This could - // fail for a variety of reasons. If it does fail we need to remove - // the dir entry we just added to the buyer before we leave. - bool lineCreated = false; - if (!isXRP(purchaseAmount) && !sleDstLine && !sellerIssuer) - { - // clang-format off - if (TER const ter = trustCreate( - view(), // payment sandbox - sellerLow, // is dest low? - purchaseAmount.getIssuer(), // source - *owner, // destination - tlSeller->key, // ledger index - sleOwner, // Account to add to - false, // authorize account - (sleOwner->getFlags() & lsfDefaultRipple) == 0, - false, // freeze trust line - *dstAmt, // initial balance zero - Issue( - purchaseAmount.getCurrency(), - *owner), // limit of zero - 0, // quality in - 0, // quality out - j); // journal - !isTesSuccess(ter)) - { - // remove the newly inserted directory entry before we leave - // - if (!view().dirRemove(keylet::ownerDir(account_), *newPage, kl->key, true)) + if (purchaseAmount > availableFunds) + return tecINSUFFICIENT_FUNDS; + + // check if the seller has a line + tlSeller = keylet::line( + *owner, + purchaseAmount.getIssuer(), + purchaseAmount.getCurrency()); + Keylet tlBuyer = keylet::line( + account_, + purchaseAmount.getIssuer(), + purchaseAmount.getCurrency()); + + sleDstLine = sb.peek(*tlSeller); + sleSrcLine = sb.peek(tlBuyer); + + if (sellerIssuer) { - JLOG(j.fatal()) - << "Could not remove URIToken from owner directory"; + // pass: issuer does not create own trustline + } + else if (!sleDstLine) + { + // they do not, so we can create one if they have + // sufficient reserve + + if (std::uint32_t const ownerCount = {sleOwner->at( + sfOwnerCount)}; + (*sleOwner)[sfBalance] < + sb.fees().accountReserve(ownerCount + 1)) + { + JLOG(j_.trace()) + << "Trust line does not exist. " + "Insufficent reserve to create line."; + + return tecNO_LINE_INSUF_RESERVE; + } + } + if (buyerIssuer) + { + // pass: issuer does not adjust own trustline + initBuyerBal = purchaseAmount.zeroed(); + finBuyerBal = purchaseAmount.zeroed(); + } + else + { + // remove from buyer + initBuyerBal = buyerLow ? ((*sleSrcLine)[sfBalance]) + : -((*sleSrcLine)[sfBalance]); + finBuyerBal = *initBuyerBal - purchaseAmount; + } - return tefBAD_LEDGER; + dstAmt = purchaseAmount; + static Rate const parityRate(QUALITY_ONE); + auto xferRate = transferRate(sb, saleAmount->getIssuer()); + if (!sellerIssuer && !buyerIssuer && xferRate != parityRate) + { + dstAmt = multiplyRound( + purchaseAmount, + xferRate, + purchaseAmount.issue(), + true); } - // leave - return ter; + initSellerBal = !sleDstLine + ? purchaseAmount.zeroed() + : sellerLow ? ((*sleDstLine)[sfBalance]) + : -((*sleDstLine)[sfBalance]); + + finSellerBal = *initSellerBal + *dstAmt; } - // clang-format on - // add their trustline to their ownercount - lineCreated = true; - } + // sanity check balance mutations (xrp or iou, both are checked + // the same way now) + if (*finSellerBal < *initSellerBal) + { + JLOG(j.warn()) + << "URIToken txid=" << ctx_.tx.getTransactionID() << " " + << "finSellerBal < initSellerBal"; + return tecINTERNAL; + } - // execution to here means we added the URIToken to the buyer's - // directory and we definitely have a way to send the funds to the - // seller. + if (*finBuyerBal > *initBuyerBal) + { + JLOG(j.warn()) + << "URIToken txid=" << ctx_.tx.getTransactionID() << " " + << "finBuyerBal > initBuyerBal"; + return tecINTERNAL; + } - // remove from current owner directory - if (!view().dirRemove( - keylet::ownerDir(*owner), - sleU->getFieldU64(sfOwnerNode), - kl->key, - true)) - { - JLOG(j.fatal()) - << "Could not remove URIToken from owner directory"; + if (*finBuyerBal < beast::zero) + { + JLOG(j.warn()) + << "URIToken txid=" << ctx_.tx.getTransactionID() << " " + << "finBuyerBal < 0"; + return tecINTERNAL; + } - // remove the newly inserted directory entry before we leave - if (!view().dirRemove( - keylet::ownerDir(account_), *newPage, kl->key, true)) + if (*finSellerBal < beast::zero) { - JLOG(j.fatal()) - << "Could not remove URIToken from owner directory (2)"; + JLOG(j.warn()) + << "URIToken txid=" << ctx_.tx.getTransactionID() << " " + << "finSellerBal < 0"; + return tecINTERNAL; } - // clean up any trustline we might have made - if (lineCreated) + // to this point no ledger changes have been made + // make them in a sensible order such that failure doesn't + // require cleanup + + // add to new owner's directory first, this can fail if they + // have too many objects + auto const newPage = sb.dirInsert( + keylet::ownerDir(account_), + *kl, + describeOwnerDir(account_)); + + JLOG(j_.trace()) << "Adding URIToken to owner directory " + << to_string(kl->key) << ": " + << (newPage ? "success" : "failure"); + + if (!newPage) { - auto line = view().peek(*tlSeller); - if (line) - view().erase(line); + // nothing has happened at all and there is nothing to clean + // up we can just leave with DIR_FULL + return tecDIR_FULL; } - return tefBAD_LEDGER; - } + // Next create destination trustline where applicable. This + // could fail for a variety of reasons. If it does fail we need + // to remove the dir entry we just added to the buyer before we + // leave. + bool lineCreated = false; + if (!isXRP(purchaseAmount) && !sleDstLine && !sellerIssuer) + { + // clang-format off + if (TER const ter = trustCreate( + sb, // payment sandbox + sellerLow, // is dest low? + purchaseAmount.getIssuer(), // source + *owner, // destination + tlSeller->key, // ledger index + sleOwner, // Account to add to + false, // authorize account + (sleOwner->getFlags() & lsfDefaultRipple) == 0, + false, // freeze trust line + *dstAmt, // initial balance zero + Issue( + purchaseAmount.getCurrency(), + *owner), // limit of zero + 0, // quality in + 0, // quality out + j); // journal + !isTesSuccess(ter)) + { + // remove the newly inserted directory entry before we leave + // + if (!sb.dirRemove(keylet::ownerDir(account_), *newPage, kl->key, true)) + { + JLOG(j.fatal()) + << "Could not remove URIToken from owner directory"; + + return tefBAD_LEDGER; + } + + // leave + return ter; + } + // clang-format on - // above is all the things that could fail. we now have swapped the - // ownership as far as the ownerdirs are concerned, and we have a - // place to pay to and from. + // add their trustline to their ownercount + lineCreated = true; + } - // if a trustline was created then the ownercount stays the same on - // the seller +1 TL -1 URIToken - if (!lineCreated && !isXRP(purchaseAmount)) - adjustOwnerCount(view(), sleOwner, -1, j); + // execution to here means we added the URIToken to the buyer's + // directory and we definitely have a way to send the funds to + // the seller. - // the buyer gets a new object - adjustOwnerCount(view(), sle, 1, j); + // remove from current owner directory + if (!sb.dirRemove( + keylet::ownerDir(*owner), + sleU->getFieldU64(sfOwnerNode), + kl->key, + true)) + { + JLOG(j.fatal()) + << "Could not remove URIToken from owner directory"; - // clean the offer off the object - sleU->makeFieldAbsent(sfAmount); - if (sleU->isFieldPresent(sfDestination)) - sleU->makeFieldAbsent(sfDestination); + // remove the newly inserted directory entry before we leave + if (!sb.dirRemove( + keylet::ownerDir(account_), + *newPage, + kl->key, + true)) + { + JLOG(j.fatal()) << "Could not remove URIToken from " + "owner directory (2)"; + } - // set the new owner of the object - sleU->setAccountID(sfOwner, account_); + // clean up any trustline we might have made + if (lineCreated) + { + auto line = sb.peek(*tlSeller); + if (line) + sb.erase(line); + } - // tell the ledger where to find it - sleU->setFieldU64(sfOwnerNode, *newPage); + return tefBAD_LEDGER; + } - // update the buyer's balance - if (isXRP(purchaseAmount)) - { - // the sale is for xrp, so set the balance - sle->setFieldAmount(sfBalance, *finBuyerBal); - } - else if (sleSrcLine) - { - // update the buyer's line to reflect the reduction of the - // purchase price - sleSrcLine->setFieldAmount( - sfBalance, buyerLow ? *finBuyerBal : -(*finBuyerBal)); - } - else if (buyerIssuer) - { - // pass: buyer is issuer, no update required. - } - else - return tecINTERNAL; + // above is all the things that could fail. we now have swapped + // the ownership as far as the ownerdirs are concerned, and we + // have a place to pay to and from. - // update the seller's balance - if (isXRP(purchaseAmount)) - { - // the sale is for xrp, so set the balance - sleOwner->setFieldAmount(sfBalance, *finSellerBal); - } - else if (sleDstLine) - { - // the line already existed on the seller side so update it - sleDstLine->setFieldAmount( - sfBalance, sellerLow ? *finSellerBal : -(*finSellerBal)); - } - else if (lineCreated) - { - // pass, the TL already has this balance set on it at creation - } - else if (sellerIssuer) - { - // pass: seller is issuer, no update required. - } - else - return tecINTERNAL; + // if a trustline was created then the ownercount stays the same + // on the seller +1 TL -1 URIToken + if (!lineCreated && !isXRP(purchaseAmount)) + adjustOwnerCount(sb, sleOwner, -1, j); - if (sleSrcLine) - view().update(sleSrcLine); - if (sleDstLine) - view().update(sleDstLine); + // the buyer gets a new object + adjustOwnerCount(sb, sle, 1, j); - view().update(sleU); - view().update(sleOwner); - return tesSUCCESS; + // clean the offer off the object + sleU->makeFieldAbsent(sfAmount); + if (sleU->isFieldPresent(sfDestination)) + sleU->makeFieldAbsent(sfDestination); + + // set the new owner of the object + sleU->setAccountID(sfOwner, account_); + + // tell the ledger where to find it + sleU->setFieldU64(sfOwnerNode, *newPage); + + // update the buyer's balance + if (isXRP(purchaseAmount)) + { + // the sale is for xrp, so set the balance + sle->setFieldAmount(sfBalance, *finBuyerBal); + } + else if (sleSrcLine) + { + // update the buyer's line to reflect the reduction of the + // purchase price + sleSrcLine->setFieldAmount( + sfBalance, buyerLow ? *finBuyerBal : -(*finBuyerBal)); + } + else if (buyerIssuer) + { + // pass: buyer is issuer, no update required. + } + else + return tecINTERNAL; + + // update the seller's balance + if (isXRP(purchaseAmount)) + { + // the sale is for xrp, so set the balance + sleOwner->setFieldAmount(sfBalance, *finSellerBal); + } + else if (sleDstLine) + { + // the line already existed on the seller side so update it + sleDstLine->setFieldAmount( + sfBalance, + sellerLow ? *finSellerBal : -(*finSellerBal)); + } + else if (lineCreated) + { + // pass, the TL already has this balance set on it at + // creation + } + else if (sellerIssuer) + { + // pass: seller is issuer, no update required. + } + else + return tecINTERNAL; + + if (sleSrcLine) + sb.update(sleSrcLine); + if (sleDstLine) + sb.update(sleDstLine); + + sb.update(sle); + sb.update(sleU); + sb.update(sleOwner); + sb.apply(ctx_.rawView()); + return tesSUCCESS; + } } case ttURITOKEN_BURN: { @@ -777,16 +934,20 @@ URIToken::doApply() // execution to here means there is permission to burn auto const page = (*sleU)[sfOwnerNode]; - if (!view().dirRemove( - keylet::ownerDir(*owner), page, kl->key, true)) + if (!sb.dirRemove(keylet::ownerDir(*owner), page, kl->key, true)) { JLOG(j.fatal()) << "Could not remove URIToken from owner directory"; return tefBAD_LEDGER; } - view().erase(sleU); - adjustOwnerCount(view(), sle, -1, j); + sb.erase(sleU); + + auto& sleAcc = fixV1 ? sleOwner : sle; + + adjustOwnerCount(sb, sleAcc, -1, j); + sb.update(sleAcc); + sb.apply(ctx_.rawView()); return tesSUCCESS; } @@ -804,7 +965,8 @@ URIToken::doApply() sleU->setFieldAmount(sfAmount, ctx_.tx[sfAmount]); - view().update(sleU); + sb.update(sleU); + sb.apply(ctx_.rawView()); return tesSUCCESS; } diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h index 9dd497558ed..86ccf93d854 100644 --- a/src/ripple/ledger/View.h +++ b/src/ripple/ledger/View.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -394,7 +395,8 @@ accountSend( AccountID const& from, AccountID const& to, const STAmount& saAmount, - beast::Journal j); + beast::Journal j, + bool const senderPaysXferFees = true); [[nodiscard]] TER issueIOU( @@ -627,7 +629,7 @@ trustTransferAllowed( { static_assert( std::is_same::value || - std::is_same::value); + std::is_same::value || std::is_same::value); typedef typename std::conditional< std::is_same::value, diff --git a/src/ripple/ledger/impl/ApplyStateTable.cpp b/src/ripple/ledger/impl/ApplyStateTable.cpp index b944d0feb93..85ce5848212 100644 --- a/src/ripple/ledger/impl/ApplyStateTable.cpp +++ b/src/ripple/ledger/impl/ApplyStateTable.cpp @@ -130,6 +130,8 @@ ApplyStateTable::generateTxMeta( if (!hookEmission.empty()) meta.setHookEmissions(STArray{hookEmission, sfHookEmissions}); + bool const recordDefaultAmounts = to.rules().enabled(fixXahauV1); + Mods newMod; for (auto& item : items_) { @@ -232,8 +234,12 @@ ApplyStateTable::generateTxMeta( STObject news(sfNewFields); for (auto const& obj : *curNode) { + bool const shouldRecord = + (obj.getSType() == STI_AMOUNT && recordDefaultAmounts) || + !obj.isDefault(); + // save non-default values - if (!obj.isDefault() && + if (shouldRecord && obj.getFName().shouldMeta( SField::sMD_Create | SField::sMD_Always)) news.emplace_back(obj); diff --git a/src/ripple/ledger/impl/View.cpp b/src/ripple/ledger/impl/View.cpp index 42bd3fe6aa8..060e8d1ab87 100644 --- a/src/ripple/ledger/impl/View.cpp +++ b/src/ripple/ledger/impl/View.cpp @@ -1157,7 +1157,8 @@ rippleSend( AccountID const& uReceiverID, STAmount const& saAmount, STAmount& saActual, - beast::Journal j) + beast::Journal j, + bool const senderPaysXferFees) { auto const issuer = saAmount.getIssuer(); @@ -1179,17 +1180,30 @@ rippleSend( // Calculate the amount to transfer accounting // for any transfer fees: - saActual = multiply(saAmount, transferRate(view, issuer)); + + STAmount senderPays = saAmount; + STAmount destReceives = saAmount; + if (senderPaysXferFees) + { + senderPays = multiply(saAmount, transferRate(view, issuer)); + saActual = senderPays; + } + else + { + destReceives = divide(saAmount, transferRate(view, issuer)); + saActual = destReceives; + } JLOG(j.debug()) << "rippleSend> " << to_string(uSenderID) << " - > " << to_string(uReceiverID) << " : deliver=" << saAmount.getFullText() << " cost=" << saActual.getFullText(); - TER terResult = rippleCredit(view, issuer, uReceiverID, saAmount, true, j); + TER terResult = + rippleCredit(view, issuer, uReceiverID, destReceives, true, j); if (tesSUCCESS == terResult) - terResult = rippleCredit(view, uSenderID, issuer, saActual, true, j); + terResult = rippleCredit(view, uSenderID, issuer, senderPays, true, j); return terResult; } @@ -1200,7 +1214,8 @@ accountSend( AccountID const& uSenderID, AccountID const& uReceiverID, STAmount const& saAmount, - beast::Journal j) + beast::Journal j, + bool const senderPaysXferFees) { assert(saAmount >= beast::zero); @@ -1218,7 +1233,14 @@ accountSend( << to_string(uReceiverID) << " : " << saAmount.getFullText(); - return rippleSend(view, uSenderID, uReceiverID, saAmount, saActual, j); + return rippleSend( + view, + uSenderID, + uReceiverID, + saAmount, + saActual, + j, + senderPaysXferFees); } /* XRP send which does not check reserve and can do pure adjustment. @@ -1227,7 +1249,6 @@ accountSend( * ensure that transfers are balanced. */ TER terResult(tesSUCCESS); - SLE::pointer sender = uSenderID != beast::zero ? view.peek(keylet::account(uSenderID)) : SLE::pointer(); diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 14cb3c8a0d8..e3ddeb63262 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -353,7 +353,7 @@ extern uint256 const fixNFTokenRemint; extern uint256 const featureImport; extern uint256 const featureXahauGenesis; extern uint256 const featureHooksUpdate1; -extern uint256 const fixURITokenV1; +extern uint256 const fixXahauV1; } // namespace ripple diff --git a/src/ripple/protocol/TER.h b/src/ripple/protocol/TER.h index 2a91b4465bf..2e2fb41f6c3 100644 --- a/src/ripple/protocol/TER.h +++ b/src/ripple/protocol/TER.h @@ -337,6 +337,7 @@ enum TECcodes : TERUnderlyingType { tecXCHAIN_PAYMENT_FAILED = 184, // RESERVED - XCHAIN tecXCHAIN_SELF_COMMIT = 185, // RESERVED - XCHAIN tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 186, // RESERVED - XCHAIN + tecINSUF_RESERVE_SELLER = 187, tecLAST_POSSIBLE_ENTRY = 255, }; diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 8ccadef8ecb..6603685fb68 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -459,7 +459,7 @@ REGISTER_FEATURE(URIToken, Supported::yes, VoteBehavior::De REGISTER_FEATURE(Import, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FEATURE(XahauGenesis, Supported::yes, VoteBehavior::DefaultYes); REGISTER_FEATURE(HooksUpdate1, Supported::yes, VoteBehavior::DefaultYes); -REGISTER_FIX (fixURITokenV1, Supported::yes, VoteBehavior::DefaultNo); +REGISTER_FIX (fixXahauV1, Supported::yes, VoteBehavior::DefaultNo); // The following amendments are obsolete, but must remain supported diff --git a/src/ripple/protocol/impl/TER.cpp b/src/ripple/protocol/impl/TER.cpp index ff7f9729c12..8e8f3784747 100644 --- a/src/ripple/protocol/impl/TER.cpp +++ b/src/ripple/protocol/impl/TER.cpp @@ -91,6 +91,7 @@ transResults() MAKE_ERROR(tecHOOK_REJECTED, "Rejected by hook on sending or receiving account."), MAKE_ERROR(tecREQUIRES_FLAG, "The transaction or part-thereof requires a flag that wasn't set."), MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."), + MAKE_ERROR(tecINSUF_RESERVE_SELLER, "The seller of an object has insufficient reserves, and thus cannot complete the sale."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), MAKE_ERROR(tefBAD_AUTH, "Transaction's public key is not authorized."), diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 23c39e3cdfc..61eec476252 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -88,7 +88,7 @@ TxFormats::TxFormats() add(jss::OfferCancel, ttOFFER_CANCEL, { - {sfOfferSequence, soeREQUIRED}, + {sfOfferSequence, soeOPTIONAL}, {sfOfferID, soeOPTIONAL}, // keylet as alternative to offerseq {sfTicketSequence, soeOPTIONAL}, }, @@ -133,7 +133,7 @@ TxFormats::TxFormats() ttESCROW_FINISH, { {sfOwner, soeREQUIRED}, - {sfOfferSequence, soeREQUIRED}, + {sfOfferSequence, soeOPTIONAL}, {sfEscrowID, soeOPTIONAL}, // keylet as alternative to offerseq {sfFulfillment, soeOPTIONAL}, {sfCondition, soeOPTIONAL}, @@ -145,7 +145,7 @@ TxFormats::TxFormats() ttESCROW_CANCEL, { {sfOwner, soeREQUIRED}, - {sfOfferSequence, soeREQUIRED}, + {sfOfferSequence, soeOPTIONAL}, {sfEscrowID, soeOPTIONAL}, // keylet as alternative to offerseq {sfTicketSequence, soeOPTIONAL}, }, diff --git a/src/ripple/protocol/jss.h b/src/ripple/protocol/jss.h index 9671a83993e..699928f7ead 100644 --- a/src/ripple/protocol/jss.h +++ b/src/ripple/protocol/jss.h @@ -173,29 +173,30 @@ JSS(accounts); // in: LedgerEntry, Subscribe, JSS(accounts_proposed); // in: Subscribe, Unsubscribe JSS(acroot); JSS(action); -JSS(acquiring); // out: LedgerRequest -JSS(address); // out: PeerImp -JSS(affected); // out: AcceptedLedgerTx -JSS(age); // out: NetworkOPs, Peers -JSS(alternatives); // out: PathRequest, RipplePathFind -JSS(amendment_blocked); // out: NetworkOPs -JSS(amendments); // in: AccountObjects, out: NetworkOPs -JSS(amount); // out: AccountChannels -JSS(api_version); // in: many, out: Version -JSS(api_version_low); // out: Version -JSS(applied); // out: SubmitTransaction -JSS(asks); // out: Subscribe -JSS(assets); // out: GatewayBalances -JSS(authorized); // out: AccountLines -JSS(auth_change); // out: AccountInfo -JSS(auth_change_queued); // out: AccountInfo -JSS(available); // out: ValidatorList -JSS(avg_bps_recv); // out: Peers -JSS(avg_bps_sent); // out: Peers -JSS(balance); // out: AccountLines -JSS(balances); // out: GatewayBalances -JSS(base); // out: LogLevel -JSS(base_fee); // out: NetworkOPs +JSS(acquiring); // out: LedgerRequest +JSS(address); // out: PeerImp +JSS(affected); // out: AcceptedLedgerTx +JSS(age); // out: NetworkOPs, Peers +JSS(alternatives); // out: PathRequest, RipplePathFind +JSS(amendment_blocked); // out: NetworkOPs +JSS(amendments); // in: AccountObjects, out: NetworkOPs +JSS(amount); // out: AccountChannels +JSS(api_version); // in: many, out: Version +JSS(api_version_low); // out: Version +JSS(applied); // out: SubmitTransaction +JSS(asks); // out: Subscribe +JSS(assets); // out: GatewayBalances +JSS(authorized); // out: AccountLines +JSS(auth_change); // out: AccountInfo +JSS(auth_change_queued); // out: AccountInfo +JSS(available); // out: ValidatorList +JSS(avg_bps_recv); // out: Peers +JSS(avg_bps_sent); // out: Peers +JSS(balance); // out: AccountLines +JSS(balances); // out: GatewayBalances +JSS(base); // out: LogLevel +JSS(base_fee); // out: NetworkOPs +JSS(base_fee_no_hooks); JSS(base_fee_xrp); // out: NetworkOPs JSS(base_fee_native); // out: NetworkOPs JSS(bids); // out: Subscribe diff --git a/src/test/app/BaseFee_test.cpp b/src/test/app/BaseFee_test.cpp index 57b449b31ef..23a3423183c 100644 --- a/src/test/app/BaseFee_test.cpp +++ b/src/test/app/BaseFee_test.cpp @@ -59,7 +59,7 @@ class BaseFee_test : public beast::unit_test::suite // verify base fee & open ledger fee auto const drops = jrr[jss::result][jss::drops]; - auto const baseFee = drops[jss::base_fee]; + auto const baseFee = drops[jss::base_fee_no_hooks]; BEAST_EXPECT(baseFee == to_string(feeDrops)); auto const openLedgerFee = drops[jss::open_ledger_fee]; BEAST_EXPECT(openLedgerFee == expected); @@ -77,7 +77,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -87,7 +87,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = fset(account, asfTshCollect); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -98,7 +100,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const bene = Account("bob"); @@ -109,7 +111,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = acctdelete(account, bene); // verify hooks fee - testRPCCall(env, tx, "200000"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "200000" : "200000"; + testRPCCall(env, tx, feeResult); } static uint256 @@ -126,7 +130,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -137,7 +141,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = check::cancel(account, checkId); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -148,7 +154,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -160,7 +166,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = check::cash(dest, checkId, XRP(100)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -171,7 +179,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -182,7 +190,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = check::create(account, dest, XRP(100)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -193,7 +203,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -203,7 +213,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = reward::claim(account); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -214,7 +226,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const authed = Account("bob"); @@ -225,7 +237,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = deposit::auth(account, authed); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -236,7 +250,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -247,7 +261,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = escrow::cancel(account, account, seq1); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -258,7 +274,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -269,7 +285,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = escrow::create(account, dest, XRP(10)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -280,7 +298,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -291,7 +309,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = escrow::finish(account, account, seq1); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -302,7 +322,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -313,7 +333,9 @@ class BaseFee_test : public beast::unit_test::suite account, import::loadXpop(ImportTCAccountSet::w_seed)); // verify hooks fee - testRPCCall(env, tx, "106"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "106" : "100"; + testRPCCall(env, tx, feeResult); } void @@ -324,7 +346,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -334,7 +356,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = invoke::invoke(account); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "16"; + testRPCCall(env, tx, feeResult); } void @@ -345,7 +369,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -356,7 +380,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = offer_cancel(account, offerSeq); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -367,7 +393,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const gw = Account("gw"); @@ -379,7 +405,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = offer(account, USD(1000), XRP(1000)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -390,7 +418,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -401,7 +429,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = pay(account, dest, XRP(1)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } static uint256 @@ -422,7 +452,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -437,7 +467,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = paychan::claim(account, chan, reqBal, authAmt); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -448,7 +480,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -461,7 +493,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = paychan::create(account, dest, XRP(10), settleDelay, pk); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -472,7 +506,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -484,7 +518,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = paychan::fund(account, chan, XRP(1)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -495,7 +531,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -515,7 +551,9 @@ class BaseFee_test : public beast::unit_test::suite hookParams[jss::HookParameter][jss::HookParameterValue] = "DEADBEEF"; // verify hooks fee - testRPCCall(env, tx, "73022"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "73022" : "73016"; + testRPCCall(env, tx, feeResult); } void @@ -526,7 +564,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const dest = Account("bob"); @@ -537,7 +575,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = regkey(account, dest); // verify hooks fee - testRPCCall(env, tx, "0"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "0" : "0"; + testRPCCall(env, tx, feeResult); } void @@ -548,7 +588,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const signer1 = Account("bob"); @@ -560,7 +600,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = signers(account, 2, {{signer1, 1}, {signer2, 1}}); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -571,7 +613,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -581,7 +623,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = ticket::create(account, 2); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -592,7 +636,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); auto const gw = Account("gw"); @@ -604,7 +648,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = trust(account, USD(1000)); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -615,7 +661,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const issuer = Account("alice"); env.fund(XRP(1000), issuer); @@ -628,7 +674,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = uritoken::burn(issuer, hexid); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -639,7 +687,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const issuer = Account("alice"); env.fund(XRP(1000), issuer); @@ -653,7 +701,9 @@ class BaseFee_test : public beast::unit_test::suite tx[jss::Amount] = "1000000"; // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -664,7 +714,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const issuer = Account("alice"); env.fund(XRP(1000), issuer); @@ -677,7 +727,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = uritoken::cancel(issuer, hexid); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -688,7 +740,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const issuer = Account("alice"); auto const buyer = Account("bob"); @@ -704,7 +756,9 @@ class BaseFee_test : public beast::unit_test::suite tx[jss::Amount] = "1000000"; // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -715,7 +769,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; using namespace std::literals; - Env env{*this, network::makeNetworkConfig(21337)}; + Env env{*this, network::makeNetworkConfig(21337), features}; auto const account = Account("alice"); env.fund(XRP(1000), account); @@ -726,7 +780,9 @@ class BaseFee_test : public beast::unit_test::suite auto tx = uritoken::mint(account, uri); // verify hooks fee - testRPCCall(env, tx, "16"); + std::string const feeResult = + env.current()->rules().enabled(fixXahauV1) ? "16" : "10"; + testRPCCall(env, tx, feeResult); } void @@ -770,6 +826,7 @@ class BaseFee_test : public beast::unit_test::suite using namespace test::jtx; auto const sa = supported_amendments(); testWithFeats(sa); + testWithFeats(sa - fixXahauV1); } }; diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index f35ad4cd2f5..d60f8cc6cbc 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -4256,128 +4256,216 @@ struct Escrow_test : public beast::unit_test::suite auto const gw = Account{"gateway"}; auto const USD = gw["USD"]; - Env env{*this, features}; - env.fund(XRP(10000), alice, bob, gw); - env.close(); - env.trust(USD(1000000), alice); - env.trust(USD(1000000), bob); - env.close(); - env(pay(gw, alice, USD(10000))); - env(pay(gw, bob, USD(10000))); - env.close(); - - // EscrowCancel - EscrowID + for (bool const withXahauV1 : {true, false}) { - uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; - env(escrow::create(alice, bob, USD(1000)), - escrow::finish_time(env.now() + 1s), - escrow::cancel_time(env.now() + 2s), - fee(1500)); - env.close(); - - env(escrow::cancel(bob, alice, 0), - escrow::escrow_id(escrowId), - fee(1500)); + auto const amend = withXahauV1 ? features : features - fixXahauV1; + Env env{*this, amend}; + env.fund(XRP(10000), alice, bob, gw); env.close(); - - auto const escrowLE = env.le(keylet::unchecked(escrowId)); - BEAST_EXPECT(!escrowLE); - } - - // EscrowCancel - no EscrowID or OfferSequence - { - uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; - env(escrow::create(alice, bob, USD(1000)), - escrow::finish_time(env.now() + 1s), - escrow::cancel_time(env.now() + 2s), - fee(1500)); + env.trust(USD(1000000), alice); + env.trust(USD(1000000), bob); env.close(); - - Json::Value jv; - jv[jss::TransactionType] = jss::EscrowCancel; - jv[jss::Flags] = tfUniversal; - jv[jss::Account] = bob.human(); - jv[sfOwner.jsonName] = alice.human(); - env(jv, fee(1500), ter(temMALFORMED)); + env(pay(gw, alice, USD(10000))); + env(pay(gw, bob, USD(10000))); env.close(); - auto const escrowLE = env.le(keylet::unchecked(escrowId)); - BEAST_EXPECT(escrowLE); - } + // EscrowCancel - EscrowID + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + escrow::cancel_time(env.now() + 2s), + fee(1500)); + env.close(); + + if (withXahauV1) + { + // withXahauV1 - no OfferSequence + env(escrow::cancel(bob, alice), + escrow::escrow_id(escrowId), + fee(1500), + ter(tesSUCCESS)); + env.close(); + } + else + { + // !withXahauV1 - OfferSequence == 0 + env(escrow::cancel(bob, alice, 0), + escrow::escrow_id(escrowId), + fee(1500), + ter(tesSUCCESS)); + env.close(); + } + + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(!escrowLE); + } - // EscrowCancel - EscrowID & OfferSequence - { - uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; - env(escrow::create(alice, bob, USD(1000)), - escrow::finish_time(env.now() + 1s), - escrow::cancel_time(env.now() + 2s), - fee(1500)); - env.close(); + // EscrowCancel - no EscrowID or OfferSequence + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + escrow::cancel_time(env.now() + 2s), + fee(1500)); + env.close(); + + env(escrow::cancel(bob, alice), fee(1500), ter(temMALFORMED)); + env.close(); + + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(escrowLE); + } - env(escrow::cancel(bob, alice, 0), - escrow::escrow_id(escrowId), - fee(1500), - ter(tesSUCCESS)); - env.close(); + // EscrowCancel - EscrowID & OfferSequence + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + escrow::cancel_time(env.now() + 2s), + fee(1500)); + env.close(); + + env(escrow::cancel(bob, alice, seq), + escrow::escrow_id(escrowId), + fee(1500), + ter(temMALFORMED)); + env.close(); - auto const escrowLE = env.le(keylet::unchecked(escrowId)); - BEAST_EXPECT(!escrowLE); - } + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(escrowLE); + } - // EscrowFinish - EscrowID - { - uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; - env(escrow::create(alice, bob, USD(1000)), - escrow::finish_time(env.now() + 1s), - fee(1500)); - env.close(5s); + // EscrowCancel - EscrowID & OfferSequence 0 + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + escrow::cancel_time(env.now() + 2s), + fee(1500)); + env.close(); + + if (withXahauV1) + { + // withXahauV1 - OfferSequence 0 == temMALFORMED + env(escrow::cancel(bob, alice, 0), + escrow::escrow_id(escrowId), + fee(1500), + ter(temMALFORMED)); + env.close(); + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(escrowLE); + } + else + { + // withXahauV1 - OfferSequence 0 == tesSUCCESS + env(escrow::cancel(bob, alice, 0), + escrow::escrow_id(escrowId), + fee(1500), + ter(tesSUCCESS)); + env.close(); + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(!escrowLE); + } + } - env(escrow::finish(bob, alice, 0), - escrow::escrow_id(escrowId), - fee(1500)); - env.close(); + // EscrowFinish - EscrowID + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + fee(1500)); + env.close(5s); + + if (withXahauV1) + { + // withXahauV1 - no OfferSequence + env(escrow::finish(bob, alice), + escrow::escrow_id(escrowId), + fee(1500), + ter(tesSUCCESS)); + env.close(); + } + else + { + // !withXahauV1 - OfferSequence == 0 + env(escrow::finish(bob, alice, 0), + escrow::escrow_id(escrowId), + fee(1500), + ter(tesSUCCESS)); + env.close(); + } + + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(!escrowLE); + } - auto const escrowLE = env.le(keylet::unchecked(escrowId)); - BEAST_EXPECT(!escrowLE); - } + // EscrowFinish - no EscrowID or OfferSequence + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + fee(1500)); + env.close(); - // EscrowFinish - no EscrowID or OfferSequence - { - uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; - env(escrow::create(alice, bob, USD(1000)), - escrow::finish_time(env.now() + 1s), - escrow::cancel_time(env.now() + 2s), - fee(1500)); - env.close(); + env(escrow::finish(bob, alice), fee(1500), ter(temMALFORMED)); + env.close(); - Json::Value jv; - jv[jss::TransactionType] = jss::EscrowFinish; - jv[jss::Flags] = tfUniversal; - jv[jss::Account] = bob.human(); - jv[sfOwner.jsonName] = alice.human(); - env(jv, fee(1500), ter(temMALFORMED)); - env.close(); + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(escrowLE); + } - auto const escrowLE = env.le(keylet::unchecked(escrowId)); - BEAST_EXPECT(escrowLE); - } + // EscrowFinish- EscrowID & OfferSequence + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + auto const seq = env.seq(alice); + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + fee(1500)); + env.close(5s); - // EscrowFinish- EscrowID & OfferSequence - { - uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; - env(escrow::create(alice, bob, USD(1000)), - escrow::finish_time(env.now() + 1s), - fee(1500)); - env.close(5s); + env(escrow::finish(bob, alice, seq), + escrow::escrow_id(escrowId), + fee(1500), + ter(temMALFORMED)); + env.close(); - env(escrow::finish(bob, alice, 0), - escrow::escrow_id(escrowId), - fee(1500), - ter(tesSUCCESS)); - env.close(); + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(escrowLE); + } - auto const escrowLE = env.le(keylet::unchecked(escrowId)); - BEAST_EXPECT(!escrowLE); + // EscrowFinish- EscrowID & OfferSequence 0 + { + uint256 const escrowId{getEscrowIndex(alice, env.seq(alice))}; + env(escrow::create(alice, bob, USD(1000)), + escrow::finish_time(env.now() + 1s), + fee(1500)); + env.close(5s); + + if (withXahauV1) + { + // withXahauV1 - OfferSequence 0 == temMALFORMED + env(escrow::finish(bob, alice, 0), + escrow::escrow_id(escrowId), + fee(1500), + ter(temMALFORMED)); + env.close(); + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(escrowLE); + } + else + { + // !withXahauV1 - OfferSequence 0 == tesSUCCESS + env(escrow::finish(bob, alice, 0), + escrow::escrow_id(escrowId), + fee(1500), + ter(tesSUCCESS)); + env.close(); + auto const escrowLE = env.le(keylet::unchecked(escrowId)); + BEAST_EXPECT(!escrowLE); + } + } } } diff --git a/src/test/app/GenesisMint_test.cpp b/src/test/app/GenesisMint_test.cpp index da591937761..3abcf0c8508 100644 --- a/src/test/app/GenesisMint_test.cpp +++ b/src/test/app/GenesisMint_test.cpp @@ -24,6 +24,34 @@ namespace ripple { namespace test { struct GenesisMint_test : public beast::unit_test::suite { + void + validateEmittedTxn(jtx::Env& env, std::string result, uint64_t lineno) + { + // get the emitted txn id + Json::Value params; + params[jss::transaction] = + env.tx()->getJson(JsonOptions::none)[jss::hash]; + auto const jrr = env.rpc("json", "tx", to_string(params)); + auto const meta = jrr[jss::result][jss::meta]; + auto const emissions = meta[sfHookEmissions.jsonName]; + auto const emission = emissions[0u][sfHookEmission.jsonName]; + auto const txId = emission[sfEmittedTxnID.jsonName]; + env.close(); + + // verify emitted result + Json::Value params1; + params1[jss::transaction] = txId; + auto const jrr1 = env.rpc("json", "tx", to_string(params1)); + auto const meta1 = jrr1[jss::result][jss::meta]; + BEAST_EXPECT(meta1[sfTransactionResult.jsonName] == result); + if (meta1[sfTransactionResult.jsonName] != result) + { + std::cout << "validateEmittedTxn failed " << lineno + << " expected: " << result + << " result: " << meta1[sfTransactionResult.jsonName] + << "\n"; + } + } void testDisabled(FeatureBitset features) { @@ -151,7 +179,9 @@ struct GenesisMint_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); { auto acc = env.le(keylet::account(bob.id())); @@ -190,7 +220,10 @@ struct GenesisMint_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); + env.close(); { @@ -229,7 +262,9 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); for (auto const& [acc, amt, _, __] : mints) { @@ -242,7 +277,9 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); for (auto const& [acc, amt, _, __] : mints) { @@ -295,23 +332,11 @@ struct GenesisMint_test : public beast::unit_test::suite env.close(); - // get the emitted txn id - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const emissions = meta[sfHookEmissions.jsonName]; - auto const emission = emissions[0u][sfHookEmission.jsonName]; - auto const txId = emission[sfEmittedTxnID.jsonName]; - env.close(); - - // verify emitted result - Json::Value params1; - params1[jss::transaction] = txId; - auto const jrr1 = env.rpc("json", "tx", to_string(params1)); - auto const meta1 = jrr1[jss::result][jss::meta]; - BEAST_EXPECT(meta1[sfTransactionResult.jsonName] == "tecINTERNAL"); + // validate emitted txn + auto const txResult = env.current()->rules().enabled(fixXahauV1) + ? "tesSUCCESS" + : "tecINTERNAL"; + validateEmittedTxn(env, txResult, __LINE__); } // missing an amount @@ -354,7 +379,9 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); } // check that alice has the right balance, and Governance Flags set @@ -378,7 +405,9 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); } // check that bob has the right balance, and Governance Marks set @@ -403,7 +432,9 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); } // check @@ -430,9 +461,12 @@ struct GenesisMint_test : public beast::unit_test::suite XRP(0).value(), std::nullopt, std::nullopt}})), - fee(XRP(1))); - env.close(); + fee(XRP(1)), + ter(tesSUCCESS)); env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); } // check @@ -500,7 +534,9 @@ struct GenesisMint_test : public beast::unit_test::suite std::nullopt}})), fee(XRP(1))); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); } // check @@ -525,11 +561,19 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1)), ter(tesSUCCESS)); env.close(); - env.close(); + + // validate emitted txn + validateEmittedTxn(env, "tesSUCCESS", __LINE__); } + auto const amtResult = env.current()->rules().enabled(fixXahauV1) + ? 30000000ULL + : 10000000ULL; // try to include the same destination twice { + auto const txResult = env.current()->rules().enabled(fixXahauV1) + ? ter(tesSUCCESS) + : ter(tecHOOK_REJECTED); env(invoke::invoke( invoker, env.master, @@ -544,7 +588,7 @@ struct GenesisMint_test : public beast::unit_test::suite std::nullopt}, })), fee(XRP(1)), - ter(tecHOOK_REJECTED)); + txResult); env.close(); env.close(); } @@ -554,7 +598,7 @@ struct GenesisMint_test : public beast::unit_test::suite auto const le = env.le(keylet::account(greg.id())); BEAST_EXPECT( !!le && - le->getFieldAmount(sfBalance).xrp().drops() == 10000000ULL); + le->getFieldAmount(sfBalance).xrp().drops() == amtResult); } // trip the supply cap invariant @@ -572,30 +616,14 @@ struct GenesisMint_test : public beast::unit_test::suite fee(XRP(1))); env.close(); - // get the emitted txn id - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const emissions = meta[sfHookEmissions.jsonName]; - auto const emission = emissions[0u][sfHookEmission.jsonName]; - auto const txId = emission[sfEmittedTxnID.jsonName]; - env.close(); - - // verify emitted result - Json::Value params1; - params1[jss::transaction] = txId; - auto const jrr1 = env.rpc("json", "tx", to_string(params1)); - auto const meta1 = jrr1[jss::result][jss::meta]; - BEAST_EXPECT( - meta1[sfTransactionResult.jsonName] == "tecINVARIANT_FAILED"); + // validate emitted txn + validateEmittedTxn(env, "tecINVARIANT_FAILED", __LINE__); // check balance wasn't changed auto const le = env.le(keylet::account(greg.id())); BEAST_EXPECT( !!le && - le->getFieldAmount(sfBalance).xrp().drops() == 10000000ULL); + le->getFieldAmount(sfBalance).xrp().drops() == amtResult); auto const postCoins = env.current()->info().drops; BEAST_EXPECT( @@ -664,10 +692,11 @@ struct GenesisMint_test : public beast::unit_test::suite using namespace test::jtx; auto const sa = supported_amendments(); testWithFeats(sa); + testWithFeats(sa - fixXahauV1); } }; BEAST_DEFINE_TESTSUITE(GenesisMint, app, ripple); } // namespace test -} // namespace ripple +} // namespace ripple \ No newline at end of file diff --git a/src/test/app/Import_json.h b/src/test/app/Import_json.h index 116686d1311..37cd6363ff3 100644 --- a/src/test/app/Import_json.h +++ b/src/test/app/Import_json.h @@ -105,93 +105,85 @@ inline std::string ImportTCAccountSet::max = R"json({ })json"; inline std::string ImportTCAccountSet::w_seed = R"json({ "ledger": { - "acroot": "8D612AAAEA2C875978527851BB0727CE64B21D792E91B791FEC769C01410B405", - "close": 743212280, - "coins": "99999998999999832", + "acroot": "3975FE3ACF65A9BCC64F07F49B5AEC93E9176CB8DDFE7B5AEDCDC191EA3A8B8C", + "close": 754758421, + "coins": "99999998000000000", "cres": 10, "flags": 0, - "index": 21, - "pclose": 743212272, - "phash": "B94C4657B7529C72DDCE970A87E2EB9EE8EE12580ADFE6CF93B8672E4B289BBC", - "txroot": "0B7479863581D57CB97867293AA2DD17C2DA4005FC139082925AA5806D956389" + "index": 149, + "pclose": 754758420, + "phash": "8F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B15", + "txroot": "7B4454B7542F049427CE7A5148BDC385E741F2239C19E71E1CC7E4231A383589" }, "transaction": { - "blob": "12000322000000002400000014201B00000027201D0000535968400000003B9ACA0073210388935426E0D08083314842EDFBB2D517BD47699F9A4527318A8E10468C97C0527446304402205A21EE95C4EBAAC3BB6E438B86BE0FFF864D7EDAB0827F04621B3C5EB0F84B2002206A025D68D8112C87913F228ED47C7CDFE79E217186056876E3FE60C8D0D7BB5E8114AE123A8556F3CF91154711376AFB0F894F832B3D", - "meta": "201C00000002F8E51100612500000014550664762F06918B9DDF5D8933481466D394E8D137DE112C9EFC74AE00F5B1BD0D5692FA6A9FC8EA6018D5D16532D7795C91BFB0831355BDFDA177E86C8BF997985FE624000000146240000002540BE400E1E7220000000024000000152D00000000624000000218711A008114AE123A8556F3CF91154711376AFB0F894F832B3DE1E1F1031000", + "blob": "1200032200000000240000007D201B000000A7201D0000535968400000003B9ACA0073210388935426E0D08083314842EDFBB2D517BD47699F9A4527318A8E10468C97C05274473045022100BED5EC411AA84FF23B26D60673C1B42901E253C72DFD92B326DF11B94FAB653D022072B734E038F30D4693A392B1609DEFCE57508F6BB5A3FBD721CC55DDDE929BCD8114AE123A8556F3CF91154711376AFB0F894F832B3D", + "meta": "201C00000003F8E5110061250000009555248470440C362FE1DA9C45D43C68583632588F8D5FAACC0038A60F1C292109875692FA6A9FC8EA6018D5D16532D7795C91BFB0831355BDFDA177E86C8BF997985FE6240000007D624000000218711A00E1E72200000000240000007E2D000000016240000001DCD650008114AE123A8556F3CF91154711376AFB0F894F832B3DE1E1F1031000", "proof": { "children": { "0": { - "children": {}, - "hash": "509C0CFF6C808D0A97271FC67C694D95BF177AF4C47D86CD1182516BDDEB6025", - "key": "005D3A84B6EF5A5BF673019F4C583E812F2429D2E6524119498803BD4D82B616" + "children": { + "2": { + "children": {}, + "hash": "B19EFD49679890EA362F3938AFFC6FCC3D4B57AAE0054F42237E51ADEFC00E2A", + "key": "02418867190CC2BBA46600E334985E6915C861DBB69E030D2939CFE1FC94D4CC" + }, + "F": { + "children": {}, + "hash": "48E8A4BAE146645DA9FB45AA126676BF94FA767DBE2FEB63D570175C5C6EABF6", + "key": "0F84DB191B9B813AC94EC51EA58B69EB5B5C606A69075A64068F911246DFEFD5" + } + }, + "hash": "B9AC3B86E69AFE74C4AE62E2E2A82BBBC4578BE84C093A9E5ECF15FD6C85164D", + "key": "0000000000000000000000000000000000000000000000000000000000000000" }, "1": { "children": {}, - "hash": "7049738FBCD2A02C6424D2D0AED1E686525E72CBEE7E1E46665153D547152BEC", - "key": "1B0C515B4719732CA8E192DCE7C853EC9FDCB555044C7DBF6304842D1131B3F8" + "hash": "8521D6C26199F9F6D54E9522385C44C52E2E3EF48560AE64D3D089BC268A1311", + "key": "1F7D6AC7280BA77EC0676E08111CA9FC552C48DA16662B49A197C2996A594574" }, - "4": { + "2": { "children": {}, - "hash": "DF2BD3EE2061F7B6BA108AFD2E0879BBC7A416F004EC31721B1E35327AC84CC0", - "key": "4636DAF80989AD827DB21924CCA58DA67188569631135A5E04E173B7142E2B4F" + "hash": "01404B9BC507C42FC466B3BCC056B544DFEEB51E5C3030F90CC63D092DA9EDFF", + "key": "248470440C362FE1DA9C45D43C68583632588F8D5FAACC0038A60F1C29210987" }, "5": { "children": {}, - "hash": "E000CC0736630D66DAB573A2642E1BD0646DFB13A871B2CCFA6E4226F477D88C", - "key": "52C1D70065FC14B118011B14B3936FF71D97F9F1D21891BBBD2293B27AC6F8BF" + "hash": "8A05CC08E8238765E4B94C14E09C2AD218B81F7FDD0415D6FE7F57B9B626899B", + "key": "5BC558F8699A824BE7C09A56E1D0A0FACA6F44F57AB7BC0306C8F1C4870FCE1E" }, - "7": { - "children": { - "1": { - "children": {}, - "hash": "4156BB37D4BBA887A646879B3585065B95157FA15A0A68DDCA87CC90B6A8E370", - "key": "71C688E6B6B99744C01D908E0C75D12D91090BB109E9E39AC08150A973A90810" - }, - "2": { - "children": {}, - "hash": "EA93162C432C54B8CEDA63184FA9F0ABC76C5CB547671AC4069A8EEF7CE1DD1A", - "key": "72092AC8B4A50C6E191650344AFC93FE6EDCD00AD4B7D929A524DCCA7DA06074" - } - }, - "hash": "191973AFD8C1667F39C050554AC62D67311CD7DE1E406EE7DF8EB0407930D891", - "key": "7000000000000000000000000000000000000000000000000000000000000000" - }, - "B": { - "children": { - "9": { - "children": {}, - "hash": "5109702FDCFFAF311C0AD8E7D4BECE7FA350395A2EAF522A5A7D04337B136B31", - "key": "B9448E0093010B019347E2D0BF9C2EB2B8F529877DABFAF7DF4672CFFEC642D8" - }, - "C": { - "children": {}, - "hash": "E80D50B1E6475C8588059C966BBEF247A15D7C8DE0674F0D7122316F9D424BFC", - "key": "BCBB6F4F04F0B108630E5CB04E6C9927D2F2621FD83F5548F12D4E44233AC2AA" - } - }, - "hash": "01664A1CB97086A0E222BB2FEEE8CD5222E9535064C366CF3DF76A80A17BD7B2", - "key": "B000000000000000000000000000000000000000000000000000000000000000" + "C": { + "children": {}, + "hash": "652BC7C2C0F3FBA842AD24BB92C9DBFC2ABBB47D11E5255BD10A5BF5A2F12DEF", + "key": "C041798921B80DB9F9B017A27514BE6942ED00CDE078E7E7E1C9F011E0DD9B3D" }, - "F": { + "D": { "children": {}, - "hash": "8C26E194C7B44E108EFF98082D19BB2B739C5F8E6BB0C281D9ECEE11F98C3770", - "key": "F4DCF93061C61D249A16934AF83C3EFCE51AD98469AFC65B260C2100683CB761" + "hash": "D4772ADC693366E7ADC55437496A80E0073556CCC6F34F28A3CA22F27B8FC804", + "key": "D0F5762BD1D415558E1C825A0933E7A0B2AC648AB719CE224E25E6A1EAEFB51D" } }, - "hash": "0B7479863581D57CB97867293AA2DD17C2DA4005FC139082925AA5806D956389", + "hash": "7B4454B7542F049427CE7A5148BDC385E741F2239C19E71E1CC7E4231A383589", "key": "0000000000000000000000000000000000000000000000000000000000000000" } }, "validation": { "data": { - "n94QWAYxKUHacmyFTnzK4bvqVcUfr6RwtaNxCM2cJRY59UHmz1Fr": "22800000012600000015292C4C84F53A29EC0A36EDAB6C61510AD4F33846A123D86FDAD30CBF175E217BA7B5394A5A761DA5C6B7A45D678DA8501720806024F4F741C65B2E44B005CA293120889A6BC5F1E179335E20384AB3C6D15019B94C4657B7529C72DDCE970A87E2EB9EE8EE12580ADFE6CF93B8672E4B289BBC732103FCA947A7F08B146457BEF95AF0CF7C3ABF0D09CD1DC02099F7185C37BB32807576473045022100BBBE6EDE0B2B61CD369E2188C8FBFACCB35CA2D166FD29D5E3D7B2195083E74302201FAA160136301A43E518B9424A0DA5DC1E7EF8B90DAE2FA87310047498514EB4", - "n9KqAeJTJEJaMZNN35SNrPDbs324rwjDPy6BFHjZ4oM4en4snKjf": "22800000012600000015292C4C84F53A434EDA585468FD92510AD4F33846A123D86FDAD30CBF175E217BA7B5394A5A761DA5C6B7A45D678DA8501720806024F4F741C65B2E44B005CA293120889A6BC5F1E179335E20384AB3C6D15019B94C4657B7529C72DDCE970A87E2EB9EE8EE12580ADFE6CF93B8672E4B289BBC732102AB4E3B7C53A4265C51952DD9D2CD2829219CDD4F55F63969E38C5C910F3C5F1C76473045022100A011F2C1F61E78CEF76007BB72CE076255027793052DCF7027B10C0C30BA07AC02202380E5FF8330592C594784C4752736D7177D121763D91795C3AD3FEB7AF3A0B5" + "n94J5LCRu9bBWrJiwRWujvuVECVPWbXFcQ2VN38qLD378F5pDSDM": "22800000012600000095292CFCB3163A74499C839A2536F051CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B15732103EF7BD71691FE8352670866E018ACD21BE011971500485150AFA1A66DC564667676473045022100CD612D4D66388BC6F5B55C6FC79024419354F7C8F64421E18A2F92F8F12C044E02201E95057C863044378B94BD80D17022CE0FC6FEAF26482FA5C8A02C061BFB0529", + "n94M7rLruFgtroHGaxjG81DYKssEfSk5kWK7aiyzcfYKrVVDfZfk": "22800000012600000095292CFCB3163A22A5D454FFFE67CD51CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B15732103F5BC074629DFF09DA827417F891FBDCB97B08BF4D4A17715C46D89BF36C8FC2876473045022100E2F7EB279A55545A2241E44BB440A3489612189D7F352FA7756F1C145D707C530220550206F6C48FB16D0C63CE4899BE5DF91DF330BA1CB47BB6310DAD0FC40A1461", + "n94U45VGVjD4Gp9aZpuAKJHRF5AJXiARzHmmcR38i4cB3zbHKSWZ": "22800000012600000095292CFCB3163AFC08FE942F7912AF51CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B15732103E1177E8CC69B97B21B6C25813CC545BEEA49B03FD668F1701EC29C7FB8E609467647304502210089AD42057DF50C0324F1DC0B4935E124C0C58554369AEE7869E603B8C4AF686102205521AB9889240487CE9ADFE14F5844030D4C2A01B23EF49E53F3C4619A02AF15", + "n9KZofcLaHcfujXR4a3E17jKSvyNp8SZHu9rWVjhB9mGFXxB2w4L": "22800000012600000095292CFCB3153ABFAC3C292F2B6FE551CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B1573210286A448918DA7F8BF7693552EF9E47AFC57C20D9E090132633D521D32889040C876473045022100D6A52670D463FD1994030A39CEA91E87BA26353B6384FD4218DDA8E3AC8B162E022011295AE9BDDBD41F9489FA3D7E8A36B40D946DC4B39FEA654A77EFEADD112AFA", + "n9KjgPCVkmyJdWAVBCVKERdz5Xt6x7dLA1Y7ZqayZzJqVnnG982j": "22800000012600000095292CFCB3173AE187F1490B28C1FA51CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B157321029D15B24621B84804E7360DA1CC35F36B8A2771A316A3B83008F85CB370738C2876473045022100A3ECFC40D87E280D7FEDC184BA6DBBE6E77CF6FF08FCF7AE2D4B4FE8CD98B3BF0220726B6A7E9A6AB574EA82A6B5DF628B23C1193A094AF4CC2F28695FFA49CF22BC", + "n9L7FECrrDnTTLRJHc7rVxyTsKnjLcQXea5uhTFWxKh3TqnSR2Rz": "22800000012600000095292CFCB3163A89DF43A5821D7C4851CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B15732102FF077FE93DDC6626CADE5DB5436B03DC4CE81B0387A2B56C078D5E413FE20DC576473045022100EFFD4EC7B5B003BC5CCF07BE6406EE2A218AA46C0FC51902B801EFBE45C8FB0102200FC2D21E57E785076AA6D2D749F5FBAC9B99FDD9259DD7B8F9EB2D058B434807", + "n9LCBA8uJWJ2moif8wbm7SBhuR8uenVgBm7VxKSX4VmzcQH6Mw8j": "22800000012600000095292CFCB3163A6C13F3590B29FCC651CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B157321031695B0FB0D95F9C41931F14F49841F44997E76607D1E1D4FC57729F20E6C66397646304402203FAD0DF9CE9AD6A9987229E2AF83E80DACF5D0BFD14CC6ED2036CEAD068BB88002206F50EEA8AF44ACAB94100B0A6061396559D60C1D0E532EEA991F3D6E2B88C4D8", + "n9LtFW3Hda5qBS8TUPP9NnNnxUYXbhiWSLaNsGXJKyEkEUiSLvtU": "22800000012600000095292CFCB3163AD3E2E9C72D6F8A9E51CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B157321033588B42423B7F797AF19ADDA19470D7F6C5DC384B26D3DA14D859345ADB496247646304402207A5ABA310A90FD46D7BB164172750AE752FA389C697BFACE75CE2E9F2842DC4402200A42A054462531CE381047C7FD47DB8F8AC83963F1E7F3091251437912F9BD06", + "n9MNStvsHZxrCYnShytCfKMCXBcq8wJjR6fLTS7L2Q8qoFVNioQ8": "22800000012600000095292CFCB3163AA0E5E217DB17866851CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B157321036219BE1721FF5F57EBCB1997EA568BA8BED880ED0CF1FD81F25C888EC8E66D67764630440220467BE8918BBF65D201B705D0C83B0DE347ABC7E9353CEA8AEBE9495BC78AEFC602206FFCB1A2BD08B1F3ED18E3A737C83D9AD6CE20E4B90FF61621650E61F2A7A05A", + "n9MTTavZe6EPqqyQ27pJbNWpfHw8ZNspVgznrwx5HWm7cdTjKQie": "22800000012600000095292CFCB3163A3E9A11AB5CFA712751CAD3AEA30BDAC743D24F91EEF18A03DFB7EDA477194429C9132656ACF85867FA501775530CC549425FB587CBEE269283F976D923CBF68A9AD5C636DE22A96F33FBB350198F84F7AB19B38FA4377B34E1234EA613A2A5FE522AF08F0B5E0B957C726A6B157321037FA072E183DC2822AF50EBC43280C9AADF0DFE3BCD57D2CB0DD1179034B07543764730450221008A3D649F423D4B3BB74B4943DD3B4841BA2EF281DC8D1F48C6D3A7E72966344302200C465A03A50F33BD04A9751F4261B96F8058710D18DFC7DCE09850C735FE8B12" }, "unl": { - "blob": "eyJzZXF1ZW5jZSI6MSwiZXhwaXJhdGlvbiI6NzY3Nzg0NjQ1LCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDExREMwN0E2REEzRDA3QzAxMkUxOUZGOUFDNjdBQ0U1MzlBMjk1MTQ1QzhEQTM5NjQzN0NBQ0FFQzM2NzA5RjYiLCJtYW5pZmVzdCI6IkpBQUFBQUZ4SWUwUjNBZW0yajBId0JMaG4vbXNaNnpsT2FLVkZGeU5vNVpEZkt5dXcyY0o5bk1oQXF0T08zeFRwQ1pjVVpVdDJkTE5LQ2tobk4xUFZmWTVhZU9NWEpFUFBGOGNka2N3UlFJaEFOYlBEZlZLUzdwSWFqejlOMlFNNEY4Q3hhd1dJcmF0c3QyQjd3ZWR5czFGQWlCR0s3L1YwRDRBMlp2VFJEQlVQb0JLUVhpQk83QUV3ckRiOFlvN2RUSTJTWEFTUU9lQTIrVGdGVlRwNzdlN3VFZGpYNU1BYk8yU0t2dk8zOW1POEFyQkNqemRPUElxVmIrWi9QMDJUTmYzNjUrYjVCOGc3V0xEY0x2VkNpZUxjemw4MHcwPSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURBMTY0RjRCMzZDMkQ3MzA0NjJENUY3NjJCRkEyODA4QUE1MDkyQUJDRUNFQkIyNzA4OTUyNUQxRDA1NEJFMzNCIiwibWFuaWZlc3QiOiJKQUFBQUFGeEllMmhaUFN6YkMxekJHTFY5Mksvb29DS3BRa3F2T3pyc25DSlVsMGRCVXZqTzNNaEEveXBSNmZ3aXhSa1Y3NzVXdkRQZkRxL0RRbk5IY0FnbWZjWVhEZTdNb0IxZGtjd1JRSWhBTjRldTFvSGV0bFVDUmZmZ2FaeTkvTWJrQ0prWlFZdlJoMFVJZWFCaVZodkFpQWhGWXZKTXBPeVFpNGxFdzdzM0pTVTBMRm5FckRWVlRnK3NYUVk0c3Zta25BU1FCSGswakIweG1yL1UwbnkyajNrRmVvdEJUVEExVzdXcHBKbXFvYmR4SXkyR0w0QXBRaEpPcm5aRy93bXZqeFJCNXV4Y05FNUdUR1NZekQ3azhhVEx3bz0ifV19", + "blob": "eyJzZXF1ZW5jZSI6MSwiZXhwaXJhdGlvbiI6NzU3MzQ5NjI0LCJ2YWxpZGF0b3JzIjpbeyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFREJBOUZGMzEzNjgyMzlGNDIxRjA0QkY2ODEzQkJGNzZDNkJCRDQ1NkMyRTEyNDg2MDY2MjdCOTkwRjA5MTc2ODkiLCJtYW5pZmVzdCI6IkpBQUFBRVJ4SWUyNm4vTVRhQ09mUWg4RXYyZ1R1L2RzYTcxRmJDNFNTR0JtSjdtUThKRjJpWE1oQTMrZ2N1R0QzQ2dpcjFEcnhES0F5YXJmRGY0N3pWZlN5dzNSRjVBMHNIVkRka1l3UkFJZ2J4bVdKWXoxMFI2RVh6OG1RRmxKazlMM1UwZkQxT2UzbDJYK3NVOGFqajBDSUNIb2RoNzRWaDJNL0oxRnlzS2NOUmp1YWpKaCtSN1dqeXVBNHFqWWwvSHVkeDEwWlhOMExYSnBjSEJzWlM1MmJtOWtaVEV1ZEhKaGJuTnBZUzVqYjNBU1FEcDJuUVVTbk9CYlVxMHpCU05wMzRhRktBWnRrWHRaS3BjejBlQitEVG12TXRGN3M0Y240ZW8veHhkZU5UbmxJK1FRVmloZUpLSkZKQlJ3NzZwdXdROD0ifSx7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEQkJDRDNGRjdGRkVGOTc3REU0Mzg0QUMzODE0NTMwNEM1MUI5MTJDRUYxMTgwNjBGMzQzMTBGRjgwN0NDRUU3QiIsIm1hbmlmZXN0IjoiSkFBQUFEeHhJZTI3elQvMy8rK1hmZVE0U3NPQlJUQk1VYmtTenZFWUJnODBNUS80Qjh6dWUzTWhBMkladmhjaC8xOVg2OHNabCtwV2k2aSsySUR0RFBIOWdmSmNpSTdJNW0xbmRrY3dSUUloQUtMWnFVYjIyYWg0YmJzL3JtTU1VQjl4MXE3MDY0RzczcW5uSytmaU9UVHZBaUFNdUJ4QVFlcEw4a2owRVVlY3ZIT2dKOEo2TUFUdS9CVnFXVzk1alhRNUMzY2RkR1Z6ZEMxeWFYQndiR1V1ZG01dlpHVXlMblJ5WVc1emFXRXVZMjl3RWtCcXpTMmFmdm9XUGJOcWM2UGRxaGV4ZnNUVDBEdUJZd2ltN0c4Y0xvSzg1bktTTW1TQy9ZaGw1cXRTRzkzNWZnQUlqVm9YZXMrZS9Kd3FFU01xZ2ZZQiJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRUQ2N0ZFMkU1MTc1Njc2NjBCMTU0NUEzQjg0OUU2OThERTg5Q0Y4MTUxODg2MjIwMzAxNzc2OEUxMjJBMTM3N0JDIiwibWFuaWZlc3QiOiJKQUFBQUR4eEllMW4vaTVSZFdkbUN4VkZvN2hKNXBqZWljK0JVWWhpSURBWGRvNFNLaE4zdkhNaEEvVzhCMFlwMy9DZHFDZEJmNGtmdmN1WHNJdjAxS0YzRmNSdGliODJ5UHdvZGtZd1JBSWdZV3NQMVMwYzhvZkFlRWp1WjJ3a2NpM29idStLV0tuOWxqMmVqOEJ1YmxVQ0lHTGl2UmJxazRHUW9VWVk2bC9NS0hpeWkwbzBwbmtpSTFLRDV2UHRYRHRnZHgxMFpYTjBMWEpwY0hCc1pTNTJibTlrWlRNdWRISmhibk5wWVM1amIzQVNRSzdZZUtoYnQwNlc1QnpQODk1V09NT1p2SlJLakhaVGdtN0VqYUtjakNLRTQwWTQwaTczeTUreVFHQWpISFlrRGgydE5UdVExQ09HT1E2b2N1VEFpZ1U9In0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDc1OUJFRTREQzIxQzVBMDYzMEZCODdENDJFQ0MzRDk1OUE3RkI0QkY0RUM3RkYxMkRDOUZDQjFCNkFCNDE5MTUiLCJtYW5pZmVzdCI6IkpBQUFBRHB4SWUxMW0rNU53aHhhQmpEN2g5UXV6RDJWbW4rMHYwN0gveExjbjhzYmFyUVpGWE1oQW9ha1NKR05wL2kvZHBOVkx2bmtldnhYd2cyZUNRRXlZejFTSFRLSWtFRElka1l3UkFJZ0JUTHFROXFna3VrVzFwYTBwQ0xRVld2QnFua09NVkVPK3JQTEVoL2praHdDSUZsQURXb3RLc0JBQ0hyWW1EY3N0dXczYjRkdWV4V3dXY0FCdTdkNmR1T3FkeDEwWlhOMExYSnBjSEJzWlM1MmJtOWtaVFF1ZEhKaGJuTnBZUzVqYjNBU1FBN0UrdHVzMHZ0aDk4eGQwWVdLb2FVOVJTL1VUZVQ1RUcwSURzbW9NVHdrN014VTRGVktTL0dXOTUrMHJ0cG0xV0NYM1VRWThaN1pIZmtGMVlxenZRUT0ifSx7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEMTFDQzhEQUZDREUxMTFGRDFCNTM4OUJFRDM1QjcwNkQzMUQwMDRGMjBGQUNERjEwQ0RGMTI2MjhDRkE0NUQ2RSIsIm1hbmlmZXN0IjoiSkFBQUFEcHhJZTBSekkydnplRVIvUnRUaWI3VFczQnRNZEFFOGcrczN4RE44U1lvejZSZGJuTWhBdjhIZitrOTNHWW15dDVkdFVOckE5eE02QnNEaDZLMWJBZU5Ya0UvNGczRmRrWXdSQUlnRS84cTI2R0lSaDlLd1ZvUjFLZVBnS3VGcUlyRjV5NnMrREUvaXNmWHJpSUNJR3hQdHZSUjNLZFI3d2haMzI3VXFpUXFvZnM3d2VVRzQ0dEpiZnhZWndCV2R4MTBaWE4wTFhKcGNIQnNaUzUyYm05a1pUVXVkSEpoYm5OcFlTNWpiM0FTUU5hRFNNSmdpU2kxNzRoazYrdnlBRlhzdUFYZkw0YnRFT3c5OVBSSVJwQVAvUGxRa1ZmMXpNdUJrLzJGNzdqai9BTzFnd09va3hjbEJHRmF5MC9jcGc4PSJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRUQ5MjI5NDFEMjQwMEM4M0I3QjU1QzE4QkZFRDRCNzA5QkI4QzAxRkNGMEE3RTlCRTQ5RTA5RDBCOUIyNTRDRTU1IiwibWFuaWZlc3QiOiJKQUFBQURweEllMlNLVUhTUUF5RHQ3VmNHTC90UzNDYnVNQWZ6d3ArbStTZUNkQzVzbFRPVlhNaEF4YVZzUHNObGZuRUdUSHhUMG1FSDBTWmZuWmdmUjRkVDhWM0tmSU9iR1k1ZGtjd1JRSWhBUDBkOHRSZ0JlczNmKzl5Y2Y4c0FNdEdLcnUxN05neGN2azJpRXJQTks5MUFpQTI5WFM4VzNYYVZ4bS9xS0pmSzc0T1JTVlJFL3MzUmFZbGJhMmZoT3JCTDNjZGRHVnpkQzF5YVhCd2JHVXVkbTV2WkdVMkxuUnlZVzV6YVdFdVkyOXdFa0F6V2RkcGxHMkVKUmo2c1ZabEdxTFN6RWhqWEQ3VXFKV1pNSC9IMUZKaW9sWFVBbk5VOXVidjRsWDE0WVFlWmpJbitLR2gwekoxek5KS2YvbGhsRnNKIn0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDY1OThGNjYzRTNDRjQwRDczREMxOUU4NzUwRUQ2N0I4NjAzNTREQjhGMkEzMTVENUZFN0REMDY1MkY0OUQyMUUiLCJtYW5pZmVzdCI6IkpBQUFBRHB4SWUxbG1QWmo0ODlBMXozQm5vZFE3V2U0WURWTnVQS2pGZFgrZmRCbEwwblNIbk1oQSs5NzF4YVIvb05TWndobTRCaXMwaHZnRVpjVkFFaFJVSytocG0zRlpHWjJka1l3UkFJZ1VKbmg3VTNQV3U5LzVKblFBcFpOdmRZanhFTmZkcHIzcFUwY05NZHJ1eXdDSUVLVG5XSzB4RnM0UWVIMFhFZkNSQTFJdjQrMlJzSFhJZHZyR0I3NlVxSmdkeDEwWlhOMExYSnBjSEJzWlM1MmJtOWtaVGN1ZEhKaGJuTnBZUzVqYjNBU1FOakRvVTRjL2ZkbDJONCtFbnR0TXBQeTV5QlhqRldEbXVYZThrZ0FTaERaZllpVGNYZU5LT3RzU2xzQW5PVGIxbllzZzdFbFZpZTB6bGM5dTdQTWVBRT0ifSx7InZhbGlkYXRpb25fcHVibGljX2tleSI6IkVEOTk5RkZGRUY1MUQ3MTJEN0Q0MjVCQTA4M0U3OUU2MDJGNzhBNDkyMDU0QThDMjM4RjRBOTQ0ODc2OTlFRDkwNyIsIm1hbmlmZXN0IjoiSkFBQUFEcHhJZTJabi8vdlVkY1MxOVFsdWdnK2VlWUM5NHBKSUZTb3dqajBxVVNIYVo3WkIzTWhBeldJdENRanQvZVhyeG10MmhsSERYOXNYY09Fc20wOW9VMkZrMFd0dEpZa2RrY3dSUUloQU9RMFRhbFlVYnE3aml0T2MybXMxQm5wUTdsWHJVc2x2WEJSbS9tRG1UZEVBaUE0KzdPSmhkN0pFSlV2QUhSYjByaG5GSkd3b3ZoWWNQNjBBTG81Zm8xaTJIY2RkR1Z6ZEMxeWFYQndiR1V1ZG01dlpHVTRMblJ5WVc1emFXRXVZMjl3RWtBVzR2TlRPMEU5ZEptRC93eFNmcG9qN0V6bUxMV1pFN1dTeWZuTFk0WTBTVEVLR09jK0FGZ251Ty9IekhhdnQ3aTJBUHJkckZhT3IySm1Zb2lIa1RVSiJ9LHsidmFsaWRhdGlvbl9wdWJsaWNfa2V5IjoiRURGNjVFNTAwNkE1M0FGNzU2MTdFQUUyOUM3MzNDNUYzNDFBOTQxNzNDMjkyM0Y2MjY5Mjk2M0QxQTJGMTQwNzYyIiwibWFuaWZlc3QiOiJKQUFBQURweEllMzJYbEFHcFRyM1ZoZnE0cHh6UEY4MEdwUVhQQ2tqOWlhU2xqMGFMeFFIWW5NaEFwMFZza1lodUVnRTV6WU5vY3cxODJ1S0ozR2pGcU80TUFqNFhMTndjNHdvZGtZd1JBSWdYcDJYMWFqcmNzNWJzRnRFSnI3MkVPT2NNVmxmRDFMWkJNQnRQY1k4UnJVQ0lBa2tXdVRrWmQxNUNmMXNBeXgza2ZTUENzeWdWVkw1anBwSXVLdUxMOU1sZHgxMFpYTjBMWEpwY0hCc1pTNTJibTlrWlRrdWRISmhibk5wWVM1amIzQVNRSXVwTXE1dE5uNzBzUytGY0lHQlZ3aUV1N3RLRGtIM09uemc5TzBCZWdLUk04SUI3ajVMUFdCNE93c0FWQ2tvYWdlMkorMURDdHQ0OHYyOG9Henkwd0E9In0seyJ2YWxpZGF0aW9uX3B1YmxpY19rZXkiOiJFRDA0QzM4MzBBQzQ5NEI0Nzg5QTRDMEVBMUMxN0MxRTExREQzRUEyNDQ3QzU2QTM2MEIyMDFCOTY3MUVCRjdGRjYiLCJtYW5pZmVzdCI6IkpBQUFBRHB4SWUwRXc0TUt4SlMwZUpwTURxSEJmQjRSM1Q2aVJIeFdvMkN5QWJsbkhyOS85bk1oQStFWGZvekdtNWV5RzJ3bGdUekZSYjdxU2JBLzFtanhjQjdDbkgrNDVnbEdka1l3UkFJZ04zNUZ1QW1ralgvTGhSY3NILzFxVXpyNmE1UW1YMWJOOUluekZyeC83dzRDSUZPTUZUL053eG1FNUZLWlYvMG5nMTA4bWhId3ZGV0RjQldmK2dMVUJ2QUVkeDUwWlhOMExYSnBjSEJzWlM1MmJtOWtaVEV3TG5SeVlXNXphV0V1WTI5d0VrRGEvYWNiS3Q5SFV6b2dLbC8yYm55N0xoNVg0a0VTN1NWY2FzcEdMR0s4aUd1NUdLZUJsY3VxbFE4VXIxM2JXNVJ2dWlqd2M4dWUrTXVFOXczQWVtQU4ifV19", "manifest": "JAAAAAFxIe101ANsZZGkvfnFTO+jm5lqXc5fhtEf2hh0SBzp1aHNwXMh7TN9+b62cZqTngaFYU5tbGpYHC8oYuI3G3vwj9OW2Z9gdkAnUjfY5zOEkhq31tU4338jcyUpVA5/VTsANFce7unDo+JeVoEhfuOb/Y8WA3Diu9XzuOD4U/ikfgf9SZOlOGcBcBJAw44PLjH+HUtEnwX45lIRmo0x5aINFMvZsBpE9QteSDBXKwYzLdnSW4e1bs21o+IILJIiIKU/+1Uxx0FRpQbMDA==", "public_key": "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1CDC1", - "signature": "77809A0938EA47F3A03BA0B71DD2C6879416F06574F6B5DC0041C9D806D3D5971282EF99D9EF911F8F37B56DA7016679744EF042C7A98ED425EFFFC6CC8AEA05", + "signature": "B7619B4AD763E7E59715C8710E1836558F9B0B3F216F30A7922CCE288434AFDA8C12993D716CE750E603F8A6B21EFB53448758F2D90F3FE5DFD1063350F7040C", "version": 1 } } diff --git a/src/test/app/Import_test.cpp b/src/test/app/Import_test.cpp index b23aa044d4f..708f241df78 100644 --- a/src/test/app/Import_test.cpp +++ b/src/test/app/Import_test.cpp @@ -1682,6 +1682,36 @@ class Import_test : public beast::unit_test::suite ter(temDISABLED)); } + // temMALFORMED - Import: xpop did not contain an 80% quorum for the txn + // it purports to prove. + for (bool const withXahauV1 : {true, false}) + { + auto const amend = withXahauV1 ? features : features - fixXahauV1; + test::jtx::Env env{ + *this, network::makeNetworkVLConfig(21337, keys), amend}; + + auto const alice = Account("alice"); + env.fund(XRP(1000), alice); + env.close(); + + Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed); + Json::Value valData = tmpXpop[jss::validation][jss::data]; + valData["n9MNStvsHZxrCYnShytCfKMCXBcq8wJjR6fLTS7L2Q8qoFVNioQ8"] = + ""; + valData["n9MTTavZe6EPqqyQ27pJbNWpfHw8ZNspVgznrwx5HWm7cdTjKQie"] = + ""; + if (withXahauV1) + { + valData + ["n94J5LCRu9bBWrJiwRWujvuVECVPWbXFcQ2VN38qLD378F5pDSDM"] = + ""; + } + tmpXpop[jss::validation][jss::data] = valData; + auto const txResult = + withXahauV1 ? ter(temMALFORMED) : ter(temMALFORMED); + env(import::import(alice, tmpXpop), txResult); + } + test::jtx::Env env{*this, network::makeNetworkVLConfig(21337, keys)}; auto const feeDrops = env.current()->fees().base; @@ -2573,19 +2603,6 @@ class Import_test : public beast::unit_test::suite // parsed // DA: Catch All - // temMALFORMED - Import: xpop did not contain an 80% quorum for the txn - // it purports to prove. - { - Json::Value tmpXpop = import::loadXpop(ImportTCAccountSet::w_seed); - Json::Value valData; - valData["n94at1vSdHSBEun25yT4ZfgqD1tVQNsx1nqRZG3T6ygbuvwgcMZN"] = - ""; - valData["n9KXYzdZD8YpsNiChtMjP6yhvQAhkkh5XeSTbvYyV1waF8wkNnBT"] = - ""; - tmpXpop[jss::validation][jss::data] = valData; - env(import::import(alice, tmpXpop), ter(temMALFORMED)); - } - // temMALFORMED - Import: xpop inner txn did not contain a sequence // number or fee No Sequence { diff --git a/src/test/app/Offer_test.cpp b/src/test/app/Offer_test.cpp index ea0d8bcbb85..576a810fa72 100644 --- a/src/test/app/Offer_test.cpp +++ b/src/test/app/Offer_test.cpp @@ -5113,10 +5113,11 @@ class Offer_test : public beast::unit_test::suite using namespace jtx; - // OfferCreate - OfferID + // OfferCreate + for (bool const withXahauV1 : {true, false}) { - Env env{*this, features}; - + auto const amend = withXahauV1 ? features : features - fixXahauV1; + Env env{*this, amend}; auto const gw = Account{"gateway"}; auto const alice = Account{"alice"}; auto const USD = gw["USD"]; @@ -5130,58 +5131,80 @@ class Offer_test : public beast::unit_test::suite env(pay(gw, alice, USD(200))); env.close(); - uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; - env(offer(alice, XRP(50), USD(50))); - env.close(); - - auto offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 1); - - auto tx = offer(alice, XRP(50), USD(50)); - tx[sfOfferID.jsonName] = to_string(offerId); - env(tx, ter(tesSUCCESS)); - env.close(); + // sequence id - no offer id + { + std::uint32_t const offerSeqId{env.seq(alice)}; + env(offer(alice, XRP(50), USD(50))); + env.close(); - offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 1); - } + auto tx = offer(alice, XRP(50), USD(50)); + tx[sfOfferSequence.jsonName] = offerSeqId; + env(tx, ter(tesSUCCESS)); + env.close(); + auto const offerLE = env.le(keylet::offer(alice, offerSeqId)); + BEAST_EXPECT(!offerLE); + } - // OfferCreate - OfferSequence - { - Env env{*this, features}; - auto const gw = Account{"gateway"}; - auto const alice = Account{"alice"}; - auto const USD = gw["USD"]; + // no sequence id - offer id + { + uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; + env(offer(alice, XRP(50), USD(50))); + env.close(); - env.fund(XRP(10000), gw, alice); - env.close(); + auto tx = offer(alice, XRP(50), USD(50)); + env(tx, offer_id(offerId), ter(tesSUCCESS)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(!offerLE); + } - env(trust(alice, USD(1000))); - env.close(); + // no offer id or offer sequence + { + uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; + env(offer(alice, XRP(50), USD(50))); + env.close(); - env(pay(gw, alice, USD(200))); - env.close(); + env(offer(alice, XRP(50), USD(50))); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); + } - // OfferCreate - OfferID - std::uint32_t const offerSeqId{env.seq(alice)}; - env(offer(alice, XRP(50), USD(50))); - env.close(); + // offer id and offer sequence + { + uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; + std::uint32_t const offerSeqId{env.seq(alice)}; + env(offer(alice, XRP(50), USD(50))); + env.close(); - auto offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 1); + auto tx = offer(alice, XRP(50), USD(50)); + tx[sfOfferSequence.jsonName] = offerSeqId; + env(tx, offer_id(offerId), ter(temBAD_SEQUENCE)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); + } - auto tx = offer(alice, XRP(50), USD(50)); - tx[sfOfferSequence.jsonName] = offerSeqId; - env(tx, ter(tesSUCCESS)); - env.close(); + // offer id and offer sequence 0 + { + uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; + env(offer(alice, XRP(50), USD(50))); + env.close(); - offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 1); + auto tx = offer(alice, XRP(50), USD(50)); + tx[sfOfferSequence.jsonName] = 0; + env(tx, offer_id(offerId), ter(temBAD_SEQUENCE)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); + } } // OfferCancel + for (bool const withXahauV1 : {true, false}) { - Env env{*this, features}; + auto const amend = withXahauV1 ? features : features - fixXahauV1; + Env env{*this, amend}; auto const gw = Account{"gateway"}; auto const alice = Account{"alice"}; auto const USD = gw["USD"]; @@ -5203,36 +5226,46 @@ class Offer_test : public beast::unit_test::suite env(offer_cancel(alice, offerSeqId), ter(tesSUCCESS)); env.close(); - auto const offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 0); + auto const offerLE = env.le(keylet::offer(alice, offerSeqId)); + BEAST_EXPECT(!offerLE); } - // offer id (offer sequence required) + // no sequence id - offer id { uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; env(offer(alice, XRP(50), USD(50))); env.close(); - env(offer_cancel(alice, 0), - offer_id(offerId), - ter(temBAD_SEQUENCE)); - env.close(); - auto const offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 1); + if (withXahauV1) + { + env(offer_cancel(alice), + offer_id(offerId), + ter(tesSUCCESS)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(!offerLE); + } + else + { + env(offer_cancel(alice), + offer_id(offerId), + ter(temBAD_SEQUENCE)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); + } } // no offer id or offer sequence { + uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; env(offer(alice, XRP(50), USD(50))); env.close(); - Json::Value jv; - jv[jss::Account] = alice.human(); - jv[jss::TransactionType] = jss::OfferCancel; - env(jv, ter(temMALFORMED)); + env(offer_cancel(alice), ter(temBAD_SEQUENCE)); env.close(); - auto const offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 2); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); } // both offer id and offer sequence @@ -5242,12 +5275,38 @@ class Offer_test : public beast::unit_test::suite env(offer(alice, XRP(50), USD(50))); env.close(); - env(offer_cancel(alice, offerSeqId), + if (withXahauV1) + { + env(offer_cancel(alice, offerSeqId), + offer_id(offerId), + ter(temBAD_SEQUENCE)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); + } + else + { + env(offer_cancel(alice, offerSeqId), + offer_id(offerId), + ter(tesSUCCESS)); + env.close(); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(!offerLE); + } + } + + // both offer id and offer sequence 0 + { + uint256 const offerId{getOfferIndex(alice, env.seq(alice))}; + env(offer(alice, XRP(50), USD(50))); + env.close(); + + env(offer_cancel(alice, 0), offer_id(offerId), - ter(tesSUCCESS)); + ter(temBAD_SEQUENCE)); env.close(); - auto const offers = sortedOffersOnAccount(env, alice); - BEAST_EXPECT(offers.size() == 2); + auto const offerLE = env.le(keylet::unchecked(offerId)); + BEAST_EXPECT(offerLE); } } } @@ -5350,6 +5409,7 @@ class Offer_test : public beast::unit_test::suite testAll(all - rmSmallIncreasedQOffers - immediateOfferKilled); testAll(all); testFalseAssert(); + testOfferID(all); } }; diff --git a/src/test/app/SetHookTSH_test.cpp b/src/test/app/SetHookTSH_test.cpp index 2d99a81ae4a..44ea1d795f2 100644 --- a/src/test/app/SetHookTSH_test.cpp +++ b/src/test/app/SetHookTSH_test.cpp @@ -31,6 +31,10 @@ namespace test { struct SetHookTSH_test : public beast::unit_test::suite { private: + const uint64_t tshSTRONG = 0; + const uint64_t tshWEAK = 1; + const uint64_t tshNONE = 2; + // helper void static overrideFlag(Json::Value& jv) { @@ -127,6 +131,105 @@ struct SetHookTSH_test : public beast::unit_test::suite 0x2eU, 0x22U, 0x00U, 0x22U, 0x74U, 0x73U, 0x68U, 0x2eU, 0x63U, 0x3aU, 0x20U, 0x45U, 0x6eU, 0x64U, 0x2eU, 0x22U}; + void + addWeakTSH(jtx::Env& env, jtx::Account const& account) + { + using namespace test::jtx; + env(fset(account, asfTshCollect)); + env.close(); + } + + void + setTSHHook( + jtx::Env& env, + jtx::Account const& account, + bool const& testStrong) + { + using namespace test::jtx; + auto const tshFlag = testStrong ? overrideFlag : collectFlag; + env(hook(account, {{hso(TshHook, tshFlag)}}, 0), + fee(XRP(1)), + ter(tesSUCCESS)); + env.close(); + } + + void + validateTSHStrongWeak( + Json::Value meta, + uint64_t const& expected, + uint64_t const& lineno) + { + switch (expected) + { + // tshSTRONG + case 0: { + auto const executions = meta[sfHookExecutions.jsonName]; + auto const execution = executions[0u][sfHookExecution.jsonName]; + BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); + BEAST_EXPECT( + execution[sfHookReturnString.jsonName] == "00000000"); + if (execution[sfHookReturnString.jsonName] != "00000000") + { + std::cout << "testTSHStrongWeak Line: " << lineno << "\n"; + std::cout + << "testTSHStrongWeak Expected: " << expected + << " Result: " << execution[sfHookReturnString.jsonName] + << "\n"; + std::cout << "testTSHStrongWeak Meta: " << meta << "\n"; + } + break; + } + // tshWEAK + case 1: { + auto const executions = meta[sfHookExecutions.jsonName]; + auto const execution = executions[0u][sfHookExecution.jsonName]; + BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); + BEAST_EXPECT( + execution[sfHookReturnString.jsonName] == "00000001"); + if (execution[sfHookReturnString.jsonName] != "00000001") + { + std::cout << "testTSHStrongWeak Line: " << lineno << "\n"; + std::cout + << "testTSHStrongWeak Expected: " << expected + << " Result: " << execution[sfHookReturnString.jsonName] + << "\n"; + std::cout << "testTSHStrongWeak Meta: " << meta << "\n"; + } + break; + } + // tshNONE + case 2: { + auto const executions = meta[sfHookExecutions.jsonName]; + BEAST_EXPECT(executions.size() == 0); + if (executions.size() != 0) + { + std::cout << "testTSHStrongWeak Line: " << lineno << "\n"; + std::cout << "testTSHStrongWeak Expected: " << expected + << " Result: " << executions.size() << "\n"; + std::cout << "testTSHStrongWeak Meta: " << meta << "\n"; + } + break; + } + + default: + break; + } + } + + void + testTSHStrongWeak( + jtx::Env& env, + int const& expected, + uint64_t const& lineno) + { + Json::Value params; + params[jss::transaction] = + env.tx()->getJson(JsonOptions::none)[jss::hash]; + auto const jrr = env.rpc("json", "tx", to_string(params)); + auto const meta = jrr[jss::result][jss::meta]; + validateTSHStrongWeak(meta, expected, lineno); + } + // AccountSet // | otxn | tsh | set | // | A | A | S | @@ -135,10 +238,12 @@ struct SetHookTSH_test : public beast::unit_test::suite { using namespace test::jtx; using namespace std::literals; + testcase("account set TSH"); // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -149,42 +254,37 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account); env.close(); - // set tsh hook on bob - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + if (!testStrong) + addWeakTSH(env, account); + + // set tsh hook + setTSHHook(env, account, testStrong); // account set - env(fset(account, asfTshCollect), fee(XRP(1)), ter(tesSUCCESS)); + env(fset(account, asfDefaultRipple), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } // AccountDelete - // | otxn | tsh | delete | - // | A | A | N | - // | A | B | S | + // | otxn | tsh | delete | + // | A | A | N/A | + // | A | B | S | + // Account cannot delete with a hook installed void testAccountDeleteTSH(FeatureBitset features) { using namespace test::jtx; using namespace std::literals; + testcase("account delete TSH"); // otxn: account // tsh bene // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -196,11 +296,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, bene); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, bene); + // set tsh hook - env(hook(bene, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, bene, testStrong); // AccountDelete incLgrSeqForAccDel(env, account); @@ -208,26 +309,16 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } // Check - // | otxn | tsh | cancel | create | cash | - // | A | A | S | S | N | - // | A | D | N | S | N | - // | D | D | S | N | S | - // | D | A | S | N | S | + // | otxn | tsh | cancel | create | cash | + // | A | A | S | S | N/A | + // | A | D | N | S | N/A | + // | D | D | S | N/A | S | + // | D | A | S | N/A | S | static uint256 getCheckIndex(AccountID const& account, std::uint32_t uSequence) { @@ -245,6 +336,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -261,31 +353,25 @@ struct SetHookTSH_test : public beast::unit_test::suite env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel check env(check::cancel(account, checkId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh destination // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -297,38 +383,30 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(dest, asfTshCollect)); - env.close(); - // create check uint256 const checkId{getCheckIndex(account, env.seq(account))}; env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // cancel check env(check::cancel(account, checkId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + testTSHStrongWeak(env, tshNONE, __LINE__); } // otxn: dest // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -345,31 +423,25 @@ struct SetHookTSH_test : public beast::unit_test::suite env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // cancel check env(check::cancel(dest, checkId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -386,40 +458,34 @@ struct SetHookTSH_test : public beast::unit_test::suite env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel check env(check::cancel(dest, checkId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } void - testCheckCashTSH(FeatureBitset features) + testCheckCreateTSH(FeatureBitset features) { - testcase("check cash tsh"); + testcase("check create tsh"); using namespace test::jtx; using namespace std::literals; - // otxn: dest - // tsh dest + // otxn: account + // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -431,38 +497,27 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // create check - uint256 const checkId{getCheckIndex(account, env.seq(account))}; - env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); - env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); - // cash check - env(check::cash(dest, checkId, XRP(100)), + // create check + env(check::create(account, dest, XRP(100)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } - // otxn: dest - // tsh account + // otxn: account + // tsh destination // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -475,50 +530,35 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // set tsh collect - env(fset(account, asfTshCollect)); - env.close(); - - // create check - uint256 const checkId{getCheckIndex(account, env.seq(account))}; - env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); - env.close(); + if (!testStrong) + addWeakTSH(env, dest); // set tsh hook - env(hook(account, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); - // cash check - env(check::cash(dest, checkId, XRP(100)), + // create check + env(check::create(account, dest, XRP(100)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } void - testCheckCreateTSH(FeatureBitset features) + testCheckCashTSH(FeatureBitset features) { - testcase("check create tsh"); + testcase("check cash tsh"); using namespace test::jtx; using namespace std::literals; - // otxn: account - // tsh account + // otxn: dest + // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -530,33 +570,32 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); + // create check + uint256 const checkId{getCheckIndex(account, env.seq(account))}; + env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); env.close(); - // create check - env(check::create(account, dest, XRP(100)), + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + + // set tsh hook + setTSHHook(env, dest, testStrong); + + // cash check + env(check::cash(dest, checkId, XRP(100)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } - // otxn: account - // tsh destination + // otxn: dest + // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -568,28 +607,26 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); + // create check + uint256 const checkId{getCheckIndex(account, env.seq(account))}; + env(check::create(account, dest, XRP(100)), ter(tesSUCCESS)); env.close(); - // create check - env(check::create(account, dest, XRP(100)), + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + + // set tsh hook + setTSHHook(env, account, testStrong); + + // cash check + env(check::cash(dest, checkId, XRP(100)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -609,6 +646,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -620,11 +658,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, issuer); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // claim reward env(reward::claim(account), @@ -634,20 +673,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -659,11 +691,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, issuer); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // claim reward env(reward::claim(account), @@ -673,15 +706,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -701,6 +726,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -715,31 +741,25 @@ struct SetHookTSH_test : public beast::unit_test::suite // require authorization for deposits. env(fset(account, asfDepositAuth)); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // deposit preauth env(deposit::auth(account, authed), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh authorize // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -754,35 +774,28 @@ struct SetHookTSH_test : public beast::unit_test::suite // require authorization for deposits. env(fset(account, asfDepositAuth)); + // set tsh collect + if (!testStrong) + addWeakTSH(env, authed); + // set tsh hook - env(hook(authed, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, authed, testStrong); // deposit preauth env(deposit::auth(account, authed), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } // Escrow - // | otxn | tsh | cancel | create | finish | - // | A | A | S | S | S | - // | A | D | W | S | W | - // | D | D | S | N | S | - // | D | A | S | N | S | + // | otxn | tsh | cancel | cancel(id) | create | finish | finish(id) + // | A | A | S | S | S | S | S + // | A | D | N | N | S | S | S + // | D | D | S | S | N/A | S | S + // | D | A | S | S | N/A | S | S static uint256 getEscrowIndex(AccountID const& account, std::uint32_t uSequence) @@ -802,6 +815,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -825,11 +839,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel escrow env(escrow::cancel(account, account, seq1), @@ -838,20 +853,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest - // w/s: weak + // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -863,10 +871,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(dest, asfTshCollect)); - env.close(); - // create escrow auto const seq1 = env.seq(account); NetClock::time_point const finishTime = env.now() + 1s; @@ -879,11 +883,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // cancel escrow env(escrow::cancel(account, account, seq1), @@ -892,18 +897,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + testTSHStrongWeak(env, tshNONE, __LINE__); } // otxn: dest // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -927,11 +927,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // cancel escrow env(escrow::cancel(dest, account, seq1), @@ -940,20 +941,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -965,10 +959,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(account, asfTshCollect)); - env.close(); - // create escrow auto const seq1 = env.seq(account); NetClock::time_point const finishTime = env.now() + 1s; @@ -981,11 +971,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel escrow env(escrow::cancel(dest, account, seq1), @@ -994,15 +985,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -1018,6 +1001,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1041,34 +1025,34 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel escrow - env(escrow::cancel(account, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + Json::Value tx; + if (!env.current()->rules().enabled(fixXahauV1)) + { + tx = escrow::cancel(account, account, 0); + } + else + { + tx = escrow::cancel(account, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest - // w/s: weak + // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1080,10 +1064,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(dest, asfTshCollect)); - env.close(); - // create escrow uint256 const escrowId{getEscrowIndex(account, env.seq(account))}; NetClock::time_point const finishTime = env.now() + 1s; @@ -1096,36 +1076,34 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // cancel escrow - env(escrow::cancel(account, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + Json::Value tx; + if (!env.current()->rules().enabled(fixXahauV1)) + { + tx = escrow::cancel(account, account, 0); + } + else + { + tx = escrow::cancel(account, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); - // auto const execution = executions[0u][sfHookExecution.jsonName]; - // BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - // BEAST_EXPECT(execution[sfHookReturnString.jsonName] == - // "00000001"); + testTSHStrongWeak(env, tshNONE, __LINE__); } // otxn: dest // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1149,34 +1127,34 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // cancel escrow - env(escrow::cancel(dest, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + Json::Value tx; + if (!env.current()->rules().enabled(fixXahauV1)) + { + tx = escrow::cancel(dest, account, 0); + } + else + { + tx = escrow::cancel(dest, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh account - // w/s: weak + // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1188,10 +1166,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(account, asfTshCollect)); - env.close(); - // create escrow uint256 const escrowId{getEscrowIndex(account, env.seq(account))}; NetClock::time_point const finishTime = env.now() + 1s; @@ -1204,27 +1178,32 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel escrow - env(escrow::cancel(dest, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + bool const fixV1 = env.current()->rules().enabled(fixXahauV1); + Json::Value tx; + if (!fixV1) + { + tx = escrow::cancel(dest, account, 0); + } + else + { + tx = escrow::cancel(dest, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + auto const expected = + (fixV1 ? (testStrong ? tshSTRONG : tshSTRONG) + : (testStrong ? tshNONE : tshNONE)); + testTSHStrongWeak(env, expected, __LINE__); } } @@ -1240,6 +1219,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1251,11 +1231,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // create escrow NetClock::time_point const finishTime = env.now() + 1s; @@ -1269,20 +1250,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1294,11 +1268,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // create escrow NetClock::time_point const finishTime = env.now() + 1s; @@ -1312,15 +1287,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -1336,6 +1303,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1356,11 +1324,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // finish escrow env(escrow::finish(account, account, seq1), @@ -1369,20 +1338,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1394,10 +1356,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - // env(fset(dest, asfTshCollect)); - // env.close(); - // create escrow auto const seq1 = env.seq(account); NetClock::time_point const finishTime = env.now() + 1s; @@ -1407,11 +1365,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // finish escrow env(escrow::finish(account, account, seq1), @@ -1420,20 +1379,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1454,11 +1406,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // finish escrow env(escrow::finish(dest, account, seq1), @@ -1467,20 +1420,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1501,11 +1447,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // finish escrow env(escrow::finish(dest, account, seq1), @@ -1514,15 +1461,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -1538,6 +1477,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1558,34 +1498,34 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // finish escrow - env(escrow::finish(account, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + Json::Value tx; + if (!env.current()->rules().enabled(fixXahauV1)) + { + tx = escrow::finish(account, account, 0); + } + else + { + tx = escrow::finish(account, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest - // w/s: weak + // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1597,10 +1537,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(dest, asfTshCollect)); - env.close(); - // create escrow uint256 const escrowId{getEscrowIndex(account, env.seq(account))}; NetClock::time_point const finishTime = env.now() + 1s; @@ -1610,32 +1546,38 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // finish escrow - env(escrow::finish(account, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + bool const fixV1 = env.current()->rules().enabled(fixXahauV1); + Json::Value tx; + if (!fixV1) + { + tx = escrow::finish(account, account, 0); + } + else + { + tx = escrow::finish(account, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + auto const expected = + (fixV1 ? (testStrong ? tshSTRONG : tshSTRONG) + : (testStrong ? tshNONE : tshNONE)); + testTSHStrongWeak(env, expected, __LINE__); } // otxn: dest // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1656,34 +1598,34 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // finish escrow - env(escrow::finish(dest, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + Json::Value tx; + if (!env.current()->rules().enabled(fixXahauV1)) + { + tx = escrow::finish(dest, account, 0); + } + else + { + tx = escrow::finish(dest, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh account - // w/s: weak + // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1695,10 +1637,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(account, asfTshCollect)); - env.close(); - // create escrow uint256 const escrowId{getEscrowIndex(account, env.seq(account))}; NetClock::time_point const finishTime = env.now() + 1s; @@ -1708,27 +1646,32 @@ struct SetHookTSH_test : public beast::unit_test::suite env(createTx, ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // finish escrow - env(escrow::finish(dest, account, 0), - escrow::escrow_id(escrowId), - fee(XRP(1)), - ter(tesSUCCESS)); + bool const fixV1 = env.current()->rules().enabled(fixXahauV1); + Json::Value tx; + if (!fixV1) + { + tx = escrow::finish(dest, account, 0); + } + else + { + tx = escrow::finish(dest, account); + } + env(tx, escrow::escrow_id(escrowId), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + auto const expected = + (fixV1 ? (testStrong ? tshSTRONG : tshSTRONG) + : (testStrong ? tshNONE : tshNONE)); + testTSHStrongWeak(env, expected, __LINE__); } } @@ -1751,6 +1694,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1768,11 +1712,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(noop(issuer), fee(XRP(10'000'000ULL))); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // set mint hook on master env(hook(issuer, {{hso(genesis::MintTestHook, overrideFlag)}}, 0), @@ -1793,21 +1738,14 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - // get the emitted txn id - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution1 = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution1[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution1[sfHookReturnString.jsonName] == "00000000"); + // verify tsh hook triggered + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh issuer // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1825,6 +1763,10 @@ struct SetHookTSH_test : public beast::unit_test::suite env(noop(issuer), fee(XRP(10'000'000ULL))); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook && set mint hook on master env(hook( issuer, @@ -1848,21 +1790,14 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - // get the emitted txn id - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution1 = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution1[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution1[sfHookReturnString.jsonName] == "00000000"); + // verify tsh hook triggered + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh bene // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1881,14 +1816,11 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // set tsh collect - env(fset(bene, asfTshCollect)); - env.close(); + if (!testStrong) + addWeakTSH(env, bene); // set tsh hook - env(hook(bene, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, bene, testStrong); // set mint hook on master env(hook(issuer, {{hso(genesis::MintTestHook, overrideFlag)}}, 0), @@ -1925,10 +1857,8 @@ struct SetHookTSH_test : public beast::unit_test::suite params1[jss::transaction] = txId; auto const jrr1 = env.rpc("json", "tx", to_string(params1)); auto const meta1 = jrr1[jss::result][jss::meta]; - auto const executions1 = meta1[sfHookExecutions.jsonName]; - auto const execution1 = executions1[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution1[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution1[sfHookReturnString.jsonName] == "00000001"); + auto const expected = testStrong ? tshNONE : tshWEAK; + validateTSHStrongWeak(meta1, expected, __LINE__); } } @@ -1953,6 +1883,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -1970,11 +1901,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(noop(env.master), fee(XRP(10'000'000ULL))); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // import env(import::import( @@ -1984,21 +1916,14 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - // get the emitted txn id - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution1 = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution1[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution1[sfHookReturnString.jsonName] == "00000000"); + // verify tsh hook triggered + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2016,11 +1941,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(noop(env.master), fee(XRP(10'000'000ULL))); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // import env(import::import( @@ -2030,23 +1956,15 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - // get the emitted txn id - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution1 = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution1[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution1[sfHookReturnString.jsonName] == "00000000"); + // verify tsh hook triggered + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } // Invoke // | otxn | tsh | invoke | // | A | A | S | - // | A | D | W | + // | A | D | S | void testInvokeTSH(FeatureBitset features) @@ -2059,6 +1977,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2070,11 +1989,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // ttINVOKE env(invoke::invoke(account), @@ -2084,20 +2004,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2109,11 +2022,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // ttINVOKE env(invoke::invoke(account), @@ -2123,22 +2037,14 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } // Offer - // | otxn | tsh | cancel | create | - // | A | A | S | S | - // | A | C | N | N | + // | otxn | tsh | cancel | create | + // | A | A | S | S | + // | A | C | N/A | N | void testOfferCancelTSH(FeatureBitset features) @@ -2151,6 +2057,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2172,26 +2079,19 @@ struct SetHookTSH_test : public beast::unit_test::suite env(offer(account, USD(1000), XRP(1000)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // cancel offer env(offer_cancel(account, offerSeq), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -2206,6 +2106,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2222,11 +2123,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env(offer(gw, USD(1000), XRP(1000))); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // create offer env(offer(account, USD(1000), XRP(1000)), @@ -2235,20 +2137,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh cross - // w/s: weak + // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2262,18 +2157,16 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, cross, gw); env.close(); - // set tsh collect - env(fset(cross, asfTshCollect)); - // gw create offer env(offer(gw, USD(1000), XRP(1000))); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, cross); + // set tsh hook - env(hook(cross, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, cross, testStrong); // create offer env(offer(account, USD(1000), XRP(1000)), @@ -2282,13 +2175,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + testTSHStrongWeak(env, tshNONE, __LINE__); } } @@ -2309,6 +2196,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2320,31 +2208,25 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // payment env(pay(account, dest, XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2356,31 +2238,25 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // payment env(pay(account, dest, XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh cross // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2403,37 +2279,28 @@ struct SetHookTSH_test : public beast::unit_test::suite env.trust(USDB(10), dest); // set tsh collect - env(fset(cross, asfTshCollect)); + if (!testStrong) + addWeakTSH(env, cross); // set tsh hook - env(hook(cross, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, cross, testStrong); // payment env(pay(account, dest, USDB(10)), paths(USDA), fee(XRP(1))); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001"); + auto const expected = testStrong ? tshNONE : tshWEAK; + testTSHStrongWeak(env, expected, __LINE__); } } // PaymentChannel - // | otxn | tsh | claim | create | fund | - // | A | A | S | S | S | - // | A | D | W | S | W | - // | D | D | S | N | N | - // | D | A | S | N | N | + // | otxn | tsh | claim | create | fund | + // | A | A | S | S | S | + // | A | D | W | S | W | + // | D | D | S | N/A | N/A | + // | D | A | S | N/A | N/A | static uint256 channel( @@ -2468,6 +2335,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2487,11 +2355,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); auto const delta = XRP(1); auto const reqBal = delta; @@ -2499,25 +2368,19 @@ struct SetHookTSH_test : public beast::unit_test::suite // claim paychannel env(paychan::claim(account, chan, reqBal, authAmt), + txflags(tfClose), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2529,9 +2392,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(dest, asfTshCollect)); - // create paychannel auto const pk = account.pk(); auto const settleDelay = 100s; @@ -2540,11 +2400,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); auto const delta = XRP(1); auto const reqBal = delta; @@ -2552,25 +2413,20 @@ struct SetHookTSH_test : public beast::unit_test::suite // claim paychannel env(paychan::claim(account, chan, reqBal, authAmt), + txflags(tfClose), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001"); + auto const expected = testStrong ? tshNONE : tshWEAK; + testTSHStrongWeak(env, expected, __LINE__); } // otxn: dest // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2590,11 +2446,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); auto const delta = XRP(1); auto const reqBal = delta; @@ -2605,25 +2462,19 @@ struct SetHookTSH_test : public beast::unit_test::suite signClaimAuth(account.pk(), account.sk(), chan, authAmt); env(paychan::claim( dest, chan, reqBal, authAmt, Slice(sig), account.pk()), + txflags(tfClose), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: dest // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2643,11 +2494,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); auto const delta = XRP(1); auto const reqBal = delta; @@ -2658,20 +2510,13 @@ struct SetHookTSH_test : public beast::unit_test::suite signClaimAuth(account.pk(), account.sk(), chan, authAmt); env(paychan::claim( dest, chan, reqBal, authAmt, Slice(sig), account.pk()), + txflags(tfClose), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -2686,6 +2531,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2697,36 +2543,29 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // create paychannel auto const pk = account.pk(); auto const settleDelay = 100s; - auto const chan = channel(account, dest, env.seq(account)); env(paychan::create(account, dest, XRP(10), settleDelay, pk), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2738,31 +2577,23 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // create paychannel auto const pk = account.pk(); auto const settleDelay = 100s; - auto const chan = channel(account, dest, env.seq(account)); env(paychan::create(account, dest, XRP(10), settleDelay, pk), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -2777,6 +2608,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2796,11 +2628,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // fund paychannel env(paychan::fund(account, chan, XRP(1)), @@ -2809,20 +2642,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2834,9 +2660,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); - // set tsh collect - env(fset(dest, asfTshCollect)); - // create paychannel auto const pk = account.pk(); auto const settleDelay = 100s; @@ -2845,11 +2668,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // fund paychannel env(paychan::fund(account, chan, XRP(1)), @@ -2858,15 +2682,8 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001"); + auto const expected = testStrong ? tshNONE : tshWEAK; + testTSHStrongWeak(env, expected, __LINE__); } } @@ -2884,6 +2701,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2894,6 +2712,10 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook auto hook1 = hso(TshHook, overrideFlag); hook1[jss::HookOn] = @@ -2902,22 +2724,11 @@ struct SetHookTSH_test : public beast::unit_test::suite env(hook(account, {{hook1}}, 0), fee(XRP(1)), ter(tesSUCCESS)); env.close(); - // set hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + // set tsh hook + setTSHHook(env, account, testStrong); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -2936,6 +2747,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2947,31 +2759,25 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // set regular key env(regkey(account, dest), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh dest // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -2983,26 +2789,19 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, dest); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, dest); + // set tsh hook - env(hook(dest, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, dest, testStrong); // set regular key env(regkey(account, dest), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -3021,6 +2820,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3033,11 +2833,12 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, signer1, signer2); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // signers list set env(signers(account, 2, {{signer1, 1}, {signer2, 1}}), @@ -3046,20 +2847,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh signer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3078,11 +2872,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, signer2); + // set tsh hook - env(hook(signer2, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, signer2, testStrong); // signers list set env(signers(account, 2, {{signer1, 1}, {signer2, 1}}), @@ -3120,6 +2915,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3130,26 +2926,19 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // ticket create env(ticket::create(account, 2), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -3168,6 +2957,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // otxn: account // tsh account // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3180,31 +2970,25 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, issuer); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, account); + // set tsh hook - env(hook(account, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, account, testStrong); // trust set env(trust(account, USD(1000)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: account // tsh issuer // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3217,60 +3001,54 @@ struct SetHookTSH_test : public beast::unit_test::suite env.fund(XRP(1000), account, issuer); env.close(); - // set tsh collect on bob - env(fset(issuer, asfTshCollect)); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); // set tsh hook - env(hook(issuer, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // trust set env(trust(account, USD(1000)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001"); + auto const expected = testStrong ? tshNONE : tshWEAK; + testTSHStrongWeak(env, expected, __LINE__); } } - // | otxn | tfBurnable | tsh | mint | burn | buy | sell | cancel - // | O | false | O | N | S | S | S | S - // | O | false | I | N | W | W | W | N - // | O | false | B | N | N | N | S | N - // | O | true | B | N | N | N | S | N - // | O | true | O | N | S | S | S | S - // | O | true | I | N | W | S | S | N - // | I | false | O | N | N | N | N | N - // | I | false | I | S | N | N | N | N - // | I | false | B | N | N | N | N | N - // | I | true | O | N | W | N | N | N - // | I | true | I | S | S | N | N | N - // | I | true | B | N | N | N | N | N - // | B | true | O | N | N | ? | N | N - // | B | true | B | N | N | ? | N | N + // | otxn | tfBurnable | tsh | mint | burn | buy | sell | cancel + // | O | false | O | N/A | S | N/A | S | S + // | O | false | I | N/A | N | N/A | W | N/A + // | O | false | B | N/A | N/A | N/A | N | N/A + // | O | true | B | N/A | N/A | N/A | N | N/A + // | O | true | O | N/A | S | N/A | S | S + // | O | true | I | N/A | N | N/A | S | N/A + // | I | false | O | N/A | N/A | N/A | N/A | N/A + // | I | false | I | S | N/A | N/A | N/A | N/A + // | I | false | B | N | N/A | N/A | N/A | N/A + // | I | true | O | N/A | N | N/A | N/A | N/A + // | I | true | I | S | S | N/A | N/A | N/A + // | I | true | B | N | N/A | N/A | N/A | N/A + // | B | true | O | N/A | N/A | S | N/A | N/A + // | B | true | B | N/A | N/A | S | N/A | N/A + // | B | false | I | N/A | N/A | W | N/A | N/A + // | B | true | I | N/A | N/A | S | N/A | N/A void - testURITokenBurnTSH(FeatureBitset features) + testURITokenMintTSH(FeatureBitset features) { - testcase("uritoken burn tsh"); + testcase("uritoken mint tsh"); using namespace test::jtx; using namespace std::literals; - // otxn: owner + // otxn: issuer // flag: not burnable - // tsh owner + // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3278,53 +3056,77 @@ struct SetHookTSH_test : public beast::unit_test::suite features}; auto const issuer = Account("alice"); - auto const owner = Account("bob"); - env.fund(XRP(1000), issuer, owner); + auto const buyer = Account("carol"); + env.fund(XRP(1000), issuer, buyer); env.close(); std::string const uri(2, '?'); auto const tid = uritoken::tokenid(issuer, uri); std::string const hexid{strHex(tid)}; + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + + // set tsh hook + setTSHHook(env, issuer, testStrong); + // mint uritoken env(uritoken::mint(issuer, uri), - uritoken::dest(owner), + uritoken::dest(buyer), uritoken::amt(XRP(1)), + fee(XRP(1)), ter(tesSUCCESS)); env.close(); - // buy uritoken - env(uritoken::buy(owner, hexid), - uritoken::amt(XRP(1)), - ter(tesSUCCESS)); + // verify tsh hook triggered + testTSHStrongWeak(env, tshSTRONG, __LINE__); + } + + // otxn: issuer + // flag: not burnable + // tsh buyer + // w/s: none + for (bool const testStrong : {true, false}) + { + test::jtx::Env env{ + *this, + network::makeNetworkConfig(21337, "10", "1000000", "200000"), + features}; + + auto const issuer = Account("alice"); + auto const buyer = Account("carol"); + env.fund(XRP(1000), issuer, buyer); env.close(); + std::string const uri(2, '?'); + auto const tid = uritoken::tokenid(issuer, uri); + std::string const hexid{strHex(tid)}; + + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), + setTSHHook(env, buyer, testStrong); + + // mint uritoken + env(uritoken::mint(issuer, uri), + uritoken::dest(buyer), + uritoken::amt(XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); - // ttURITOKEN_BURN - env(uritoken::burn(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); - env.close(); - // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshNONE, __LINE__); } - // otxn: owner - // flag: not burnable + // otxn: issuer + // flag: burnable // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3332,53 +3134,88 @@ struct SetHookTSH_test : public beast::unit_test::suite features}; auto const issuer = Account("alice"); - auto const owner = Account("bob"); - env.fund(XRP(1000), issuer, owner); + auto const buyer = Account("carol"); + env.fund(XRP(1000), issuer, buyer); env.close(); std::string const uri(2, '?'); auto const tid = uritoken::tokenid(issuer, uri); std::string const hexid{strHex(tid)}; + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + + // set tsh hook + setTSHHook(env, issuer, testStrong); + // mint uritoken env(uritoken::mint(issuer, uri), - uritoken::dest(owner), + uritoken::dest(buyer), uritoken::amt(XRP(1)), + fee(XRP(1)), + txflags(tfBurnable), ter(tesSUCCESS)); env.close(); - // buy uritoken - env(uritoken::buy(owner, hexid), - uritoken::amt(XRP(1)), - ter(tesSUCCESS)); + // verify tsh hook triggered + testTSHStrongWeak(env, tshSTRONG, __LINE__); + } + + // otxn: issuer + // flag: burnable + // tsh buyer + // w/s: none + for (bool const testStrong : {true, false}) + { + test::jtx::Env env{ + *this, + network::makeNetworkConfig(21337, "10", "1000000", "200000"), + features}; + + auto const issuer = Account("alice"); + auto const buyer = Account("carol"); + env.fund(XRP(1000), issuer, buyer); env.close(); + std::string const uri(2, '?'); + auto const tid = uritoken::tokenid(issuer, uri); + std::string const hexid{strHex(tid)}; + + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), + setTSHHook(env, buyer, testStrong); + + // mint uritoken + env(uritoken::mint(issuer, uri), + uritoken::dest(buyer), + uritoken::amt(XRP(1)), fee(XRP(1)), + txflags(tfBurnable), ter(tesSUCCESS)); env.close(); - // ttURITOKEN_BURN - env(uritoken::burn(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); - env.close(); - // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshNONE, __LINE__); } + } + + void + testURITokenBurnTSH(FeatureBitset features) + { + testcase("uritoken burn tsh"); + + using namespace test::jtx; + using namespace std::literals; // otxn: owner - // flag: burnable + // flag: not burnable // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3398,7 +3235,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env(uritoken::mint(issuer, uri), uritoken::dest(owner), uritoken::amt(XRP(1)), - txflags(tfBurnable), ter(tesSUCCESS)); env.close(); @@ -3408,32 +3244,26 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // ttURITOKEN_BURN env(uritoken::burn(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: owner - // flag: burnable + // flag: not burnable // tsh issuer - // w/s: strong + // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3453,7 +3283,6 @@ struct SetHookTSH_test : public beast::unit_test::suite env(uritoken::mint(issuer, uri), uritoken::dest(owner), uritoken::amt(XRP(1)), - txflags(tfBurnable), ter(tesSUCCESS)); env.close(); @@ -3463,32 +3292,30 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // ttURITOKEN_BURN env(uritoken::burn(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + bool const fixV1 = env.current()->rules().enabled(fixXahauV1); + auto const expected = + (fixV1 ? (testStrong ? tshNONE : tshNONE) + : (testStrong ? tshSTRONG : tshSTRONG)); + testTSHStrongWeak(env, expected, __LINE__); } - // otxn: issuer + // otxn: owner // flag: burnable // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3518,32 +3345,26 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // ttURITOKEN_BURN - env(uritoken::burn(issuer, hexid), fee(XRP(1)), ter(tesSUCCESS)); + env(uritoken::burn(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } - // otxn: issuer + // otxn: owner // flag: burnable // tsh issuer - // w/s: strong + // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3573,41 +3394,30 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // ttURITOKEN_BURN - env(uritoken::burn(issuer, hexid), fee(XRP(1)), ter(tesSUCCESS)); + env(uritoken::burn(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + bool const fixV1 = env.current()->rules().enabled(fixXahauV1); + auto const expected = + (fixV1 ? (testStrong ? tshNONE : tshNONE) + : (testStrong ? tshSTRONG : tshSTRONG)); + testTSHStrongWeak(env, expected, __LINE__); } - } - - void - testURITokenBuyTSH(FeatureBitset features) - { - testcase("uritoken buy tsh"); - - using namespace test::jtx; - using namespace std::literals; - // otxn: owner - // flag: not burnable + // otxn: issuer + // flag: burnable // tsh owner - // w/s: strong + // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3627,38 +3437,40 @@ struct SetHookTSH_test : public beast::unit_test::suite env(uritoken::mint(issuer, uri), uritoken::dest(owner), uritoken::amt(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), + txflags(tfBurnable), ter(tesSUCCESS)); env.close(); // buy uritoken env(uritoken::buy(owner, hexid), uritoken::amt(XRP(1)), - fee(XRP(1)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + + // set tsh hook + setTSHHook(env, owner, testStrong); + + // ttURITOKEN_BURN + env(uritoken::burn(issuer, hexid), fee(XRP(1)), ter(tesSUCCESS)); + env.close(); + // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + bool const fixV1 = env.current()->rules().enabled(fixXahauV1); + auto const expected = + (fixV1 ? (testStrong ? tshNONE : tshNONE) + : (testStrong ? tshSTRONG : tshSTRONG)); + testTSHStrongWeak(env, expected, __LINE__); } - // otxn: owner - // flag: not burnable + // otxn: issuer + // flag: burnable // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3678,38 +3490,45 @@ struct SetHookTSH_test : public beast::unit_test::suite env(uritoken::mint(issuer, uri), uritoken::dest(owner), uritoken::amt(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), + txflags(tfBurnable), ter(tesSUCCESS)); env.close(); // buy uritoken env(uritoken::buy(owner, hexid), uritoken::amt(XRP(1)), - fee(XRP(1)), ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + + // set tsh hook + setTSHHook(env, issuer, testStrong); + + // ttURITOKEN_BURN + env(uritoken::burn(issuer, hexid), fee(XRP(1)), ter(tesSUCCESS)); + env.close(); + // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } + } - // otxn: owner - // flag: burnable - // tsh owner - // w/s: strong + void + testURITokenBuyTSH(FeatureBitset features) + { + testcase("uritoken buy tsh"); + + using namespace test::jtx; + using namespace std::literals; + + // otxn: buyer + // flag: not burnable + // tsh issuer + // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3718,7 +3537,8 @@ struct SetHookTSH_test : public beast::unit_test::suite auto const issuer = Account("alice"); auto const owner = Account("bob"); - env.fund(XRP(1000), issuer, owner); + auto const buyer = Account("carol"); + env.fund(XRP(1000), issuer, owner, buyer); env.close(); std::string const uri(2, '?'); @@ -3729,39 +3549,47 @@ struct SetHookTSH_test : public beast::unit_test::suite env(uritoken::mint(issuer, uri), uritoken::dest(owner), uritoken::amt(XRP(1)), - txflags(tfBurnable), ter(tesSUCCESS)); env.close(); - // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), + // buy uritoken + env(uritoken::buy(owner, hexid), + uritoken::amt(XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); + // sell uritoken + env(uritoken::sell(owner, hexid), + uritoken::dest(buyer), + uritoken::amt(XRP(1)), + ter(tesSUCCESS)); + env.close(); + + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + + // set tsh hook + setTSHHook(env, issuer, testStrong); + // buy uritoken - env(uritoken::buy(owner, hexid), + env(uritoken::buy(buyer, hexid), uritoken::amt(XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + auto const expected = testStrong ? tshNONE : tshWEAK; + testTSHStrongWeak(env, expected, __LINE__); } - // otxn: owner + // otxn: buyer // flag: burnable // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3770,7 +3598,8 @@ struct SetHookTSH_test : public beast::unit_test::suite auto const issuer = Account("alice"); auto const owner = Account("bob"); - env.fund(XRP(1000), issuer, owner); + auto const buyer = Account("carol"); + env.fund(XRP(1000), issuer, owner, buyer); env.close(); std::string const uri(2, '?'); @@ -3785,34 +3614,42 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); - // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), + // buy uritoken + env(uritoken::buy(owner, hexid), + uritoken::amt(XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); + // sell uritoken + env(uritoken::sell(owner, hexid), + uritoken::dest(buyer), + uritoken::amt(XRP(1)), + ter(tesSUCCESS)); + env.close(); + + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + + // set tsh hook + setTSHHook(env, issuer, testStrong); + // buy uritoken - env(uritoken::buy(owner, hexid), + env(uritoken::buy(buyer, hexid), uritoken::amt(XRP(1)), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: buyer // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3850,11 +3687,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // buy uritoken env(uritoken::buy(buyer, hexid), @@ -3864,20 +3702,13 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: buyer // tsh buyer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3915,11 +3746,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(buyer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, buyer, testStrong); // buy uritoken env(uritoken::buy(buyer, hexid), @@ -3929,15 +3761,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -3953,6 +3777,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // flag: not burnable // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -3989,32 +3814,26 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // cancel uritoken env(uritoken::cancel(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: owner // flag: not burnable // tsh buyer // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4031,9 +3850,6 @@ struct SetHookTSH_test : public beast::unit_test::suite auto const tid = uritoken::tokenid(issuer, uri); std::string const hexid{strHex(tid)}; - env(fset(buyer, asfTshCollect)); - env.close(); - // mint uritoken env(uritoken::mint(issuer, uri), uritoken::dest(owner), @@ -4054,30 +3870,26 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(buyer, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, buyer, testStrong); // cancel uritoken env(uritoken::cancel(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + testTSHStrongWeak(env, tshNONE, __LINE__); } // otxn: owner // flag: burnable // tsh buyer // w/s: none + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4094,9 +3906,6 @@ struct SetHookTSH_test : public beast::unit_test::suite auto const tid = uritoken::tokenid(issuer, uri); std::string const hexid{strHex(tid)}; - env(fset(buyer, asfTshCollect)); - env.close(); - // mint uritoken env(uritoken::mint(issuer, uri), uritoken::dest(owner), @@ -4118,30 +3927,26 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(buyer, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, buyer, testStrong); // cancel uritoken env(uritoken::cancel(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + testTSHStrongWeak(env, tshNONE, __LINE__); } // otxn: owner // flag: burnable // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4179,26 +3984,19 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // cancel uritoken env(uritoken::cancel(owner, hexid), fee(XRP(1)), ter(tesSUCCESS)); env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -4214,6 +4012,7 @@ struct SetHookTSH_test : public beast::unit_test::suite // flag: not burnable // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4243,11 +4042,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // sell uritoken env(uritoken::sell(owner, hexid), @@ -4258,21 +4058,14 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: owner // flag: not burnable // tsh issuer // w/s: weak + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4289,9 +4082,6 @@ struct SetHookTSH_test : public beast::unit_test::suite auto const tid = uritoken::tokenid(issuer, uri); std::string const hexid{strHex(tid)}; - env(fset(issuer, asfTshCollect)); - env.close(); - // mint uritoken env(uritoken::mint(issuer, uri), uritoken::dest(owner), @@ -4305,11 +4095,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // sell uritoken env(uritoken::sell(owner, hexid), @@ -4320,21 +4111,15 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000001"); + auto const expected = testStrong ? tshNONE : tshWEAK; + testTSHStrongWeak(env, expected, __LINE__); } // otxn: owner // flag: not burnable // tsh buyer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4364,11 +4149,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(buyer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, buyer, testStrong); // sell uritoken env(uritoken::sell(owner, hexid), @@ -4379,21 +4165,14 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: owner // flag: not burnable // tsh buyer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4423,11 +4202,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, buyer); + // set tsh hook - env(hook(buyer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, buyer, testStrong); // sell uritoken env(uritoken::sell(owner, hexid), @@ -4438,21 +4218,14 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: owner // flag: burnable // tsh owner // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4483,11 +4256,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, owner); + // set tsh hook - env(hook(owner, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, owner, testStrong); // sell uritoken env(uritoken::sell(owner, hexid), @@ -4498,21 +4272,14 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } // otxn: owner // flag: burnable // tsh issuer // w/s: strong + for (bool const testStrong : {true, false}) { test::jtx::Env env{ *this, @@ -4543,11 +4310,12 @@ struct SetHookTSH_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); + // set tsh collect + if (!testStrong) + addWeakTSH(env, issuer); + // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); + setTSHHook(env, issuer, testStrong); // sell uritoken env(uritoken::sell(owner, hexid), @@ -4558,208 +4326,7 @@ struct SetHookTSH_test : public beast::unit_test::suite env.close(); // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); - } - } - - void - testURITokenMintTSH(FeatureBitset features) - { - testcase("uritoken mint tsh"); - - using namespace test::jtx; - using namespace std::literals; - - // otxn: issuer - // flag: not burnable - // tsh issuer - // w/s: strong - { - test::jtx::Env env{ - *this, - network::makeNetworkConfig(21337, "10", "1000000", "200000"), - features}; - - auto const issuer = Account("alice"); - auto const buyer = Account("carol"); - env.fund(XRP(1000), issuer, buyer); - env.close(); - - std::string const uri(2, '?'); - auto const tid = uritoken::tokenid(issuer, uri); - std::string const hexid{strHex(tid)}; - - // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // mint uritoken - env(uritoken::mint(issuer, uri), - uritoken::dest(buyer), - uritoken::amt(XRP(1)), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); - } - - // otxn: issuer - // flag: not burnable - // tsh buyer - // w/s: none - { - test::jtx::Env env{ - *this, - network::makeNetworkConfig(21337, "10", "1000000", "200000"), - features}; - - auto const issuer = Account("alice"); - auto const buyer = Account("carol"); - env.fund(XRP(1000), issuer, buyer); - env.close(); - - env(fset(buyer, asfTshCollect)); - env.close(); - - std::string const uri(2, '?'); - auto const tid = uritoken::tokenid(issuer, uri); - std::string const hexid{strHex(tid)}; - - // set tsh hook - env(hook(buyer, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // mint uritoken - env(uritoken::mint(issuer, uri), - uritoken::dest(buyer), - uritoken::amt(XRP(1)), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); - } - - // otxn: issuer - // flag: burnable - // tsh issuer - // w/s: strong - { - test::jtx::Env env{ - *this, - network::makeNetworkConfig(21337, "10", "1000000", "200000"), - features}; - - auto const issuer = Account("alice"); - auto const buyer = Account("carol"); - env.fund(XRP(1000), issuer, buyer); - env.close(); - - std::string const uri(2, '?'); - auto const tid = uritoken::tokenid(issuer, uri); - std::string const hexid{strHex(tid)}; - - // set tsh hook - env(hook(issuer, {{hso(TshHook, overrideFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // mint uritoken - env(uritoken::mint(issuer, uri), - uritoken::dest(buyer), - uritoken::amt(XRP(1)), - fee(XRP(1)), - txflags(tfBurnable), - ter(tesSUCCESS)); - env.close(); - - // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - auto const execution = executions[0u][sfHookExecution.jsonName]; - BEAST_EXPECT(execution[sfHookResult.jsonName] == 3); - BEAST_EXPECT(execution[sfHookReturnString.jsonName] == "00000000"); - } - - // otxn: issuer - // flag: burnable - // tsh buyer - // w/s: none - { - test::jtx::Env env{ - *this, - network::makeNetworkConfig(21337, "10", "1000000", "200000"), - features}; - - auto const issuer = Account("alice"); - auto const buyer = Account("carol"); - env.fund(XRP(1000), issuer, buyer); - env.close(); - - env(fset(buyer, asfTshCollect)); - env.close(); - - std::string const uri(2, '?'); - auto const tid = uritoken::tokenid(issuer, uri); - std::string const hexid{strHex(tid)}; - - // set tsh hook - env(hook(buyer, {{hso(TshHook, collectFlag)}}, 0), - fee(XRP(1)), - ter(tesSUCCESS)); - env.close(); - - // mint uritoken - env(uritoken::mint(issuer, uri), - uritoken::dest(buyer), - uritoken::amt(XRP(1)), - fee(XRP(1)), - txflags(tfBurnable), - ter(tesSUCCESS)); - env.close(); - - // verify tsh hook triggered - Json::Value params; - params[jss::transaction] = - env.tx()->getJson(JsonOptions::none)[jss::hash]; - auto const jrr = env.rpc("json", "tx", to_string(params)); - auto const meta = jrr[jss::result][jss::meta]; - auto const executions = meta[sfHookExecutions.jsonName]; - BEAST_EXPECT(executions.size() == 0); + testTSHStrongWeak(env, tshSTRONG, __LINE__); } } @@ -4792,11 +4359,11 @@ struct SetHookTSH_test : public beast::unit_test::suite testSignersListSetTSH(features); testTicketCreateTSH(features); testTrustSetTSH(features); + testURITokenMintTSH(features); testURITokenBurnTSH(features); testURITokenBuyTSH(features); testURITokenCancelSellOfferTSH(features); testURITokenCreateSellOfferTSH(features); - testURITokenMintTSH(features); } public: @@ -4805,6 +4372,7 @@ struct SetHookTSH_test : public beast::unit_test::suite { using namespace test::jtx; auto const sa = supported_amendments(); + testWithFeats(sa - fixXahauV1); testWithFeats(sa); } }; diff --git a/src/test/app/SetHook_test.cpp b/src/test/app/SetHook_test.cpp index 49aec56e165..f75c7c8761a 100644 --- a/src/test/app/SetHook_test.cpp +++ b/src/test/app/SetHook_test.cpp @@ -89,6 +89,67 @@ class SetHook_test : public beast::unit_test::suite // fee unit tests, the rest of the time we want to ignore it. #define HSFEE fee(100'000'000) #define M(m) memo(m, "", "") + void + testHooksOwnerDir() + { + testcase("Test owner directory"); + + using namespace jtx; + + for (bool const withXahauV1 : {true, false}) + { + auto const amend = withXahauV1 + ? supported_amendments() + : supported_amendments() - fixXahauV1; + + Env env{*this, amend}; + + auto const alice = Account{"alice"}; + 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))); + + for (int i = 1; i < 34; ++i) + { + std::string const uri(i, '?'); + env(uritoken::mint(alice, uri)); + } + env.close(); + + env(ripple::test::jtx::hook( + alice, {{hso(accept_wasm, overrideFlag)}}, 0), + HSFEE, + ter(tesSUCCESS)); + env.close(); + + env(ripple::test::jtx::hook( + alice, {{hso(accept_wasm, overrideFlag)}}, 0), + HSFEE, + ter(tesSUCCESS)); + env.close(); + + // delete hook + Json::Value jv; + jv[jss::Account] = alice.human(); + jv[jss::TransactionType] = jss::SetHook; + jv[jss::Flags] = 0; + jv[jss::Hooks] = Json::Value{Json::arrayValue}; + Json::Value iv; + iv[jss::Flags] = 1; + iv[jss::CreateCode] = ""; + jv[jss::Hooks][0U][jss::Hook] = iv; + + auto const txResult = + withXahauV1 ? ter(tesSUCCESS) : ter(tefBAD_LEDGER); + env(jv, HSFEE, txResult); + env.close(); + } + } + void testHooksDisabled() { @@ -100,7 +161,8 @@ class SetHook_test : public beast::unit_test::suite // RH TODO: does it matter that passing malformed txn here gives back // temMALFORMED (and not disabled)? - env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0), + env(ripple::test::jtx::hook( + alice, {{hso(accept_wasm, overrideFlag)}}, 0), M("Hooks Disabled"), HSFEE, ter(temDISABLED)); @@ -7764,6 +7826,134 @@ class SetHook_test : public beast::unit_test::suite } } + void + test_state_foreign_set_max() + { + testcase("Test state_foreign_set max"); + using namespace jtx; + + static const std::vector ns_maxHook = { + 0x00U, 0x61U, 0x73U, 0x6dU, 0x01U, 0x00U, 0x00U, 0x00U, 0x01U, + 0x36U, 0x07U, 0x60U, 0x02U, 0x7fU, 0x7fU, 0x01U, 0x7fU, 0x60U, + 0x02U, 0x7fU, 0x7fU, 0x01U, 0x7eU, 0x60U, 0x03U, 0x7fU, 0x7fU, + 0x7eU, 0x01U, 0x7eU, 0x60U, 0x04U, 0x7fU, 0x7fU, 0x7fU, 0x7fU, + 0x01U, 0x7eU, 0x60U, 0x05U, 0x7fU, 0x7fU, 0x7fU, 0x7fU, 0x7fU, + 0x01U, 0x7eU, 0x60U, 0x08U, 0x7fU, 0x7fU, 0x7fU, 0x7fU, 0x7fU, + 0x7fU, 0x7fU, 0x7fU, 0x01U, 0x7eU, 0x60U, 0x01U, 0x7fU, 0x01U, + 0x7eU, 0x02U, 0x79U, 0x08U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x02U, + 0x5fU, 0x67U, 0x00U, 0x00U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x0cU, + 0x68U, 0x6fU, 0x6fU, 0x6bU, 0x5fU, 0x61U, 0x63U, 0x63U, 0x6fU, + 0x75U, 0x6eU, 0x74U, 0x00U, 0x01U, 0x03U, 0x65U, 0x6eU, 0x76U, + 0x08U, 0x72U, 0x6fU, 0x6cU, 0x6cU, 0x62U, 0x61U, 0x63U, 0x6bU, + 0x00U, 0x02U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x05U, 0x73U, 0x74U, + 0x61U, 0x74U, 0x65U, 0x00U, 0x03U, 0x03U, 0x65U, 0x6eU, 0x76U, + 0x05U, 0x74U, 0x72U, 0x61U, 0x63U, 0x65U, 0x00U, 0x04U, 0x03U, + 0x65U, 0x6eU, 0x76U, 0x11U, 0x73U, 0x74U, 0x61U, 0x74U, 0x65U, + 0x5fU, 0x66U, 0x6fU, 0x72U, 0x65U, 0x69U, 0x67U, 0x6eU, 0x5fU, + 0x73U, 0x65U, 0x74U, 0x00U, 0x05U, 0x03U, 0x65U, 0x6eU, 0x76U, + 0x09U, 0x73U, 0x74U, 0x61U, 0x74U, 0x65U, 0x5fU, 0x73U, 0x65U, + 0x74U, 0x00U, 0x03U, 0x03U, 0x65U, 0x6eU, 0x76U, 0x06U, 0x61U, + 0x63U, 0x63U, 0x65U, 0x70U, 0x74U, 0x00U, 0x02U, 0x03U, 0x02U, + 0x01U, 0x06U, 0x05U, 0x03U, 0x01U, 0x00U, 0x02U, 0x06U, 0x2bU, + 0x07U, 0x7fU, 0x01U, 0x41U, 0xc0U, 0x88U, 0x04U, 0x0bU, 0x7fU, + 0x00U, 0x41U, 0x80U, 0x08U, 0x0bU, 0x7fU, 0x00U, 0x41U, 0xb2U, + 0x08U, 0x0bU, 0x7fU, 0x00U, 0x41U, 0x80U, 0x08U, 0x0bU, 0x7fU, + 0x00U, 0x41U, 0xc0U, 0x88U, 0x04U, 0x0bU, 0x7fU, 0x00U, 0x41U, + 0x00U, 0x0bU, 0x7fU, 0x00U, 0x41U, 0x01U, 0x0bU, 0x07U, 0x08U, + 0x01U, 0x04U, 0x68U, 0x6fU, 0x6fU, 0x6bU, 0x00U, 0x08U, 0x0aU, + 0xd4U, 0x81U, 0x00U, 0x01U, 0xd0U, 0x81U, 0x00U, 0x02U, 0x02U, + 0x7fU, 0x01U, 0x7eU, 0x23U, 0x00U, 0x41U, 0xe0U, 0x00U, 0x6bU, + 0x22U, 0x01U, 0x24U, 0x00U, 0x20U, 0x01U, 0x20U, 0x00U, 0x36U, + 0x02U, 0x5cU, 0x41U, 0x01U, 0x41U, 0x01U, 0x10U, 0x00U, 0x1aU, + 0x20U, 0x01U, 0x41U, 0x40U, 0x6bU, 0x41U, 0x14U, 0x10U, 0x01U, + 0x42U, 0x14U, 0x52U, 0x04U, 0x40U, 0x41U, 0x00U, 0x41U, 0x00U, + 0x42U, 0x0cU, 0x10U, 0x02U, 0x1aU, 0x0bU, 0x20U, 0x01U, 0x41U, + 0x38U, 0x6aU, 0x41U, 0x08U, 0x20U, 0x01U, 0x41U, 0x40U, 0x6bU, + 0x22U, 0x00U, 0x41U, 0x14U, 0x10U, 0x03U, 0x1aU, 0x20U, 0x01U, + 0x20U, 0x01U, 0x29U, 0x03U, 0x38U, 0x42U, 0x01U, 0x7cU, 0x37U, + 0x03U, 0x38U, 0x20U, 0x01U, 0x41U, 0xabU, 0x01U, 0x3aU, 0x00U, + 0x19U, 0x20U, 0x01U, 0x20U, 0x01U, 0x29U, 0x03U, 0x38U, 0x3cU, + 0x00U, 0x1aU, 0x41U, 0x80U, 0x08U, 0x41U, 0x02U, 0x20U, 0x01U, + 0x41U, 0x10U, 0x6aU, 0x22U, 0x02U, 0x41U, 0x20U, 0x41U, 0x01U, + 0x10U, 0x04U, 0x1aU, 0x20U, 0x01U, 0x41U, 0xa9U, 0x08U, 0x41U, + 0x09U, 0x41U, 0xa4U, 0x08U, 0x41U, 0x05U, 0x20U, 0x02U, 0x41U, + 0x20U, 0x20U, 0x00U, 0x41U, 0x14U, 0x10U, 0x05U, 0x37U, 0x03U, + 0x08U, 0x20U, 0x01U, 0x29U, 0x03U, 0x08U, 0x42U, 0x00U, 0x53U, + 0x04U, 0x40U, 0x41U, 0x83U, 0x08U, 0x41U, 0x21U, 0x20U, 0x01U, + 0x29U, 0x03U, 0x08U, 0x10U, 0x02U, 0x1aU, 0x0bU, 0x20U, 0x01U, + 0x41U, 0x38U, 0x6aU, 0x41U, 0x08U, 0x20U, 0x01U, 0x41U, 0x40U, + 0x6bU, 0x41U, 0x14U, 0x10U, 0x06U, 0x1aU, 0x41U, 0x00U, 0x41U, + 0x00U, 0x20U, 0x01U, 0x29U, 0x03U, 0x08U, 0x10U, 0x07U, 0x20U, + 0x01U, 0x41U, 0xe0U, 0x00U, 0x6aU, 0x24U, 0x00U, 0x0bU, 0x0bU, + 0x38U, 0x01U, 0x00U, 0x41U, 0x80U, 0x08U, 0x0bU, 0x31U, 0x6eU, + 0x73U, 0x00U, 0x6eU, 0x73U, 0x5fU, 0x6dU, 0x61U, 0x78U, 0x2eU, + 0x63U, 0x3aU, 0x20U, 0x4dU, 0x61U, 0x78U, 0x20U, 0x4eU, 0x61U, + 0x6dU, 0x65U, 0x73U, 0x70U, 0x61U, 0x63U, 0x65U, 0x73U, 0x20U, + 0x52U, 0x65U, 0x61U, 0x63U, 0x68U, 0x65U, 0x64U, 0x00U, 0x6bU, + 0x65U, 0x79U, 0x32U, 0x00U, 0x63U, 0x6fU, 0x6eU, 0x74U, 0x65U, + 0x6eU, 0x74U, 0x32U}; + + // fixXahauV1 + { + for (bool const withfixXahauV1 : {true, false}) + { + auto const amend = withfixXahauV1 + ? supported_amendments() + : supported_amendments() - fixXahauV1; + + Env env{*this, amend}; + + // Env env{*this, envconfig(), amend, nullptr, + // // beast::severities::kWarning + // beast::severities::kTrace + // }; + + auto const bob = Account{"bob"}; + auto const alice = Account{"alice"}; + env.fund(XRP(10000000), alice); + env.fund(XRP(10000000), bob); + + // install the hook on alice + env(ripple::test::jtx::hook( + alice, {{hso(ns_maxHook, overrideFlag)}}, 0), + M("set state_foreign_set_max"), + HSFEE); + env.close(); + + // invoke the hook + for (uint32_t i = 0; i < 255; ++i) + { + env(pay(bob, alice, XRP(1)), + M("test state_foreign_set_max"), + fee(XRP(1))); + } + + auto const txResult = + withfixXahauV1 ? ter(tecHOOK_REJECTED) : ter(tesSUCCESS); + env(pay(bob, alice, XRP(1)), + M("test state_foreign_set_max"), + fee(XRP(1)), + ter(txResult)); + env.close(); + + // verify hook result + std::string const hookResult = withfixXahauV1 + ? "800000000000002d" // TOO_MANY_NAMESPACES / -45 + : "9"; + + Json::Value params; + params[jss::transaction] = + env.tx()->getJson(JsonOptions::none)[jss::hash]; + auto const jrr = env.rpc("json", "tx", to_string(params)); + auto const meta = jrr[jss::result][jss::meta]; + auto const executions = meta[sfHookExecutions.jsonName]; + auto const execution = executions[0u][sfHookExecution.jsonName]; + BEAST_EXPECT( + execution[sfHookReturnCode.jsonName] == hookResult); + } + } + } + void test_state_foreign_set() { @@ -11437,6 +11627,7 @@ class SetHook_test : public beast::unit_test::suite void run() override { + testHooksOwnerDir(); testHooksDisabled(); testTxStructure(); testInferHookSetOperation(); @@ -11520,10 +11711,11 @@ class SetHook_test : public beast::unit_test::suite test_slot_subfield(); // test_slot_type(); // - test_state(); // - test_state_foreign(); // - test_state_foreign_set(); // - test_state_set(); // + test_state(); // + test_state_foreign(); // + test_state_foreign_set(); // + test_state_foreign_set_max(); // + test_state_set(); // test_sto_emplace(); // test_sto_erase(); // diff --git a/src/test/app/URIToken_test.cpp b/src/test/app/URIToken_test.cpp index 97e5df3c630..fa5d1ae6dfc 100644 --- a/src/test/app/URIToken_test.cpp +++ b/src/test/app/URIToken_test.cpp @@ -191,30 +191,25 @@ struct URIToken_test : public beast::unit_test::suite using namespace jtx; using namespace std::literals::chrono_literals; - // fixURITokenV1 + // fixXahauV1 { - for (bool const withFixURITokenV1 : {true, false}) - { - auto const amend = - withFixURITokenV1 ? features : features - fixURITokenV1; - - auto const txResult = - withFixURITokenV1 ? ter(temMALFORMED) : ter(tefINTERNAL); - - Env env{*this, amend}; - auto const alice = Account("alice"); - auto const bob = Account("bob"); - env.fund(XRP(1000), alice, bob); - env.close(); + Env env{*this, features}; + auto const alice = Account("alice"); + auto const bob = Account("bob"); + env.fund(XRP(1000), alice, bob); + env.close(); - std::string const uri(2, '?'); - auto const tid = uritoken::tokenid(alice, uri); - std::string const hexid{strHex(tid)}; + std::string const uri(2, '?'); + auto const tid = uritoken::tokenid(alice, uri); + std::string const hexid{strHex(tid)}; - // temMALFORMED - cannot include sfDestination without sfAmount - env(uritoken::mint(alice, uri), uritoken::dest(bob), txResult); - env.close(); - } + // temMALFORMED - cannot include sfDestination without sfAmount + bool const withFixXahauV1 = + env.current()->rules().enabled(fixXahauV1); + auto const txResult = + withFixXahauV1 ? ter(temMALFORMED) : ter(tefINTERNAL); + env(uritoken::mint(alice, uri), uritoken::dest(bob), txResult); + env.close(); } // setup env @@ -602,7 +597,7 @@ struct URIToken_test : public beast::unit_test::suite env.close(); // tecNO_LINE_INSUF_RESERVE - insuficient xrp to create line { - // fund dave 251 xrp (not enough for line reserve) + // fund echo 251 xrp (not enough for line reserve) env.fund(XRP(251), echo); env.fund(XRP(301), dave); env.close(); @@ -620,9 +615,12 @@ struct URIToken_test : public beast::unit_test::suite env.close(); // tecNO_LINE_INSUF_RESERVE - insuficient xrp to create line - env(uritoken::buy(dave, hexid), - uritoken::amt(USD(1)), - ter(tecNO_LINE_INSUF_RESERVE)); + auto const txResult = env.current()->rules().enabled(fixXahauV1) + ? ter(tecINSUF_RESERVE_SELLER) + : ter(tecNO_LINE_INSUF_RESERVE); + + env(noop(echo), fee(XRP(50)), ter(tesSUCCESS)); + env(uritoken::buy(dave, hexid), uritoken::amt(USD(1)), txResult); env.close(); } @@ -711,12 +709,14 @@ struct URIToken_test : public beast::unit_test::suite json(sfDigest.fieldName, digestval)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); BEAST_EXPECT( to_string(tokenDigest(*env.current(), tid)) == digestval); // cleanup env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); } // has digest - has uri - burnable flag { @@ -726,12 +726,14 @@ struct URIToken_test : public beast::unit_test::suite json(sfDigest.fieldName, digestval)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); BEAST_EXPECT( to_string(tokenDigest(*env.current(), tid)) == digestval); // cleanup env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); } // has uri - no flags { @@ -739,10 +741,12 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); // cleanup env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); } // has uri - burnable flag { @@ -750,10 +754,12 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri), txflags(tfBurnable)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); // cleanup env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); } // 0 amount and destination @@ -766,14 +772,23 @@ struct URIToken_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // buy env(uritoken::buy(bob, hexid), uritoken::amt(XRP(0))); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // cleanup env(uritoken::burn(bob, hexid)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); } // has amount and destination @@ -786,14 +801,23 @@ struct URIToken_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // buy env(uritoken::buy(bob, hexid), uritoken::amt(XRP(10))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // cleanup env(uritoken::burn(bob, hexid)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); } // has amount and no destination @@ -805,14 +829,23 @@ struct URIToken_test : public beast::unit_test::suite ter(tesSUCCESS)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // buy env(uritoken::buy(bob, hexid), uritoken::amt(XRP(10))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // cleanup env(uritoken::burn(bob, hexid)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); } } @@ -840,16 +873,30 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri), txflags(tfBurnable)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(XRP(1))); env.close(); + BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // bob buys env(uritoken::buy(bob, hexid), uritoken::amt(XRP(1))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice burns env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); } // issuer cannot burn { @@ -857,21 +904,33 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(XRP(1))); env.close(); // bob buys env(uritoken::buy(bob, hexid), uritoken::amt(XRP(1))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice tries to burn env(uritoken::burn(alice, hexid), ter(tecNO_PERMISSION)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // burn for test reset env(uritoken::burn(bob, hexid)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); } // owner can burn { @@ -879,18 +938,30 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(XRP(1))); + BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); env.close(); // bob buys env(uritoken::buy(bob, hexid), uritoken::amt(XRP(1))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // bob burns env(uritoken::burn(bob, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 0); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 0); } } @@ -930,6 +1001,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); BEAST_EXPECT(env.balance(alice) == preAlice - (1 * feeDrops)); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta)); @@ -939,7 +1013,10 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::buy(bob, hexid), uritoken::amt(delta)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 2); BEAST_EXPECT( env.balance(alice) == preAlice + delta - (2 * feeDrops)); BEAST_EXPECT(env.balance(bob) == preBob - delta - feeDrops); @@ -950,6 +1027,8 @@ struct URIToken_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } // bob can buy with USD { @@ -962,6 +1041,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); BEAST_EXPECT(env.balance(alice) == preAliceXrp - (1 * feeDrops)); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta)); @@ -971,7 +1053,10 @@ struct URIToken_test : public beast::unit_test::suite // bob buys env(uritoken::buy(bob, hexid), uritoken::amt(delta)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 2); BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta); BEAST_EXPECT(env.balance(alice) == preAliceXrp - (2 * feeDrops)); BEAST_EXPECT(env.balance(bob, USD.issue()) == preBob - delta); @@ -983,6 +1068,8 @@ struct URIToken_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } } @@ -1024,6 +1111,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta)); env.close(); @@ -1041,7 +1131,10 @@ struct URIToken_test : public beast::unit_test::suite // bob buys at higher price env(uritoken::buy(bob, hexid), uritoken::amt(XRP(11))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 2); BEAST_EXPECT( env.balance(alice) == preAlice + XRP(11) - (4 * feeDrops)); BEAST_EXPECT(env.balance(bob) == preBob - XRP(11) - (2 * feeDrops)); @@ -1052,6 +1145,8 @@ struct URIToken_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } // alice can sell with XRP and dest { @@ -1062,6 +1157,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta), @@ -1076,7 +1174,10 @@ struct URIToken_test : public beast::unit_test::suite // bob buys env(uritoken::buy(bob, hexid), uritoken::amt(delta)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 2); BEAST_EXPECT( env.balance(alice) == preAlice + delta - (2 * feeDrops)); BEAST_EXPECT(env.balance(bob) == preBob - delta - (1 * feeDrops)); @@ -1087,6 +1188,8 @@ struct URIToken_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } // alice can sell with USD @@ -1100,6 +1203,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta)); env.close(); @@ -1118,7 +1224,10 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::buy(bob, hexid), uritoken::amt(USD(11))); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 2); BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + USD(11)); BEAST_EXPECT(env.balance(alice) == preAliceXrp - (4 * feeDrops)); BEAST_EXPECT(env.balance(bob, USD.issue()) == preBob - USD(11)); @@ -1130,6 +1239,8 @@ struct URIToken_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } // alice can sell with USD and dest { @@ -1142,6 +1253,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta), @@ -1157,7 +1271,10 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::buy(bob, hexid), uritoken::amt(delta)); env.close(); + BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 2); BEAST_EXPECT(env.balance(alice, USD.issue()) == preAlice + delta); BEAST_EXPECT(env.balance(alice) == preAliceXrp - (2 * feeDrops)); BEAST_EXPECT(env.balance(bob, USD.issue()) == preBob - delta); @@ -1169,6 +1286,8 @@ struct URIToken_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } } @@ -1209,6 +1328,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta)); env.close(); @@ -1228,6 +1350,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } // alice can clear / reset USD amount { @@ -1236,6 +1361,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::mint(alice, uri)); env.close(); BEAST_EXPECT(inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); // alice sells env(uritoken::sell(alice, hexid), uritoken::amt(delta)); env.close(); @@ -1255,6 +1383,9 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::burn(alice, hexid)); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); + BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); + BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); + BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); } } @@ -1294,8 +1425,8 @@ struct URIToken_test : public beast::unit_test::suite BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); - // // alice sets the sell offer - // // bob sets the buy offer + // alice sets the sell offer + // bob sets the buy offer env(uritoken::buy(bob, hexid), uritoken::amt(USD(10))); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 1); @@ -1321,8 +1452,8 @@ struct URIToken_test : public beast::unit_test::suite BEAST_EXPECT(ownerDirCount(*env.current(), alice) == 2); BEAST_EXPECT(!inOwnerDir(*env.current(), bob, tid)); BEAST_EXPECT(ownerDirCount(*env.current(), bob) == 1); - // // alice sets the sell offer - // // bob sets the buy offer + // alice sets the sell offer + // bob sets the buy offer env(uritoken::buy(bob, hexid), uritoken::amt(USD(10))); env.close(); BEAST_EXPECT(!inOwnerDir(*env.current(), alice, tid)); @@ -1833,11 +1964,14 @@ struct URIToken_test : public beast::unit_test::suite auto const gw = Account{"gateway"}; auto const USD = gw["USD"]; - // test transfer rate + // Rate between 1.0 & 2.0 + std::array testCases = {{1, 1.1, 1.0005, 1.25, 2}}; + + for (auto const& tc : testCases) { Env env{*this, features}; env.fund(XRP(10000), alice, bob, gw); - env(rate(gw, 1.25)); + env(rate(gw, tc)); env.close(); env.trust(USD(100000), alice, bob); env.close(); @@ -1845,6 +1979,7 @@ struct URIToken_test : public beast::unit_test::suite env(pay(gw, bob, USD(1000))); env.close(); + auto const preAlice = env.balance(alice, USD.issue()); auto const preBob = env.balance(bob, USD.issue()); // setup mint @@ -1857,9 +1992,23 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::buy(bob, id), uritoken::amt(delta)); env.close(); - BEAST_EXPECT(env.balance(alice, USD.issue()) == USD(1125)); + auto xferRate = transferRate(*env.current(), gw); + if (!env.current()->rules().enabled(fixXahauV1)) + { + BEAST_EXPECT( + env.balance(alice, USD.issue()) == + preAlice + + multiplyRound(delta, xferRate, USD.issue(), true)); + } + else + { + BEAST_EXPECT( + env.balance(alice, USD.issue()) == + preAlice + divideRound(delta, xferRate, USD.issue(), true)); + } BEAST_EXPECT(env.balance(bob, USD.issue()) == preBob - delta); } + // test rate change { Env env{*this, features}; @@ -1882,29 +2031,16 @@ struct URIToken_test : public beast::unit_test::suite env(uritoken::sell(alice, id), uritoken::amt(delta)); env.close(); - // bob buys at higher rate and burns - env(uritoken::buy(bob, id), uritoken::amt(delta)); - env.close(); - BEAST_EXPECT(env.balance(alice, USD.issue()) == USD(10125)); - BEAST_EXPECT(env.balance(bob, USD.issue()) == preBob - delta); - env(uritoken::burn(bob, id)); - // issuer changes rate lower env(rate(gw, 1.00)); env.close(); - preBob = env.balance(bob, USD.issue()); - - // alice mints and sells - env(uritoken::mint(alice, uri)); - env(uritoken::sell(alice, id), uritoken::amt(delta)); - env.close(); - - // bob buys at lower rate + // bob buys at higher rate and burns env(uritoken::buy(bob, id), uritoken::amt(delta)); env.close(); - BEAST_EXPECT(env.balance(alice, USD.issue()) == USD(10225)); + BEAST_EXPECT(env.balance(alice, USD.issue()) == USD(10100)); BEAST_EXPECT(env.balance(bob, USD.issue()) == preBob - delta); + env(uritoken::burn(bob, id)); } // test issuer doesnt pay own rate { @@ -2377,6 +2513,7 @@ struct URIToken_test : public beast::unit_test::suite using namespace test::jtx; auto const sa = supported_amendments(); testWithFeats(sa); + testWithFeats(sa - fixXahauV1); } }; diff --git a/src/test/app/XahauGenesis_test.cpp b/src/test/app/XahauGenesis_test.cpp index a170eca9e9b..23ec46a60d8 100644 --- a/src/test/app/XahauGenesis_test.cpp +++ b/src/test/app/XahauGenesis_test.cpp @@ -4245,6 +4245,142 @@ struct XahauGenesis_test : public beast::unit_test::suite env, user, preLedger, preLedger + 1, postUser, preTime)); } + void + testValidL1WithUNLReport(FeatureBitset features) + { + using namespace jtx; + using namespace std::chrono_literals; + testcase("test claim reward valid for L1 with unl report"); + + for (bool const withXahauV1 : {true, false}) + { + FeatureBitset _features = features - featureXahauGenesis; + auto const amend = withXahauV1 ? _features : _features - fixXahauV1; + Env env{*this, envconfig(), amend}; + + double const rateDrops = 0.00333333333 * 1'000'000; + STAmount const feesXRP = XRP(1); + + // setup governance + Account const alice = Account("alice"); + Account const bob = Account("bob"); + Account const carol = Account("carol"); + Account const david = Account("david"); + Account const edward = Account("edward"); + + env.fund(XRP(10000), alice, bob, carol, david, edward); + env.close(); + + std::vector initial_members_ids{ + alice.id(), bob.id(), carol.id(), david.id(), edward.id()}; + + setupGov(env, initial_members_ids); + + // update reward delay + { + // this will be the new reward delay + // 100 + std::vector vote_data{ + 0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U}; + + updateTopic( + env, alice, bob, carol, david, edward, 'R', 'D', vote_data); + } + + // setup unl report + std::vector const _ivlKeys = { + "ED74D4036C6591A4BDF9C54CEFA39B996A5DCE5F86D11FDA1874481CE9D5A1" + "CDC1", + }; + std::vector ivlKeys; + for (auto const& strPk : _ivlKeys) + { + auto pkHex = strUnHex(strPk); + ivlKeys.emplace_back(makeSlice(*pkHex)); + } + std::vector const _vlKeys = { + "0388935426E0D08083314842EDFBB2D517BD47699F9A4527318A8E10468C97" + "C05" + "2", + "02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006" + "E3F" + "E", + "028949021029D5CC87E78BCF053AFEC0CAFD15108EC119EAAFEC466F5C0954" + "07B" + "F", + "027BAEF0CB02EA8B95F50DF4BC16C740B17B50C85F3757AA06A5DB6ADE0ED9" + "210" + "6", + "0318E0D644F3D2911D7B7E1B0B17684E7E625A6C36AECCE851BD16A4AD628B" + "213" + "6"}; + std::vector vlKeys; + for (auto const& strPk : _vlKeys) + { + auto pkHex = strUnHex(strPk); + vlKeys.emplace_back(makeSlice(*pkHex)); + } + + // activate unl report + activateUNLReport(env, ivlKeys, vlKeys); + + // validate unl report + BEAST_EXPECT(hasUNLReport(env) == true); + + // opt in claim reward + env(claimReward(alice, env.master), fee(feesXRP), ter(tesSUCCESS)); + env.close(); + + // close ledgers + validateTime(lastClose(env), 90); + for (int i = 0; i < 5; ++i) + { + env.close(10s); + } + validateTime(lastClose(env), 180); + + // define pre close values + STAmount const preAlice = env.balance(alice); + STAmount const preBob = env.balance(bob); + STAmount const preCarol = env.balance(carol); + STAmount const preDavid = env.balance(david); + STAmount const preEdward = env.balance(edward); + NetClock::time_point const preTime = lastClose(env); + std::uint32_t const preLedger = env.current()->seq(); + auto const [acct, acctSle] = + accountKeyAndSle(*env.current(), alice); + + // claim reward + auto const txResult = + withXahauV1 ? ter(tesSUCCESS) : ter(tecHOOK_REJECTED); + env(claimReward(alice, env.master), fee(feesXRP), txResult); + env.close(); + + // trigger emitted txn + env.close(); + + // calculate rewards + STAmount const netReward = + rewardUserAmount(*acctSle, preLedger, rateDrops); + STAmount const l1Reward = + withXahauV1 ? rewardL1Amount(netReward, 20) : STAmount(0); + + // validate govern rewards + BEAST_EXPECT(env.balance(bob) == preBob + l1Reward); + BEAST_EXPECT(env.balance(carol) == preCarol + l1Reward); + BEAST_EXPECT(env.balance(david) == preDavid + l1Reward); + BEAST_EXPECT(env.balance(edward) == preEdward + l1Reward); + + // validate account fields + STAmount const postAlice = preAlice + netReward + l1Reward; + bool const boolResult = withXahauV1 ? true : false; + BEAST_EXPECT( + expectAccountFields( + env, alice, preLedger, preLedger + 1, postAlice, preTime) == + boolResult); + } + } + void testOptInOptOut(FeatureBitset features) { @@ -4873,6 +5009,7 @@ struct XahauGenesis_test : public beast::unit_test::suite testInvalidBeforeTime(features); testValidWithoutUNLReport(features); testValidWithUNLReport(features); + testValidL1WithUNLReport(features); testOptInOptOut(features); testValidLowBalance(features); testValidElapsed1(features); diff --git a/src/test/jtx/escrow.h b/src/test/jtx/escrow.h index f7788e35443..3639cee982b 100644 --- a/src/test/jtx/escrow.h +++ b/src/test/jtx/escrow.h @@ -43,12 +43,18 @@ finish( jtx::Account const& from, std::uint32_t seq); +Json::Value +finish(jtx::Account const& account, jtx::Account const& from); + Json::Value cancel( jtx::Account const& account, jtx::Account const& from, std::uint32_t seq); +Json::Value +cancel(jtx::Account const& account, jtx::Account const& from); + /** Set the "FinishAfter" time tag on a JTx */ class finish_time { diff --git a/src/test/jtx/impl/escrow.cpp b/src/test/jtx/impl/escrow.cpp index 9b233329264..2fb1e22f1bc 100644 --- a/src/test/jtx/impl/escrow.cpp +++ b/src/test/jtx/impl/escrow.cpp @@ -84,6 +84,17 @@ finish(jtx::Account const& account, jtx::Account const& from, std::uint32_t seq) return jv; } +Json::Value +finish(jtx::Account const& account, jtx::Account const& from) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::EscrowFinish; + jv[jss::Flags] = tfUniversal; + jv[jss::Account] = account.human(); + jv[sfOwner.jsonName] = from.human(); + return jv; +} + Json::Value cancel(jtx::Account const& account, jtx::Account const& from, std::uint32_t seq) { @@ -96,6 +107,17 @@ cancel(jtx::Account const& account, jtx::Account const& from, std::uint32_t seq) return jv; } +Json::Value +cancel(jtx::Account const& account, jtx::Account const& from) +{ + Json::Value jv; + jv[jss::TransactionType] = jss::EscrowCancel; + jv[jss::Flags] = tfUniversal; + jv[jss::Account] = account.human(); + jv[sfOwner.jsonName] = from.human(); + return jv; +} + } // namespace escrow } // namespace jtx diff --git a/src/test/jtx/impl/offer.cpp b/src/test/jtx/impl/offer.cpp index d7c331be9bc..e1c65946550 100644 --- a/src/test/jtx/impl/offer.cpp +++ b/src/test/jtx/impl/offer.cpp @@ -51,6 +51,15 @@ offer_cancel(Account const& account, std::uint32_t offerSeq) return jv; } +Json::Value +offer_cancel(Account const& account) +{ + Json::Value jv; + jv[jss::Account] = account.human(); + jv[jss::TransactionType] = jss::OfferCancel; + return jv; +} + void offer_id::operator()(Env& env, JTx& jt) const { diff --git a/src/test/jtx/offer.h b/src/test/jtx/offer.h index 85eabede2bd..2ff9725ae6a 100644 --- a/src/test/jtx/offer.h +++ b/src/test/jtx/offer.h @@ -40,6 +40,10 @@ offer( Json::Value offer_cancel(Account const& account, std::uint32_t offerSeq); +/** Cancel an offer. */ +Json::Value +offer_cancel(Account const& account); + /** Sets the optional "OfferID" on a JTx. */ class offer_id { diff --git a/src/test/rpc/LedgerRPC_test.cpp b/src/test/rpc/LedgerRPC_test.cpp index c4b0d1eba4e..7eb4e44844f 100644 --- a/src/test/rpc/LedgerRPC_test.cpp +++ b/src/test/rpc/LedgerRPC_test.cpp @@ -1750,13 +1750,10 @@ class LedgerRPC_test : public beast::unit_test::suite env.fund(XRP(10000), "alice"); env.close(); - std::cout << to_string(env.closed()->info().hash) << "\n"; env.fund(XRP(10000), "bob"); env.close(); - std::cout << to_string(env.closed()->info().hash) << "\n"; env.fund(XRP(10000), "jim"); env.close(); - std::cout << to_string(env.closed()->info().hash) << "\n"; env.fund(XRP(10000), "jill"); {