mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 11:55:51 +00:00
feat: ETLng MPT support (#2154)
This commit is contained in:
@@ -39,6 +39,11 @@
|
||||
#include "etlng/LoadBalancerInterface.hpp"
|
||||
#include "etlng/impl/LedgerPublisher.hpp"
|
||||
#include "etlng/impl/TaskManagerProvider.hpp"
|
||||
#include "etlng/impl/ext/Cache.hpp"
|
||||
#include "etlng/impl/ext/Core.hpp"
|
||||
#include "etlng/impl/ext/MPT.hpp"
|
||||
#include "etlng/impl/ext/NFT.hpp"
|
||||
#include "etlng/impl/ext/Successor.hpp"
|
||||
#include "feed/SubscriptionManagerInterface.hpp"
|
||||
#include "util/Assert.hpp"
|
||||
#include "util/Constants.hpp"
|
||||
@@ -96,7 +101,8 @@ ETLService::makeETLService(
|
||||
etlng::impl::CacheExt{cacheUpdater},
|
||||
etlng::impl::CoreExt{backend},
|
||||
etlng::impl::SuccessorExt{backend, backend->cache()},
|
||||
etlng::impl::NFTExt{backend}
|
||||
etlng::impl::NFTExt{backend},
|
||||
etlng::impl::MPTExt{backend}
|
||||
),
|
||||
amendmentBlockHandler
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ target_sources(
|
||||
impl/TaskManager.cpp
|
||||
impl/ext/Cache.cpp
|
||||
impl/ext/Core.cpp
|
||||
impl/ext/MPT.cpp
|
||||
impl/ext/NFT.cpp
|
||||
impl/ext/Successor.cpp
|
||||
)
|
||||
|
||||
@@ -36,14 +36,14 @@ CacheExt::CacheExt(std::shared_ptr<CacheUpdaterInterface> cacheUpdater) : cacheU
|
||||
}
|
||||
|
||||
void
|
||||
CacheExt::onLedgerData(model::LedgerData const& data) const
|
||||
CacheExt::onLedgerData(model::LedgerData const& data)
|
||||
{
|
||||
cacheUpdater_->update(data);
|
||||
LOG(log_.trace()) << "got data. objects cnt = " << data.objects.size();
|
||||
cacheUpdater_->update(data);
|
||||
}
|
||||
|
||||
void
|
||||
CacheExt::onInitialData(model::LedgerData const& data) const
|
||||
CacheExt::onInitialData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.trace()) << "got initial data. objects cnt = " << data.objects.size();
|
||||
cacheUpdater_->update(data);
|
||||
@@ -52,7 +52,6 @@ CacheExt::onInitialData(model::LedgerData const& data) const
|
||||
|
||||
void
|
||||
CacheExt::onInitialObjects(uint32_t seq, std::vector<model::Object> const& objs, [[maybe_unused]] std::string lastKey)
|
||||
const
|
||||
{
|
||||
LOG(log_.trace()) << "got initial objects cnt = " << objs.size();
|
||||
cacheUpdater_->update(seq, objs);
|
||||
|
||||
@@ -40,13 +40,13 @@ public:
|
||||
CacheExt(std::shared_ptr<CacheUpdaterInterface> cacheUpdater);
|
||||
|
||||
void
|
||||
onLedgerData(model::LedgerData const& data) const;
|
||||
onLedgerData(model::LedgerData const& data);
|
||||
|
||||
void
|
||||
onInitialData(model::LedgerData const& data) const;
|
||||
onInitialData(model::LedgerData const& data);
|
||||
|
||||
void
|
||||
onInitialObjects(uint32_t seq, std::vector<model::Object> const& objs, [[maybe_unused]] std::string lastKey) const;
|
||||
onInitialObjects(uint32_t seq, std::vector<model::Object> const& objs, [[maybe_unused]] std::string lastKey);
|
||||
|
||||
// We want cache updates through ETL if we are a potential writer but currently are not writing to DB
|
||||
static bool
|
||||
|
||||
@@ -34,7 +34,7 @@ CoreExt::CoreExt(std::shared_ptr<BackendInterface> backend) : backend_(std::move
|
||||
}
|
||||
|
||||
void
|
||||
CoreExt::onLedgerData(model::LedgerData const& data) const
|
||||
CoreExt::onLedgerData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.debug()) << "Loading ledger data for " << data.seq;
|
||||
backend_->writeLedger(data.header, auto{data.rawHeader});
|
||||
@@ -42,7 +42,7 @@ CoreExt::onLedgerData(model::LedgerData const& data) const
|
||||
}
|
||||
|
||||
void
|
||||
CoreExt::onInitialData(model::LedgerData const& data) const
|
||||
CoreExt::onInitialData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.info()) << "Loading initial ledger data for " << data.seq;
|
||||
backend_->writeLedger(data.header, auto{data.rawHeader});
|
||||
@@ -50,21 +50,21 @@ CoreExt::onInitialData(model::LedgerData const& data) const
|
||||
}
|
||||
|
||||
void
|
||||
CoreExt::onInitialObject(uint32_t seq, model::Object const& obj) const
|
||||
CoreExt::onInitialObject(uint32_t seq, model::Object const& obj)
|
||||
{
|
||||
LOG(log_.trace()) << "got initial OBJ = " << obj.key << " for seq " << seq;
|
||||
backend_->writeLedgerObject(auto{obj.keyRaw}, seq, auto{obj.dataRaw});
|
||||
}
|
||||
|
||||
void
|
||||
CoreExt::onObject(uint32_t seq, model::Object const& obj) const
|
||||
CoreExt::onObject(uint32_t seq, model::Object const& obj)
|
||||
{
|
||||
LOG(log_.trace()) << "got OBJ = " << obj.key << " for seq " << seq;
|
||||
backend_->writeLedgerObject(auto{obj.keyRaw}, seq, auto{obj.dataRaw});
|
||||
}
|
||||
|
||||
void
|
||||
CoreExt::insertTransactions(model::LedgerData const& data) const
|
||||
CoreExt::insertTransactions(model::LedgerData const& data)
|
||||
{
|
||||
for (auto const& txn : data.transactions) {
|
||||
LOG(log_.trace()) << "Inserting transaction = " << txn.sttx.getTransactionID();
|
||||
|
||||
@@ -39,20 +39,20 @@ public:
|
||||
CoreExt(std::shared_ptr<BackendInterface> backend);
|
||||
|
||||
void
|
||||
onLedgerData(model::LedgerData const& data) const;
|
||||
onLedgerData(model::LedgerData const& data);
|
||||
|
||||
void
|
||||
onInitialData(model::LedgerData const& data) const;
|
||||
onInitialData(model::LedgerData const& data);
|
||||
|
||||
void
|
||||
onInitialObject(uint32_t seq, model::Object const& obj) const;
|
||||
onInitialObject(uint32_t seq, model::Object const& obj);
|
||||
|
||||
void
|
||||
onObject(uint32_t seq, model::Object const& obj) const;
|
||||
onObject(uint32_t seq, model::Object const& obj);
|
||||
|
||||
private:
|
||||
void
|
||||
insertTransactions(model::LedgerData const& data) const;
|
||||
insertTransactions(model::LedgerData const& data);
|
||||
};
|
||||
|
||||
} // namespace etlng::impl
|
||||
|
||||
81
src/etlng/impl/ext/MPT.cpp
Normal file
81
src/etlng/impl/ext/MPT.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, 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 "etlng/impl/ext/MPT.hpp"
|
||||
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "data/DBHelpers.hpp"
|
||||
#include "etl/MPTHelpers.hpp"
|
||||
#include "etlng/Models.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace etlng::impl {
|
||||
|
||||
MPTExt::MPTExt(std::shared_ptr<BackendInterface> backend) : backend_(std::move(backend))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MPTExt::onLedgerData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.trace()) << "got TXS cnt = " << data.transactions.size() << "; OBJS size = " << data.objects.size();
|
||||
writeMPTHoldersFromTransactions(data);
|
||||
}
|
||||
|
||||
void
|
||||
MPTExt::onInitialObject(uint32_t, model::Object const& obj)
|
||||
{
|
||||
LOG(log_.trace()) << "got initial object with key: " << ripple::strHex(obj.key);
|
||||
if (auto const mptHolder = etl::getMPTHolderFromObj(obj.keyRaw, obj.dataRaw); mptHolder.has_value())
|
||||
backend_->writeMPTHolders({*mptHolder});
|
||||
}
|
||||
|
||||
void
|
||||
MPTExt::onInitialData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.trace()) << "got initial TXS cnt = " << data.transactions.size();
|
||||
writeMPTHoldersFromTransactions(data);
|
||||
}
|
||||
|
||||
void
|
||||
MPTExt::writeMPTHoldersFromTransactions(model::LedgerData const& data)
|
||||
{
|
||||
std::vector<MPTHolderData> holders;
|
||||
|
||||
for (auto const& tx : data.transactions) {
|
||||
if (auto const mptHolder = etl::getMPTHolderFromTx(tx.meta, tx.sttx); mptHolder.has_value())
|
||||
holders.push_back(*mptHolder);
|
||||
}
|
||||
|
||||
if (not holders.empty())
|
||||
backend_->writeMPTHolders(holders);
|
||||
}
|
||||
|
||||
} // namespace etlng::impl
|
||||
57
src/etlng/impl/ext/MPT.hpp
Normal file
57
src/etlng/impl/ext/MPT.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "etlng/Models.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace etlng::impl {
|
||||
|
||||
class MPTExt {
|
||||
std::shared_ptr<BackendInterface> backend_;
|
||||
util::Logger log_{"ETL"};
|
||||
|
||||
public:
|
||||
explicit MPTExt(std::shared_ptr<BackendInterface> backend);
|
||||
|
||||
void
|
||||
onLedgerData(model::LedgerData const& data);
|
||||
|
||||
void
|
||||
onInitialObject(uint32_t seq, model::Object const& obj);
|
||||
|
||||
void
|
||||
onInitialData(model::LedgerData const& data);
|
||||
|
||||
private:
|
||||
void
|
||||
writeMPTHoldersFromTransactions(model::LedgerData const& data);
|
||||
};
|
||||
|
||||
} // namespace etlng::impl
|
||||
@@ -37,27 +37,28 @@ NFTExt::NFTExt(std::shared_ptr<BackendInterface> backend) : backend_(std::move(b
|
||||
}
|
||||
|
||||
void
|
||||
NFTExt::onLedgerData(model::LedgerData const& data) const
|
||||
NFTExt::onLedgerData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.trace()) << "got TXS cnt = " << data.transactions.size() << "; OBJS size = " << data.objects.size();
|
||||
writeNFTs(data);
|
||||
}
|
||||
|
||||
void
|
||||
NFTExt::onInitialObject(uint32_t seq, model::Object const& obj) const
|
||||
NFTExt::onInitialObject(uint32_t seq, model::Object const& obj)
|
||||
{
|
||||
LOG(log_.trace()) << "got initial object with key = " << obj.key;
|
||||
backend_->writeNFTs(etl::getNFTDataFromObj(seq, obj.keyRaw, obj.dataRaw));
|
||||
}
|
||||
|
||||
void
|
||||
NFTExt::onInitialData(model::LedgerData const& data) const
|
||||
NFTExt::onInitialData(model::LedgerData const& data)
|
||||
{
|
||||
LOG(log_.trace()) << "got initial TXS cnt = " << data.transactions.size();
|
||||
writeNFTs(data);
|
||||
}
|
||||
|
||||
void
|
||||
NFTExt::writeNFTs(model::LedgerData const& data) const
|
||||
NFTExt::writeNFTs(model::LedgerData const& data)
|
||||
{
|
||||
std::vector<NFTsData> nfts;
|
||||
std::vector<NFTTransactionsData> nftTxs;
|
||||
|
||||
@@ -36,17 +36,17 @@ public:
|
||||
NFTExt(std::shared_ptr<BackendInterface> backend);
|
||||
|
||||
void
|
||||
onLedgerData(model::LedgerData const& data) const;
|
||||
onLedgerData(model::LedgerData const& data);
|
||||
|
||||
void
|
||||
onInitialObject(uint32_t seq, model::Object const& obj) const;
|
||||
onInitialObject(uint32_t seq, model::Object const& obj);
|
||||
|
||||
void
|
||||
onInitialData(model::LedgerData const& data) const;
|
||||
onInitialData(model::LedgerData const& data);
|
||||
|
||||
private:
|
||||
void
|
||||
writeNFTs(model::LedgerData const& data) const;
|
||||
writeNFTs(model::LedgerData const& data);
|
||||
};
|
||||
|
||||
} // namespace etlng::impl
|
||||
|
||||
@@ -56,18 +56,18 @@ constinit auto const kRAW_HEADER =
|
||||
namespace util {
|
||||
|
||||
std::pair<std::string, std::string>
|
||||
createNftTxAndMetaBlobs(std::string metaStr, std::string txnStr)
|
||||
createTxAndMetaBlobs(std::string metaStr, std::string txnStr)
|
||||
{
|
||||
return {hexStringToBinaryString(metaStr), hexStringToBinaryString(txnStr)};
|
||||
}
|
||||
|
||||
std::pair<ripple::STTx, ripple::TxMeta>
|
||||
createNftTxAndMeta(std::string hashStr, std::string metaStr, std::string txnStr)
|
||||
createTxAndMeta(std::string hashStr, std::string metaStr, std::string txnStr)
|
||||
{
|
||||
ripple::uint256 hash;
|
||||
EXPECT_TRUE(hash.parseHex(hashStr));
|
||||
|
||||
auto const [metaBlob, txnBlob] = createNftTxAndMetaBlobs(metaStr, txnStr);
|
||||
auto const [metaBlob, txnBlob] = createTxAndMetaBlobs(metaStr, txnStr);
|
||||
|
||||
ripple::SerialIter it{txnBlob.data(), txnBlob.size()};
|
||||
return {ripple::STTx{it}, ripple::TxMeta{hash, kSEQ, metaBlob}};
|
||||
@@ -76,7 +76,7 @@ createNftTxAndMeta(std::string hashStr, std::string metaStr, std::string txnStr)
|
||||
etlng::model::Transaction
|
||||
createTransaction(ripple::TxType type, std::string hashStr, std::string metaStr, std::string txnStr)
|
||||
{
|
||||
auto const [sttx, meta] = createNftTxAndMeta(hashStr, metaStr, txnStr);
|
||||
auto const [sttx, meta] = createTxAndMeta(hashStr, metaStr, txnStr);
|
||||
return {
|
||||
.raw = "",
|
||||
.metaRaw = "",
|
||||
@@ -161,6 +161,28 @@ createObjectWithTwoNFTs()
|
||||
};
|
||||
}
|
||||
|
||||
etlng::model::Object
|
||||
createObjectWithMPT()
|
||||
{
|
||||
constexpr auto kACCOUNT = "rM2AGCCCRb373FRuD8wHyUwUsh2dV4BW5Q";
|
||||
|
||||
auto const account = getAccountIdWithString(kACCOUNT);
|
||||
auto const mptokenObject = createMpTokenObject(kACCOUNT, ripple::makeMptID(2, getAccountIdWithString(kACCOUNT)));
|
||||
|
||||
return {
|
||||
.key = {},
|
||||
.keyRaw = std::string(reinterpret_cast<char const*>(account.data()), ripple::AccountID::size()),
|
||||
.data = {},
|
||||
.dataRaw = std::string(
|
||||
static_cast<char const*>(mptokenObject.getSerializer().getDataPtr()),
|
||||
mptokenObject.getSerializer().getDataLength()
|
||||
),
|
||||
.successor = "",
|
||||
.predecessor = "",
|
||||
.type = etlng::model::Object::ModType::Created,
|
||||
};
|
||||
}
|
||||
|
||||
etlng::model::BookSuccessor
|
||||
createSuccessor()
|
||||
{
|
||||
@@ -184,7 +206,7 @@ createDataAndDiff()
|
||||
|
||||
{
|
||||
auto original = org::xrpl::rpc::v1::TransactionAndMetadata();
|
||||
auto const [metaRaw, txRaw] = createNftTxAndMetaBlobs();
|
||||
auto const [metaRaw, txRaw] = createTxAndMetaBlobs();
|
||||
original.set_transaction_blob(txRaw);
|
||||
original.set_metadata_blob(metaRaw);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
@@ -230,7 +252,7 @@ createData()
|
||||
|
||||
{
|
||||
auto original = org::xrpl::rpc::v1::TransactionAndMetadata();
|
||||
auto const [metaRaw, txRaw] = createNftTxAndMetaBlobs();
|
||||
auto const [metaRaw, txRaw] = createTxAndMetaBlobs();
|
||||
original.set_transaction_blob(txRaw);
|
||||
original.set_metadata_blob(metaRaw);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
|
||||
@@ -144,10 +144,10 @@ static constexpr auto kDEFAULT_HASH = "6C7F69A6D25A13AC4A2E9145999F45D4674F93990
|
||||
static constexpr auto kDEFAULT_OBJ_KEY = "B00AA769C00726371689ED66A7CF57C2502F1BF4BDFF2ACADF67A2A7B5E8960D";
|
||||
|
||||
[[maybe_unused, nodiscard]] std::pair<std::string, std::string>
|
||||
createNftTxAndMetaBlobs(std::string metaStr = kDEFAULT_TXN_META, std::string txnStr = kDEFAULT_TXN_HEX);
|
||||
createTxAndMetaBlobs(std::string metaStr = kDEFAULT_TXN_META, std::string txnStr = kDEFAULT_TXN_HEX);
|
||||
|
||||
[[maybe_unused, nodiscard]] std::pair<ripple::STTx, ripple::TxMeta>
|
||||
createNftTxAndMeta(
|
||||
createTxAndMeta(
|
||||
std::string hashStr = kDEFAULT_HASH,
|
||||
std::string metaStr = kDEFAULT_TXN_META,
|
||||
std::string txnStr = kDEFAULT_TXN_HEX
|
||||
@@ -176,6 +176,9 @@ createObjectWithBookBase(
|
||||
[[maybe_unused, nodiscard]] etlng::model::Object
|
||||
createObjectWithTwoNFTs();
|
||||
|
||||
[[maybe_unused, nodiscard]] etlng::model::Object
|
||||
createObjectWithMPT();
|
||||
|
||||
[[maybe_unused, nodiscard]] etlng::model::BookSuccessor
|
||||
createSuccessor();
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ target_sources(
|
||||
etlng/SourceImplTests.cpp
|
||||
etlng/ext/CoreTests.cpp
|
||||
etlng/ext/CacheTests.cpp
|
||||
etlng/ext/MPTTests.cpp
|
||||
etlng/ext/NFTTests.cpp
|
||||
etlng/ext/SuccessorTests.cpp
|
||||
# Feed
|
||||
|
||||
@@ -198,7 +198,7 @@ TEST_F(ExtractionNgTests, OneTransaction)
|
||||
auto expected = util::createTransaction(ripple::TxType::ttNFTOKEN_CREATE_OFFER);
|
||||
|
||||
auto original = org::xrpl::rpc::v1::TransactionAndMetadata();
|
||||
auto [metaRaw, txRaw] = util::createNftTxAndMetaBlobs();
|
||||
auto [metaRaw, txRaw] = util::createTxAndMetaBlobs();
|
||||
original.set_transaction_blob(txRaw);
|
||||
original.set_metadata_blob(metaRaw);
|
||||
|
||||
@@ -216,7 +216,7 @@ TEST_F(ExtractionNgTests, MultipleTransactions)
|
||||
auto expected = util::createTransaction(ripple::TxType::ttNFTOKEN_CREATE_OFFER);
|
||||
|
||||
auto original = org::xrpl::rpc::v1::TransactionAndMetadata();
|
||||
auto [metaRaw, txRaw] = util::createNftTxAndMetaBlobs();
|
||||
auto [metaRaw, txRaw] = util::createTxAndMetaBlobs();
|
||||
original.set_transaction_blob(txRaw);
|
||||
original.set_metadata_blob(metaRaw);
|
||||
|
||||
|
||||
173
tests/unit/etlng/ext/MPTTests.cpp
Normal file
173
tests/unit/etlng/ext/MPTTests.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, 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 "etlng/Models.hpp"
|
||||
#include "etlng/impl/ext/MPT.hpp"
|
||||
#include "rpc/RPCHelpers.hpp"
|
||||
#include "util/BinaryTestObject.hpp"
|
||||
#include "util/MockBackendTestFixture.hpp"
|
||||
#include "util/MockPrometheus.hpp"
|
||||
#include "util/TestObject.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/AMMCore.h>
|
||||
#include <xrpl/protocol/AccountID.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/SField.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <xrpl/protocol/TER.h>
|
||||
#include <xrpl/protocol/TxFormats.h>
|
||||
#include <xrpl/protocol/TxMeta.h>
|
||||
#include <xrpl/protocol/serialize.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace etlng;
|
||||
using namespace etlng::impl;
|
||||
using namespace data;
|
||||
using namespace testing;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit auto const kSEQ = 123u;
|
||||
constinit auto const kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||
constinit auto const kHOLDER_ACCOUNT = "rK1EX542EgA9m948JrJRaEzwLVEhqWvnr9";
|
||||
|
||||
constinit auto const kTXN_HEX =
|
||||
"120039220000000024002DBD1A201B002DBDA36840000000000000017321EDECF25C029811CAD07AFD616EB75E3803E44D0D59A6826AC25FE3"
|
||||
"4A43626D2D157440244262E760314164843026CE2F100D0BFEB0DD6F75026FEB3F75FCAA943F5C874FF0411BC82A85DE504B434B5EC3C6A692"
|
||||
"3CC37A1C2ABD3E98EFFC8240B9D0018114CEF330DB51154D8DEE249CC3D6DFD04B91F648EE0115002DBD1817E0AF9FDE4F9978B8FCD8A50636"
|
||||
"30B5737DA605";
|
||||
|
||||
constinit auto const kTXN_META =
|
||||
"201C00000002F8E311007F562668E165750018E0AE5808C131BAF4C26441D2BCF76F8628774DFDF098B7250BE88114CEF330DB51154D8DEE24"
|
||||
"9CC3D6DFD04B91F648EE0115002DBD1817E0AF9FDE4F9978B8FCD8A5063630B5737DA605E1E1E511006425002DBD2F55E85C182A243C7CBF0E"
|
||||
"F7B8B3E0C8AE68E3DE6616DE1EFE168CD913CA6520444D568F18252475DFAC9D5DE5423DFA08842F398F346DEB2BD546C526D26BF81E345CE7"
|
||||
"2200000000588F18252475DFAC9D5DE5423DFA08842F398F346DEB2BD546C526D26BF81E345C8214CEF330DB51154D8DEE249CC3D6DFD04B91"
|
||||
"F648EEE1E1E511006125002DBD2F55E85C182A243C7CBF0EF7B8B3E0C8AE68E3DE6616DE1EFE168CD913CA6520444D56F7D3073515F1C71F2A"
|
||||
"D00941BA714A3FBE3D91AEAFCD6345B5389004AD707E95E624002DBD1A2D00000001624000000005F5E0FFE1E7220000000024002DBD1B2D00"
|
||||
"000002624000000005F5E0FE8114CEF330DB51154D8DEE249CC3D6DFD04B91F648EEE1E1F1031000";
|
||||
|
||||
constinit auto const kHASH = "6005B465CBBF7FA8E41AC0C0CD38491026D9411FCB7BA46E2AEBB3AF7654261B";
|
||||
constinit auto const kHASH2 = "6005B465CBBF7FA8E41AC0C0CD38491026D9411FCB7BA46E2AEBB3AF7654261C";
|
||||
constinit auto const kHASH3 = "6005B465CBBF7FA8E41AC0C0CD38491026D9411FCB7BA46E2AEBB3AF7654261D";
|
||||
|
||||
auto
|
||||
createTestData()
|
||||
{
|
||||
auto transactions = std::vector{
|
||||
util::createTransaction(ripple::TxType::ttMPTOKEN_ISSUANCE_CREATE), // not AUTHORIZE so will not be written
|
||||
util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH, kTXN_META, kTXN_HEX),
|
||||
util::createTransaction(ripple::TxType::ttAMM_CREATE), // not MPT - will be filtered
|
||||
util::createTransaction(ripple::TxType::ttMPTOKEN_ISSUANCE_CREATE), // not unique - will be filtered
|
||||
};
|
||||
|
||||
auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ);
|
||||
return etlng::model::LedgerData{
|
||||
.transactions = std::move(transactions),
|
||||
.objects = {},
|
||||
.successors = {},
|
||||
.edgeKeys = {},
|
||||
.header = header,
|
||||
.rawHeader = {},
|
||||
.seq = kSEQ
|
||||
};
|
||||
}
|
||||
|
||||
auto
|
||||
createMultipleHoldersTestData()
|
||||
{
|
||||
auto transactions = std::vector{
|
||||
util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH, kTXN_META, kTXN_HEX),
|
||||
util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH2, kTXN_META, kTXN_HEX),
|
||||
util::createTransaction(ripple::TxType::ttMPTOKEN_AUTHORIZE, kHASH3, kTXN_META, kTXN_HEX)
|
||||
};
|
||||
|
||||
auto const header = createLedgerHeader(kLEDGER_HASH, kSEQ);
|
||||
return etlng::model::LedgerData{
|
||||
.transactions = std::move(transactions),
|
||||
.objects = {},
|
||||
.successors = {},
|
||||
.edgeKeys = {},
|
||||
.header = header,
|
||||
.rawHeader = {},
|
||||
.seq = kSEQ
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct MPTExtTests : util::prometheus::WithPrometheus, MockBackendTest {
|
||||
protected:
|
||||
MPTExt ext_{backend_};
|
||||
};
|
||||
|
||||
TEST_F(MPTExtTests, OnLedgerDataFiltersAndWritesMPTs)
|
||||
{
|
||||
auto const data = createTestData();
|
||||
|
||||
EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) {
|
||||
EXPECT_EQ(holders.size(), 1); // Only AUTHORIZE is written in the end
|
||||
});
|
||||
|
||||
ext_.onLedgerData(data);
|
||||
}
|
||||
|
||||
TEST_F(MPTExtTests, OnInitialDataFiltersAndWritesMPTs)
|
||||
{
|
||||
auto const data = createTestData();
|
||||
|
||||
EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) {
|
||||
EXPECT_EQ(holders.size(), 1); // Only AUTHORIZE is written in the end
|
||||
});
|
||||
|
||||
ext_.onInitialData(data);
|
||||
}
|
||||
|
||||
TEST_F(MPTExtTests, OnInitialObjectWritesMPT)
|
||||
{
|
||||
auto const data = util::createObjectWithMPT();
|
||||
|
||||
EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) { EXPECT_EQ(holders.size(), 1); });
|
||||
|
||||
ext_.onInitialObject(kSEQ, data);
|
||||
}
|
||||
|
||||
TEST_F(MPTExtTests, OnInitialDataWithMultipleHolders)
|
||||
{
|
||||
auto const data = createMultipleHoldersTestData();
|
||||
|
||||
EXPECT_CALL(*backend_, writeMPTHolders).WillOnce([](auto const& holders) {
|
||||
EXPECT_EQ(holders.size(), 3); // Expect all three AUTHORIZE transactions
|
||||
|
||||
auto const expectedAccount = rpc::accountFromStringStrict(kHOLDER_ACCOUNT); // Expect all three to be the same
|
||||
EXPECT_TRUE(std::ranges::all_of(holders, [&expectedAccount](auto const& data) {
|
||||
return data.holder == expectedAccount;
|
||||
}));
|
||||
});
|
||||
|
||||
ext_.onInitialData(data);
|
||||
}
|
||||
Reference in New Issue
Block a user