mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-23 23:20:33 +00:00
fix: Update PermissionedDEX invariant domain tracking for valid offer replacement (#7387)
Co-authored-by: Bart <bthomee@users.noreply.github.com>
This commit is contained in:
@@ -17,7 +17,8 @@ class ValidPermissionedDEX
|
||||
bool regularOffers_ = false; // post-fixCleanup3_2_0: excludes deleted offers
|
||||
bool badHybridsOld_ = false; // pre-fixCleanup3_1_3: missing field/domain or size > 1
|
||||
bool badHybrids_ = false; // post-fixCleanup3_1_3: also catches size == 0 (size != 1)
|
||||
hash_set<uint256> domains_;
|
||||
hash_set<uint256> domainsOld_; // pre-fixCleanup3_4_0: also flags deleted domains
|
||||
hash_set<uint256> domains_; // post-fixCleanup3_4_0: excludes deleted domains
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <xrpl/tx/invariants/PermissionedDEXInvariant.h>
|
||||
|
||||
#include <xrpl/basics/Log.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
@@ -19,17 +20,23 @@ namespace xrpl {
|
||||
void
|
||||
ValidPermissionedDEX::visitEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after)
|
||||
{
|
||||
auto trackDomain = [this, isDelete](uint256 const& domain) {
|
||||
domainsOld_.insert(domain);
|
||||
if (!isDelete)
|
||||
domains_.insert(domain);
|
||||
};
|
||||
|
||||
if (after && after->getType() == ltDIR_NODE)
|
||||
{
|
||||
if (after->isFieldPresent(sfDomainID))
|
||||
domains_.insert(after->getFieldH256(sfDomainID));
|
||||
trackDomain(after->getFieldH256(sfDomainID));
|
||||
}
|
||||
|
||||
if (after && after->getType() == ltOFFER)
|
||||
{
|
||||
if (after->isFieldPresent(sfDomainID))
|
||||
{
|
||||
domains_.insert(after->getFieldH256(sfDomainID));
|
||||
trackDomain(after->getFieldH256(sfDomainID));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -87,7 +94,8 @@ ValidPermissionedDEX::finalize(
|
||||
|
||||
// for both payment and offercreate, there shouldn't be another domain
|
||||
// that's different from the domain specified
|
||||
for (auto const& d : domains_)
|
||||
auto const& domains = view.rules().enabled(fixCleanup3_4_0) ? domains_ : domainsOld_;
|
||||
for (auto const& d : domains)
|
||||
{
|
||||
if (d != domain)
|
||||
{
|
||||
|
||||
@@ -2001,6 +2001,61 @@ class PermissionedDEX_test : public beast::unit_test::Suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testReplaceDomainOfferWithOtherDomainOffer(FeatureBitset features)
|
||||
{
|
||||
bool const fixEnabled = features[fixCleanup3_4_0];
|
||||
|
||||
testcase << "Replace domain offer via OfferCreate"
|
||||
<< (fixEnabled ? " (fixCleanup3_4_0 enabled)" : " (fixCleanup3_4_0 disabled)");
|
||||
|
||||
Env env(*this, features);
|
||||
auto const& [gw, domainOwner, alice, bob, carol, USD, domainA, credType] =
|
||||
PermissionedDEX(env);
|
||||
|
||||
Account const domainOwnerB("permdex-domainOwnerB");
|
||||
auto const domainB =
|
||||
setupDomain(env, {alice, bob, carol, gw}, domainOwnerB, "permdex-other-domain");
|
||||
BEAST_EXPECT(domainA != domainB);
|
||||
|
||||
auto const oldSeq = env.seq(alice);
|
||||
env(offer(alice, USD(100), XRP(1)), Domain(domainA));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(checkOffer(env, alice, oldSeq, USD(100), XRP(1), 0, true));
|
||||
auto const oldOffer = env.le(keylet::offer(alice.id(), oldSeq));
|
||||
if (!BEAST_EXPECT(oldOffer))
|
||||
return;
|
||||
BEAST_EXPECT(oldOffer->getFieldH256(sfDomainID) == domainA);
|
||||
|
||||
auto const newSeq = env.seq(alice);
|
||||
// The invariant should reject mixing active Permissioned DEX domains,
|
||||
// not a domain that is only touched because its offer is being deleted.
|
||||
if (fixEnabled)
|
||||
{
|
||||
env(offer(alice, USD(100), XRP(2)), Domain(domainB), Json(jss::OfferSequence, oldSeq));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(!offerExists(env, alice, oldSeq));
|
||||
BEAST_EXPECT(checkOffer(env, alice, newSeq, USD(100), XRP(2), 0, true));
|
||||
auto const newOffer = env.le(keylet::offer(alice.id(), newSeq));
|
||||
if (!BEAST_EXPECT(newOffer))
|
||||
return;
|
||||
BEAST_EXPECT(newOffer->getFieldH256(sfDomainID) == domainB);
|
||||
}
|
||||
else
|
||||
{
|
||||
env(offer(alice, USD(100), XRP(2)),
|
||||
Domain(domainB),
|
||||
Json(jss::OfferSequence, oldSeq),
|
||||
Ter(tecINVARIANT_FAILED));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(checkOffer(env, alice, oldSeq, USD(100), XRP(1), 0, true));
|
||||
BEAST_EXPECT(!offerExists(env, alice, newSeq));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -2038,6 +2093,8 @@ public:
|
||||
// only after fixCleanup3_2_0.
|
||||
testCancelRegularOfferWithDomainCreate(all);
|
||||
testCancelRegularOfferWithDomainCreate(all - fixCleanup3_2_0);
|
||||
testReplaceDomainOfferWithOtherDomainOffer(all);
|
||||
testReplaceDomainOfferWithOtherDomainOffer(all - fixCleanup3_4_0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user