From 261e7d77697e5b3a69239640109119f47e130c8c Mon Sep 17 00:00:00 2001 From: Shawn Xie Date: Mon, 6 Apr 2026 12:00:07 -0400 Subject: [PATCH] Resolve conflicts --- .../transactions/ConfidentialMPTClawback.h | 209 ++++++++++ .../transactions/ConfidentialMPTConvert.h | 344 ++++++++++++++++ .../transactions/ConfidentialMPTConvertBack.h | 324 +++++++++++++++ .../transactions/ConfidentialMPTMergeInbox.h | 137 +++++++ .../transactions/ConfidentialMPTSend.h | 382 ++++++++++++++++++ .../transactions/MPTokenIssuanceSet.h | 98 ++++- src/libxrpl/ledger/helpers/MPTokenHelpers.cpp | 7 + src/libxrpl/ledger/helpers/TokenHelpers.cpp | 3 +- .../token/ConfidentialMPTConvert.cpp | 1 + .../token/ConfidentialMPTConvertBack.cpp | 1 + .../token/ConfidentialMPTMergeInbox.cpp | 1 + .../transactors/token/ConfidentialMPTSend.cpp | 3 +- src/test/app/ConfidentialTransfer_test.cpp | 2 +- src/test/jtx/impl/mpt.cpp | 243 +++++------ .../ConfidentialMPTClawbackTests.cpp | 186 +++++++++ .../ConfidentialMPTConvertBackTests.cpp | 296 ++++++++++++++ .../ConfidentialMPTConvertTests.cpp | 304 ++++++++++++++ .../ConfidentialMPTMergeInboxTests.cpp | 132 ++++++ .../transactions/ConfidentialMPTSendTests.cpp | 335 +++++++++++++++ .../transactions/MPTokenIssuanceSetTests.cpp | 82 ++-- 20 files changed, 2929 insertions(+), 161 deletions(-) create mode 100644 include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h create mode 100644 include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h create mode 100644 include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h create mode 100644 include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h create mode 100644 include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTClawbackTests.cpp create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertBackTests.cpp create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertTests.cpp create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTMergeInboxTests.cpp create mode 100644 src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTSendTests.cpp diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h new file mode 100644 index 0000000000..e352e813d2 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTClawback.h @@ -0,0 +1,209 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ConfidentialMPTClawbackBuilder; + +/** + * @brief Transaction: ConfidentialMPTClawback + * + * Type: ttCONFIDENTIAL_MPT_CLAWBACK (89) + * Delegable: Delegation::delegable + * Amendment: featureConfidentialTransfer + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ConfidentialMPTClawbackBuilder to construct new transactions. + */ +class ConfidentialMPTClawback : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CLAWBACK; + + /** + * @brief Construct a ConfidentialMPTClawback transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ConfidentialMPTClawback(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawback"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfHolder (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getHolder() const + { + return this->tx_->at(sfHolder); + } + + /** + * @brief Get sfMPTAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getMPTAmount() const + { + return this->tx_->at(sfMPTAmount); + } + + /** + * @brief Get sfZKProof (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getZKProof() const + { + return this->tx_->at(sfZKProof); + } +}; + +/** + * @brief Builder for ConfidentialMPTClawback transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ConfidentialMPTClawbackBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ConfidentialMPTClawbackBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param holder The sfHolder field value. + * @param mPTAmount The sfMPTAmount field value. + * @param zKProof The sfZKProof field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ConfidentialMPTClawbackBuilder( + SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, + std::decay_t const& holder, + std::decay_t const& mPTAmount, + std::decay_t const& zKProof, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt) + : TransactionBuilderBase( + ttCONFIDENTIAL_MPT_CLAWBACK, + account, + sequence, + fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + setHolder(holder); + setMPTAmount(mPTAmount); + setZKProof(zKProof); + } + + /** + * @brief Construct a ConfidentialMPTClawbackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ConfidentialMPTClawbackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CLAWBACK) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTClawbackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTClawbackBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfHolder (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTClawbackBuilder& + setHolder(std::decay_t const& value) + { + object_[sfHolder] = value; + return *this; + } + + /** + * @brief Set sfMPTAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTClawbackBuilder& + setMPTAmount(std::decay_t const& value) + { + object_[sfMPTAmount] = value; + return *this; + } + + /** + * @brief Set sfZKProof (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTClawbackBuilder& + setZKProof(std::decay_t const& value) + { + object_[sfZKProof] = value; + return *this; + } + + /** + * @brief Build and return the ConfidentialMPTClawback wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ConfidentialMPTClawback + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ConfidentialMPTClawback{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h new file mode 100644 index 0000000000..f6c2c9be10 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvert.h @@ -0,0 +1,344 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ConfidentialMPTConvertBuilder; + +/** + * @brief Transaction: ConfidentialMPTConvert + * + * Type: ttCONFIDENTIAL_MPT_CONVERT (85) + * Delegable: Delegation::delegable + * Amendment: featureConfidentialTransfer + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ConfidentialMPTConvertBuilder to construct new transactions. + */ +class ConfidentialMPTConvert : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT; + + /** + * @brief Construct a ConfidentialMPTConvert transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ConfidentialMPTConvert(std::shared_ptr tx) : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvert"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfMPTAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getMPTAmount() const + { + return this->tx_->at(sfMPTAmount); + } + + /** + * @brief Get sfHolderEncryptionKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getHolderEncryptionKey() const + { + if (hasHolderEncryptionKey()) + { + return this->tx_->at(sfHolderEncryptionKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfHolderEncryptionKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasHolderEncryptionKey() const + { + return this->tx_->isFieldPresent(sfHolderEncryptionKey); + } + + /** + * @brief Get sfHolderEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getHolderEncryptedAmount() const + { + return this->tx_->at(sfHolderEncryptedAmount); + } + + /** + * @brief Get sfIssuerEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getIssuerEncryptedAmount() const + { + return this->tx_->at(sfIssuerEncryptedAmount); + } + + /** + * @brief Get sfAuditorEncryptedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuditorEncryptedAmount() const + { + if (hasAuditorEncryptedAmount()) + { + return this->tx_->at(sfAuditorEncryptedAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAuditorEncryptedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuditorEncryptedAmount() const + { + return this->tx_->isFieldPresent(sfAuditorEncryptedAmount); + } + + /** + * @brief Get sfBlindingFactor (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getBlindingFactor() const + { + return this->tx_->at(sfBlindingFactor); + } + + /** + * @brief Get sfZKProof (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getZKProof() const + { + if (hasZKProof()) + { + return this->tx_->at(sfZKProof); + } + return std::nullopt; + } + + /** + * @brief Check if sfZKProof is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasZKProof() const + { + return this->tx_->isFieldPresent(sfZKProof); + } +}; + +/** + * @brief Builder for ConfidentialMPTConvert transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ConfidentialMPTConvertBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ConfidentialMPTConvertBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param mPTAmount The sfMPTAmount field value. + * @param holderEncryptedAmount The sfHolderEncryptedAmount field value. + * @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value. + * @param blindingFactor The sfBlindingFactor field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ConfidentialMPTConvertBuilder( + SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, + std::decay_t const& mPTAmount, + std::decay_t const& holderEncryptedAmount, + std::decay_t const& issuerEncryptedAmount, + std::decay_t const& blindingFactor, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt) + : TransactionBuilderBase( + ttCONFIDENTIAL_MPT_CONVERT, + account, + sequence, + fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + setMPTAmount(mPTAmount); + setHolderEncryptedAmount(holderEncryptedAmount); + setIssuerEncryptedAmount(issuerEncryptedAmount); + setBlindingFactor(blindingFactor); + } + + /** + * @brief Construct a ConfidentialMPTConvertBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ConfidentialMPTConvertBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfMPTAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setMPTAmount(std::decay_t const& value) + { + object_[sfMPTAmount] = value; + return *this; + } + + /** + * @brief Set sfHolderEncryptionKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setHolderEncryptionKey(std::decay_t const& value) + { + object_[sfHolderEncryptionKey] = value; + return *this; + } + + /** + * @brief Set sfHolderEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setHolderEncryptedAmount(std::decay_t const& value) + { + object_[sfHolderEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfIssuerEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setIssuerEncryptedAmount(std::decay_t const& value) + { + object_[sfIssuerEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfAuditorEncryptedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setAuditorEncryptedAmount(std::decay_t const& value) + { + object_[sfAuditorEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfBlindingFactor (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setBlindingFactor(std::decay_t const& value) + { + object_[sfBlindingFactor] = value; + return *this; + } + + /** + * @brief Set sfZKProof (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBuilder& + setZKProof(std::decay_t const& value) + { + object_[sfZKProof] = value; + return *this; + } + + /** + * @brief Build and return the ConfidentialMPTConvert wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ConfidentialMPTConvert + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ConfidentialMPTConvert{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h new file mode 100644 index 0000000000..e4d5409f23 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTConvertBack.h @@ -0,0 +1,324 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ConfidentialMPTConvertBackBuilder; + +/** + * @brief Transaction: ConfidentialMPTConvertBack + * + * Type: ttCONFIDENTIAL_MPT_CONVERT_BACK (87) + * Delegable: Delegation::delegable + * Amendment: featureConfidentialTransfer + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ConfidentialMPTConvertBackBuilder to construct new transactions. + */ +class ConfidentialMPTConvertBack : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_CONVERT_BACK; + + /** + * @brief Construct a ConfidentialMPTConvertBack transaction wrapper from an existing STTx + * object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ConfidentialMPTConvertBack(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTConvertBack"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfMPTAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT64::type::value_type + getMPTAmount() const + { + return this->tx_->at(sfMPTAmount); + } + + /** + * @brief Get sfHolderEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getHolderEncryptedAmount() const + { + return this->tx_->at(sfHolderEncryptedAmount); + } + + /** + * @brief Get sfIssuerEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getIssuerEncryptedAmount() const + { + return this->tx_->at(sfIssuerEncryptedAmount); + } + + /** + * @brief Get sfAuditorEncryptedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuditorEncryptedAmount() const + { + if (hasAuditorEncryptedAmount()) + { + return this->tx_->at(sfAuditorEncryptedAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAuditorEncryptedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuditorEncryptedAmount() const + { + return this->tx_->isFieldPresent(sfAuditorEncryptedAmount); + } + + /** + * @brief Get sfBlindingFactor (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT256::type::value_type + getBlindingFactor() const + { + return this->tx_->at(sfBlindingFactor); + } + + /** + * @brief Get sfZKProof (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getZKProof() const + { + return this->tx_->at(sfZKProof); + } + + /** + * @brief Get sfBalanceCommitment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getBalanceCommitment() const + { + return this->tx_->at(sfBalanceCommitment); + } +}; + +/** + * @brief Builder for ConfidentialMPTConvertBack transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ConfidentialMPTConvertBackBuilder + : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ConfidentialMPTConvertBackBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param mPTAmount The sfMPTAmount field value. + * @param holderEncryptedAmount The sfHolderEncryptedAmount field value. + * @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value. + * @param blindingFactor The sfBlindingFactor field value. + * @param zKProof The sfZKProof field value. + * @param balanceCommitment The sfBalanceCommitment field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ConfidentialMPTConvertBackBuilder( + SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, + std::decay_t const& mPTAmount, + std::decay_t const& holderEncryptedAmount, + std::decay_t const& issuerEncryptedAmount, + std::decay_t const& blindingFactor, + std::decay_t const& zKProof, + std::decay_t const& balanceCommitment, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt) + : TransactionBuilderBase( + ttCONFIDENTIAL_MPT_CONVERT_BACK, + account, + sequence, + fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + setMPTAmount(mPTAmount); + setHolderEncryptedAmount(holderEncryptedAmount); + setIssuerEncryptedAmount(issuerEncryptedAmount); + setBlindingFactor(blindingFactor); + setZKProof(zKProof); + setBalanceCommitment(balanceCommitment); + } + + /** + * @brief Construct a ConfidentialMPTConvertBackBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ConfidentialMPTConvertBackBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCONFIDENTIAL_MPT_CONVERT_BACK) + { + throw std::runtime_error( + "Invalid transaction type for ConfidentialMPTConvertBackBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfMPTAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setMPTAmount(std::decay_t const& value) + { + object_[sfMPTAmount] = value; + return *this; + } + + /** + * @brief Set sfHolderEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setHolderEncryptedAmount(std::decay_t const& value) + { + object_[sfHolderEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfIssuerEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setIssuerEncryptedAmount(std::decay_t const& value) + { + object_[sfIssuerEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfAuditorEncryptedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setAuditorEncryptedAmount(std::decay_t const& value) + { + object_[sfAuditorEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfBlindingFactor (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setBlindingFactor(std::decay_t const& value) + { + object_[sfBlindingFactor] = value; + return *this; + } + + /** + * @brief Set sfZKProof (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setZKProof(std::decay_t const& value) + { + object_[sfZKProof] = value; + return *this; + } + + /** + * @brief Set sfBalanceCommitment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTConvertBackBuilder& + setBalanceCommitment(std::decay_t const& value) + { + object_[sfBalanceCommitment] = value; + return *this; + } + + /** + * @brief Build and return the ConfidentialMPTConvertBack wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ConfidentialMPTConvertBack + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ConfidentialMPTConvertBack{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h new file mode 100644 index 0000000000..589a2f9cd4 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTMergeInbox.h @@ -0,0 +1,137 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ConfidentialMPTMergeInboxBuilder; + +/** + * @brief Transaction: ConfidentialMPTMergeInbox + * + * Type: ttCONFIDENTIAL_MPT_MERGE_INBOX (86) + * Delegable: Delegation::delegable + * Amendment: featureConfidentialTransfer + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ConfidentialMPTMergeInboxBuilder to construct new transactions. + */ +class ConfidentialMPTMergeInbox : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_MERGE_INBOX; + + /** + * @brief Construct a ConfidentialMPTMergeInbox transaction wrapper from an existing STTx + * object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ConfidentialMPTMergeInbox(std::shared_ptr tx) + : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTMergeInbox"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } +}; + +/** + * @brief Builder for ConfidentialMPTMergeInbox transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ConfidentialMPTMergeInboxBuilder + : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ConfidentialMPTMergeInboxBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ConfidentialMPTMergeInboxBuilder( + SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt) + : TransactionBuilderBase( + ttCONFIDENTIAL_MPT_MERGE_INBOX, + account, + sequence, + fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + } + + /** + * @brief Construct a ConfidentialMPTMergeInboxBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ConfidentialMPTMergeInboxBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCONFIDENTIAL_MPT_MERGE_INBOX) + { + throw std::runtime_error( + "Invalid transaction type for ConfidentialMPTMergeInboxBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTMergeInboxBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Build and return the ConfidentialMPTMergeInbox wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ConfidentialMPTMergeInbox + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ConfidentialMPTMergeInbox{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h new file mode 100644 index 0000000000..69106a4731 --- /dev/null +++ b/include/xrpl/protocol_autogen/transactions/ConfidentialMPTSend.h @@ -0,0 +1,382 @@ +// This file is auto-generated. Do not edit. +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace xrpl::transactions { + +class ConfidentialMPTSendBuilder; + +/** + * @brief Transaction: ConfidentialMPTSend + * + * Type: ttCONFIDENTIAL_MPT_SEND (88) + * Delegable: Delegation::delegable + * Amendment: featureConfidentialTransfer + * Privileges: noPriv + * + * Immutable wrapper around STTx providing type-safe field access. + * Use ConfidentialMPTSendBuilder to construct new transactions. + */ +class ConfidentialMPTSend : public TransactionBase +{ +public: + static constexpr xrpl::TxType txType = ttCONFIDENTIAL_MPT_SEND; + + /** + * @brief Construct a ConfidentialMPTSend transaction wrapper from an existing STTx object. + * @throws std::runtime_error if the transaction type doesn't match. + */ + explicit ConfidentialMPTSend(std::shared_ptr tx) : TransactionBase(std::move(tx)) + { + // Verify transaction type + if (tx_->getTxnType() != txType) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTSend"); + } + } + + // Transaction-specific field getters + + /** + * @brief Get sfMPTokenIssuanceID (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_UINT192::type::value_type + getMPTokenIssuanceID() const + { + return this->tx_->at(sfMPTokenIssuanceID); + } + + /** + * @brief Get sfDestination (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_ACCOUNT::type::value_type + getDestination() const + { + return this->tx_->at(sfDestination); + } + + /** + * @brief Get sfSenderEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getSenderEncryptedAmount() const + { + return this->tx_->at(sfSenderEncryptedAmount); + } + + /** + * @brief Get sfDestinationEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getDestinationEncryptedAmount() const + { + return this->tx_->at(sfDestinationEncryptedAmount); + } + + /** + * @brief Get sfIssuerEncryptedAmount (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getIssuerEncryptedAmount() const + { + return this->tx_->at(sfIssuerEncryptedAmount); + } + + /** + * @brief Get sfAuditorEncryptedAmount (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuditorEncryptedAmount() const + { + if (hasAuditorEncryptedAmount()) + { + return this->tx_->at(sfAuditorEncryptedAmount); + } + return std::nullopt; + } + + /** + * @brief Check if sfAuditorEncryptedAmount is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuditorEncryptedAmount() const + { + return this->tx_->isFieldPresent(sfAuditorEncryptedAmount); + } + + /** + * @brief Get sfZKProof (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getZKProof() const + { + return this->tx_->at(sfZKProof); + } + + /** + * @brief Get sfAmountCommitment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getAmountCommitment() const + { + return this->tx_->at(sfAmountCommitment); + } + + /** + * @brief Get sfBalanceCommitment (soeREQUIRED) + * @return The field value. + */ + [[nodiscard]] + SF_VL::type::value_type + getBalanceCommitment() const + { + return this->tx_->at(sfBalanceCommitment); + } + + /** + * @brief Get sfCredentialIDs (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getCredentialIDs() const + { + if (hasCredentialIDs()) + { + return this->tx_->at(sfCredentialIDs); + } + return std::nullopt; + } + + /** + * @brief Check if sfCredentialIDs is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasCredentialIDs() const + { + return this->tx_->isFieldPresent(sfCredentialIDs); + } +}; + +/** + * @brief Builder for ConfidentialMPTSend transactions. + * + * Provides a fluent interface for constructing transactions with method chaining. + * Uses Json::Value internally for flexible transaction construction. + * Inherits common field setters from TransactionBuilderBase. + */ +class ConfidentialMPTSendBuilder : public TransactionBuilderBase +{ +public: + /** + * @brief Construct a new ConfidentialMPTSendBuilder with required fields. + * @param account The account initiating the transaction. + * @param mPTokenIssuanceID The sfMPTokenIssuanceID field value. + * @param destination The sfDestination field value. + * @param senderEncryptedAmount The sfSenderEncryptedAmount field value. + * @param destinationEncryptedAmount The sfDestinationEncryptedAmount field value. + * @param issuerEncryptedAmount The sfIssuerEncryptedAmount field value. + * @param zKProof The sfZKProof field value. + * @param amountCommitment The sfAmountCommitment field value. + * @param balanceCommitment The sfBalanceCommitment field value. + * @param sequence Optional sequence number for the transaction. + * @param fee Optional fee for the transaction. + */ + ConfidentialMPTSendBuilder( + SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, + std::decay_t const& destination, + std::decay_t const& senderEncryptedAmount, + std::decay_t const& destinationEncryptedAmount, + std::decay_t const& issuerEncryptedAmount, + std::decay_t const& zKProof, + std::decay_t const& amountCommitment, + std::decay_t const& balanceCommitment, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt) + : TransactionBuilderBase( + ttCONFIDENTIAL_MPT_SEND, + account, + sequence, + fee) + { + setMPTokenIssuanceID(mPTokenIssuanceID); + setDestination(destination); + setSenderEncryptedAmount(senderEncryptedAmount); + setDestinationEncryptedAmount(destinationEncryptedAmount); + setIssuerEncryptedAmount(issuerEncryptedAmount); + setZKProof(zKProof); + setAmountCommitment(amountCommitment); + setBalanceCommitment(balanceCommitment); + } + + /** + * @brief Construct a ConfidentialMPTSendBuilder from an existing STTx object. + * @param tx The existing transaction to copy from. + * @throws std::runtime_error if the transaction type doesn't match. + */ + ConfidentialMPTSendBuilder(std::shared_ptr tx) + { + if (tx->getTxnType() != ttCONFIDENTIAL_MPT_SEND) + { + throw std::runtime_error("Invalid transaction type for ConfidentialMPTSendBuilder"); + } + object_ = *tx; + } + + /** @brief Transaction-specific field setters */ + + /** + * @brief Set sfMPTokenIssuanceID (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setMPTokenIssuanceID(std::decay_t const& value) + { + object_[sfMPTokenIssuanceID] = value; + return *this; + } + + /** + * @brief Set sfDestination (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setDestination(std::decay_t const& value) + { + object_[sfDestination] = value; + return *this; + } + + /** + * @brief Set sfSenderEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setSenderEncryptedAmount(std::decay_t const& value) + { + object_[sfSenderEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfDestinationEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setDestinationEncryptedAmount(std::decay_t const& value) + { + object_[sfDestinationEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfIssuerEncryptedAmount (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setIssuerEncryptedAmount(std::decay_t const& value) + { + object_[sfIssuerEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfAuditorEncryptedAmount (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setAuditorEncryptedAmount(std::decay_t const& value) + { + object_[sfAuditorEncryptedAmount] = value; + return *this; + } + + /** + * @brief Set sfZKProof (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setZKProof(std::decay_t const& value) + { + object_[sfZKProof] = value; + return *this; + } + + /** + * @brief Set sfAmountCommitment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setAmountCommitment(std::decay_t const& value) + { + object_[sfAmountCommitment] = value; + return *this; + } + + /** + * @brief Set sfBalanceCommitment (soeREQUIRED) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setBalanceCommitment(std::decay_t const& value) + { + object_[sfBalanceCommitment] = value; + return *this; + } + + /** + * @brief Set sfCredentialIDs (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + ConfidentialMPTSendBuilder& + setCredentialIDs(std::decay_t const& value) + { + object_[sfCredentialIDs] = value; + return *this; + } + + /** + * @brief Build and return the ConfidentialMPTSend wrapper. + * @param publicKey The public key for signing. + * @param secretKey The secret key for signing. + * @return The constructed transaction wrapper. + */ + ConfidentialMPTSend + build(PublicKey const& publicKey, SecretKey const& secretKey) + { + sign(publicKey, secretKey); + return ConfidentialMPTSend{std::make_shared(std::move(object_))}; + } +}; + +} // namespace xrpl::transactions diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h index 429b252e95..6a5a105944 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h @@ -1,15 +1,15 @@ // This file is auto-generated. Do not edit. #pragma once -#include +#include #include +#include #include #include #include -#include -#include #include +#include namespace xrpl::transactions { @@ -35,8 +35,7 @@ public: * @brief Construct a MPTokenIssuanceSet transaction wrapper from an existing STTx object. * @throws std::runtime_error if the transaction type doesn't match. */ - explicit MPTokenIssuanceSet(std::shared_ptr tx) - : TransactionBase(std::move(tx)) + explicit MPTokenIssuanceSet(std::shared_ptr tx) : TransactionBase(std::move(tx)) { // Verify transaction type if (tx_->getTxnType() != txType) @@ -187,6 +186,58 @@ public: { return this->tx_->isFieldPresent(sfMutableFlags); } + + /** + * @brief Get sfIssuerEncryptionKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getIssuerEncryptionKey() const + { + if (hasIssuerEncryptionKey()) + { + return this->tx_->at(sfIssuerEncryptionKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfIssuerEncryptionKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasIssuerEncryptionKey() const + { + return this->tx_->isFieldPresent(sfIssuerEncryptionKey); + } + + /** + * @brief Get sfAuditorEncryptionKey (soeOPTIONAL) + * @return The field value, or std::nullopt if not present. + */ + [[nodiscard]] + protocol_autogen::Optional + getAuditorEncryptionKey() const + { + if (hasAuditorEncryptionKey()) + { + return this->tx_->at(sfAuditorEncryptionKey); + } + return std::nullopt; + } + + /** + * @brief Check if sfAuditorEncryptionKey is present. + * @return True if the field is present, false otherwise. + */ + [[nodiscard]] + bool + hasAuditorEncryptionKey() const + { + return this->tx_->isFieldPresent(sfAuditorEncryptionKey); + } }; /** @@ -206,11 +257,16 @@ public: * @param sequence Optional sequence number for the transaction. * @param fee Optional fee for the transaction. */ - MPTokenIssuanceSetBuilder(SF_ACCOUNT::type::value_type account, - std::decay_t const& mPTokenIssuanceID, std::optional sequence = std::nullopt, - std::optional fee = std::nullopt -) - : TransactionBuilderBase(ttMPTOKEN_ISSUANCE_SET, account, sequence, fee) + MPTokenIssuanceSetBuilder( + SF_ACCOUNT::type::value_type account, + std::decay_t const& mPTokenIssuanceID, + std::optional sequence = std::nullopt, + std::optional fee = std::nullopt) + : TransactionBuilderBase( + ttMPTOKEN_ISSUANCE_SET, + account, + sequence, + fee) { setMPTokenIssuanceID(mPTokenIssuanceID); } @@ -297,6 +353,28 @@ public: return *this; } + /** + * @brief Set sfIssuerEncryptionKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setIssuerEncryptionKey(std::decay_t const& value) + { + object_[sfIssuerEncryptionKey] = value; + return *this; + } + + /** + * @brief Set sfAuditorEncryptionKey (soeOPTIONAL) + * @return Reference to this builder for method chaining. + */ + MPTokenIssuanceSetBuilder& + setAuditorEncryptionKey(std::decay_t const& value) + { + object_[sfAuditorEncryptionKey] = value; + return *this; + } + /** * @brief Build and return the MPTokenIssuanceSet wrapper. * @param publicKey The public key for signing. diff --git a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp index 31c1d543f5..d974d009cb 100644 --- a/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/MPTokenHelpers.cpp @@ -255,6 +255,13 @@ removeEmptyHolding( if (mptoken->at(sfMPTAmount) != 0) return tecHAS_OBLIGATIONS; + // Don't delete if the token still has confidential balances + if (mptoken->isFieldPresent(sfConfidentialBalanceInbox) || + mptoken->isFieldPresent(sfConfidentialBalanceSpending)) + { + return tecHAS_OBLIGATIONS; + } + return authorizeMPToken( view, {}, // priorBalance diff --git a/src/libxrpl/ledger/helpers/TokenHelpers.cpp b/src/libxrpl/ledger/helpers/TokenHelpers.cpp index 8d2b0d5fff..a37c23dfd2 100644 --- a/src/libxrpl/ledger/helpers/TokenHelpers.cpp +++ b/src/libxrpl/ledger/helpers/TokenHelpers.cpp @@ -334,7 +334,8 @@ accountHolds( // Only if auth check is needed, as it needs to do an additional read // operation. Note featureSingleAssetVault will affect error codes. if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && - view.rules().enabled(featureSingleAssetVault)) + (view.rules().enabled(featureSingleAssetVault) || + view.rules().enabled(featureConfidentialTransfer))) { if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); !isTesSuccess(err)) diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp index cf3fcea801..eca20414de 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp index 6de8a8679b..43c32516d6 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTMergeInbox.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTMergeInbox.cpp index d8c4111b50..a133bb934a 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTMergeInbox.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTMergeInbox.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp index a5a68d1862..e5e5df47a2 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp @@ -1,5 +1,6 @@ -#include #include +#include +#include #include #include #include diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index 78dd85f21e..37cf5853e1 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -3269,7 +3269,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite // Inject confidential balance fields on the share MPToken // to simulate a scenario where vault shares somehow have // confidential balances - env.app().openLedger().modify([&](OpenView& view, beast::Journal) { + env.app().getOpenLedger().modify([&](OpenView& view, beast::Journal) { // Set lsfMPTCanConfidentialAmount on the share issuance // so the invariant allows encrypted fields on the MPToken auto issuance = std::const_pointer_cast(view.read(keylet::mptIssuance(share))); diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 11fcf490b7..75a7568907 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -1,9 +1,9 @@ #include #include +#include #include #include -#include #include #include @@ -421,132 +421,133 @@ MPTTester::set(MPTSet const& arg) { if ((arg.flags.value_or(0) || arg.mutableFlags)) { - auto require = [&](std::optional const& holder, bool unchanged) { - auto flags = getFlags(holder); - if (!unchanged) + auto require = [&](std::optional const& holder, bool unchanged) { + auto flags = getFlags(holder); + if (!unchanged) + { + if (arg.flags) + { + if (*arg.flags & tfMPTLock) + { + flags |= lsfMPTLocked; + } + else if (*arg.flags & tfMPTUnlock) + { + flags &= ~lsfMPTLocked; + } + } + + if (arg.mutableFlags) + { + if (*arg.mutableFlags & tmfMPTSetCanLock) + { + flags |= lsfMPTCanLock; + } + else if (*arg.mutableFlags & tmfMPTClearCanLock) + { + flags &= ~lsfMPTCanLock; + } + + if (*arg.mutableFlags & tmfMPTSetRequireAuth) + { + flags |= lsfMPTRequireAuth; + } + else if (*arg.mutableFlags & tmfMPTClearRequireAuth) + { + flags &= ~lsfMPTRequireAuth; + } + + if (*arg.mutableFlags & tmfMPTSetCanEscrow) + { + flags |= lsfMPTCanEscrow; + } + else if (*arg.mutableFlags & tmfMPTClearCanEscrow) + { + flags &= ~lsfMPTCanEscrow; + } + + if (*arg.mutableFlags & tmfMPTSetCanClawback) + { + flags |= lsfMPTCanClawback; + } + else if (*arg.mutableFlags & tmfMPTClearCanClawback) + { + flags &= ~lsfMPTCanClawback; + } + + if (*arg.mutableFlags & tmfMPTSetCanTrade) + { + flags |= lsfMPTCanTrade; + } + else if (*arg.mutableFlags & tmfMPTClearCanTrade) + { + flags &= ~lsfMPTCanTrade; + } + + if (*arg.mutableFlags & tmfMPTSetCanTransfer) + { + flags |= lsfMPTCanTransfer; + } + else if (*arg.mutableFlags & tmfMPTClearCanTransfer) + { + flags &= ~lsfMPTCanTransfer; + } + + if (*arg.mutableFlags & tmfMPTSetCanConfidentialAmount) + { + flags |= lsfMPTCanConfidentialAmount; + } + else if (*arg.mutableFlags & tmfMPTClearCanConfidentialAmount) + { + flags &= ~lsfMPTCanConfidentialAmount; + } + } + } + env_.require(mptflags(*this, flags, holder)); + }; + if (arg.account) + require(std::nullopt, arg.holder.has_value()); + if (auto const account = (arg.holder ? std::get_if(&(*arg.holder)) : nullptr)) + require(*account, false); + + if (arg.issuerPubKey) { - if (arg.flags) - { - if (*arg.flags & tfMPTLock) - { - flags |= lsfMPTLocked; - } - else if (*arg.flags & tfMPTUnlock) - { - flags &= ~lsfMPTLocked; - } - } + env_.require(requireAny([&]() -> bool { + return forObject([&](SLEP const& sle) -> bool { + if (sle) + { + auto const issuerPubKey = getPubKey(issuer_); + if (!issuerPubKey) + Throw( + "MPTTester::set: issuer's pubkey is not set"); - if (arg.mutableFlags) - { - if (*arg.mutableFlags & tmfMPTSetCanLock) - { - flags |= lsfMPTCanLock; - } - else if (*arg.mutableFlags & tmfMPTClearCanLock) - { - flags &= ~lsfMPTCanLock; - } - - if (*arg.mutableFlags & tmfMPTSetRequireAuth) - { - flags |= lsfMPTRequireAuth; - } - else if (*arg.mutableFlags & tmfMPTClearRequireAuth) - { - flags &= ~lsfMPTRequireAuth; - } - - if (*arg.mutableFlags & tmfMPTSetCanEscrow) - { - flags |= lsfMPTCanEscrow; - } - else if (*arg.mutableFlags & tmfMPTClearCanEscrow) - { - flags &= ~lsfMPTCanEscrow; - } - - if (*arg.mutableFlags & tmfMPTSetCanClawback) - { - flags |= lsfMPTCanClawback; - } - else if (*arg.mutableFlags & tmfMPTClearCanClawback) - { - flags &= ~lsfMPTCanClawback; - } - - if (*arg.mutableFlags & tmfMPTSetCanTrade) - { - flags |= lsfMPTCanTrade; - } - else if (*arg.mutableFlags & tmfMPTClearCanTrade) - { - flags &= ~lsfMPTCanTrade; - } - - if (*arg.mutableFlags & tmfMPTSetCanTransfer) - { - flags |= lsfMPTCanTransfer; - } - else if (*arg.mutableFlags & tmfMPTClearCanTransfer) - { - flags &= ~lsfMPTCanTransfer; - } - - if (*arg.mutableFlags & tmfMPTSetCanConfidentialAmount) - { - flags |= lsfMPTCanConfidentialAmount; - } - else if (*arg.mutableFlags & tmfMPTClearCanConfidentialAmount) - { - flags &= ~lsfMPTCanConfidentialAmount; - } - } + return strHex((*sle)[sfIssuerEncryptionKey]) == strHex(*issuerPubKey); + } + return false; + }); + })); } - env_.require(mptflags(*this, flags, holder)); - }; - if (arg.account) - require(std::nullopt, arg.holder.has_value()); - if (auto const account = (arg.holder ? std::get_if(&(*arg.holder)) : nullptr)) - require(*account, false); - if (arg.issuerPubKey) - { - env_.require(requireAny([&]() -> bool { - return forObject([&](SLEP const& sle) -> bool { - if (sle) - { - auto const issuerPubKey = getPubKey(issuer_); - if (!issuerPubKey) - Throw("MPTTester::set: issuer's pubkey is not set"); + if (arg.auditorPubKey) + { + env_.require(requireAny([&]() -> bool { + return forObject([&](SLEP const& sle) -> bool { + if (sle) + { + if (!auditor_.has_value()) + Throw("MPTTester::set: auditor is not set"); - return strHex((*sle)[sfIssuerEncryptionKey]) == strHex(*issuerPubKey); - } - return false; - }); - })); - } + auto const auditorPubKey = getPubKey(*auditor_); + if (!auditorPubKey) + Throw( + "MPTTester::set: auditor's pubkey is not set"); - if (arg.auditorPubKey) - { - env_.require(requireAny([&]() -> bool { - return forObject([&](SLEP const& sle) -> bool { - if (sle) - { - if (!auditor_.has_value()) - Throw("MPTTester::set: auditor is not set"); - - auto const auditorPubKey = getPubKey(*auditor_); - if (!auditorPubKey) - Throw( - "MPTTester::set: auditor's pubkey is not set"); - - return strHex((*sle)[sfAuditorEncryptionKey]) == strHex(*auditorPubKey); - } - return false; - }); - })); - } + return strHex((*sle)[sfAuditorEncryptionKey]) == strHex(*auditorPubKey); + } + return false; + }); + })); + } } } } diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTClawbackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTClawbackTests.cpp new file mode 100644 index 0000000000..f5ea7c7e92 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTClawbackTests.cpp @@ -0,0 +1,186 @@ +// Auto-generated unit tests for transaction ConfidentialMPTClawback + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsConfidentialMPTClawbackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTClawback")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + auto const mPTAmountValue = canonical_UINT64(); + auto const zKProofValue = canonical_VL(); + + ConfidentialMPTClawbackBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + holderValue, + mPTAmountValue, + zKProofValue, + sequenceValue, + feeValue}; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = holderValue; + auto const actual = tx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + { + auto const& expected = mPTAmountValue; + auto const actual = tx.getMPTAmount(); + expectEqualField(expected, actual, "sfMPTAmount"); + } + + { + auto const& expected = zKProofValue; + auto const actual = tx.getZKProof(); + expectEqualField(expected, actual, "sfZKProof"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsConfidentialMPTClawbackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTClawbackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const holderValue = canonical_ACCOUNT(); + auto const mPTAmountValue = canonical_UINT64(); + auto const zKProofValue = canonical_VL(); + + // Build an initial transaction + ConfidentialMPTClawbackBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + holderValue, + mPTAmountValue, + zKProofValue, + sequenceValue, + feeValue}; + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ConfidentialMPTClawbackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = holderValue; + auto const actual = rebuiltTx.getHolder(); + expectEqualField(expected, actual, "sfHolder"); + } + + { + auto const& expected = mPTAmountValue; + auto const actual = rebuiltTx.getMPTAmount(); + expectEqualField(expected, actual, "sfMPTAmount"); + } + + { + auto const& expected = zKProofValue; + auto const actual = rebuiltTx.getZKProof(); + expectEqualField(expected, actual, "sfZKProof"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTClawbackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTClawback{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTClawbackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTClawbackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +} // namespace xrpl::transactions diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertBackTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertBackTests.cpp new file mode 100644 index 0000000000..8c52719449 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertBackTests.cpp @@ -0,0 +1,296 @@ +// Auto-generated unit tests for transaction ConfidentialMPTConvertBack + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsConfidentialMPTConvertBackTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTConvertBack")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const holderEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const auditorEncryptedAmountValue = canonical_VL(); + auto const blindingFactorValue = canonical_UINT256(); + auto const zKProofValue = canonical_VL(); + auto const balanceCommitmentValue = canonical_VL(); + + ConfidentialMPTConvertBackBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + mPTAmountValue, + holderEncryptedAmountValue, + issuerEncryptedAmountValue, + blindingFactorValue, + zKProofValue, + balanceCommitmentValue, + sequenceValue, + feeValue}; + + // Set optional fields + builder.setAuditorEncryptedAmount(auditorEncryptedAmountValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = mPTAmountValue; + auto const actual = tx.getMPTAmount(); + expectEqualField(expected, actual, "sfMPTAmount"); + } + + { + auto const& expected = holderEncryptedAmountValue; + auto const actual = tx.getHolderEncryptedAmount(); + expectEqualField(expected, actual, "sfHolderEncryptedAmount"); + } + + { + auto const& expected = issuerEncryptedAmountValue; + auto const actual = tx.getIssuerEncryptedAmount(); + expectEqualField(expected, actual, "sfIssuerEncryptedAmount"); + } + + { + auto const& expected = blindingFactorValue; + auto const actual = tx.getBlindingFactor(); + expectEqualField(expected, actual, "sfBlindingFactor"); + } + + { + auto const& expected = zKProofValue; + auto const actual = tx.getZKProof(); + expectEqualField(expected, actual, "sfZKProof"); + } + + { + auto const& expected = balanceCommitmentValue; + auto const actual = tx.getBalanceCommitment(); + expectEqualField(expected, actual, "sfBalanceCommitment"); + } + + // Verify optional fields + { + auto const& expected = auditorEncryptedAmountValue; + auto const actualOpt = tx.getAuditorEncryptedAmount(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptedAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount"); + EXPECT_TRUE(tx.hasAuditorEncryptedAmount()); + } +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsConfidentialMPTConvertBackTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTConvertBackFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const holderEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const auditorEncryptedAmountValue = canonical_VL(); + auto const blindingFactorValue = canonical_UINT256(); + auto const zKProofValue = canonical_VL(); + auto const balanceCommitmentValue = canonical_VL(); + + // Build an initial transaction + ConfidentialMPTConvertBackBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + mPTAmountValue, + holderEncryptedAmountValue, + issuerEncryptedAmountValue, + blindingFactorValue, + zKProofValue, + balanceCommitmentValue, + sequenceValue, + feeValue}; + + initialBuilder.setAuditorEncryptedAmount(auditorEncryptedAmountValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ConfidentialMPTConvertBackBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = mPTAmountValue; + auto const actual = rebuiltTx.getMPTAmount(); + expectEqualField(expected, actual, "sfMPTAmount"); + } + + { + auto const& expected = holderEncryptedAmountValue; + auto const actual = rebuiltTx.getHolderEncryptedAmount(); + expectEqualField(expected, actual, "sfHolderEncryptedAmount"); + } + + { + auto const& expected = issuerEncryptedAmountValue; + auto const actual = rebuiltTx.getIssuerEncryptedAmount(); + expectEqualField(expected, actual, "sfIssuerEncryptedAmount"); + } + + { + auto const& expected = blindingFactorValue; + auto const actual = rebuiltTx.getBlindingFactor(); + expectEqualField(expected, actual, "sfBlindingFactor"); + } + + { + auto const& expected = zKProofValue; + auto const actual = rebuiltTx.getZKProof(); + expectEqualField(expected, actual, "sfZKProof"); + } + + { + auto const& expected = balanceCommitmentValue; + auto const actual = rebuiltTx.getBalanceCommitment(); + expectEqualField(expected, actual, "sfBalanceCommitment"); + } + + // Verify optional fields + { + auto const& expected = auditorEncryptedAmountValue; + auto const actualOpt = rebuiltTx.getAuditorEncryptedAmount(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptedAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount"); + } +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTConvertBackTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTConvertBack{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTConvertBackTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTConvertBackBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsConfidentialMPTConvertBackTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTConvertBackNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const holderEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const blindingFactorValue = canonical_UINT256(); + auto const zKProofValue = canonical_VL(); + auto const balanceCommitmentValue = canonical_VL(); + + ConfidentialMPTConvertBackBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + mPTAmountValue, + holderEncryptedAmountValue, + issuerEncryptedAmountValue, + blindingFactorValue, + zKProofValue, + balanceCommitmentValue, + sequenceValue, + feeValue}; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAuditorEncryptedAmount()); + EXPECT_FALSE(tx.getAuditorEncryptedAmount().has_value()); +} + +} // namespace xrpl::transactions diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertTests.cpp new file mode 100644 index 0000000000..226755b87b --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTConvertTests.cpp @@ -0,0 +1,304 @@ +// Auto-generated unit tests for transaction ConfidentialMPTConvert + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsConfidentialMPTConvertTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTConvert")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const holderEncryptionKeyValue = canonical_VL(); + auto const holderEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const auditorEncryptedAmountValue = canonical_VL(); + auto const blindingFactorValue = canonical_UINT256(); + auto const zKProofValue = canonical_VL(); + + ConfidentialMPTConvertBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + mPTAmountValue, + holderEncryptedAmountValue, + issuerEncryptedAmountValue, + blindingFactorValue, + sequenceValue, + feeValue}; + + // Set optional fields + builder.setHolderEncryptionKey(holderEncryptionKeyValue); + builder.setAuditorEncryptedAmount(auditorEncryptedAmountValue); + builder.setZKProof(zKProofValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = mPTAmountValue; + auto const actual = tx.getMPTAmount(); + expectEqualField(expected, actual, "sfMPTAmount"); + } + + { + auto const& expected = holderEncryptedAmountValue; + auto const actual = tx.getHolderEncryptedAmount(); + expectEqualField(expected, actual, "sfHolderEncryptedAmount"); + } + + { + auto const& expected = issuerEncryptedAmountValue; + auto const actual = tx.getIssuerEncryptedAmount(); + expectEqualField(expected, actual, "sfIssuerEncryptedAmount"); + } + + { + auto const& expected = blindingFactorValue; + auto const actual = tx.getBlindingFactor(); + expectEqualField(expected, actual, "sfBlindingFactor"); + } + + // Verify optional fields + { + auto const& expected = holderEncryptionKeyValue; + auto const actualOpt = tx.getHolderEncryptionKey(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfHolderEncryptionKey should be present"; + expectEqualField(expected, *actualOpt, "sfHolderEncryptionKey"); + EXPECT_TRUE(tx.hasHolderEncryptionKey()); + } + + { + auto const& expected = auditorEncryptedAmountValue; + auto const actualOpt = tx.getAuditorEncryptedAmount(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptedAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount"); + EXPECT_TRUE(tx.hasAuditorEncryptedAmount()); + } + + { + auto const& expected = zKProofValue; + auto const actualOpt = tx.getZKProof(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfZKProof should be present"; + expectEqualField(expected, *actualOpt, "sfZKProof"); + EXPECT_TRUE(tx.hasZKProof()); + } +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsConfidentialMPTConvertTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTConvertFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const holderEncryptionKeyValue = canonical_VL(); + auto const holderEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const auditorEncryptedAmountValue = canonical_VL(); + auto const blindingFactorValue = canonical_UINT256(); + auto const zKProofValue = canonical_VL(); + + // Build an initial transaction + ConfidentialMPTConvertBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + mPTAmountValue, + holderEncryptedAmountValue, + issuerEncryptedAmountValue, + blindingFactorValue, + sequenceValue, + feeValue}; + + initialBuilder.setHolderEncryptionKey(holderEncryptionKeyValue); + initialBuilder.setAuditorEncryptedAmount(auditorEncryptedAmountValue); + initialBuilder.setZKProof(zKProofValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ConfidentialMPTConvertBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = mPTAmountValue; + auto const actual = rebuiltTx.getMPTAmount(); + expectEqualField(expected, actual, "sfMPTAmount"); + } + + { + auto const& expected = holderEncryptedAmountValue; + auto const actual = rebuiltTx.getHolderEncryptedAmount(); + expectEqualField(expected, actual, "sfHolderEncryptedAmount"); + } + + { + auto const& expected = issuerEncryptedAmountValue; + auto const actual = rebuiltTx.getIssuerEncryptedAmount(); + expectEqualField(expected, actual, "sfIssuerEncryptedAmount"); + } + + { + auto const& expected = blindingFactorValue; + auto const actual = rebuiltTx.getBlindingFactor(); + expectEqualField(expected, actual, "sfBlindingFactor"); + } + + // Verify optional fields + { + auto const& expected = holderEncryptionKeyValue; + auto const actualOpt = rebuiltTx.getHolderEncryptionKey(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfHolderEncryptionKey should be present"; + expectEqualField(expected, *actualOpt, "sfHolderEncryptionKey"); + } + + { + auto const& expected = auditorEncryptedAmountValue; + auto const actualOpt = rebuiltTx.getAuditorEncryptedAmount(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptedAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount"); + } + + { + auto const& expected = zKProofValue; + auto const actualOpt = rebuiltTx.getZKProof(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfZKProof should be present"; + expectEqualField(expected, *actualOpt, "sfZKProof"); + } +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTConvertTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTConvert{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTConvertTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTConvertBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsConfidentialMPTConvertTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTConvertNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const mPTAmountValue = canonical_UINT64(); + auto const holderEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const blindingFactorValue = canonical_UINT256(); + + ConfidentialMPTConvertBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + mPTAmountValue, + holderEncryptedAmountValue, + issuerEncryptedAmountValue, + blindingFactorValue, + sequenceValue, + feeValue}; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasHolderEncryptionKey()); + EXPECT_FALSE(tx.getHolderEncryptionKey().has_value()); + EXPECT_FALSE(tx.hasAuditorEncryptedAmount()); + EXPECT_FALSE(tx.getAuditorEncryptedAmount().has_value()); + EXPECT_FALSE(tx.hasZKProof()); + EXPECT_FALSE(tx.getZKProof().has_value()); +} + +} // namespace xrpl::transactions diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTMergeInboxTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTMergeInboxTests.cpp new file mode 100644 index 0000000000..75b2ed9821 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTMergeInboxTests.cpp @@ -0,0 +1,132 @@ +// Auto-generated unit tests for transaction ConfidentialMPTMergeInbox + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsConfidentialMPTMergeInboxTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTMergeInbox")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + ConfidentialMPTMergeInboxBuilder builder{ + accountValue, mPTokenIssuanceIDValue, sequenceValue, feeValue}; + + // Set optional fields + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsConfidentialMPTMergeInboxTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTMergeInboxFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + + // Build an initial transaction + ConfidentialMPTMergeInboxBuilder initialBuilder{ + accountValue, mPTokenIssuanceIDValue, sequenceValue, feeValue}; + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ConfidentialMPTMergeInboxBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + // Verify optional fields +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTMergeInboxTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTMergeInbox{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTMergeInboxTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTMergeInboxBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +} // namespace xrpl::transactions diff --git a/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTSendTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTSendTests.cpp new file mode 100644 index 0000000000..eac4970b16 --- /dev/null +++ b/src/tests/libxrpl/protocol_autogen/transactions/ConfidentialMPTSendTests.cpp @@ -0,0 +1,335 @@ +// Auto-generated unit tests for transaction ConfidentialMPTSend + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace xrpl::transactions { + +// 1 & 4) Set fields via builder setters, build, then read them back via +// wrapper getters. After build(), validate() should succeed. +TEST(TransactionsConfidentialMPTSendTests, BuilderSettersRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTSend")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 1; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const destinationValue = canonical_ACCOUNT(); + auto const senderEncryptedAmountValue = canonical_VL(); + auto const destinationEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const auditorEncryptedAmountValue = canonical_VL(); + auto const zKProofValue = canonical_VL(); + auto const amountCommitmentValue = canonical_VL(); + auto const balanceCommitmentValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + ConfidentialMPTSendBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + destinationValue, + senderEncryptedAmountValue, + destinationEncryptedAmountValue, + issuerEncryptedAmountValue, + zKProofValue, + amountCommitmentValue, + balanceCommitmentValue, + sequenceValue, + feeValue}; + + // Set optional fields + builder.setAuditorEncryptedAmount(auditorEncryptedAmountValue); + builder.setCredentialIDs(credentialIDsValue); + + auto tx = builder.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(tx.validate(reason)) << reason; + + // Verify signing was applied + EXPECT_FALSE(tx.getSigningPubKey().empty()); + EXPECT_TRUE(tx.hasTxnSignature()); + + // Verify common fields + EXPECT_EQ(tx.getAccount(), accountValue); + EXPECT_EQ(tx.getSequence(), sequenceValue); + EXPECT_EQ(tx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = tx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = destinationValue; + auto const actual = tx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = senderEncryptedAmountValue; + auto const actual = tx.getSenderEncryptedAmount(); + expectEqualField(expected, actual, "sfSenderEncryptedAmount"); + } + + { + auto const& expected = destinationEncryptedAmountValue; + auto const actual = tx.getDestinationEncryptedAmount(); + expectEqualField(expected, actual, "sfDestinationEncryptedAmount"); + } + + { + auto const& expected = issuerEncryptedAmountValue; + auto const actual = tx.getIssuerEncryptedAmount(); + expectEqualField(expected, actual, "sfIssuerEncryptedAmount"); + } + + { + auto const& expected = zKProofValue; + auto const actual = tx.getZKProof(); + expectEqualField(expected, actual, "sfZKProof"); + } + + { + auto const& expected = amountCommitmentValue; + auto const actual = tx.getAmountCommitment(); + expectEqualField(expected, actual, "sfAmountCommitment"); + } + + { + auto const& expected = balanceCommitmentValue; + auto const actual = tx.getBalanceCommitment(); + expectEqualField(expected, actual, "sfBalanceCommitment"); + } + + // Verify optional fields + { + auto const& expected = auditorEncryptedAmountValue; + auto const actualOpt = tx.getAuditorEncryptedAmount(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptedAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount"); + EXPECT_TRUE(tx.hasAuditorEncryptedAmount()); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = tx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + EXPECT_TRUE(tx.hasCredentialIDs()); + } +} + +// 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, +// and verify all fields match. +TEST(TransactionsConfidentialMPTSendTests, BuilderFromStTxRoundTrip) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTSendFromTx")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 2; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const destinationValue = canonical_ACCOUNT(); + auto const senderEncryptedAmountValue = canonical_VL(); + auto const destinationEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const auditorEncryptedAmountValue = canonical_VL(); + auto const zKProofValue = canonical_VL(); + auto const amountCommitmentValue = canonical_VL(); + auto const balanceCommitmentValue = canonical_VL(); + auto const credentialIDsValue = canonical_VECTOR256(); + + // Build an initial transaction + ConfidentialMPTSendBuilder initialBuilder{ + accountValue, + mPTokenIssuanceIDValue, + destinationValue, + senderEncryptedAmountValue, + destinationEncryptedAmountValue, + issuerEncryptedAmountValue, + zKProofValue, + amountCommitmentValue, + balanceCommitmentValue, + sequenceValue, + feeValue}; + + initialBuilder.setAuditorEncryptedAmount(auditorEncryptedAmountValue); + initialBuilder.setCredentialIDs(credentialIDsValue); + + auto initialTx = initialBuilder.build(publicKey, secretKey); + + // Create builder from existing STTx + ConfidentialMPTSendBuilder builderFromTx{initialTx.getSTTx()}; + + auto rebuiltTx = builderFromTx.build(publicKey, secretKey); + + std::string reason; + EXPECT_TRUE(rebuiltTx.validate(reason)) << reason; + + // Verify common fields + EXPECT_EQ(rebuiltTx.getAccount(), accountValue); + EXPECT_EQ(rebuiltTx.getSequence(), sequenceValue); + EXPECT_EQ(rebuiltTx.getFee(), feeValue); + + // Verify required fields + { + auto const& expected = mPTokenIssuanceIDValue; + auto const actual = rebuiltTx.getMPTokenIssuanceID(); + expectEqualField(expected, actual, "sfMPTokenIssuanceID"); + } + + { + auto const& expected = destinationValue; + auto const actual = rebuiltTx.getDestination(); + expectEqualField(expected, actual, "sfDestination"); + } + + { + auto const& expected = senderEncryptedAmountValue; + auto const actual = rebuiltTx.getSenderEncryptedAmount(); + expectEqualField(expected, actual, "sfSenderEncryptedAmount"); + } + + { + auto const& expected = destinationEncryptedAmountValue; + auto const actual = rebuiltTx.getDestinationEncryptedAmount(); + expectEqualField(expected, actual, "sfDestinationEncryptedAmount"); + } + + { + auto const& expected = issuerEncryptedAmountValue; + auto const actual = rebuiltTx.getIssuerEncryptedAmount(); + expectEqualField(expected, actual, "sfIssuerEncryptedAmount"); + } + + { + auto const& expected = zKProofValue; + auto const actual = rebuiltTx.getZKProof(); + expectEqualField(expected, actual, "sfZKProof"); + } + + { + auto const& expected = amountCommitmentValue; + auto const actual = rebuiltTx.getAmountCommitment(); + expectEqualField(expected, actual, "sfAmountCommitment"); + } + + { + auto const& expected = balanceCommitmentValue; + auto const actual = rebuiltTx.getBalanceCommitment(); + expectEqualField(expected, actual, "sfBalanceCommitment"); + } + + // Verify optional fields + { + auto const& expected = auditorEncryptedAmountValue; + auto const actualOpt = rebuiltTx.getAuditorEncryptedAmount(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptedAmount should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptedAmount"); + } + + { + auto const& expected = credentialIDsValue; + auto const actualOpt = rebuiltTx.getCredentialIDs(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfCredentialIDs should be present"; + expectEqualField(expected, *actualOpt, "sfCredentialIDs"); + } +} + +// 3) Verify wrapper throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTSendTests, WrapperThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTSend{wrongTx.getSTTx()}, std::runtime_error); +} + +// 4) Verify builder throws when constructed from wrong transaction type. +TEST(TransactionsConfidentialMPTSendTests, BuilderThrowsOnWrongTxType) +{ + // Build a valid transaction of a different type + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const account = calcAccountID(pk); + + AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; + auto wrongTx = wrongBuilder.build(pk, sk); + + EXPECT_THROW(ConfidentialMPTSendBuilder{wrongTx.getSTTx()}, std::runtime_error); +} + +// 5) Build with only required fields and verify optional fields return nullopt. +TEST(TransactionsConfidentialMPTSendTests, OptionalFieldsReturnNullopt) +{ + // Generate a deterministic keypair for signing + auto const [publicKey, secretKey] = + generateKeyPair(KeyType::secp256k1, generateSeed("testConfidentialMPTSendNullopt")); + + // Common transaction fields + auto const accountValue = calcAccountID(publicKey); + std::uint32_t const sequenceValue = 3; + auto const feeValue = canonical_AMOUNT(); + + // Transaction-specific required field values + auto const mPTokenIssuanceIDValue = canonical_UINT192(); + auto const destinationValue = canonical_ACCOUNT(); + auto const senderEncryptedAmountValue = canonical_VL(); + auto const destinationEncryptedAmountValue = canonical_VL(); + auto const issuerEncryptedAmountValue = canonical_VL(); + auto const zKProofValue = canonical_VL(); + auto const amountCommitmentValue = canonical_VL(); + auto const balanceCommitmentValue = canonical_VL(); + + ConfidentialMPTSendBuilder builder{ + accountValue, + mPTokenIssuanceIDValue, + destinationValue, + senderEncryptedAmountValue, + destinationEncryptedAmountValue, + issuerEncryptedAmountValue, + zKProofValue, + amountCommitmentValue, + balanceCommitmentValue, + sequenceValue, + feeValue}; + + // Do NOT set optional fields + + auto tx = builder.build(publicKey, secretKey); + + // Verify optional fields are not present + EXPECT_FALSE(tx.hasAuditorEncryptedAmount()); + EXPECT_FALSE(tx.getAuditorEncryptedAmount().has_value()); + EXPECT_FALSE(tx.hasCredentialIDs()); + EXPECT_FALSE(tx.getCredentialIDs().has_value()); +} + +} // namespace xrpl::transactions diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp index 2ed9e42184..804e5978d0 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp @@ -1,15 +1,13 @@ // Auto-generated unit tests for transaction MPTokenIssuanceSet - -#include - -#include - +#include #include #include -#include -#include #include +#include + +#include +#include #include @@ -35,13 +33,11 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) auto const mPTokenMetadataValue = canonical_VL(); auto const transferFeeValue = canonical_UINT16(); auto const mutableFlagsValue = canonical_UINT32(); + auto const issuerEncryptionKeyValue = canonical_VL(); + auto const auditorEncryptionKeyValue = canonical_VL(); MPTokenIssuanceSetBuilder builder{ - accountValue, - mPTokenIssuanceIDValue, - sequenceValue, - feeValue - }; + accountValue, mPTokenIssuanceIDValue, sequenceValue, feeValue}; // Set optional fields builder.setHolder(holderValue); @@ -49,6 +45,8 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) builder.setMPTokenMetadata(mPTokenMetadataValue); builder.setTransferFee(transferFeeValue); builder.setMutableFlags(mutableFlagsValue); + builder.setIssuerEncryptionKey(issuerEncryptionKeyValue); + builder.setAuditorEncryptionKey(auditorEncryptionKeyValue); auto tx = builder.build(publicKey, secretKey); @@ -112,6 +110,23 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) EXPECT_TRUE(tx.hasMutableFlags()); } + { + auto const& expected = issuerEncryptionKeyValue; + auto const actualOpt = tx.getIssuerEncryptionKey(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfIssuerEncryptionKey should be present"; + expectEqualField(expected, *actualOpt, "sfIssuerEncryptionKey"); + EXPECT_TRUE(tx.hasIssuerEncryptionKey()); + } + + { + auto const& expected = auditorEncryptionKeyValue; + auto const actualOpt = tx.getAuditorEncryptionKey(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptionKey should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptionKey"); + EXPECT_TRUE(tx.hasAuditorEncryptionKey()); + } } // 2 & 4) Start from an STTx, construct a builder from it, build a new wrapper, @@ -134,20 +149,20 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) auto const mPTokenMetadataValue = canonical_VL(); auto const transferFeeValue = canonical_UINT16(); auto const mutableFlagsValue = canonical_UINT32(); + auto const issuerEncryptionKeyValue = canonical_VL(); + auto const auditorEncryptionKeyValue = canonical_VL(); // Build an initial transaction MPTokenIssuanceSetBuilder initialBuilder{ - accountValue, - mPTokenIssuanceIDValue, - sequenceValue, - feeValue - }; + accountValue, mPTokenIssuanceIDValue, sequenceValue, feeValue}; initialBuilder.setHolder(holderValue); initialBuilder.setDomainID(domainIDValue); initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); initialBuilder.setTransferFee(transferFeeValue); initialBuilder.setMutableFlags(mutableFlagsValue); + initialBuilder.setIssuerEncryptionKey(issuerEncryptionKeyValue); + initialBuilder.setAuditorEncryptionKey(auditorEncryptionKeyValue); auto initialTx = initialBuilder.build(publicKey, secretKey); @@ -207,14 +222,28 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) expectEqualField(expected, *actualOpt, "sfMutableFlags"); } + { + auto const& expected = issuerEncryptionKeyValue; + auto const actualOpt = rebuiltTx.getIssuerEncryptionKey(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfIssuerEncryptionKey should be present"; + expectEqualField(expected, *actualOpt, "sfIssuerEncryptionKey"); + } + + { + auto const& expected = auditorEncryptionKeyValue; + auto const actualOpt = rebuiltTx.getAuditorEncryptionKey(); + ASSERT_TRUE(actualOpt.has_value()) + << "Optional field sfAuditorEncryptionKey should be present"; + expectEqualField(expected, *actualOpt, "sfAuditorEncryptionKey"); + } } // 3) Verify wrapper throws when constructed from wrong transaction type. TEST(TransactionsMPTokenIssuanceSetTests, WrapperThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = - generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongType")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -227,8 +256,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, WrapperThrowsOnWrongTxType) TEST(TransactionsMPTokenIssuanceSetTests, BuilderThrowsOnWrongTxType) { // Build a valid transaction of a different type - auto const [pk, sk] = - generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); + auto const [pk, sk] = generateKeyPair(KeyType::secp256k1, generateSeed("testWrongTypeBuilder")); auto const account = calcAccountID(pk); AccountSetBuilder wrongBuilder{account, 1, canonical_AMOUNT()}; @@ -253,11 +281,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, OptionalFieldsReturnNullopt) auto const mPTokenIssuanceIDValue = canonical_UINT192(); MPTokenIssuanceSetBuilder builder{ - accountValue, - mPTokenIssuanceIDValue, - sequenceValue, - feeValue - }; + accountValue, mPTokenIssuanceIDValue, sequenceValue, feeValue}; // Do NOT set optional fields @@ -274,6 +298,10 @@ TEST(TransactionsMPTokenIssuanceSetTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getTransferFee().has_value()); EXPECT_FALSE(tx.hasMutableFlags()); EXPECT_FALSE(tx.getMutableFlags().has_value()); + EXPECT_FALSE(tx.hasIssuerEncryptionKey()); + EXPECT_FALSE(tx.getIssuerEncryptionKey().has_value()); + EXPECT_FALSE(tx.hasAuditorEncryptionKey()); + EXPECT_FALSE(tx.getAuditorEncryptionKey().has_value()); } -} +} // namespace xrpl::transactions