Skip to content

Commit

Permalink
Support DNFT
Browse files Browse the repository at this point in the history
  • Loading branch information
cindyyan317 committed Jul 25, 2024
1 parent 895f3c0 commit 1edb890
Show file tree
Hide file tree
Showing 12 changed files with 870 additions and 40 deletions.
3 changes: 2 additions & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class Clio(ConanFile):
'protobuf/3.21.9',
'grpc/1.50.1',
'openssl/1.1.1u',
'xrpl/2.3.0-b1',
'xrpl/2.2.0-dnft-local',
'zlib/1.3.1',
'libbacktrace/cci.20210118'
]

Expand Down
36 changes: 21 additions & 15 deletions src/data/CassandraBackend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,6 @@ class BasicCassandraBackend : public BackendInterface {
return seq;
}
LOG(log_.debug()) << "Could not fetch ledger object sequence - no rows";

} else {
LOG(log_.error()) << "Could not fetch ledger object sequence: " << res.error();
}
Expand Down Expand Up @@ -882,21 +881,28 @@ class BasicCassandraBackend : public BackendInterface {
statements.reserve(data.size() * 3);

for (NFTsData const& record : data) {
statements.push_back(
schema_->insertNFT.bind(record.tokenID, record.ledgerSequence, record.owner, record.isBurned)
);
if (!record.onlyUriChanged) {
statements.push_back(
schema_->insertNFT.bind(record.tokenID, record.ledgerSequence, record.owner, record.isBurned)
);

// If `uri` is set (and it can be set to an empty uri), we know this
// is a net-new NFT. That is, this NFT has not been seen before by
// us _OR_ it is in the extreme edge case of a re-minted NFT ID with
// the same NFT ID as an already-burned token. In this case, we need
// to record the URI and link to the issuer_nf_tokens table.
if (record.uri) {
statements.push_back(schema_->insertIssuerNFT.bind(
ripple::nft::getIssuer(record.tokenID),
static_cast<uint32_t>(ripple::nft::getTaxon(record.tokenID)),
record.tokenID
));
// If `uri` is set (and it can be set to an empty uri), we know this
// is a net-new NFT. That is, this NFT has not been seen before by
// us _OR_ it is in the extreme edge case of a re-minted NFT ID with
// the same NFT ID as an already-burned token. In this case, we need
// to record the URI and link to the issuer_nf_tokens table.
if (record.uri) {
statements.push_back(schema_->insertIssuerNFT.bind(
ripple::nft::getIssuer(record.tokenID),
static_cast<uint32_t>(ripple::nft::getTaxon(record.tokenID)),
record.tokenID
));
statements.push_back(
schema_->insertNFTURI.bind(record.tokenID, record.ledgerSequence, record.uri.value())
);
}
} else {
// only uri changed, we update the uri table only
statements.push_back(
schema_->insertNFTURI.bind(record.tokenID, record.ledgerSequence, record.uri.value())
);
Expand Down
18 changes: 18 additions & 0 deletions src/data/DBHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ struct NFTsData {
ripple::AccountID owner;
std::optional<ripple::Blob> uri;
bool isBurned = false;
bool onlyUriChanged = false; // Whether only the URI was changed

/**
* @brief Construct a new NFTsData object
Expand Down Expand Up @@ -170,6 +171,23 @@ struct NFTsData {
: tokenID(tokenID), ledgerSequence(ledgerSequence), owner(owner), uri(uri)
{
}

/**
* @brief Construct a new NFTsData object with only the URI changed
*
* @param tokenID The token ID
* @param meta The transaction metadata
* @param uri The new URI
*
*/
NFTsData(ripple::uint256 const& tokenID, ripple::TxMeta const& meta, ripple::Blob const& uri)
: tokenID(tokenID)
, ledgerSequence(meta.getLgrSeq())
, transactionIndex(meta.getIndex())
, uri(uri)
, onlyUriChanged(true)
{
}
};

/**
Expand Down
16 changes: 15 additions & 1 deletion src/etl/NFTHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@

namespace etl {

std::pair<std::vector<NFTTransactionsData>, std::optional<NFTsData>>
getNFTokenMofidyData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
{
auto const tokenID = sttx.getFieldH256(ripple::sfNFTokenID);
// note: sfURI is optional, if it is absent, we will update the uri as empty string
return {
{NFTTransactionsData(sttx.getFieldH256(ripple::sfNFTokenID), txMeta, sttx.getTransactionID())},
NFTsData(tokenID, txMeta, sttx.getFieldVL(ripple::sfURI))
};
}

std::pair<std::vector<NFTTransactionsData>, std::optional<NFTsData>>
getNFTokenMintData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
{
Expand Down Expand Up @@ -165,7 +176,7 @@ getNFTokenBurnData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
node.peekAtField(ripple::sfPreviousFields).downcast<ripple::STObject>();
if (previousFields.isFieldPresent(ripple::sfNFTokens))
prevNFTs = previousFields.getFieldArray(ripple::sfNFTokens);
} else if (!prevNFTs && node.getFName() == ripple::sfDeletedNode) {
} else if (node.getFName() == ripple::sfDeletedNode) {
prevNFTs =
node.peekAtField(ripple::sfFinalFields).downcast<ripple::STObject>().getFieldArray(ripple::sfNFTokens);
}
Expand Down Expand Up @@ -335,6 +346,9 @@ getNFTDataFromTx(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
case ripple::TxType::ttNFTOKEN_CREATE_OFFER:
return getNFTokenCreateOfferData(txMeta, sttx);

case ripple::TxType::ttNFTOKEN_MODIFY:
return getNFTokenMofidyData(txMeta, sttx);

default:
return {{}, {}};
}
Expand Down
10 changes: 10 additions & 0 deletions src/etl/NFTHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@

namespace etl {

/**
* @brief Get the NFT URI change data from a NFToken Modify transaction
*
* @param txMeta Transaction metadata
* @param sttx The transaction
* @return NFT URI change data as a pair of transactions and optional NFTsData
*/
std::pair<std::vector<NFTTransactionsData>, std::optional<NFTsData>>
getNFTokenMofidyData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx);

/**
* @brief Get the NFT Token mint data from a transaction
*
Expand Down
17 changes: 15 additions & 2 deletions src/etl/impl/LedgerLoader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct FormattedTransactionsData {
std::vector<AccountTransactionsData> accountTxData;
std::vector<NFTTransactionsData> nfTokenTxData;
std::vector<NFTsData> nfTokensData;
std::vector<NFTsData> nfTokenURIChanges;
};

namespace etl::impl {
Expand Down Expand Up @@ -109,6 +110,7 @@ class LedgerLoader {
{
FormattedTransactionsData result;

std::vector<NFTsData> nfTokenURIChanges;
for (auto& txn : *(data.mutable_transactions_list()->mutable_transactions())) {
std::string* raw = txn.mutable_transaction_blob();

Expand All @@ -121,8 +123,15 @@ class LedgerLoader {

auto const [nftTxs, maybeNFT] = getNFTDataFromTx(txMeta, sttx);
result.nfTokenTxData.insert(result.nfTokenTxData.end(), nftTxs.begin(), nftTxs.end());
if (maybeNFT)
result.nfTokensData.push_back(*maybeNFT);

// We need to unique the URI changes separately, in case the URI changes are discarded
if (maybeNFT) {
if (maybeNFT->onlyUriChanged) {
nfTokenURIChanges.push_back(*maybeNFT);
} else {
result.nfTokensData.push_back(*maybeNFT);
}
}

result.accountTxData.emplace_back(txMeta, sttx.getTransactionID());
static constexpr std::size_t KEY_SIZE = 32;
Expand All @@ -137,6 +146,10 @@ class LedgerLoader {
}

result.nfTokensData = getUniqueNFTsDatas(result.nfTokensData);
nfTokenURIChanges = getUniqueNFTsDatas(nfTokenURIChanges);

// Put uri change at the end to ensure the uri not overwritten
result.nfTokensData.insert(result.nfTokensData.end(), nfTokenURIChanges.begin(), nfTokenURIChanges.end());
return result;
}

Expand Down
Loading

0 comments on commit 1edb890

Please sign in to comment.