Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
rval param
  • Loading branch information
shawnxie999 committed Sep 28, 2023
1 parent d7f3673 commit be32b86
Show file tree
Hide file tree
Showing 15 changed files with 633 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ target_sources (clio PRIVATE
src/etl/ETLService.cpp
src/etl/LoadBalancer.cpp
src/etl/impl/ForwardCache.cpp
src/etl/CFTHelpers.cpp
## Feed
src/feed/SubscriptionManager.cpp
## Web
Expand All @@ -113,6 +114,7 @@ target_sources (clio PRIVATE
src/rpc/handlers/AccountTx.cpp
src/rpc/handlers/BookChanges.cpp
src/rpc/handlers/BookOffers.cpp
src/rpc/handlers/CFTsByIssuer.cpp
src/rpc/handlers/DepositAuthorized.cpp
src/rpc/handlers/GatewayBalances.cpp
src/rpc/handlers/Ledger.cpp
Expand Down
18 changes: 18 additions & 0 deletions src/data/BackendInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,15 @@ class BackendInterface
std::optional<TransactionsCursor> const& cursorIn,
boost::asio::yield_context yield) const = 0;

virtual
CFTIssuancesAndCursor
fetchIssuerCFTs(
ripple::AccountID const& issuer,
std::uint32_t const limit,
std::optional<ripple::uint256> const& cursorIn,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield) const = 0;

/**
* @brief Fetches a specific ledger object.
*
Expand Down Expand Up @@ -352,6 +361,12 @@ class BackendInterface
std::uint32_t const sequence,
boost::asio::yield_context yield) const = 0;

virtual std::vector<std::optional<std::pair<Blob, uint32_t>>>
doFetchLedgerObjectsPair(
std::vector<ripple::uint256> const& keys,
std::uint32_t const sequence,
boost::asio::yield_context yield) const = 0;

/**
* @brief Returns the difference between ledgers.
*
Expand Down Expand Up @@ -520,6 +535,9 @@ class BackendInterface
virtual void
writeNFTTransactions(std::vector<NFTTransactionsData>&& data) = 0;

virtual void
writeCFTIssuancePairs(std::vector<std::pair<ripple::uint256, ripple::AccountID>>&& data) = 0;

/**
* @brief Write a new successor.
*
Expand Down
112 changes: 112 additions & 0 deletions src/data/CassandraBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,102 @@ class BasicCassandraBackend : public BackendInterface
return {txns, {}};
}

CFTIssuancesAndCursor
fetchIssuerCFTs(ripple::AccountID const& issuer,
std::uint32_t const limit,
std::optional<ripple::uint256> const& cursorIn,
std::uint32_t const ledgerSequence,
boost::asio::yield_context yield)const override
{
// Statement statement = schema_->selectIssuerCFTs.bind(issuer);
// statement.bindAt(1, cursorIn.value_or(ripple::uint256(0)));
// statement.bindAt(2, Limit{limit});
// auto const issuerRes = executor_.read(yield, statement);

auto const issuerRes = executor_.read(yield, schema_->selectIssuerCFTs, issuer, cursorIn.value_or(ripple::uint256(0)), Limit{limit});

auto const& issuerResults = issuerRes.value();
if (not issuerResults.hasRows())
{
LOG(log_.debug()) << "No rows returned";
return {};
}

std::vector<ripple::uint256> cftIssuances;
std::optional<ripple::uint256> cursor;
for (auto [cftIssuanceID] : extract<ripple::uint256>(issuerResults))
{
cftIssuances.push_back(cftIssuanceID);
cursor = cftIssuanceID;
}

auto const maybeCFTIssuanceObjects = doFetchLedgerObjectsPair(cftIssuances, ledgerSequence, yield);

std::vector<std::tuple<ripple::uint256, Blob, uint32_t>> cftIssuanceObjects;

//need to filter out the objs that don't exist at the ledger seq because these CFTs are in no particular time order
for(size_t i = 0; i < cftIssuances.size(); i++){
if(!maybeCFTIssuanceObjects[i].has_value())
continue;

auto const objSeqPair = maybeCFTIssuanceObjects[i].value();
cftIssuanceObjects.push_back(std::make_tuple(cftIssuances[i], objSeqPair.first, objSeqPair.second));

}

if(cftIssuances.size() == limit)
return {cftIssuanceObjects, cursor};

return {cftIssuanceObjects, {}};
}

std::vector<std::optional<std::pair<Blob, uint32_t>>>
doFetchLedgerObjectsPair(
std::vector<ripple::uint256> const& keys,
std::uint32_t const sequence,
boost::asio::yield_context yield) const override
{
if (keys.size() == 0)
return {};

auto const numKeys = keys.size();
LOG(log_.trace()) << "Fetching " << numKeys << " objects";

std::vector<std::optional<std::pair<Blob, uint32_t>>> results;
results.reserve(numKeys);

std::vector<Statement> statements;
statements.reserve(numKeys);

// TODO: seems like a job for "key IN (list of keys)" instead?
std::transform(
std::cbegin(keys), std::cend(keys), std::back_inserter(statements), [this, &sequence](auto const& key) {
return schema_->selectObject.bind(key, sequence);
});

auto const entries = executor_.readEach(yield, statements);
if(entries.size() != statements.size())
{
LOG(log_.debug()) << "Input and output vector sizes are different";
return {};
}


std::transform(
std::cbegin(entries), std::cend(entries), std::back_inserter(results), [](auto const& res) -> std::optional<std::pair<Blob, uint32_t>> {
if (auto const maybeValue = res.template get<Blob, uint32_t>(); maybeValue){
auto [object, seq] = *maybeValue;
return std::make_pair(object, seq);
}
else
return {};
});

assert(numKeys == results.size());
LOG(log_.trace()) << "Fetched " << numKeys << " objects";
return results;
}

std::optional<Blob>
doFetchLedgerObject(ripple::uint256 const& key, std::uint32_t const sequence, boost::asio::yield_context yield)
const override
Expand Down Expand Up @@ -783,6 +879,22 @@ class BasicCassandraBackend : public BackendInterface
executor_.write(std::move(statements));
}

void
writeCFTIssuancePairs(std::vector<std::pair<ripple::uint256, ripple::AccountID>>&& data) override
{
std::vector<Statement> statements;
for (auto const& record : data)
{
auto const token_id = record.first;
auto const issuer = record.second;

statements.push_back(
schema_->insertIssuerCFT.bind(issuer, token_id));
}

executor_.write(std::move(statements));
}

void
startWrites() const override
{
Expand Down
37 changes: 37 additions & 0 deletions src/data/DBHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,40 @@ uint256ToString(ripple::uint256 const& input)

/** @brief The ripple epoch start timestamp. Midnight on 1st January 2000. */
static constexpr std::uint32_t rippleEpochStart = 946684800;


/**
* @brief Represents an CFT state at a particular ledger.
*
*/
struct CFTsData
{
ripple::uint256 cftIssuanceID;
ripple::AccountID holder;
std::uint32_t ledgerSequence;
std::uint32_t flags;
std::uint64_t amount;
std::uint64_t lockedAmount;
bool isUntrusted = false;


CFTsData(
ripple::uint256 const& cftIssuanceID,
ripple::AccountID const& holder,
std::uint32_t ledgerSequence,
std::uint32_t flags,
std::uint64_t amount,
std::uint64_t lockedAmount,
bool isUntrusted)
: cftIssuanceID(cftIssuanceID),
holder(holder),
ledgerSequence(ledgerSequence),
flags(flags),
amount(amount),
lockedAmount(lockedAmount),
isUntrusted(isUntrusted)
{
}


};
6 changes: 6 additions & 0 deletions src/data/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ struct NFT
}
};

struct CFTIssuancesAndCursor
{
std::vector<std::tuple<ripple::uint256, Blob, uint32_t>> cftIssuances;
std::optional<ripple::uint256> cursor;
};

/**
* @brief Stores a range of sequences as a min and max pair.
*/
Expand Down
36 changes: 36 additions & 0 deletions src/data/cassandra/Schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,19 @@ class Schema
qualifiedTableName(settingsProvider_.get(), "nf_token_transactions"),
settingsProvider_.get().getTtl()));

statements.emplace_back(fmt::format(
R"(
CREATE TABLE IF NOT EXISTS {}
(
issuer blob,
token_id blob,
PRIMARY KEY (issuer, token_id)
)
WITH CLUSTERING ORDER BY (token_id ASC)
AND default_time_to_live = {}
)",
qualifiedTableName(settingsProvider_.get(), "issuer_cf_tokens"),
settingsProvider_.get().getTtl()));
return statements;
}();

Expand Down Expand Up @@ -393,6 +406,15 @@ class Schema
qualifiedTableName(settingsProvider_.get(), "ledger_hashes")));
}();

PreparedStatement insertIssuerCFT = [this]() {
return handle_.get().prepare(fmt::format(
R"(
INSERT INTO {}
(issuer, token_id)
VALUES (?, ?)
)",
qualifiedTableName(settingsProvider_.get(), "issuer_cf_tokens")));
}();
//
// Update (and "delete") queries
//
Expand Down Expand Up @@ -633,6 +655,20 @@ class Schema
)",
qualifiedTableName(settingsProvider_.get(), "ledger_range")));
}();

PreparedStatement selectIssuerCFTs = [this]() {
return handle_.get().prepare(fmt::format(
R"(
SELECT token_id
FROM {}
WHERE issuer = ?
AND token_id > ?
ORDER BY token_id ASC
LIMIT ?
)",
qualifiedTableName(settingsProvider_.get(), "issuer_cf_tokens")));
}();

};

/**
Expand Down
81 changes: 81 additions & 0 deletions src/etl/CFTHelpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2023, 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 <ripple/protocol/STBase.h>
#include <ripple/protocol/STTx.h>
#include <ripple/protocol/TxMeta.h>
#include <vector>

#include <data/BackendInterface.h>
#include <data/DBHelpers.h>
#include <data/Types.h>
#include <fmt/core.h>

namespace etl {

std::optional<std::pair<ripple::uint256, ripple::AccountID>>
getCFTokenIssuanceCreate(ripple::TxMeta const& txMeta, ripple::STTx const& sttx){

ripple::AccountID issuer = sttx.getAccountID(ripple::sfAccount);

for (ripple::STObject const& node : txMeta.getNodes())
{
if (node.getFieldU16(ripple::sfLedgerEntryType) != ripple::ltCFTOKEN_ISSUANCE)
continue;

if (node.getFName() == ripple::sfCreatedNode)
return std::make_pair(node.getFieldH256(ripple::sfLedgerIndex), issuer);
}
return {};
}

std::optional<std::pair<ripple::uint256, ripple::AccountID>>
getCFTIssuancePairFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx){
if (txMeta.getResultTER() != ripple::tesSUCCESS
|| sttx.getTxnType()!= ripple::TxType::ttCFTOKEN_ISSUANCE_CREATE)
return {};

return getCFTokenIssuanceCreate(txMeta, sttx);
}

std::optional<std::pair<ripple::uint256, ripple::AccountID>>
getCFTIssuancePairFromObj(std::uint32_t const seq, std::string const& key, std::string const& blob){
ripple::STLedgerEntry const sle =
ripple::STLedgerEntry(ripple::SerialIter{blob.data(), blob.size()}, ripple::uint256::fromVoid(key.data()));

if (sle.getFieldU16(ripple::sfLedgerEntryType) != ripple::ltCFTOKEN_ISSUANCE)
return {};


auto const cftIssuanceID = ripple::uint256::fromVoid(key.data());
auto const issuer = sle.getAccountID(ripple::sfIssuer);

return std::make_pair(cftIssuanceID, issuer);
}

// std::vector<CFTsData>
// getCFTDataFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx){

// }

// std::vector<CFTsData>
// getCFTDataFromObj(std::uint32_t const seq, std::string const& key, std::string const& blob){

// }
} // namespace etl
Loading

0 comments on commit be32b86

Please sign in to comment.