diff --git a/src/etl/NFTHelpers.cpp b/src/etl/NFTHelpers.cpp index 3593463d..f15915c9 100644 --- a/src/etl/NFTHelpers.cpp +++ b/src/etl/NFTHelpers.cpp @@ -294,14 +294,12 @@ getNFTokenCancelOfferData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx txs.emplace_back(tokenID, txMeta, sttx.getTransactionID()); } - // Deduplicate any transactions based on tokenID/txIdx combo. Can't just - // use txIdx because in this case one tx can cancel offers for several - // NFTs. + // Deduplicate any transactions based on tokenID std::sort(txs.begin(), txs.end(), [](NFTTransactionsData const& a, NFTTransactionsData const& b) { - return a.tokenID < b.tokenID && a.transactionIndex < b.transactionIndex; + return a.tokenID < b.tokenID; }); auto last = std::unique(txs.begin(), txs.end(), [](NFTTransactionsData const& a, NFTTransactionsData const& b) { - return a.tokenID == b.tokenID && a.transactionIndex == b.transactionIndex; + return a.tokenID == b.tokenID; }); txs.erase(last, txs.end()); return {txs, {}}; diff --git a/tests/common/util/TestObject.cpp b/tests/common/util/TestObject.cpp index 29bc78f6..5230241b 100644 --- a/tests/common/util/TestObject.cpp +++ b/tests/common/util/TestObject.cpp @@ -758,7 +758,7 @@ CreateCancelNFTOffersTxWithMetadata( tx.setFieldAmount(ripple::sfFee, amount); tx.setFieldU32(ripple::sfSequence, seq); ripple::STVector256 offers; - offers.resize(2); + offers.resize(nftOffers.size()); std::transform(nftOffers.cbegin(), nftOffers.cend(), offers.begin(), [&](auto const& nftId) { return ripple::uint256{nftId.c_str()}; }); diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index aa770e7c..c9e471c1 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -28,6 +28,7 @@ target_sources( etl/GrpcSourceTests.cpp etl/LedgerPublisherTests.cpp etl/LoadBalancerTests.cpp + etl/NFTHelpersTests.cpp etl/SourceImplTests.cpp etl/SubscriptionSourceTests.cpp etl/TransformerTests.cpp diff --git a/tests/unit/etl/NFTHelpersTests.cpp b/tests/unit/etl/NFTHelpersTests.cpp new file mode 100644 index 00000000..38c9c9bc --- /dev/null +++ b/tests/unit/etl/NFTHelpersTests.cpp @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2024, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include "etl/NFTHelpers.hpp" +#include "util/LoggerFixtures.hpp" +#include "util/TestObject.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +constexpr static auto ACCOUNT = "rM2AGCCCRb373FRuD8wHyUwUsh2dV4BW5Q"; +constexpr static auto NFTID = "0008013AE1CD8B79A8BCB52335CD40DE97401B2D60A828720000099B00000000"; +constexpr static auto NFTID2 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"; +constexpr static auto TX = "13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8"; + +struct NFTHelpersTests : public NoLoggerFixture {}; + +TEST_F(NFTHelpersTests, ConvertDataFromNFTCancelOfferTx) +{ + auto const tx = CreateCancelNFTOffersTxWithMetadata(ACCOUNT, 1, 2, std::vector{NFTID2, NFTID}); + ripple::TxMeta txMeta(ripple::uint256(TX), 1, tx.metadata); + auto const [nftTxs, nftDatas] = + etl::getNFTDataFromTx(txMeta, ripple::STTx(ripple::SerialIter{tx.transaction.data(), tx.transaction.size()})); + + EXPECT_EQ(nftTxs.size(), 2); + EXPECT_FALSE(nftDatas); +} + +TEST_F(NFTHelpersTests, ConvertDataFromNFTCancelOfferTxContainingDuplicateNFT) +{ + auto const tx = + CreateCancelNFTOffersTxWithMetadata(ACCOUNT, 1, 2, std::vector{NFTID2, NFTID, NFTID2, NFTID}); + ripple::TxMeta txMeta(ripple::uint256(TX), 1, tx.metadata); + auto const [nftTxs, nftDatas] = + etl::getNFTDataFromTx(txMeta, ripple::STTx(ripple::SerialIter{tx.transaction.data(), tx.transaction.size()})); + + EXPECT_EQ(nftTxs.size(), 2); + EXPECT_FALSE(nftDatas); +}