Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add issuer_nfts #2

Draft
wants to merge 71 commits into
base: issuer_nfts
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
66c351d
inital commit
shawnxie999 Sep 15, 2022
4d5951e
edit limit
shawnxie999 Sep 15, 2022
355ae17
add dec and add semicolon
shawnxie999 Sep 20, 2022
8516003
add declarations
shawnxie999 Sep 20, 2022
891502b
fix fetchissuer
shawnxie999 Sep 20, 2022
46662bc
delete space
shawnxie999 Sep 20, 2022
916bac5
add optional cursor
shawnxie999 Sep 20, 2022
7499d85
optional cursor param
shawnxie999 Sep 20, 2022
485b967
fix optional
shawnxie999 Sep 20, 2022
64c282f
wip
shawnxie999 Sep 21, 2022
7b40870
add spacing
shawnxie999 Sep 21, 2022
911071e
remove last row if cursor exitsts
shawnxie999 Sep 21, 2022
5d3d368
remove debugging code
shawnxie999 Sep 21, 2022
69593bf
Merge branch 'develop' into issuer_nft_db_func
shawnxie999 Sep 21, 2022
2cbdec2
add test
shawnxie999 Sep 26, 2022
73344dc
change from struct to pair
shawnxie999 Sep 26, 2022
b42ce3b
remvoe lines
shawnxie999 Sep 26, 2022
362b0e9
wip
shawnxie999 Sep 27, 2022
5d8b8a8
add ledger sequence , working copy
shawnxie999 Sep 28, 2022
77d2d47
wip: renaming
shawnxie999 Sep 28, 2022
6f05ac4
working cleaned
shawnxie999 Sep 28, 2022
7576b6a
remove debug comments
shawnxie999 Sep 28, 2022
c5e14bc
comments
shawnxie999 Sep 28, 2022
079b517
whitespae
shawnxie999 Sep 28, 2022
a9bdccd
cap
shawnxie999 Sep 28, 2022
ea3723c
unused var
shawnxie999 Sep 28, 2022
ac1d8b7
cmts
shawnxie999 Sep 28, 2022
0c0ba26
improvement
shawnxie999 Sep 29, 2022
08ddc3b
comments
shawnxie999 Sep 29, 2022
09212d5
recusrion
shawnxie999 Sep 29, 2022
00db7f2
remove recursion
shawnxie999 Sep 29, 2022
89bab5b
remove extra db fetch, renaming
shawnxie999 Oct 3, 2022
f9808c0
handler api
shawnxie999 Oct 3, 2022
c606425
improvements, wip
shawnxie999 Oct 4, 2022
a817bfb
rearranging
shawnxie999 Oct 4, 2022
f4cdff6
unnecessary braces
shawnxie999 Oct 4, 2022
05006ae
add limit
shawnxie999 Oct 11, 2022
ffd5ff5
taxon wip
shawnxie999 Oct 11, 2022
8a914a0
taxon remove nesting if
shawnxie999 Oct 11, 2022
13af1f6
update uri fetching
shawnxie999 Oct 12, 2022
757a99f
remove else if
shawnxie999 Oct 12, 2022
a413723
add nf_token_uris table, write to it with NFTokenMint txs, and pull f…
ledhed2222 Sep 17, 2022
b9f6c6d
backfill nft data from ledger objects at initial state load
ledhed2222 Sep 21, 2022
1781e49
clang
ledhed2222 Sep 21, 2022
463ffef
separate header file for nfthelpers
ledhed2222 Sep 24, 2022
00462c1
add limit to select
ledhed2222 Sep 24, 2022
7d0066e
use JS macro in nft_info
ledhed2222 Sep 24, 2022
d04382a
add some consts
ledhed2222 Sep 24, 2022
ceb7ed9
remove comments
ledhed2222 Sep 29, 2022
c57ea6d
add taxon
ledhed2222 Oct 8, 2022
6319ba9
rename to to match rippled
ledhed2222 Oct 12, 2022
76a0cfe
Merge remote-tracking branch 'other-guys-repo/gw/store_nft_uri' into …
shawnxie999 Oct 13, 2022
63ecd3b
wip
shawnxie999 Oct 13, 2022
5b7a609
wip
shawnxie999 Oct 13, 2022
c7344d8
wip
shawnxie999 Oct 13, 2022
49ee7f7
wip
shawnxie999 Oct 13, 2022
0ab14af
comments
shawnxie999 Oct 13, 2022
cfb4675
wip
shawnxie999 Oct 13, 2022
607e08f
marker method
shawnxie999 Oct 14, 2022
971ebcb
marker
shawnxie999 Oct 14, 2022
593d03e
parsing issues
shawnxie999 Oct 14, 2022
0e54d6e
delete old code
shawnxie999 Oct 14, 2022
bdb407a
update test
shawnxie999 Oct 14, 2022
b8010b6
bug fixes and edge cases
shawnxie999 Oct 14, 2022
ed11f69
cmts
shawnxie999 Oct 14, 2022
d227a63
renaming hotel
shawnxie999 Oct 17, 2022
0afa4ed
fix taxon condition
shawnxie999 Oct 17, 2022
1f4705c
wip
shawnxie999 Oct 24, 2022
faf01f7
sync
shawnxie999 Oct 24, 2022
e7c0deb
sync
shawnxie999 Oct 24, 2022
b8fa6aa
revert
shawnxie999 Oct 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ target_sources(clio PRIVATE
src/rpc/handlers/NFTHistory.cpp
src/rpc/handlers/NFTInfo.cpp
src/rpc/handlers/NFTOffers.cpp
src/rpc/handlers/IssuerNFTs.cpp
# Ledger
src/rpc/handlers/Ledger.cpp
src/rpc/handlers/LedgerData.cpp
Expand Down
9 changes: 9 additions & 0 deletions src/backend/BackendInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ class BackendInterface
std::uint32_t const ledgerSequence,
boost::asio::yield_context& yield) const = 0;

virtual std::optional<std::pair<std::vector<NFT>, std::optional<std::pair<std::uint32_t, ripple::uint256>>>>
fetchIssuerNFTs(
ripple::AccountID const& issuer,
std::uint32_t const ledgerSequence,
std::optional<std::uint32_t> taxon,
std::optional<std::pair<std::uint32_t, ripple::uint256>>const cursorIn,
std::uint32_t const limit,
boost::asio::yield_context& yield) const = 0;

virtual TransactionsAndCursor
fetchNFTTransactions(
ripple::uint256 const& tokenID,
Expand Down
296 changes: 272 additions & 24 deletions src/backend/CassandraBackend.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include <functional>
#include <unordered_map>

#include <ripple/app/tx/impl/details/NFTokenUtils.h>

#include <backend/CassandraBackend.h>
#include <backend/DBHelpers.h>
#include <functional>
#include <unordered_map>

namespace Backend {

Expand Down Expand Up @@ -376,17 +378,33 @@ CassandraBackend::writeNFTs(std::vector<NFTsData>&& data)
},
"nf_tokens");

makeAndExecuteAsyncWrite(
this,
std::make_tuple(record.tokenID),
[this](auto const& params) {
CassandraStatement statement{insertIssuerNFT_};
auto const& [tokenID] = params.data;
statement.bindNextBytes(ripple::nft::getIssuer(tokenID));
statement.bindNextBytes(tokenID);
return statement;
},
"issuer_nf_tokens");
if (record.issuer)
makeAndExecuteAsyncWrite(
this,
std::make_tuple(record.tokenID, record.issuer.value()),
[this](auto const& params) {
CassandraStatement statement{insertIssuerNFT_};
auto const& [tokenID, issuer] = params.data;
statement.bindNextBytes(issuer);
statement.bindNextInt(
ripple::nft::toUInt32(ripple::nft::getTaxon(tokenID)));
statement.bindNextBytes(tokenID);
return statement;
},
"issuer_nf_tokens");

if (record.uri)
makeAndExecuteAsyncWrite(
this,
std::make_tuple(record.tokenID, record.uri.value()),
[this](auto const& params) {
CassandraStatement statement{insertNFTURI_};
auto const& [tokenID, uri] = params.data;
statement.bindNextBytes(tokenID);
statement.bindNextBytes(uri);
return statement;
},
"nf_token_uris");
}
}

Expand Down Expand Up @@ -575,18 +593,179 @@ CassandraBackend::fetchNFT(
std::uint32_t const ledgerSequence,
boost::asio::yield_context& yield) const
{
CassandraStatement statement{selectNFT_};
statement.bindNextBytes(tokenID);
statement.bindNextInt(ledgerSequence);
CassandraResult response = executeAsyncRead(statement, yield);
if (!response)
CassandraStatement nftStatement{selectNFT_};
nftStatement.bindNextBytes(tokenID);
nftStatement.bindNextInt(ledgerSequence);
CassandraResult nftResponse = executeAsyncRead(nftStatement, yield);
if (!nftResponse)
return {};

NFT result;
result.tokenID = tokenID;
result.ledgerSequence = response.getUInt32();
result.owner = response.getBytes();
result.isBurned = response.getBool();
result.ledgerSequence = nftResponse.getUInt32();
result.owner = nftResponse.getBytes();
result.isBurned = nftResponse.getBool();

// now fetch URI
CassandraStatement uriStatement{selectNFTURI_};
uriStatement.bindNextBytes(tokenID);
CassandraResult uriResponse = executeAsyncRead(uriStatement, yield);
if (uriResponse.hasResult())
result.uri = uriResponse.getBytes();

return result;
}

std::optional<std::pair<std::vector<NFT>, std::optional<std::pair<std::uint32_t, ripple::uint256>>>>
CassandraBackend::fetchIssuerNFTs(
ripple::AccountID const& issuer,
std::uint32_t const ledgerSequence,
std::optional<std::uint32_t> const taxon,
std::optional<std::pair<std::uint32_t, ripple::uint256>> const cursorIn,
std::uint32_t const limit,
boost::asio::yield_context& yield) const
{
// When taxon or cursor param exists, we need to query the taxon and token_id columns
// otherswise, we only query by issuer
CassandraStatement issuerNFTStatement = taxon || cursorIn
? CassandraStatement(selectIssuerNFTsByTaxonID_)
: CassandraStatement(selectIssuerNFTs_);

issuerNFTStatement.bindNextBytes(issuer);


if(taxon && !cursorIn){
// If there is taxon and no cursor, we query with this specific taxon id
// and sets the token_id to 0 (we want to query from the beginning)
issuerNFTStatement.bindNextInt(taxon.value());
issuerNFTStatement.bindNextBytes(static_cast<ripple::uint256>(0));
}
else if(cursorIn){
// When there is a cursor, we use the taxon and token_id directly from it,
// regardless whether the taxon param is specified
issuerNFTStatement.bindNextInt(cursorIn.value().first);
issuerNFTStatement.bindNextBytes(cursorIn.value().second);
}

issuerNFTStatement.bindNextUInt(limit);

//queries for a list nftIDs against issuer_nf_tokens table
CassandraResult issuerNFTResponse = executeAsyncRead(issuerNFTStatement, yield);

auto cursor = cursorIn;
auto numRows = issuerNFTResponse.numRows();
auto hasCursor = (limit == static_cast<std::uint32_t>(numRows)) ? true : false;
std::vector<std::pair<std::uint32_t, ripple::uint256>> nftPairs = {};

if(numRows != 0)
{
do
{
std::uint32_t const nftTaxon = issuerNFTResponse.getUInt32();
ripple::uint256 const nftID = issuerNFTResponse.getUInt256();
std::pair<std::uint32_t, ripple::uint256> nftPair = std::make_pair(nftTaxon, nftID);
if (hasCursor && --numRows == 0)
{
cursor = nftPair;
}

nftPairs.push_back(nftPair);
} while (issuerNFTResponse.nextRow());
}


// If the prev query has not reached the limit specified (hasCursor flag),
// we need to query for additional result for the proceeding NFTs with larger taxons.
// NOTE: this if-condition is only ran if the prev query was selectIssuerNFTsByTaxonID_,
// thus why cursorIn flag is used. If the prev query was selectIssuerNFTs_,
// there is no need to query for larger taxons. If a taxon is specified, there
// no need to query for higher taxon values
if(!taxon && cursorIn && !hasCursor){
CassandraStatement higherTaxonStatement{selectIssuerNFTsByTaxon_};
higherTaxonStatement.bindNextBytes(issuer);
higherTaxonStatement.bindNextInt(cursorIn.value().first);
higherTaxonStatement.bindNextUInt(limit - numRows);
CassandraResult higherTaxonResponse = executeAsyncRead(higherTaxonStatement, yield);
if (!higherTaxonResponse)
{
// Without a response, we know there is no more NFTs for the issuer
cursor = std::nullopt;
}
else
{
// When response is valid, there are additional NFTs, and we update the cursor on the last record
auto higherTaxonNumRows = higherTaxonResponse.numRows();
hasCursor = (limit - numRows == static_cast<std::uint32_t>(higherTaxonNumRows)) ? true : false;
do
{
std::uint32_t const nftTaxon = higherTaxonResponse.getUInt32();
ripple::uint256 const nftID = higherTaxonResponse.getUInt256();
std::pair<std::uint32_t, ripple::uint256> nftPair = std::make_pair(nftTaxon, nftID);
if (hasCursor && --higherTaxonNumRows == 0)
{
cursor = nftPair;
}

nftPairs.push_back(nftPair);
} while (higherTaxonResponse.nextRow());

}
}

CassandraStatement nftListStatement{selectNFTList_};
//constructs a list to be used with the IN keyword within a query
CassCollection* collection = cass_collection_new(CASS_COLLECTION_TYPE_LIST, numRows);
for(auto const& nftPair: nftPairs)
{
auto const nftID = nftPair.second;
//append each nftID to a list that is going to be queried against the nf_tokens table
CassError append = cass_collection_append_bytes(collection, static_cast<cass_byte_t const*>(nftID.data()), nftID.size());
if (append != CASS_OK)
{
std::stringstream ss;
ss << "Error appending bytes to collection: " << append << ", "
<< cass_error_desc(append);
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
throw std::runtime_error(ss.str());
}
}

nftListStatement.bindNextByteCollection(collection);
nftListStatement.bindNextInt(ledgerSequence);
//queries for ledger_sequence, is_burned and owner of each NFT
CassandraResult nftListResponse= executeAsyncRead(nftListStatement, yield);

std::vector<NFT> nftInfoList = {};
//Even if response from nf_tokens is empty, and we have a cursor,
//it is still possible that there are unfetched NFTs
//so return an empty list with a marker.
if (!nftListResponse && hasCursor)
return std::make_pair(nftInfoList, cursor);
if (!nftListResponse)
return {};

cass_collection_free(collection);
do
{
NFT nftResult;
nftResult.tokenID = nftListResponse.getUInt256();
nftResult.ledgerSequence = nftListResponse.getUInt32();
nftResult.owner = nftListResponse.getBytes();
nftResult.isBurned = nftListResponse.getBool();

CassandraStatement uriStatement{selectNFTURI_};
uriStatement.bindNextBytes(nftResult.tokenID);
CassandraResult uriResponse = executeAsyncRead(uriStatement, yield);
if (uriResponse.hasResult())
nftResult.uri = uriResponse.getBytes();
nftInfoList.push_back(nftResult);
} while (nftListResponse.nextRow());

std::pair<std::vector<NFT>, std::optional<std::pair<std::uint32_t, ripple::uint256>>> result;
if(hasCursor)
result = std::make_pair(nftInfoList, cursor);
else
result = std::make_pair(nftInfoList, std::nullopt);
return result;
}

Expand Down Expand Up @@ -1391,8 +1570,9 @@ CassandraBackend::open(bool readOnly)
<< "issuer_nf_tokens"
<< " ("
<< " issuer blob,"
<< " token_taxon bigint,"
<< " token_id blob,"
<< " PRIMARY KEY (issuer, token_id)"
<< " PRIMARY KEY (issuer, token_taxon, token_id)"
<< " )";
if (!executeSimpleStatement(query.str()))
continue;
Expand All @@ -1403,6 +1583,21 @@ CassandraBackend::open(bool readOnly)
if (!executeSimpleStatement(query.str()))
continue;

query.str("");
query << "CREATE TABLE IF NOT EXISTS " << tablePrefix << "nf_token_uris"
<< " ("
<< " token_id blob PRIMARY KEY,"
<< " uri blob"
<< " )";
if (!executeSimpleStatement(query.str()))
continue;

query.str("");
query << "SELECT * FROM " << tablePrefix << "nf_token_uris"
<< " LIMIT 1";
if (!executeSimpleStatement(query.str()))
continue;

query.str("");
query << "CREATE TABLE IF NOT EXISTS " << tablePrefix
<< "nf_token_transactions"
Expand Down Expand Up @@ -1557,13 +1752,66 @@ CassandraBackend::open(bool readOnly)
if (!selectNFT_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "SELECT token_id,sequence,owner,is_burned"
<< " FROM " << tablePrefix << "nf_tokens WHERE"
<< " token_id IN ? AND"
<< " sequence <= ?"
<< " ORDER BY sequence DESC PER PARTITION LIMIT 1";
if (!selectNFTList_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "INSERT INTO " << tablePrefix << "issuer_nf_tokens"
<< " (issuer,token_id)"
<< " VALUES (?,?)";
<< " (issuer,token_taxon,token_id)"
<< " VALUES (?,?,?)";
if (!insertIssuerNFT_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "SELECT token_taxon, token_id"
<< " FROM " << tablePrefix << "issuer_nf_tokens WHERE"
<< " issuer = ?"
<< " ORDER BY token_taxon, token_id ASC"
<< " LIMIT ?";
if (!selectIssuerNFTs_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "SELECT token_taxon, token_id"
<< " FROM " << tablePrefix << "issuer_nf_tokens WHERE"
<< " issuer = ? AND"
<< " token_taxon > ?"
<< " ORDER BY token_taxon, token_id ASC"
<< " LIMIT ?";
if (!selectIssuerNFTsByTaxon_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "SELECT token_taxon, token_id"
<< " FROM " << tablePrefix << "issuer_nf_tokens WHERE"
<< " issuer = ? AND"
<< " token_taxon = ? AND"
<< " token_id > ? "
<< " ORDER BY token_taxon, token_id ASC"
<< " LIMIT ?";
if (!selectIssuerNFTsByTaxonID_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "INSERT INTO " << tablePrefix << "nf_token_uris"
<< " (token_id,uri)"
<< " VALUES (?,?)";
if (!insertNFTURI_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "SELECT uri FROM " << tablePrefix << "nf_token_uris"
<< " WHERE token_id = ?"
<< " LIMIT 1";
if (!selectNFTURI_.prepareStatement(query, session_.get()))
continue;

query.str("");
query << "INSERT INTO " << tablePrefix << "nf_token_transactions"
<< " (token_id,seq_idx,hash)"
Expand Down
Loading