mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-23 23:30:54 +00:00
Compare commits
15 Commits
pratik/ote
...
ximinez/fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c2064098 | ||
|
|
24ee731ab4 | ||
|
|
cc192b21d0 | ||
|
|
d634cd0e65 | ||
|
|
d7226aaf0e | ||
|
|
340d5a87df | ||
|
|
00aa62b68a | ||
|
|
ccf21b1abf | ||
|
|
447a86d96c | ||
|
|
5b076dabaf | ||
|
|
6981cfd146 | ||
|
|
378d9e7600 | ||
|
|
8549988476 | ||
|
|
75ece33e09 | ||
|
|
7db060a6c2 |
@@ -15,6 +15,7 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FEATURE(TecInvariant, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (Cleanup3_4_0, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(Sponsor, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(BatchV1_1, Supported::Yes, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/Fees.h>
|
||||
#include <xrpl/protocol/Keylet.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/Permissions.h>
|
||||
#include <xrpl/protocol/Rules.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
@@ -336,6 +337,10 @@ public:
|
||||
uint256 const& ticketIndex,
|
||||
beast::Journal j);
|
||||
|
||||
// Interface used by processPersistentChanges and Invariants
|
||||
static std::unordered_set<LedgerEntryType>
|
||||
typesForResult(TER const ter);
|
||||
|
||||
protected:
|
||||
TER
|
||||
apply();
|
||||
|
||||
@@ -113,6 +113,53 @@ public:
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief An unsuccessful transaction claiming a fee can only make a very small set of changes.
|
||||
*
|
||||
* 1. Reduce at most one AccountRoot or Sponsorship's XRP balance (pay a fee). (A transaction may
|
||||
* pay 0.)
|
||||
* 2. Increment one AccountRoot's sequence or delete a Ticket, not both.
|
||||
* 3. Delete expired objects, depending on the failure code.
|
||||
* For tecOVERSIZE and tecKILLED, ltOFFER
|
||||
* For tecINCOMPLETE, ltRIPPLE_STATE
|
||||
* For tecEXPIRED, ltNFTOKEN_OFFER or ltCREDENTIAL
|
||||
* 4. Modify or delete Directory Nodes, only if expired objects were deleted.
|
||||
*
|
||||
* Anything outside of that is bad.
|
||||
*
|
||||
* Collect change data for the known allowed types, and collect the info in errors_ for anything
|
||||
* else.
|
||||
*
|
||||
* Note that finalize() will always return true on a `tesSUCCESS`, even if there are messages
|
||||
* collected in errors_. The errors_ only apply if the transaction was NOT successful. It will also
|
||||
* do additional checks based on the transaction data.
|
||||
*/
|
||||
class FailedTransaction
|
||||
{
|
||||
struct DeletedEntry
|
||||
{
|
||||
SLE::const_pointer before;
|
||||
SLE::const_pointer after;
|
||||
};
|
||||
|
||||
// accountPaidFee and accountIncreasedSequence are usually the same account, but they don't have
|
||||
// to be.
|
||||
SLE::const_pointer accountPaidFee_;
|
||||
SLE::const_pointer sponsorPaidFee_;
|
||||
SLE::const_pointer accountIncreasedSequence_;
|
||||
SLE::const_pointer deletedTicket_;
|
||||
std::vector<DeletedEntry> deletedObjects_;
|
||||
std::vector<SLE::const_pointer> directorySideEffects_;
|
||||
std::vector<std::string> errors_;
|
||||
|
||||
public:
|
||||
void
|
||||
visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after);
|
||||
|
||||
[[nodiscard]] bool
|
||||
finalize(STTx const&, TER const, XRPAmount const, ReadView const&, beast::Journal const&);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Invariant: We should never charge a transaction a negative fee or a
|
||||
* fee that is larger than what the transaction itself specifies.
|
||||
@@ -432,6 +479,7 @@ private:
|
||||
// additional invariant checks can be declared above and then added to this
|
||||
// tuple
|
||||
using InvariantChecks = std::tuple<
|
||||
FailedTransaction,
|
||||
TransactionFeeCheck,
|
||||
AccountRootsNotDeleted,
|
||||
AccountRootsDeletedClean,
|
||||
|
||||
@@ -1426,6 +1426,26 @@ Transactor::trapTransaction(uint256 txHash) const
|
||||
JLOG(j_.debug()) << "Transaction trapped: " << txHash;
|
||||
}
|
||||
|
||||
std::unordered_set<LedgerEntryType>
|
||||
Transactor::typesForResult(TER const ter)
|
||||
{
|
||||
std::unordered_set<LedgerEntryType> types;
|
||||
if ((ter == tecOVERSIZE) || (ter == tecKILLED))
|
||||
{
|
||||
types.insert(ltOFFER);
|
||||
}
|
||||
else if (ter == tecINCOMPLETE)
|
||||
{
|
||||
types.insert(ltRIPPLE_STATE);
|
||||
}
|
||||
else if (ter == tecEXPIRED)
|
||||
{
|
||||
types.insert(ltNFTOKEN_OFFER);
|
||||
types.insert(ltCREDENTIAL);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
std::tuple<TER, XRPAmount, bool>
|
||||
Transactor::processPersistentChanges(TER result, XRPAmount fee)
|
||||
{
|
||||
@@ -1436,24 +1456,6 @@ Transactor::processPersistentChanges(TER result, XRPAmount fee)
|
||||
// should be used, making it possible to do more useful work
|
||||
// when transactions fail with a `tec` code.
|
||||
|
||||
auto typesForResult = [](TER const ter) {
|
||||
std::unordered_set<LedgerEntryType> types;
|
||||
if ((ter == tecOVERSIZE) || (ter == tecKILLED))
|
||||
{
|
||||
types.insert(ltOFFER);
|
||||
}
|
||||
else if (ter == tecINCOMPLETE)
|
||||
{
|
||||
types.insert(ltRIPPLE_STATE);
|
||||
}
|
||||
else if (ter == tecEXPIRED)
|
||||
{
|
||||
types.insert(ltNFTOKEN_OFFER);
|
||||
types.insert(ltCREDENTIAL);
|
||||
}
|
||||
return types;
|
||||
};
|
||||
|
||||
// Build a list of ledger entry types to collect, based on the
|
||||
// result code. Only deleted objects of these types will be
|
||||
// re-applied after the context is reset.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/XRPAmount.h>
|
||||
#include <xrpl/tx/Transactor.h>
|
||||
#include <xrpl/tx/invariants/InvariantCheckPrivilege.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -36,6 +37,7 @@
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -81,6 +83,335 @@ ledgerEntryTypeName(SLE const& sle)
|
||||
return item->getName();
|
||||
}
|
||||
|
||||
void
|
||||
FailedTransaction::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
|
||||
{
|
||||
std::ostringstream err;
|
||||
|
||||
XRPL_ASSERT(after, "xrpl::FailedTransaction::visitEntry : valid after");
|
||||
|
||||
if (!before)
|
||||
{
|
||||
// Nothing can be created by a failed transaction
|
||||
auto const name = [&after] -> std::string {
|
||||
switch (after->getType())
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996) // 4996 is the standard deprecation warning code
|
||||
#elifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
// These types are deprecated, which is why they are needed here. But builds with
|
||||
// warnings as errors (which includes CI) will fail. So to use them, deprecation
|
||||
// warnings need to be turned off for this block only.
|
||||
case ltNICKNAME:
|
||||
case ltCONTRACT:
|
||||
case ltGENERATOR_MAP:
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#elifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
return "[DEPRECATED TYPE]";
|
||||
default:
|
||||
return ledgerEntryTypeName(*after);
|
||||
}
|
||||
};
|
||||
err << "Unexpected ledger entry created: " << name() << ", " << after->key();
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Returns whether the object matched the target type. If it was, the caller may do additional
|
||||
// checks related to that type, and should return.
|
||||
auto validateFeePayer = [isDelete, &err, this](
|
||||
LedgerEntryType target,
|
||||
SLE::const_pointer& tracker,
|
||||
SLE::const_ref before,
|
||||
SLE::const_ref after,
|
||||
SF_AMOUNT const& field,
|
||||
std::string_view desc,
|
||||
std::function<std::string(SLE::const_ref)> labelMaker) {
|
||||
if (after->getType() != target)
|
||||
return false;
|
||||
|
||||
auto const label = labelMaker(after);
|
||||
|
||||
if (isDelete)
|
||||
{
|
||||
err << desc << " was deleted: " << label;
|
||||
errors_.emplace_back(err.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto const beforeOptional = before->at(~field);
|
||||
auto const afterOptional = after->at(~field);
|
||||
|
||||
auto const beforeBalance = beforeOptional.value_or(STAmount{xrpIssue()});
|
||||
auto const afterBalance = afterOptional.value_or(STAmount{xrpIssue()});
|
||||
|
||||
// Check both directions because there's a separate check for "balance increased".
|
||||
if (!canSubtract(beforeBalance, afterBalance) && !canSubtract(afterBalance, beforeBalance))
|
||||
{
|
||||
err << desc << " before and after balances not comparable: " << label;
|
||||
errors_.emplace_back(err.str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (afterBalance != beforeBalance)
|
||||
{
|
||||
if (afterBalance > beforeBalance)
|
||||
{
|
||||
err << desc << " balance increased: " << label;
|
||||
errors_.emplace_back(err.str());
|
||||
return true;
|
||||
}
|
||||
if (tracker)
|
||||
{
|
||||
err << "Multiple " << desc << "s were charged fees: " << labelMaker(tracker)
|
||||
<< " and " << label;
|
||||
errors_.emplace_back(err.str());
|
||||
return true;
|
||||
}
|
||||
tracker = after;
|
||||
}
|
||||
|
||||
auto const* format = LedgerFormats::getInstance().findByType(target);
|
||||
if (format == nullptr)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::FailedTransaction::visitEntry : account root has no known ledger format");
|
||||
return true;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
for (auto const& elem : format->getSOTemplate())
|
||||
{
|
||||
// skip this for now
|
||||
|
||||
break;
|
||||
|
||||
auto const& sf = elem.sField();
|
||||
|
||||
// No fields other than sfSequence, sfBalance, or sfFeeAmount may change
|
||||
if (&sf == &sfSequence || &sf == &sfBalance || &sf == &sfFeeAmount)
|
||||
continue;
|
||||
|
||||
auto const* bField = before->peekAtPField(sf);
|
||||
auto const* aField = after->peekAtPField(sf);
|
||||
bool const bPresent = (bField != nullptr) && bField->getSType() != STI_NOTPRESENT;
|
||||
bool const aPresent = (aField != nullptr) && aField->getSType() != STI_NOTPRESENT;
|
||||
if (bPresent != aPresent || (bPresent && aPresent && *bField != *aField))
|
||||
{
|
||||
err << "At least one " << desc << " field modified: " << label
|
||||
<< ", field: " << sf.getName()
|
||||
/*
|
||||
<< ", before: " << (bPresent ? bField->getText() : "(absent)")
|
||||
<< ", after: " << (aPresent ? aField->getText() : "(absent)")
|
||||
*/
|
||||
;
|
||||
errors_.emplace_back(err.str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (validateFeePayer(
|
||||
ltACCOUNT_ROOT,
|
||||
accountPaidFee_,
|
||||
before,
|
||||
after,
|
||||
sfBalance,
|
||||
"Account root",
|
||||
[](SLE::const_ref sle) { return to_string(sle->at(sfAccount)); }))
|
||||
{
|
||||
auto const beforeSequence = before->at(sfSequence);
|
||||
auto const afterSequence = after->at(sfSequence);
|
||||
if (afterSequence != beforeSequence)
|
||||
{
|
||||
if (afterSequence < beforeSequence)
|
||||
{
|
||||
// This is always bad, but we're only checking failed transactions here
|
||||
// TODO: Maybe add a "global failures" check to this Invariant, at the risk of scope
|
||||
// creep.
|
||||
err << "Account root sequence decreased: " << after->at(sfAccount);
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
if (accountIncreasedSequence_)
|
||||
{
|
||||
err << "Multiple Account root sequences were incremented: "
|
||||
<< accountIncreasedSequence_->at(sfAccount) << " and " << after->at(sfAccount);
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
if (afterSequence != beforeSequence + 1)
|
||||
{
|
||||
err << "Account root sequence incremented by " << (afterSequence - beforeSequence)
|
||||
<< ": " << after->at(sfAccount);
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
accountIncreasedSequence_ = after;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (validateFeePayer(
|
||||
ltSPONSORSHIP,
|
||||
sponsorPaidFee_,
|
||||
before,
|
||||
after,
|
||||
sfFeeAmount,
|
||||
"Sponsor",
|
||||
[](SLE::const_ref sle) {
|
||||
std::ostringstream l;
|
||||
l << sle->at(sfOwner) << " -> " << sle->at(sfSponsee);
|
||||
return l.str();
|
||||
}))
|
||||
return;
|
||||
|
||||
if (after->getType() == ltTICKET)
|
||||
{
|
||||
if (!isDelete)
|
||||
{
|
||||
err << "Ticket was modified: " << after->at(sfAccount) << ", "
|
||||
<< after->at(sfTicketSequence);
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
if (deletedTicket_)
|
||||
{
|
||||
err << "Multiple tickets were deleted: " << deletedTicket_->at(sfAccount) << ", "
|
||||
<< deletedTicket_->at(sfTicketSequence) << " and " << after->at(sfAccount) << ", "
|
||||
<< after->at(sfTicketSequence);
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
deletedTicket_ = after;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (after->getType() == ltDIR_NODE)
|
||||
{
|
||||
// Directory deletions and modifications are a legal side effect of deleting objects. Track
|
||||
// them separately.
|
||||
directorySideEffects_.emplace_back(after);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDelete)
|
||||
{
|
||||
switch (after->getType())
|
||||
{
|
||||
case ltOFFER:
|
||||
case ltRIPPLE_STATE:
|
||||
case ltNFTOKEN_OFFER:
|
||||
case ltCREDENTIAL:
|
||||
deletedObjects_.emplace_back(before, after);
|
||||
return;
|
||||
default:
|
||||
err << "Unexpected ledger entry deleted: " << ledgerEntryTypeName(*after) << ", "
|
||||
<< after->key();
|
||||
errors_.emplace_back(err.str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err << "Unexpected ledger entry modified: " << ledgerEntryTypeName(*after) << ", "
|
||||
<< after->key();
|
||||
errors_.emplace_back(err.str());
|
||||
}
|
||||
|
||||
bool
|
||||
FailedTransaction::finalize(
|
||||
STTx const& tx,
|
||||
TER const ter,
|
||||
XRPAmount const fee,
|
||||
ReadView const& view,
|
||||
beast::Journal const& j)
|
||||
{
|
||||
if (isTesSuccess(ter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
// Log all the failures regardless of amendment status, but only return false / failure if
|
||||
// featureTecInvariant is enabled
|
||||
for (auto const& err : errors_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: " << err;
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (accountPaidFee_ && sponsorPaidFee_)
|
||||
{
|
||||
// Is this legal?
|
||||
JLOG(j.fatal()) << "Invariant failed: both account and sponsor paid fee";
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (accountIncreasedSequence_ && deletedTicket_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: both account sequence increased and ticket deleted";
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (tx.getSeqProxy().isTicket() && accountIncreasedSequence_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: account sequence increased by a ticket transaction";
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (tx.getSeqProxy().isSeq() && deletedTicket_)
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: ticket deleted by a sequence transaction";
|
||||
result = false;
|
||||
}
|
||||
|
||||
auto const typesAllowedToDelete = Transactor::typesForResult(ter);
|
||||
|
||||
for (auto const& deleted : deletedObjects_)
|
||||
{
|
||||
auto const type = deleted.after->getType();
|
||||
if (!typesAllowedToDelete.contains(type))
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: unexpected ledger entry deleted: "
|
||||
<< ledgerEntryTypeName(*deleted.after);
|
||||
result = false;
|
||||
}
|
||||
// For offers, only allow unfunded removals
|
||||
// (where TakerPays is unchanged)
|
||||
if (type == ltOFFER &&
|
||||
deleted.before->getFieldAmount(sfTakerPays) !=
|
||||
deleted.after->getFieldAmount(sfTakerPays))
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: funded offer deleted: "
|
||||
<< ledgerEntryTypeName(*deleted.after);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!deletedTicket_ && deletedObjects_.empty() && !directorySideEffects_.empty())
|
||||
{
|
||||
JLOG(j.fatal()) << "Invariant failed: " << directorySideEffects_.size()
|
||||
<< " directory side effects without any deleted objects";
|
||||
result = false;
|
||||
}
|
||||
|
||||
bool const enforce = view.rules().enabled(featureTecInvariant);
|
||||
XRPL_ASSERT_IF(!result, enforce, "FailedTransaction::finalize : amendment enabled");
|
||||
return result || !enforce;
|
||||
}
|
||||
|
||||
void
|
||||
TransactionFeeCheck::visitEntry(bool, SLE::const_ref, SLE::const_ref)
|
||||
{
|
||||
|
||||
@@ -431,7 +431,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
STTx{ttACCOUNT_DELETE, [](STObject& tx) {}});
|
||||
|
||||
doInvariantCheck(
|
||||
Env{*this, FeatureBitset{featureSponsor}},
|
||||
Env{*this, FeatureBitset{featureSponsor, featureTecInvariant}},
|
||||
{{"account deletion left behind a sponsorship field"}},
|
||||
[&](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
auto const sleA1 = ac.view().peek(keylet::account(a1.id()));
|
||||
@@ -1507,7 +1507,9 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
bool const fixEnabled = features[fixCleanup3_1_3];
|
||||
bool const fix313Enabled = features[fixCleanup3_1_3];
|
||||
bool const fixTecEnabled = features[featureTecInvariant];
|
||||
bool const fixEnabled = fix313Enabled || fixTecEnabled;
|
||||
std::initializer_list<TER> const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
|
||||
@@ -1709,7 +1711,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
testcase << "PermissionedDomain set 2 domains ";
|
||||
doInvariantCheck(
|
||||
makeEnv(features),
|
||||
fixEnabled ? badMoreThan1 : emptyV,
|
||||
fix313Enabled ? badMoreThan1 : emptyV,
|
||||
[](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, a1, a2);
|
||||
createPermissionedDomain(ac, a1, a2, 2, 11);
|
||||
@@ -1717,7 +1719,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixEnabled ? failTers : goodTers);
|
||||
fix313Enabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1738,7 +1740,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
std::move(env1),
|
||||
a1,
|
||||
a2,
|
||||
fixEnabled ? badMoreThan1 : emptyV,
|
||||
fix313Enabled ? badMoreThan1 : emptyV,
|
||||
[&pd1, &pd2](Account const&, Account const&, ApplyContext& ac) {
|
||||
auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1});
|
||||
auto sle2 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd2});
|
||||
@@ -1748,18 +1750,18 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixEnabled ? failTers : goodTers);
|
||||
fix313Enabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain set 0 domains ";
|
||||
doInvariantCheck(
|
||||
makeEnv(features),
|
||||
fixEnabled ? badNoDomains : emptyV,
|
||||
fix313Enabled ? badNoDomains : emptyV,
|
||||
[](Account const&, Account const&, ApplyContext&) { return true; },
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixEnabled ? badTers : goodTers);
|
||||
fix313Enabled ? badTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1780,11 +1782,11 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
makeEnv(features),
|
||||
a1,
|
||||
a2,
|
||||
fixEnabled ? badNoDomains : emptyV,
|
||||
fix313Enabled ? badNoDomains : emptyV,
|
||||
[](Account const&, Account const&, ApplyContext&) { return true; },
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixEnabled ? badTers : goodTers);
|
||||
fix313Enabled ? badTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -1804,7 +1806,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
std::move(env1),
|
||||
a1,
|
||||
a2,
|
||||
fixEnabled ? badDeleted : emptyV,
|
||||
fix313Enabled ? badDeleted : emptyV,
|
||||
[&pd1](Account const&, Account const&, ApplyContext& ac) {
|
||||
auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1});
|
||||
ac.view().erase(sle1);
|
||||
@@ -1812,28 +1814,28 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}},
|
||||
fixEnabled ? failTers : goodTers);
|
||||
fix313Enabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain del, create domain ";
|
||||
doInvariantCheck(
|
||||
makeEnv(features),
|
||||
fixEnabled ? badNotDeleted : emptyV,
|
||||
fix313Enabled ? badNotDeleted : emptyV,
|
||||
[](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, a1, a2);
|
||||
return true;
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}},
|
||||
fixEnabled ? failTers : goodTers);
|
||||
fix313Enabled ? failTers : goodTers);
|
||||
}
|
||||
|
||||
{
|
||||
testcase << "PermissionedDomain invalid tx";
|
||||
|
||||
doInvariantCheck(
|
||||
fixEnabled ? badTx : emptyV,
|
||||
fix313Enabled ? badTx : emptyV,
|
||||
[&](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
createPermissionedDomain(ac, a1, a2);
|
||||
return true;
|
||||
@@ -2010,7 +2012,13 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
{
|
||||
using namespace test::jtx;
|
||||
|
||||
bool const fixEnabled = features[fixCleanup3_1_3];
|
||||
bool const fix313Enabled = features[fixCleanup3_1_3];
|
||||
bool const fixTecEnabled = features[featureTecInvariant];
|
||||
bool const fixEnabled = fix313Enabled || fixTecEnabled;
|
||||
|
||||
std::initializer_list<TER> const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const goodTers = {tesSUCCESS, tesSUCCESS};
|
||||
|
||||
testcase << "PermissionedDEX" + std::string(fixEnabled ? " fix" : "");
|
||||
|
||||
@@ -2038,7 +2046,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx.setFieldAmount(sfTakerPays, a1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
|
||||
// missing domain ID in offer object
|
||||
doInvariantCheck(
|
||||
@@ -2060,7 +2068,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
|
||||
// more than one entry in sfAdditionalBooks
|
||||
{
|
||||
@@ -2097,7 +2105,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
}
|
||||
|
||||
// empty sfAdditionalBooks (size 0)
|
||||
@@ -2112,12 +2120,19 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
[[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, a1, a2);
|
||||
env1.close();
|
||||
|
||||
auto const expectedTers = std::invoke([&] {
|
||||
if (!fix313Enabled)
|
||||
return goodTers;
|
||||
if (fixTecEnabled)
|
||||
return failTers;
|
||||
return badTers;
|
||||
});
|
||||
doInvariantCheck(
|
||||
std::move(env1),
|
||||
a1,
|
||||
a2,
|
||||
fixEnabled ? std::vector<std::string>{{"hybrid offer is malformed"}}
|
||||
: std::vector<std::string>{},
|
||||
fix313Enabled ? std::vector<std::string>{{"hybrid offer is malformed"}}
|
||||
: std::vector<std::string>{},
|
||||
[&pd1](Account const& a1, Account const& a2, ApplyContext& ac) {
|
||||
Keylet const offerKey = keylet::offer(a2.id(), SeqProxy::rawSequence(10));
|
||||
auto sleOffer = std::make_shared<SLE>(offerKey);
|
||||
@@ -2134,8 +2149,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
fixEnabled ? std::initializer_list<TER>{tecINVARIANT_FAILED, tecINVARIANT_FAILED}
|
||||
: std::initializer_list<TER>{tesSUCCESS, tesSUCCESS});
|
||||
expectedTers);
|
||||
}
|
||||
|
||||
// hybrid offer missing sfAdditionalBooks
|
||||
@@ -2168,7 +2182,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttOFFER_CREATE, [&](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2206,7 +2220,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx.setFieldAmount(sfTakerPays, a1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -2243,7 +2257,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx.setFieldAmount(sfTakerPays, a1["USD"](10));
|
||||
tx.setFieldAmount(sfTakerGets, XRP(1));
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2975,6 +2989,12 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
return true;
|
||||
};
|
||||
|
||||
// Most of these tests use default Env, so fixTecInvaraint will always be enabled.
|
||||
// If it's not, the invariant will usually assert.
|
||||
bool const fixTecEnabled = true;
|
||||
std::initializer_list<TER> const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
|
||||
testcase << "Vault general checks";
|
||||
doInvariantCheck(
|
||||
{"vault deletion succeeded without deleting a vault"},
|
||||
@@ -2988,7 +3008,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DELETE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, _] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3029,7 +3049,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPAYMENT, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, _] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3052,7 +3072,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPAYMENT, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
|
||||
doInvariantCheck(
|
||||
{"vault deleted by a wrong transaction type",
|
||||
@@ -3132,7 +3152,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
|
||||
doInvariantCheck(
|
||||
{"deleted vault must also delete shares",
|
||||
@@ -3200,7 +3220,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3309,7 +3329,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3352,7 +3372,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3369,7 +3389,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -3385,7 +3405,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -3401,7 +3421,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -3416,7 +3436,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3433,7 +3453,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
XRPAmount{},
|
||||
STTx{
|
||||
ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3451,7 +3471,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttLOAN_MANAGE, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3482,7 +3502,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3496,7 +3516,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3519,7 +3539,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3540,7 +3560,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3584,7 +3604,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3610,7 +3630,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3637,7 +3657,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3665,7 +3685,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3689,7 +3709,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3717,7 +3737,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Vault const vault{env};
|
||||
auto [tx, keylet] = vault.create({.owner = a1, .asset = xrpIssue()});
|
||||
@@ -3781,7 +3801,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_SET, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tefINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
|
||||
doInvariantCheck(
|
||||
{"shares issuer and vault pseudo-account must be the same",
|
||||
@@ -3841,7 +3861,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CREATE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tefINVARIANT_FAILED});
|
||||
fixTecEnabled ? failTers : badTers);
|
||||
|
||||
doInvariantCheck(
|
||||
{"shares issuer and vault pseudo-account must be the same", "shares issuer must exist"},
|
||||
@@ -3895,7 +3915,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -3909,7 +3929,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
XRPAmount{},
|
||||
STTx{
|
||||
ttVAULT_DEPOSIT, [](STObject& tx) { tx.setFieldAmount(sfAmount, XRPAmount(200)); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3940,7 +3960,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx[sfFee] = XRPAmount(100);
|
||||
tx[sfAccount] = a3.id();
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -3966,7 +3986,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -3988,7 +4008,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4002,7 +4022,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4017,7 +4037,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4035,7 +4055,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(5); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4059,7 +4079,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx[sfDelegate] = a3.id();
|
||||
tx[sfFee] = XRPAmount(2000);
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4075,7 +4095,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4090,7 +4110,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
// Almost identical to the really convoluted test for deposit, where the
|
||||
@@ -4122,7 +4142,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
// This commented out line causes the invariant violation.
|
||||
// tx[sfDestination] = A4.id();
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -4150,7 +4170,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4171,7 +4191,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [&](STObject& tx) { tx.setAccountID(sfDestination, a3.id()); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4185,7 +4205,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4199,7 +4219,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4216,7 +4236,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4232,7 +4252,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4256,7 +4276,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx[sfDelegate] = a3.id();
|
||||
tx[sfFee] = XRPAmount(2000);
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4319,7 +4339,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [&](STObject& tx) { tx[sfAccount] = a3.id(); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseMpt,
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4335,7 +4355,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CLAWBACK, [&](STObject& tx) { tx[sfAccount] = a3.id(); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseMpt);
|
||||
|
||||
// Not the same as below check: attempt to clawback XRP
|
||||
@@ -4347,7 +4367,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CLAWBACK, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseXrp);
|
||||
|
||||
// Not the same as above check: attempt to clawback MPT by bad account
|
||||
@@ -4360,7 +4380,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_CLAWBACK, [&](STObject& tx) { tx[sfAccount] = a4.id(); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseMpt);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -4381,7 +4401,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx[sfAccount] = a3.id();
|
||||
tx[sfHolder] = a4.id();
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseMpt);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -4400,7 +4420,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx[sfAccount] = a3.id();
|
||||
tx[sfHolder] = a4.id();
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseMpt);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -4423,7 +4443,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
tx[sfAccount] = a3.id();
|
||||
tx[sfHolder] = a4.id();
|
||||
}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseMpt);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
@@ -4597,7 +4617,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_DEPOSIT, [](STObject& tx) { tx[sfAmount] = XRPAmount(10); }},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseClosedEnded(/*advanceBySub=*/1, /*doDeposit=*/true),
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4612,7 +4632,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttVAULT_WITHDRAW, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseClosedEnded(/*advanceBySub=*/1, /*doDeposit=*/true),
|
||||
TxAccount::A2);
|
||||
|
||||
@@ -4631,7 +4651,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttLOAN_SET, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseClosedEnded(/*advanceBySub=*/-1, /*doDeposit=*/false));
|
||||
|
||||
testcase << "Vault loan set - closed-ended final payment past "
|
||||
@@ -4679,7 +4699,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttLOAN_SET, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const&, Env& env) -> bool {
|
||||
auto const sub = env.now().time_since_epoch().count() + 60;
|
||||
auto const red = sub + kMinInvestmentPeriod + 1'000'000;
|
||||
@@ -4811,6 +4831,12 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
return true;
|
||||
});
|
||||
|
||||
// Most of these tests use default Env, so fixTecInvaraint will always be enabled.
|
||||
// If it's not, the invariant will usually assert.
|
||||
bool const fixTecEnabled = true;
|
||||
std::initializer_list<TER> const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
|
||||
// Overflow/Invalid balance on payment
|
||||
auto testPayment = [&](std::string const& log, auto&& update) {
|
||||
MPTID id;
|
||||
@@ -4821,7 +4847,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttPAYMENT, [](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
[&](Account const& a1, Account const& a2, Env& env) {
|
||||
Account const gw("gw");
|
||||
env.fund(XRP(1'000), gw);
|
||||
@@ -5761,16 +5787,13 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
return true;
|
||||
};
|
||||
|
||||
auto test = [&](auto const txType,
|
||||
auto&& update,
|
||||
bool isMPT,
|
||||
TER error = tecINVARIANT_FAILED) {
|
||||
auto test = [&](auto const txType, auto&& update, bool isMPT) {
|
||||
doInvariantCheck(
|
||||
{{"AMM"}},
|
||||
[&](Account const&, Account const&, ApplyContext& ac) { return update(ac, isMPT); },
|
||||
XRPAmount{},
|
||||
STTx{txType, [&](STObject& tx) {}},
|
||||
{tecINVARIANT_FAILED, error},
|
||||
{tecINVARIANT_FAILED, tefINVARIANT_FAILED},
|
||||
[&](Account const&, Account const&, Env& env) {
|
||||
env.fund(XRP(1'000), gw);
|
||||
poolAsset = [&]() -> PrettyAsset {
|
||||
@@ -5792,16 +5815,15 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
|
||||
for (bool const isMPT : {false, true})
|
||||
{
|
||||
auto const error = isMPT ? TER(tecINVARIANT_FAILED) : TER(tefINVARIANT_FAILED);
|
||||
for (auto txType : {ttAMM_CREATE, ttAMM_DEPOSIT, ttAMM_CLAWBACK, ttAMM_WITHDRAW})
|
||||
{
|
||||
test(txType, deleteAMMAccount, isMPT, tefINVARIANT_FAILED);
|
||||
test(txType, deleteAMMAccount, isMPT);
|
||||
test(txType, updateLPTokensBadAmount, isMPT);
|
||||
test(txType, updateLPTokensBadBalance, isMPT);
|
||||
}
|
||||
for (auto txType : {ttAMM_BID, ttAMM_VOTE})
|
||||
{
|
||||
test(txType, updateAMMPool, isMPT, error);
|
||||
test(txType, updateAMMPool, isMPT);
|
||||
test(txType, updateLPTokensBadAmount, isMPT);
|
||||
test(txType, updateLPTokensBadBalance, isMPT);
|
||||
}
|
||||
@@ -6331,6 +6353,12 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
return true;
|
||||
};
|
||||
|
||||
// Most of these tests use default Env, so fixTecInvaraint will always be enabled.
|
||||
// If it's not, the invariant will usually assert.
|
||||
bool const fixTecEnabled = true;
|
||||
std::initializer_list<TER> const badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED};
|
||||
std::initializer_list<TER> const failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED};
|
||||
|
||||
// badDelete
|
||||
doInvariantCheck(
|
||||
{"MPToken deleted with encrypted fields while COA > 0"},
|
||||
@@ -6361,7 +6389,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
doInvariantCheck(
|
||||
@@ -6379,7 +6407,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
// requiresPrivacyFlag
|
||||
@@ -6412,7 +6440,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseNoPrivacy);
|
||||
|
||||
// badCOA
|
||||
@@ -6429,7 +6457,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttMPTOKEN_ISSUANCE_SET, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
// Conservation Violation
|
||||
@@ -6449,7 +6477,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
// Send/MergeInbox must not change OutstandingAmount (coaDelta == 0)
|
||||
@@ -6468,7 +6496,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttCONFIDENTIAL_MPT_SEND, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
// Send/MergeInbox and zero-COA-delta confidential transactions must not
|
||||
@@ -6486,7 +6514,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttCONFIDENTIAL_MPT_SEND, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
// badVersion
|
||||
@@ -6506,7 +6534,7 @@ class Invariants_test : public beast::unit_test::Suite
|
||||
},
|
||||
XRPAmount{},
|
||||
STTx{ttMPTOKEN_AUTHORIZE, [](STObject&) {}},
|
||||
{tecINVARIANT_FAILED, tecINVARIANT_FAILED},
|
||||
fixTecEnabled ? failTers : badTers,
|
||||
precloseConfidential);
|
||||
|
||||
// Skipping Deleted MPTs (Issuance deleted)
|
||||
@@ -6567,8 +6595,15 @@ public:
|
||||
testNFTokenPageInvariants();
|
||||
testAMMDeleteInvariants(defaultAmendments());
|
||||
testAMMDeleteInvariants(defaultAmendments() - fixCleanup3_3_0);
|
||||
testPermissionedDomainInvariants(defaultAmendments() | fixCleanup3_1_3);
|
||||
testPermissionedDomainInvariants(defaultAmendments() - fixCleanup3_1_3);
|
||||
testPermissionedDomainInvariants(
|
||||
defaultAmendments() | fixCleanup3_1_3 | featureTecInvariant);
|
||||
testPermissionedDomainInvariants(
|
||||
(defaultAmendments() - fixCleanup3_1_3) | featureTecInvariant);
|
||||
// Can't test without featureTecInvariant, because the invariant will assert
|
||||
// testPermissionedDomainInvariants((defaultAmendments() | fixCleanup3_1_3) -
|
||||
// featureTecInvariant );
|
||||
// testPermissionedDomainInvariants((defaultAmendments() - fixCleanup3_1_3) -
|
||||
// featureTecInvariant);
|
||||
testPermissionedDEX(defaultAmendments() | fixCleanup3_1_3);
|
||||
testPermissionedDEX(defaultAmendments() - fixCleanup3_1_3);
|
||||
testBookDirectoryExchangeRate();
|
||||
|
||||
Reference in New Issue
Block a user