diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index b1708e0b1..4658f3348 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -45,6 +45,7 @@ #include #include #include +#include namespace ripple { @@ -166,6 +167,8 @@ invoke_preflight(PreflightContext const& ctx) return invoke_preflight_helper(ctx); case ttREMIT: return invoke_preflight_helper(ctx); + case ttREMARKS_SET: + return invoke_preflight_helper(ctx); case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: @@ -287,6 +290,8 @@ invoke_preclaim(PreclaimContext const& ctx) return invoke_preclaim(ctx); case ttREMIT: return invoke_preclaim(ctx); + case ttREMARKS_SET: + return invoke_preclaim(ctx); case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: @@ -370,6 +375,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx) return Invoke::calculateBaseFee(view, tx); case ttREMIT: return Remit::calculateBaseFee(view, tx); + case ttREMARKS_SET: + return Remarks::calculateBaseFee(view, tx); case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: @@ -553,6 +560,10 @@ invoke_apply(ApplyContext& ctx) Remit p(ctx); return p(); } + case ttREMARKS_SET: { + Remarks p(ctx); + return p(); + } case ttURITOKEN_MINT: case ttURITOKEN_BURN: case ttURITOKEN_BUY: diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 2d46df876..0dfe87121 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -74,7 +74,7 @@ namespace detail { // 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 // the actual number of amendments. A LogicError on startup will verify this. -static constexpr std::size_t numFeatures = 70; +static constexpr std::size_t numFeatures = 71; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -358,6 +358,7 @@ extern uint256 const fixXahauV2; extern uint256 const featureRemit; extern uint256 const featureZeroB2M; extern uint256 const fixNSDelete; +extern uint256 const featureRemarks; } // namespace ripple diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index 1f9d15368..5a59df960 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -458,6 +458,7 @@ extern SF_UINT256 const sfNFTokenID; extern SF_UINT256 const sfEmitParentTxnID; extern SF_UINT256 const sfEmitNonce; extern SF_UINT256 const sfEmitHookHash; +extern SF_UINT256 const sfObjectID; // 256-bit (uncommon) extern SF_UINT256 const sfBookDirectory; diff --git a/src/ripple/protocol/TER.h b/src/ripple/protocol/TER.h index 42d3cabd3..ebb7bac94 100644 --- a/src/ripple/protocol/TER.h +++ b/src/ripple/protocol/TER.h @@ -340,6 +340,8 @@ enum TECcodes : TERUnderlyingType { tecXCHAIN_SELF_COMMIT = 185, // RESERVED - XCHAIN tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR = 186, // RESERVED - XCHAIN tecINSUF_RESERVE_SELLER = 187, + tecIMMUTABLE = 188, + tecTOO_MANY_REMARKS = 189, tecLAST_POSSIBLE_ENTRY = 255, }; diff --git a/src/ripple/protocol/TxFlags.h b/src/ripple/protocol/TxFlags.h index b27104a67..3f73c5e27 100644 --- a/src/ripple/protocol/TxFlags.h +++ b/src/ripple/protocol/TxFlags.h @@ -168,6 +168,9 @@ constexpr std::uint32_t const tfURITokenNonMintMask = ~tfUniversal; // ClaimReward flags: constexpr std::uint32_t const tfOptOut = 0x00000001; +// Remarks flags: +constexpr std::uint32_t const tfImmutable = 1; + // clang-format on } // namespace ripple diff --git a/src/ripple/protocol/TxFormats.h b/src/ripple/protocol/TxFormats.h index 2f287efe5..7fdd647de 100644 --- a/src/ripple/protocol/TxFormats.h +++ b/src/ripple/protocol/TxFormats.h @@ -146,6 +146,9 @@ enum TxType : std::uint16_t ttURITOKEN_CREATE_SELL_OFFER = 48, ttURITOKEN_CANCEL_SELL_OFFER = 49, + /* A note attaching transactor that allows the owner or issuer (on a object by object basis) to attach remarks */ + ttREMARKS_SET = 94, + /* A payment transactor that delivers only the exact amounts specified, creating accounts and TLs as needed * that the sender pays for. */ ttREMIT = 95, diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 9cf82e316..fa9dca5c3 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -464,6 +464,7 @@ REGISTER_FIX (fixXahauV2, Supported::yes, VoteBehavior::De REGISTER_FEATURE(Remit, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixNSDelete, Supported::yes, VoteBehavior::DefaultNo); +REGISTER_FEATURE(Remarks, Supported::yes, VoteBehavior::DefaultNo); // The following amendments are obsolete, but must remain supported // because they could potentially get enabled. diff --git a/src/ripple/protocol/impl/InnerObjectFormats.cpp b/src/ripple/protocol/impl/InnerObjectFormats.cpp index e52d6ff8f..2afcb7f28 100644 --- a/src/ripple/protocol/impl/InnerObjectFormats.cpp +++ b/src/ripple/protocol/impl/InnerObjectFormats.cpp @@ -157,6 +157,14 @@ InnerObjectFormats::InnerObjectFormats() {sfDigest, soeOPTIONAL}, {sfFlags, soeOPTIONAL}, }); + + add(sfRemark.jsonName.c_str(), + sfRemark.getCode(), + { + {sfRemarkName, soeREQUIRED}, + {sfRemarkValue, soeREQUIRED}, + {sfFlags, soeOPTIONAL}, + }); } InnerObjectFormats const& diff --git a/src/ripple/protocol/impl/LedgerFormats.cpp b/src/ripple/protocol/impl/LedgerFormats.cpp index acb07c489..ec459b59f 100644 --- a/src/ripple/protocol/impl/LedgerFormats.cpp +++ b/src/ripple/protocol/impl/LedgerFormats.cpp @@ -31,6 +31,7 @@ LedgerFormats::LedgerFormats() {sfLedgerIndex, soeOPTIONAL}, {sfLedgerEntryType, soeREQUIRED}, {sfFlags, soeREQUIRED}, + {sfRemarks, soeOPTIONAL}, }; add(jss::AccountRoot, diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index a72208607..0c7679b01 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -210,6 +210,7 @@ CONSTRUCT_TYPED_SFIELD(sfNFTokenID, "NFTokenID", UINT256, CONSTRUCT_TYPED_SFIELD(sfEmitParentTxnID, "EmitParentTxnID", UINT256, 11); CONSTRUCT_TYPED_SFIELD(sfEmitNonce, "EmitNonce", UINT256, 12); CONSTRUCT_TYPED_SFIELD(sfEmitHookHash, "EmitHookHash", UINT256, 13); +CONSTRUCT_TYPED_SFIELD(sfObjectID, "ObjectID", UINT256, 14); // 256-bit (uncommon) CONSTRUCT_TYPED_SFIELD(sfBookDirectory, "BookDirectory", UINT256, 16); @@ -290,6 +291,8 @@ CONSTRUCT_TYPED_SFIELD(sfHookReturnString, "HookReturnString", VL, CONSTRUCT_TYPED_SFIELD(sfHookParameterName, "HookParameterName", VL, 24); CONSTRUCT_TYPED_SFIELD(sfHookParameterValue, "HookParameterValue", VL, 25); CONSTRUCT_TYPED_SFIELD(sfBlob, "Blob", VL, 26); +CONSTRUCT_TYPED_SFIELD(sfRemarkValue, "RemarkValue", VL, 98); +CONSTRUCT_TYPED_SFIELD(sfRemarkName, "RemarkName", VL, 99); // account CONSTRUCT_TYPED_SFIELD(sfAccount, "Account", ACCOUNT, 1); @@ -344,6 +347,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfHookExecution, "HookExecution", OBJECT, CONSTRUCT_UNTYPED_SFIELD(sfHookDefinition, "HookDefinition", OBJECT, 22); CONSTRUCT_UNTYPED_SFIELD(sfHookParameter, "HookParameter", OBJECT, 23); CONSTRUCT_UNTYPED_SFIELD(sfHookGrant, "HookGrant", OBJECT, 24); +CONSTRUCT_UNTYPED_SFIELD(sfRemark, "Remark", OBJECT, 97); CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT, 96); CONSTRUCT_UNTYPED_SFIELD(sfActiveValidator, "ActiveValidator", OBJECT, 95); CONSTRUCT_UNTYPED_SFIELD(sfImportVLKey, "ImportVLKey", OBJECT, 94); @@ -370,6 +374,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY, CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18); CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19); CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20); +CONSTRUCT_UNTYPED_SFIELD(sfRemarks, "Remarks", ARRAY, 97); CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY, 96); CONSTRUCT_UNTYPED_SFIELD(sfActiveValidators, "ActiveValidators", ARRAY, 95); CONSTRUCT_UNTYPED_SFIELD(sfImportVLKeys, "ImportVLKeys", ARRAY, 94); diff --git a/src/ripple/protocol/impl/TER.cpp b/src/ripple/protocol/impl/TER.cpp index bc8a41fa9..41862f2c4 100644 --- a/src/ripple/protocol/impl/TER.cpp +++ b/src/ripple/protocol/impl/TER.cpp @@ -92,6 +92,8 @@ transResults() MAKE_ERROR(tecREQUIRES_FLAG, "The transaction or part-thereof requires a flag that wasn't set."), MAKE_ERROR(tecPRECISION_LOSS, "The amounts used by the transaction cannot interact."), MAKE_ERROR(tecINSUF_RESERVE_SELLER, "The seller of an object has insufficient reserves, and thus cannot complete the sale."), + MAKE_ERROR(tecIMMUTABLE, "The remark is marked immutable on the object, and therefore cannot be updated."), + MAKE_ERROR(tecTOO_MANY_REMARKS, "The number of remarks on the object would exceed the limit of 32."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), MAKE_ERROR(tefBAD_AUTH, "Transaction's public key is not authorized."), diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 6c38711ad..37f9acd37 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -419,6 +419,7 @@ TxFormats::TxFormats() {sfAmount, soeOPTIONAL}, {sfDestination, soeOPTIONAL}, {sfTicketSequence, soeOPTIONAL}, + {sfRemarks, soeOPTIONAL}, }, commonFields); @@ -456,6 +457,14 @@ TxFormats::TxFormats() {sfTicketSequence, soeOPTIONAL}, }, commonFields); + + add(jss::SetRemarks, + ttREMARKS_SET, + { + {sfObjectID, soeREQUIRED}, + {sfRemarks, soeREQUIRED}, + }, + commonFields); } TxFormats const&