Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
CassandraBackend.hpp
1#pragma once
2
3#include "data/LedgerHeaderCache.hpp"
4#include "data/Types.hpp"
5#include "data/cassandra/CassandraBackendFamily.hpp"
6#include "data/cassandra/CassandraSchema.hpp"
7#include "data/cassandra/Concepts.hpp"
8#include "data/cassandra/Handle.hpp"
9#include "data/cassandra/SettingsProvider.hpp"
10#include "data/cassandra/Types.hpp"
11#include "data/cassandra/impl/ExecutionStrategy.hpp"
12#include "util/log/Logger.hpp"
13
14#include <boost/asio/spawn.hpp>
15#include <boost/json/object.hpp>
16#include <boost/uuid/string_generator.hpp>
17#include <boost/uuid/uuid.hpp>
18#include <cassandra.h>
19#include <fmt/format.h>
20#include <xrpl/basics/Blob.h>
21#include <xrpl/basics/base_uint.h>
22#include <xrpl/basics/strHex.h>
23#include <xrpl/protocol/AccountID.h>
24#include <xrpl/protocol/Indexes.h>
25#include <xrpl/protocol/LedgerHeader.h>
26#include <xrpl/protocol/nft.h>
27
28#include <algorithm>
29#include <cstddef>
30#include <cstdint>
31#include <iterator>
32#include <optional>
33#include <string>
34#include <tuple>
35#include <vector>
36
37namespace data::cassandra {
38
46template <
47 SomeSettingsProvider SettingsProviderType,
48 SomeExecutionStrategy ExecutionStrategyType,
49 typename FetchLedgerCacheType = FetchLedgerCache>
51 SettingsProviderType,
52 ExecutionStrategyType,
53 CassandraSchema<SettingsProviderType>,
54 FetchLedgerCacheType> {
55 using DefaultCassandraFamily = CassandraBackendFamily<
56 SettingsProviderType,
57 ExecutionStrategyType,
59 FetchLedgerCacheType>;
60
61 // protected because CassandraMigrationBackend inherits from this class
62protected:
63 using DefaultCassandraFamily::executor_;
64 using DefaultCassandraFamily::ledgerSequence_;
65 using DefaultCassandraFamily::log_;
66 using DefaultCassandraFamily::range_;
67 using DefaultCassandraFamily::schema_;
68
69public:
73 using DefaultCassandraFamily::DefaultCassandraFamily;
74
79
80 bool
81 doFinishWrites() override
82 {
84
85 if (!range_) {
86 executor_.writeSync(
87 schema_->updateLedgerRange, ledgerSequence_, false, ledgerSequence_
88 );
89 }
90
91 if (not this->executeSyncUpdate(
92 schema_->updateLedgerRange.bind(ledgerSequence_, true, ledgerSequence_ - 1)
93 )) {
94 LOG(log_.warn()) << "Update failed for ledger " << ledgerSequence_;
95 return false;
96 }
97
98 LOG(log_.info()) << "Committed ledger " << ledgerSequence_;
99 return true;
100 }
101
102 [[nodiscard]] NFTsAndCursor
104 xrpl::AccountID const& issuer,
105 std::optional<std::uint32_t> const& taxon,
106 std::uint32_t const ledgerSequence,
107 std::uint32_t const limit,
108 std::optional<xrpl::uint256> const& cursorIn,
109 boost::asio::yield_context yield
110 ) const override
111 {
112 NFTsAndCursor ret;
113
114 Statement const idQueryStatement = [&taxon, &issuer, &cursorIn, &limit, this]() {
115 if (taxon.has_value()) {
116 auto r = schema_->selectNFTIDsByIssuerTaxon.bind(issuer);
117 r.bindAt(1, *taxon);
118 r.bindAt(2, cursorIn.value_or(xrpl::uint256(0)));
119 r.bindAt(3, Limit{limit});
120 return r;
121 }
122
123 auto r = schema_->selectNFTIDsByIssuer.bind(issuer);
124 r.bindAt(
125 1,
126 std::make_tuple(
127 cursorIn.has_value() ? xrpl::nft::toUInt32(xrpl::nft::getTaxon(*cursorIn)) : 0,
128 cursorIn.value_or(xrpl::uint256(0))
129 )
130 );
131 r.bindAt(2, Limit{limit});
132 return r;
133 }();
134
135 // Query for all the NFTs issued by the account, potentially filtered by the taxon
136 auto const res = executor_.read(yield, idQueryStatement);
137
138 auto const& idQueryResults = res.value();
139 if (not idQueryResults.hasRows()) {
140 LOG(log_.debug()) << "No rows returned";
141 return {};
142 }
143
144 std::vector<xrpl::uint256> nftIDs;
145 for (auto const [nftID] : extract<xrpl::uint256>(idQueryResults))
146 nftIDs.push_back(nftID);
147
148 if (nftIDs.empty())
149 return ret;
150
151 if (nftIDs.size() == limit)
152 ret.cursor = nftIDs.back();
153
154 std::vector<Statement> selectNFTStatements;
155 selectNFTStatements.reserve(nftIDs.size());
156
157 std::transform(
158 std::cbegin(nftIDs),
159 std::cend(nftIDs),
160 std::back_inserter(selectNFTStatements),
161 [&](auto const& nftID) { return schema_->selectNFT.bind(nftID, ledgerSequence); }
162 );
163
164 auto const nftInfos = executor_.readEach(yield, selectNFTStatements);
165
166 std::vector<Statement> selectNFTURIStatements;
167 selectNFTURIStatements.reserve(nftIDs.size());
168
169 std::transform(
170 std::cbegin(nftIDs),
171 std::cend(nftIDs),
172 std::back_inserter(selectNFTURIStatements),
173 [&](auto const& nftID) { return schema_->selectNFTURI.bind(nftID, ledgerSequence); }
174 );
175
176 auto const nftUris = executor_.readEach(yield, selectNFTURIStatements);
177
178 for (auto i = 0u; i < nftIDs.size(); i++) {
179 if (auto const maybeRow = nftInfos[i].template get<uint32_t, xrpl::AccountID, bool>();
180 maybeRow.has_value()) {
181 auto [seq, owner, isBurned] = *maybeRow;
182 NFT nft(nftIDs[i], seq, owner, isBurned);
183 if (auto const maybeUri = nftUris[i].template get<xrpl::Blob>();
184 maybeUri.has_value())
185 nft.uri = *maybeUri;
186 ret.nfts.push_back(nft);
187 }
188 }
189 return ret;
190 }
191
192 [[nodiscard]] std::vector<xrpl::uint256>
194 std::uint32_t number,
195 std::uint32_t pageSize,
196 std::uint32_t seq,
197 boost::asio::yield_context yield
198 ) const override
199 {
200 std::vector<xrpl::uint256> liveAccounts;
201 std::optional<xrpl::AccountID> lastItem;
202
203 while (liveAccounts.size() < number) {
204 Statement const statement = lastItem
205 ? schema_->selectAccountFromToken.bind(*lastItem, Limit{pageSize})
206 : schema_->selectAccountFromBeginning.bind(Limit{pageSize});
207
208 auto const res = executor_.read(yield, statement);
209 if (res) {
210 auto const& results = res.value();
211 if (not results.hasRows()) {
212 LOG(log_.debug()) << "No rows returned";
213 break;
214 }
215 // The results should not contain duplicates, we just filter out deleted accounts
216 std::vector<xrpl::uint256> fullAccounts;
217 for (auto [account] : extract<xrpl::AccountID>(results)) {
218 fullAccounts.push_back(xrpl::keylet::account(account).key);
219 lastItem = account;
220 }
221 auto const objs = this->doFetchLedgerObjects(fullAccounts, seq, yield);
222
223 for (auto i = 0u; i < fullAccounts.size(); i++) {
224 if (not objs[i].empty()) {
225 if (liveAccounts.size() < number) {
226 liveAccounts.push_back(fullAccounts[i]);
227 } else {
228 break;
229 }
230 }
231 }
232 } else {
233 LOG(log_.error()) << "Could not fetch account from account_tx: " << res.error();
234 break;
235 }
236 }
237
238 return liveAccounts;
239 }
240};
241
242using CassandraBackend = BasicCassandraBackend<SettingsProvider, impl::DefaultExecutionStrategy<>>;
243
244} // namespace data::cassandra
std::vector< xrpl::uint256 > fetchAccountRoots(std::uint32_t number, std::uint32_t pageSize, std::uint32_t seq, boost::asio::yield_context yield) const override
Fetch the specified number of account root object indexes by page, the accounts need to exist for seq...
Definition CassandraBackend.hpp:193
NFTsAndCursor fetchNFTsByIssuer(xrpl::AccountID const &issuer, std::optional< std::uint32_t > const &taxon, std::uint32_t const ledgerSequence, std::uint32_t const limit, std::optional< xrpl::uint256 > const &cursorIn, boost::asio::yield_context yield) const override
Fetches all NFTs issued by a given address.
Definition CassandraBackend.hpp:103
BasicCassandraBackend(BasicCassandraBackend &&)=delete
Move constructor is deleted because handle_ is shared by reference with executor.
bool doFinishWrites() override
The implementation should wait for all pending writes to finish.
Definition CassandraBackend.hpp:81
CassandraBackendFamily(SettingsProviderType settingsProvider, data::LedgerCacheInterface &cache, bool readOnly)
Definition CassandraBackendFamily.hpp:95
std::vector< Blob > doFetchLedgerObjects(std::vector< xrpl::uint256 > const &keys, std::uint32_t const sequence, boost::asio::yield_context yield) const override
Definition CassandraBackendFamily.hpp:727
Manages the DB schema and provides access to prepared statements.
Definition CassandraSchema.hpp:22
The requirements of an execution strategy.
Definition Concepts.hpp:35
The requirements of a settings provider.
Definition Concepts.hpp:24
This namespace implements a wrapper for the Cassandra C++ driver.
Definition CassandraBackendFamily.hpp:47
impl::ResultExtractor< Types... > extract(Handle::ResultType const &result)
Extracts the results into series of std::tuple<Types...> by creating a simple wrapper with an STL inp...
Definition Handle.hpp:314
Represents a NFToken.
Definition Types.hpp:161
Represents a bundle of NFTs with a cursor to the next page.
Definition Types.hpp:227
A strong type wrapper for int32_t.
Definition Types.hpp:38