Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ suggestWords:
- synched->synced
- synch->sync
words:
- cusip
- abempty
- AMMID
- AMMMPT
Expand Down
5 changes: 5 additions & 0 deletions include/xrpl/protocol/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ static_assert(Number::kMaxRep >= kMaxMpTokenAmount);
*/
constexpr std::size_t kMaxDataPayloadLength = 256;

/**
* The maximum length of a structured-data Schema
*/
constexpr std::size_t kMaxSchemaLength = 256;

/**
* Vault withdrawal policies
*/
Expand Down
78 changes: 78 additions & 0 deletions include/xrpl/protocol/StructuredData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once

#include <xrpl/basics/Slice.h>

#include <cstddef>
#include <cstdint>

namespace xrpl {

/**
* Structural validation for MPTStructuredData schemas and data.
*
* A schema is a sequence of one-byte type codes describing a packed
* record layout. Data is the corresponding values packed back-to-back
* in schema order: big-endian integers, VL-prefixed str/bin (standard
* XRPL variable-length encoding, byte counts), arrays as a one-byte
* element count followed by that many elements, and tuples with no
* framing of their own (the markers exist only in the schema).
*
* These functions validate structure only. No values are materialized
* and no semantic checks are performed beyond the bool 0x00/0x01 rule.
*/

/**
* Schema type codes. Codes not listed here are reserved and malformed.
*/
enum class SchemaType : std::uint8_t {
boolean = 0x01, // 1 byte, 0x00 or 0x01
u8 = 0x02, // 1 byte
u16 = 0x03, // 2 bytes, big-endian
u32 = 0x04, // 4 bytes, big-endian
u64 = 0x05, // 8 bytes, big-endian
u128 = 0x06, // 16 bytes
u256 = 0x07, // 32 bytes
xfl = 0x08, // 8 bytes, XLS-17 floating point
account = 0x09, // 20 bytes, AccountID
currency = 0x0A, // 20 bytes, 160-bit currency code
h160 = 0x0B, // 20 bytes
h256 = 0x0C, // 32 bytes
pubkey = 0x0D, // 33 bytes, compressed public key
str = 0x0E, // VL prefix + UTF-8 bytes
bin = 0x0F, // VL prefix + opaque bytes
array = 0x20, // followed by one element type; data: count byte + elements
tupleOpen = 0x30,
tupleClose = 0x31,
};

/**
* Maximum array/tuple nesting depth a schema may declare.
*/
constexpr std::size_t kMaxSchemaDepth = 8;

/**
* Check that a schema is well-formed.
*
* Well-formed means: non-empty, no longer than kMaxSchemaLength, every
* code is a known SchemaType, every array code is followed by an
* element type, every tupleOpen has a matching tupleClose, and nesting
* does not exceed kMaxSchemaDepth.
*/
[[nodiscard]] bool
isWellFormedSchema(Slice schema);

/**
* Check that data decodes exactly against a well-formed schema.
*
* Walks both inputs with two cursors. Every field must be present at
* its declared width (VL-prefixed for str/bin), array counts must be
* consistent with the remaining input, and both cursors must end
* exactly exhausted: truncated data and trailing bytes both fail.
*
* The schema is re-validated during the walk, so a malformed schema
* returns false rather than misreading data.
*/
[[nodiscard]] bool
dataMatchesSchema(Slice schema, Slice data);

} // namespace xrpl
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/features.macro
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// Add new amendments to the top of this list.
// Keep it sorted in reverse chronological order.

XRPL_FEATURE(MPTStructuredData, Supported::Yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(SmartEscrow, Supported::No, VoteBehavior::DefaultNo)
XRPL_FEATURE(LendingProtocolV1_2, Supported::No, VoteBehavior::DefaultNo)
XRPL_FIX (Cleanup3_5_0, Supported::Yes, VoteBehavior::DefaultNo)
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/ledger_entries.macro
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({
{sfOutstandingAmount, SoeRequired},
{sfLockedAmount, SoeOptional},
{sfMPTokenMetadata, SoeOptional},
{sfMPTokenSchema, SoeOptional},
{sfPreviousTxnID, SoeRequired},
{sfPreviousTxnLgrSeq, SoeRequired},
{sfDomainID, SoeOptional},
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/protocol/detail/sfields.macro
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44)
TYPED_SFIELD(sfAmountCommitment, VL, 45)
TYPED_SFIELD(sfBalanceCommitment, VL, 46)
TYPED_SFIELD(sfBytecode, VL, 47)
TYPED_SFIELD(sfMPTokenSchema, VL, 48)

// account (common)
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
Expand Down
2 changes: 2 additions & 0 deletions include/xrpl/protocol/detail/transactions.macro
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate,
{sfMPTokenMetadata, SoeOptional},
{sfDomainID, SoeOptional},
{sfImmutableFlags, SoeOptional},
{sfMPTokenSchema, SoeOptional},
}))

/** This transaction type destroys a MPTokensIssuance instance */
Expand Down Expand Up @@ -664,6 +665,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet,
{sfHolder, SoeOptional},
{sfDomainID, SoeOptional},
{sfMPTokenMetadata, SoeOptional},
{sfMPTokenSchema, SoeOptional},
{sfTransferFee, SoeOptional},
{sfImmutableFlags, SoeOptional},
{sfIssuerEncryptionKey, SoeOptional},
Expand Down
35 changes: 35 additions & 0 deletions include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,30 @@ class MPTokenIssuance : public LedgerEntryBase
return this->sle_->isFieldPresent(sfMPTokenMetadata);
}

/**
* @brief Get sfMPTokenSchema (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getMPTokenSchema() const
{
if (hasMPTokenSchema())
return this->sle_->at(sfMPTokenSchema);
return std::nullopt;
}

/**
* @brief Check if sfMPTokenSchema is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMPTokenSchema() const
{
return this->sle_->isFieldPresent(sfMPTokenSchema);
}

/**
* @brief Get sfPreviousTxnID (SoeRequired)
* @return The field value.
Expand Down Expand Up @@ -571,6 +595,17 @@ class MPTokenIssuanceBuilder : public LedgerEntryBuilderBase<MPTokenIssuanceBuil
return *this;
}

/**
* @brief Set sfMPTokenSchema (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceBuilder&
setMPTokenSchema(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfMPTokenSchema] = value;
return *this;
}

/**
* @brief Set sfPreviousTxnID (SoeRequired)
* @return Reference to this builder for method chaining.
Expand Down
37 changes: 37 additions & 0 deletions include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,32 @@ class MPTokenIssuanceCreate : public TransactionBase
{
return this->tx_->isFieldPresent(sfImmutableFlags);
}

/**
* @brief Get sfMPTokenSchema (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getMPTokenSchema() const
{
if (hasMPTokenSchema())
{
return this->tx_->at(sfMPTokenSchema);
}
return std::nullopt;
}

/**
* @brief Check if sfMPTokenSchema is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMPTokenSchema() const
{
return this->tx_->isFieldPresent(sfMPTokenSchema);
}
};

/**
Expand Down Expand Up @@ -312,6 +338,17 @@ class MPTokenIssuanceCreateBuilder : public TransactionBuilderBase<MPTokenIssuan
return *this;
}

/**
* @brief Set sfMPTokenSchema (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceCreateBuilder&
setMPTokenSchema(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfMPTokenSchema] = value;
return *this;
}

/**
* @brief Build and return the MPTokenIssuanceCreate wrapper.
* @param publicKey The public key for signing.
Expand Down
37 changes: 37 additions & 0 deletions include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,32 @@ class MPTokenIssuanceSet : public TransactionBase
return this->tx_->isFieldPresent(sfMPTokenMetadata);
}

/**
* @brief Get sfMPTokenSchema (SoeOptional)
* @return The field value, or std::nullopt if not present.
*/
[[nodiscard]]
protocol_autogen::Optional<SF_VL::type::value_type>
getMPTokenSchema() const
{
if (hasMPTokenSchema())
{
return this->tx_->at(sfMPTokenSchema);
}
return std::nullopt;
}

/**
* @brief Check if sfMPTokenSchema is present.
* @return True if the field is present, false otherwise.
*/
[[nodiscard]]
bool
hasMPTokenSchema() const
{
return this->tx_->isFieldPresent(sfMPTokenSchema);
}

/**
* @brief Get sfTransferFee (SoeOptional)
* @return The field value, or std::nullopt if not present.
Expand Down Expand Up @@ -329,6 +355,17 @@ class MPTokenIssuanceSetBuilder : public TransactionBuilderBase<MPTokenIssuanceS
return *this;
}

/**
* @brief Set sfMPTokenSchema (SoeOptional)
* @return Reference to this builder for method chaining.
*/
MPTokenIssuanceSetBuilder&
setMPTokenSchema(std::decay_t<typename SF_VL::type::value_type> const& value)
{
object_[sfMPTokenSchema] = value;
return *this;
}

/**
* @brief Set sfTransferFee (SoeOptional)
* @return Reference to this builder for method chaining.
Expand Down
7 changes: 7 additions & 0 deletions include/xrpl/tx/invariants/MPTInvariant.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class ValidMPTIssuance
*/
std::vector<std::shared_ptr<SLE const>> deletedHoldings_;

/**
* MPTokenIssuances whose sfMPTokenSchema or sfMPTokenMetadata was set or
* changed during apply. finalize() verifies the Schema is well-formed and
* the Metadata decodes against it. Gated on featureMPTStructuredData.
*/
std::vector<std::shared_ptr<SLE const>> structuredDataEntries_;

public:
/**
* @brief Track MPT issuance and holding creations, deletions, and
Expand Down
1 change: 1 addition & 0 deletions include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct MPTCreateArgs
std::optional<std::uint8_t> assetScale = std::nullopt;
std::optional<std::uint16_t> transferFee = std::nullopt;
std::optional<Slice> const& metadata{};
std::optional<Slice> const& schema{};
std::optional<uint256> domainId = std::nullopt;
std::optional<std::uint32_t> immutableFlags = std::nullopt;
// Set only by callers that issue an MPT representing a wrapped asset
Expand Down
Loading