Compare commits

...

1 Commits

Author SHA1 Message Date
Bronek Kozicki
c365da1208 WIP 2025-07-17 11:57:18 +01:00
9 changed files with 68 additions and 1 deletions

View File

@@ -187,6 +187,7 @@ enum LedgerSpecificFlags {
lsfMPTCanTrade = 0x00000010,
lsfMPTCanTransfer = 0x00000020,
lsfMPTCanClawback = 0x00000040,
lsfMPTMutableMeta = 0x00000080,
// ltMPTOKEN
lsfMPTAuthorized = 0x00000002,

View File

@@ -148,8 +148,9 @@ constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow;
constexpr std::uint32_t const tfMPTCanTrade = lsfMPTCanTrade;
constexpr std::uint32_t const tfMPTCanTransfer = lsfMPTCanTransfer;
constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback;
constexpr std::uint32_t const tfMPTMutableMeta = lsfMPTMutableMeta;
constexpr std::uint32_t const tfMPTokenIssuanceCreateMask =
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback);
~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback | tfMPTMutableMeta);
// MPTokenAuthorize flags:
constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001;

View File

@@ -35,6 +35,7 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FEATURE(MPTMutableMeta, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -420,6 +420,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::
TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegatable, ({
{sfMPTokenIssuanceID, soeREQUIRED},
{sfHolder, soeOPTIONAL},
{sfMPTokenMetadata, soeOPTIONAL},
}))
/** This transaction type authorizes a MPToken instance */

View File

@@ -22,6 +22,7 @@
#include <test/jtx/xchain_bridge.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
@@ -86,6 +87,14 @@ class MPToken_test : public beast::unit_test::suite
.metadata = "",
.err = temMALFORMED});
// oversized metadata returns error
mptAlice.create(
{.maxAmt = 100,
.assetScale = 0,
.transferFee = 0,
.metadata = std::string(2050, 'B'),
.err = temMALFORMED});
// MaximumAmout of 0 returns error
mptAlice.create(
{.maxAmt = 0,
@@ -108,6 +117,29 @@ class MPToken_test : public beast::unit_test::suite
.metadata = "test",
.err = temMALFORMED});
}
// test preflight tfMPTMutableMeta when feature is disabled
{
Env env{*this, features - featureMPTMutableMeta};
MPTTester mptAlice(env, alice);
mptAlice.create(
{.maxAmt = 100,
.assetScale = 0,
.transferFee = 0,
.flags = tfMPTMutableMeta,
.err = temDISABLED}
);
mptAlice.create(
{.maxAmt = 100,
.assetScale = 0,
.transferFee = 0,
.metadata = "abc",
.flags = tfMPTMutableMeta,
.err = temDISABLED}
);
}
}
void
@@ -512,6 +544,14 @@ class MPToken_test : public beast::unit_test::suite
.holder = alice,
.flags = tfMPTLock,
.err = temMALFORMED});
// Oversized metadata
mptAlice.set(
{.account = alice,
.holder = alice,
.metadata = std::string(2050, 'B'),
.flags = tfMPTLock,
.err = temMALFORMED});
}
// Validate fields in MPTokenIssuanceSet (preclaim)

View File

@@ -19,6 +19,7 @@
#include <test/jtx.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
@@ -231,6 +232,8 @@ MPTTester::set(MPTSet const& arg)
Throw<std::runtime_error>("MPT has not been created");
jv[sfMPTokenIssuanceID] = to_string(*id_);
}
if (arg.metadata)
jv[sfMPTokenMetadata] = strHex(*arg.metadata);
if (arg.holder)
jv[sfHolder] = arg.holder->human();
if (arg.delegate)

View File

@@ -135,6 +135,7 @@ struct MPTSet
std::optional<Account> account = std::nullopt;
std::optional<Account> holder = std::nullopt;
std::optional<MPTID> id = std::nullopt;
std::optional<std::string> metadata = std::nullopt;
std::optional<std::uint32_t> ownerCount = std::nullopt;
std::optional<std::uint32_t> holderCount = std::nullopt;
std::optional<std::uint32_t> flags = std::nullopt;

View File

@@ -30,6 +30,8 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureMPTokensV1))
return temDISABLED;
if (ctx.tx.isFlag(tfMPTMutableMeta) && !ctx.rules.enabled(featureMPTMutableMeta))
return temDISABLED;
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;

View File

@@ -21,6 +21,9 @@
#include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
namespace ripple {
@@ -30,6 +33,8 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
{
if (!ctx.rules.enabled(featureMPTokensV1))
return temDISABLED;
if (ctx.tx.isFieldPresent(sfMPTokenMetadata) && !ctx.rules.enabled(featureMPTMutableMeta))
return temDISABLED;
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
return ret;
@@ -43,6 +48,13 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx)
else if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock))
return temINVALID_FLAG;
if (auto const metadata = ctx.tx[~sfMPTokenMetadata])
{
// Note: metadata->length() == 0 is valid here, will erase metatdata
if (metadata->length() > maxMPTokenMetadataLength)
return temMALFORMED;
}
auto const accountID = ctx.tx[sfAccount];
auto const holderID = ctx.tx[~sfHolder];
if (holderID && accountID == holderID)
@@ -97,6 +109,11 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
if (!sleMptIssuance)
return tecOBJECT_NOT_FOUND;
if (ctx.tx.isFieldPresent(sfMPTokenMetadata) && !sleMptIssuance->isFlag(lsfMPTMutableMeta))
return tecNO_PERMISSION;
// TODO check below is invalid if we are trying to update metadata
// if the mpt has disabled locking
if (!((*sleMptIssuance)[sfFlags] & lsfMPTCanLock))
return tecNO_PERMISSION;