fix: Add MPT to txn JSON (#2392)

fixes #2332
This commit is contained in:
Peter Chen
2025-09-04 10:15:47 -07:00
committed by GitHub
parent 08bb619964
commit 9edc26a2a3
6 changed files with 183 additions and 65 deletions

View File

@@ -206,8 +206,10 @@ TransactionFeed::pub(
pubObj[txKey] = rpc::toJson(*tx); pubObj[txKey] = rpc::toJson(*tx);
pubObj[JS(meta)] = rpc::toJson(*meta); pubObj[JS(meta)] = rpc::toJson(*meta);
rpc::insertDeliveredAmount(pubObj[JS(meta)].as_object(), tx, meta, txMeta.date); rpc::insertDeliveredAmount(pubObj[JS(meta)].as_object(), tx, meta, txMeta.date);
rpc::insertDeliverMaxAlias(pubObj[txKey].as_object(), version);
rpc::insertMPTIssuanceID(pubObj[JS(meta)].as_object(), tx, meta); auto& txnPubobj = pubObj[txKey].as_object();
rpc::insertDeliverMaxAlias(txnPubobj, version);
rpc::insertMPTIssuanceID(txnPubobj, meta);
Json::Value nftJson; Json::Value nftJson;
ripple::RPC::insertNFTSyntheticInJson(nftJson, tx, *meta); ripple::RPC::insertNFTSyntheticInJson(nftJson, tx, *meta);

View File

@@ -33,6 +33,7 @@
#include "web/Context.hpp" #include "web/Context.hpp"
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/spawn.hpp> #include <boost/asio/spawn.hpp>
#include <boost/format/format_fwd.hpp> #include <boost/format/format_fwd.hpp>
#include <boost/format/free_funcs.hpp> #include <boost/format/free_funcs.hpp>
@@ -262,7 +263,7 @@ toExpandedJson(
auto metaJson = toJson(*meta); auto metaJson = toJson(*meta);
insertDeliveredAmount(metaJson, txn, meta, blobs.date); insertDeliveredAmount(metaJson, txn, meta, blobs.date);
insertDeliverMaxAlias(txnJson, apiVersion); insertDeliverMaxAlias(txnJson, apiVersion);
insertMPTIssuanceID(metaJson, txn, meta); insertMPTIssuanceID(txnJson, meta);
if (nftEnabled == NFTokenjson::ENABLE) { if (nftEnabled == NFTokenjson::ENABLE) {
Json::Value nftJson; Json::Value nftJson;
@@ -347,14 +348,15 @@ getMPTIssuanceID(std::shared_ptr<ripple::TxMeta const> const& meta)
/** /**
* @brief Check if transaction has a new MPToken created * @brief Check if transaction has a new MPToken created
* *
* @param txn The transaction * @param txnJson The transaction Json
* @param meta The metadata * @param meta The metadata
* @return true if the transaction can have a mpt_issuance_id * @return true if the transaction can have a mpt_issuance_id
*/ */
static bool static bool
canHaveMPTIssuanceID(std::shared_ptr<ripple::STTx const> const& txn, std::shared_ptr<ripple::TxMeta const> const& meta) canHaveMPTIssuanceID(boost::json::object const& txnJson, std::shared_ptr<ripple::TxMeta const> const& meta)
{ {
if (txn->getTxnType() != ripple::ttMPTOKEN_ISSUANCE_CREATE) if (txnJson.at(JS(TransactionType)).is_string() and
not boost::iequals(txnJson.at(JS(TransactionType)).as_string(), JS(MPTokenIssuanceCreate)))
return false; return false;
if (meta->getResultTER() != ripple::tesSUCCESS) if (meta->getResultTER() != ripple::tesSUCCESS)
@@ -364,17 +366,13 @@ canHaveMPTIssuanceID(std::shared_ptr<ripple::STTx const> const& txn, std::shared
} }
bool bool
insertMPTIssuanceID( insertMPTIssuanceID(boost::json::object& txnJson, std::shared_ptr<ripple::TxMeta const> const& meta)
boost::json::object& metaJson,
std::shared_ptr<ripple::STTx const> const& txn,
std::shared_ptr<ripple::TxMeta const> const& meta
)
{ {
if (!canHaveMPTIssuanceID(txn, meta)) if (!canHaveMPTIssuanceID(txnJson, meta))
return false; return false;
if (auto const id = getMPTIssuanceID(meta)) { if (auto const id = getMPTIssuanceID(meta)) {
metaJson[JS(mpt_issuance_id)] = ripple::to_string(*id); txnJson[JS(mpt_issuance_id)] = ripple::to_string(*id);
return true; return true;
} }

View File

@@ -202,17 +202,12 @@ insertDeliveredAmount(
/** /**
* @brief Add "mpt_issuance_id" into MPTokenIssuanceCreate transaction json. * @brief Add "mpt_issuance_id" into MPTokenIssuanceCreate transaction json.
* *
* @param metaJson The metadata json object to add "MPTokenIssuanceID" * @param txnJson The transaction Json object
* @param txn The transaction object
* @param meta The metadata object * @param meta The metadata object
* @return true if the "mpt_issuance_id" is added to the metadata json object * @return true if the "mpt_issuance_id" is added to the metadata json object
*/ */
bool bool
insertMPTIssuanceID( insertMPTIssuanceID(boost::json::object& txnJson, std::shared_ptr<ripple::TxMeta const> const& meta);
boost::json::object& metaJson,
std::shared_ptr<ripple::STTx const> const& txn,
std::shared_ptr<ripple::TxMeta const> const& meta
);
/** /**
* @brief Convert STBase object to JSON * @brief Convert STBase object to JSON

View File

@@ -60,6 +60,7 @@
namespace { namespace {
constexpr auto kINDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC"; constexpr auto kINDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
ripple::Slice const kSLICE("test", 4);
} // namespace } // namespace
ripple::AccountID ripple::AccountID
@@ -183,9 +184,7 @@ createPaymentTransactionObject(
auto account2 = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId2)); auto account2 = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId2));
obj.setAccountID(ripple::sfDestination, account2.value()); obj.setAccountID(ripple::sfDestination, account2.value());
obj.setFieldU32(ripple::sfSequence, seq); obj.setFieldU32(ripple::sfSequence, seq);
char const* key = "test"; obj.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
obj.setFieldVL(ripple::sfSigningPubKey, slice);
return obj; return obj;
} }
@@ -697,9 +696,7 @@ createMintNftTxWithMetadata(
// required field for ttNFTOKEN_MINT // required field for ttNFTOKEN_MINT
tx.setFieldU32(ripple::sfNFTokenTaxon, nfTokenTaxon); tx.setFieldU32(ripple::sfNFTokenTaxon, nfTokenTaxon);
tx.setFieldU32(ripple::sfSequence, seq); tx.setFieldU32(ripple::sfSequence, seq);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
ripple::STObject metaObj(ripple::sfTransactionMetaData); ripple::STObject metaObj(ripple::sfTransactionMetaData);
@@ -762,9 +759,7 @@ createMintNftTxWithMetadataOfCreatedNode(
// required field for ttNFTOKEN_MINT // required field for ttNFTOKEN_MINT
tx.setFieldU32(ripple::sfNFTokenTaxon, nfTokenTaxon); tx.setFieldU32(ripple::sfNFTokenTaxon, nfTokenTaxon);
tx.setFieldU32(ripple::sfSequence, seq); tx.setFieldU32(ripple::sfSequence, seq);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
if (uri) if (uri)
tx.setFieldVL(ripple::sfURI, ripple::Slice(uri->data(), uri->size())); tx.setFieldVL(ripple::sfURI, ripple::Slice(uri->data(), uri->size()));
@@ -820,9 +815,7 @@ createNftModifyTxWithMetadata(std::string_view accountId, std::string_view nftID
tx.setFieldAmount(ripple::sfFee, amount); tx.setFieldAmount(ripple::sfFee, amount);
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID}); tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
tx.setFieldU32(ripple::sfSequence, 100); tx.setFieldU32(ripple::sfSequence, 100);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
if (!uri.empty()) // sfURI should be absent if empty if (!uri.empty()) // sfURI should be absent if empty
tx.setFieldVL(ripple::sfURI, uri); tx.setFieldVL(ripple::sfURI, uri);
@@ -880,9 +873,7 @@ createNftBurnTxWithMetadataOfDeletedNode(std::string_view accountId, std::string
tx.setFieldAmount(ripple::sfFee, amount); tx.setFieldAmount(ripple::sfFee, amount);
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID}); tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
tx.setFieldU32(ripple::sfSequence, 100); tx.setFieldU32(ripple::sfSequence, 100);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
ripple::STObject metaObj(ripple::sfTransactionMetaData); ripple::STObject metaObj(ripple::sfTransactionMetaData);
@@ -927,9 +918,7 @@ createNftBurnTxWithMetadataOfModifiedNode(std::string_view accountId, std::strin
tx.setFieldAmount(ripple::sfFee, amount); tx.setFieldAmount(ripple::sfFee, amount);
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID}); tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
tx.setFieldU32(ripple::sfSequence, 100); tx.setFieldU32(ripple::sfSequence, 100);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
ripple::STObject metaObj(ripple::sfTransactionMetaData); ripple::STObject metaObj(ripple::sfTransactionMetaData);
@@ -976,9 +965,7 @@ createAcceptNftBuyerOfferTxWithMetadata(
tx.setFieldAmount(ripple::sfFee, amount); tx.setFieldAmount(ripple::sfFee, amount);
tx.setFieldU32(ripple::sfSequence, seq); tx.setFieldU32(ripple::sfSequence, seq);
tx.setFieldH256(ripple::sfNFTokenBuyOffer, ripple::uint256{offerId}); tx.setFieldH256(ripple::sfNFTokenBuyOffer, ripple::uint256{offerId});
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
// create deletedNode with ltNFTOKEN_OFFER // create deletedNode with ltNFTOKEN_OFFER
@@ -1025,9 +1012,7 @@ createAcceptNftSellerOfferTxWithMetadata(
tx.setFieldAmount(ripple::sfFee, amount); tx.setFieldAmount(ripple::sfFee, amount);
tx.setFieldU32(ripple::sfSequence, seq); tx.setFieldU32(ripple::sfSequence, seq);
tx.setFieldH256(ripple::sfNFTokenSellOffer, ripple::uint256{offerId}); tx.setFieldH256(ripple::sfNFTokenSellOffer, ripple::uint256{offerId});
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
// create deletedNode with ltNFTOKEN_OFFER // create deletedNode with ltNFTOKEN_OFFER
@@ -1121,9 +1106,7 @@ createCancelNftOffersTxWithMetadata(
return ripple::uint256{nftId.c_str()}; return ripple::uint256{nftId.c_str()};
}); });
tx.setFieldV256(ripple::sfNFTokenOffers, offers); tx.setFieldV256(ripple::sfNFTokenOffers, offers);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
// create deletedNode with ltNFTOKEN_OFFER // create deletedNode with ltNFTOKEN_OFFER
@@ -1172,9 +1155,7 @@ createCreateNftOfferTxWithMetadata(
tx.setFieldAmount(ripple::sfAmount, price); tx.setFieldAmount(ripple::sfAmount, price);
tx.setFieldU32(ripple::sfSequence, seq); tx.setFieldU32(ripple::sfSequence, seq);
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftId}); tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftId});
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
// meta // meta
// create createdNode with LedgerIndex // create createdNode with LedgerIndex
@@ -1219,9 +1200,7 @@ createOracleSetTxWithMetadata(
tx.setFieldU32(ripple::sfLastUpdateTime, lastUpdateTime); tx.setFieldU32(ripple::sfLastUpdateTime, lastUpdateTime);
tx.setFieldU32(ripple::sfOracleDocumentID, docId); tx.setFieldU32(ripple::sfOracleDocumentID, docId);
tx.setFieldU32(ripple::sfSequence, seq); tx.setFieldU32(ripple::sfSequence, seq);
char const* key = "test"; tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
ripple::Slice const slice(key, 4);
tx.setFieldVL(ripple::sfSigningPubKey, slice);
tx.setFieldArray(ripple::sfPriceDataSeries, priceDataSeries); tx.setFieldArray(ripple::sfPriceDataSeries, priceDataSeries);
// meta // meta
@@ -1499,6 +1478,56 @@ createMpTokenObject(std::string_view accountId, ripple::uint192 issuanceID, std:
return mptoken; return mptoken;
} }
ripple::STObject
createMPTIssuanceCreateTx(std::string_view accountId, uint32_t fee, uint32_t seq)
{
ripple::STObject tx(ripple::sfTransaction);
tx.setFieldU16(ripple::sfTransactionType, ripple::ttMPTOKEN_ISSUANCE_CREATE);
tx.setAccountID(ripple::sfAccount, getAccountIdWithString(accountId));
tx.setFieldAmount(ripple::sfFee, ripple::STAmount(fee, false));
tx.setFieldU32(ripple::sfSequence, seq);
tx.setFieldVL(ripple::sfSigningPubKey, kSLICE);
return tx;
}
data::TransactionAndMetadata
createMPTIssuanceCreateTxWithMetadata(std::string_view accountId, uint32_t fee, uint32_t seq)
{
ripple::STObject tx = createMPTIssuanceCreateTx(accountId, fee, seq);
ripple::STObject metaObj(ripple::sfTransactionMetaData);
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
ripple::STObject newFields(ripple::sfNewFields);
newFields.setAccountID(ripple::sfIssuer, getAccountIdWithString(accountId));
newFields.setFieldU16(ripple::sfLedgerEntryType, ripple::ltMPTOKEN_ISSUANCE);
newFields.setFieldU32(ripple::sfFlags, 0);
newFields.setFieldU32(ripple::sfSequence, seq);
newFields.setFieldU64(ripple::sfOwnerNode, 0);
newFields.setFieldU64(ripple::sfMaximumAmount, 0);
newFields.setFieldU64(ripple::sfOutstandingAmount, 0);
newFields.setFieldH256(ripple::sfPreviousTxnID, ripple::uint256{});
newFields.setFieldU32(ripple::sfPreviousTxnLgrSeq, 0);
std::string_view metadata = "test-meta";
ripple::Slice const sliceMetadata(metadata.data(), metadata.size());
newFields.setFieldVL(ripple::sfMPTokenMetadata, sliceMetadata);
ripple::STObject createdNode(ripple::sfCreatedNode);
createdNode.setFieldU16(ripple::sfLedgerEntryType, ripple::ltMPTOKEN_ISSUANCE);
createdNode.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{});
createdNode.emplace_back(std::move(newFields));
ripple::STArray affectedNodes(ripple::sfAffectedNodes);
affectedNodes.push_back(std::move(createdNode));
metaObj.setFieldArray(ripple::sfAffectedNodes, affectedNodes);
data::TransactionAndMetadata ret;
ret.transaction = tx.getSerializer().peekData();
ret.metadata = metaObj.getSerializer().peekData();
return ret;
}
ripple::STObject ripple::STObject
createPermissionedDomainObject( createPermissionedDomainObject(
std::string_view accountId, std::string_view accountId,

View File

@@ -456,6 +456,12 @@ createMptIssuanceObject(std::string_view accountId, std::uint32_t seq, std::stri
[[nodiscard]] ripple::STObject [[nodiscard]] ripple::STObject
createMpTokenObject(std::string_view accountId, ripple::uint192 issuanceID, std::uint64_t mptAmount = 1); createMpTokenObject(std::string_view accountId, ripple::uint192 issuanceID, std::uint64_t mptAmount = 1);
[[nodiscard]] ripple::STObject
createMPTIssuanceCreateTx(std::string_view accountId, uint32_t fee, uint32_t seq);
[[nodiscard]] data::TransactionAndMetadata
createMPTIssuanceCreateTxWithMetadata(std::string_view accountId, uint32_t fee, uint32_t seq);
[[nodiscard]] ripple::STObject [[nodiscard]] ripple::STObject
createPermissionedDomainObject( createPermissionedDomainObject(
std::string_view accountId, std::string_view accountId,

View File

@@ -37,6 +37,7 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
using namespace rpc; using namespace rpc;
@@ -55,6 +56,7 @@ constexpr auto kNFT_ID = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76
constexpr auto kNFT_ID2 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"; constexpr auto kNFT_ID2 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA";
constexpr auto kNFT_ID3 = "15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"; constexpr auto kNFT_ID3 = "15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF";
constexpr auto kINDEX = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC322"; constexpr auto kINDEX = "E6DBAFC99223B42257915A63DFC6B0C032D4070F9A574B255AD97466726FC322";
constexpr auto kMPT_ISSUANCE_ID = "000000014B4E9C06F24296074F7BC48F92A97916C6DC5EA9";
} // namespace } // namespace
@@ -907,9 +909,7 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndex)
); );
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ - 1); auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ - 1);
EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); EXPECT_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ - 1, _)).WillOnce(Return(ledgerHeader));
ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ - 1, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*mockETLServicePtr_, getETLState).WillByDefault(Return(etl::ETLState{})); ON_CALL(*mockETLServicePtr_, getETLState).WillByDefault(Return(etl::ETLState{}));
runSpawn([&, this](auto yield) { runSpawn([&, this](auto yield) {
@@ -937,8 +937,7 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndex)
TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerIntIndex) TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerIntIndex)
{ {
EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); EXPECT_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ - 1, _)).WillOnce(Return(std::nullopt));
ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ - 1, _)).WillByDefault(Return(std::nullopt));
runSpawn([&, this](auto yield) { runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}};
@@ -962,8 +961,7 @@ TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerIntIndex)
TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerStringIndex) TEST_F(RPCAccountTxHandlerTest, SpecificNonexistLedgerStringIndex)
{ {
EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); EXPECT_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ - 1, _)).WillOnce(Return(std::nullopt));
ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ - 1, _)).WillByDefault(Return(std::nullopt));
runSpawn([&, this](auto yield) { runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}}; auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}};
@@ -1000,11 +998,10 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerHash)
testing::Optional(testing::Eq(TransactionsCursor{kMAX_SEQ - 1, INT32_MAX})), testing::Optional(testing::Eq(TransactionsCursor{kMAX_SEQ - 1, INT32_MAX})),
testing::_ testing::_
) )
) );
.Times(1);
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ - 1); auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ - 1);
EXPECT_CALL(*backend_, fetchLedgerByHash).Times(1); EXPECT_CALL(*backend_, fetchLedgerByHash);
ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader)); ON_CALL(*backend_, fetchLedgerByHash(ripple::uint256{kLEDGER_HASH}, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*mockETLServicePtr_, getETLState).WillByDefault(Return(etl::ETLState{})); ON_CALL(*mockETLServicePtr_, getETLState).WillByDefault(Return(etl::ETLState{}));
@@ -1050,8 +1047,7 @@ TEST_F(RPCAccountTxHandlerTest, SpecificLedgerIndexValidated)
); );
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ); auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMAX_SEQ);
EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(1); EXPECT_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)).WillOnce(Return(ledgerHeader));
ON_CALL(*backend_, fetchLedgerBySequence(kMAX_SEQ, _)).WillByDefault(Return(ledgerHeader));
ON_CALL(*mockETLServicePtr_, getETLState).WillByDefault(Return(etl::ETLState{})); ON_CALL(*mockETLServicePtr_, getETLState).WillByDefault(Return(etl::ETLState{}));
@@ -1599,6 +1595,98 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v2)
}); });
} }
TEST_F(RPCAccountTxHandlerTest, MPTTxs_API_v2)
{
auto const out = fmt::format(
R"JSON({{
"account": "{}",
"ledger_index_min": 10,
"ledger_index_max": 30,
"transactions": [
{{
"meta": {{
"AffectedNodes": [
{{
"CreatedNode": {{
"LedgerEntryType": "MPTokenIssuance",
"LedgerIndex": "0000000000000000000000000000000000000000000000000000000000000000",
"NewFields": {{
"Flags": 0,
"Issuer": "{}",
"LedgerEntryType": "MPTokenIssuance",
"MPTokenMetadata": "746573742D6D657461",
"MaximumAmount": "0",
"OutstandingAmount": "0",
"OwnerNode": "0",
"PreviousTxnID": "0000000000000000000000000000000000000000000000000000000000000000",
"PreviousTxnLgrSeq": 0,
"Sequence": 1
}}
}}
}}
],
"TransactionIndex": 0,
"TransactionResult": "tesSUCCESS"
}},
"hash": "A52221F4003C281D3C83F501F418B55A1F9DC1C6A129EF13E1A8F0E5C008DAE3",
"ledger_index": 11,
"ledger_hash": "{}",
"close_time_iso": "2000-01-01T00:00:00Z",
"tx_json": {{
"Account": "{}",
"Fee": "50",
"Sequence": 1,
"SigningPubKey": "74657374",
"TransactionType": "MPTokenIssuanceCreate",
"mpt_issuance_id": "{}",
"ledger_index": 11,
"ctid": "C000000B00000000",
"date": 1
}},
"validated": true
}}
],
"validated": true
}})JSON",
kACCOUNT,
kACCOUNT,
kLEDGER_HASH,
kACCOUNT,
kMPT_ISSUANCE_ID
);
auto mptTx = createMPTIssuanceCreateTxWithMetadata(kACCOUNT, 50, 1);
mptTx.ledgerSequence = kMIN_SEQ + 1;
mptTx.date = 1;
auto transactions = std::vector<TransactionAndMetadata>{std::move(mptTx)};
auto const transCursor = TransactionsAndCursor{.txns = std::move(transactions), .cursor = std::nullopt};
EXPECT_CALL(*backend_, fetchAccountTransactions).WillOnce(Return(transCursor));
auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, kMIN_SEQ + 1);
EXPECT_CALL(*backend_, fetchLedgerBySequence(kMIN_SEQ + 1, _)).WillOnce(Return(ledgerHeader));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{AccountTxHandler{backend_, mockETLServicePtr_}};
static auto const kINPUT = json::parse(
fmt::format(
R"JSON({{
"account": "{}",
"ledger_index_min": {},
"ledger_index_max": {}
}})JSON",
kACCOUNT,
kMIN_SEQ,
kMAX_SEQ
)
);
auto const output = handler.process(kINPUT, Context{.yield = yield, .apiVersion = 2u});
ASSERT_TRUE(output);
EXPECT_EQ(*output.result, json::parse(out));
});
}
struct AccountTxTransactionBundle { struct AccountTxTransactionBundle {
std::string testName; std::string testName;
std::string testJson; std::string testJson;