fix: Amendment to add transaction flag checking functionality for Credentials (#5250)

CredentialCreate / CredentialAccept / CredentialDelete transactions will check sfFlags field in preflight() when the amendment is enabled.
This commit is contained in:
Olek
2025-02-10 15:33:37 -05:00
committed by GitHub
parent 81034596a8
commit fa5a85439f
6 changed files with 99 additions and 1 deletions

View File

@@ -80,7 +80,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how // Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this. // the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 87; static constexpr std::size_t numFeatures = 88;
/** Amendments that this server supports and the default voting behavior. /** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated Whether they are enabled depends on the Rules defined in the validated

View File

@@ -29,6 +29,8 @@
// If you add an amendment here, then do not forget to increment `numFeatures` // If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h. // in include/xrpl/protocol/Feature.h.
// Check flags in Credential transactions
XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo)

View File

@@ -1058,6 +1058,43 @@ struct Credentials_test : public beast::unit_test::suite
} }
} }
void
testFlags(FeatureBitset features)
{
using namespace test::jtx;
bool const enabled = features[fixInvalidTxFlags];
testcase(
std::string("Test flag, fix ") +
(enabled ? "enabled" : "disabled"));
const char credType[] = "abcde";
Account const issuer{"issuer"};
Account const subject{"subject"};
{
using namespace jtx;
Env env{*this, features};
env.fund(XRP(5000), subject, issuer);
env.close();
{
ter const expected(
enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
env(credentials::create(subject, issuer, credType),
txflags(tfTransferable),
expected);
env(credentials::accept(subject, issuer, credType),
txflags(tfSellNFToken),
expected);
env(credentials::deleteCred(subject, subject, issuer, credType),
txflags(tfPassive),
expected);
}
}
}
void void
run() override run() override
{ {
@@ -1069,6 +1106,8 @@ struct Credentials_test : public beast::unit_test::suite
testAcceptFailed(all); testAcceptFailed(all);
testDeleteFailed(all); testDeleteFailed(all);
testFeatureFailed(all - featureCredentials); testFeatureFailed(all - featureCredentials);
testFlags(all - fixInvalidTxFlags);
testFlags(all);
testRPC(); testRPC();
} }
}; };

View File

@@ -1672,6 +1672,29 @@ public:
BEAST_EXPECT(env.seq(alice) == aliceSeq + 1); BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
} }
void
test_signerListSetFlags(FeatureBitset features)
{
using namespace test::jtx;
Env env{*this, features};
Account const alice{"alice"};
env.fund(XRP(1000), alice);
env.close();
bool const enabled = features[fixInvalidTxFlags];
testcase(
std::string("SignerListSet flag, fix ") +
(enabled ? "enabled" : "disabled"));
ter const expected(enabled ? TER(temINVALID_FLAG) : TER(tesSUCCESS));
env(signers(alice, 2, {{bogie, 1}, {ghost, 1}}),
expected,
txflags(tfPassive));
env.close();
}
void void
testAll(FeatureBitset features) testAll(FeatureBitset features)
{ {
@@ -1708,6 +1731,10 @@ public:
testAll(all - featureMultiSignReserve - featureExpandedSignerList); testAll(all - featureMultiSignReserve - featureExpandedSignerList);
testAll(all - featureExpandedSignerList); testAll(all - featureExpandedSignerList);
testAll(all); testAll(all);
test_signerListSetFlags(all - fixInvalidTxFlags);
test_signerListSetFlags(all);
test_amendmentTransition(); test_amendmentTransition();
} }
}; };

View File

@@ -65,6 +65,13 @@ CredentialCreate::preflight(PreflightContext const& ctx)
auto const& tx = ctx.tx; auto const& tx = ctx.tx;
auto& j = ctx.j; auto& j = ctx.j;
if (ctx.rules.enabled(fixInvalidTxFlags) &&
(tx.getFlags() & tfUniversalMask))
{
JLOG(ctx.j.debug()) << "CredentialCreate: invalid flags.";
return temINVALID_FLAG;
}
if (!tx[sfSubject]) if (!tx[sfSubject])
{ {
JLOG(j.trace()) << "Malformed transaction: Invalid Subject"; JLOG(j.trace()) << "Malformed transaction: Invalid Subject";
@@ -209,6 +216,13 @@ CredentialDelete::preflight(PreflightContext const& ctx)
if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret; return ret;
if (ctx.rules.enabled(fixInvalidTxFlags) &&
(ctx.tx.getFlags() & tfUniversalMask))
{
JLOG(ctx.j.debug()) << "CredentialDelete: invalid flags.";
return temINVALID_FLAG;
}
auto const subject = ctx.tx[~sfSubject]; auto const subject = ctx.tx[~sfSubject];
auto const issuer = ctx.tx[~sfIssuer]; auto const issuer = ctx.tx[~sfIssuer];
@@ -289,6 +303,13 @@ CredentialAccept::preflight(PreflightContext const& ctx)
if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret; return ret;
if (ctx.rules.enabled(fixInvalidTxFlags) &&
(ctx.tx.getFlags() & tfUniversalMask))
{
JLOG(ctx.j.debug()) << "CredentialAccept: invalid flags.";
return temINVALID_FLAG;
}
if (!ctx.tx[sfIssuer]) if (!ctx.tx[sfIssuer])
{ {
JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed."; JLOG(ctx.j.trace()) << "Malformed transaction: Issuer field zeroed.";

View File

@@ -27,6 +27,8 @@
#include <xrpl/protocol/STArray.h> #include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STObject.h> #include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/STTx.h> #include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TxFlags.h>
#include <algorithm> #include <algorithm>
#include <cstdint> #include <cstdint>
@@ -81,6 +83,13 @@ SetSignerList::preflight(PreflightContext const& ctx)
if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret; return ret;
if (ctx.rules.enabled(fixInvalidTxFlags) &&
(ctx.tx.getFlags() & tfUniversalMask))
{
JLOG(ctx.j.debug()) << "SetSignerList: invalid flags.";
return temINVALID_FLAG;
}
auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j); auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
if (std::get<0>(result) != tesSUCCESS) if (std::get<0>(result) != tesSUCCESS)