diff --git a/src/omnicore/dbbase.cpp b/src/omnicore/dbbase.cpp index bec831521c085..9707428ba5806 100644 --- a/src/omnicore/dbbase.cpp +++ b/src/omnicore/dbbase.cpp @@ -33,15 +33,13 @@ void CDBBase::Clear() int64_t nTimeStart = GetTimeMicros(); unsigned int n = 0; leveldb::WriteBatch batch; - leveldb::Iterator* it = NewIterator(); + CDBaseIterator it{NewIterator()}; - for (it->SeekToFirst(); it->Valid(); it->Next()) { + for (; it; ++it) { batch.Delete(it->key()); ++n; } - delete it; - leveldb::Status status = pdb->Write(writeoptions, &batch); nRead = 0; nWritten = 0; diff --git a/src/omnicore/dbbase.h b/src/omnicore/dbbase.h index 9b1e902fc4f22..9efee806ac1fb 100644 --- a/src/omnicore/dbbase.h +++ b/src/omnicore/dbbase.h @@ -1,11 +1,13 @@ #ifndef BITCOIN_OMNICORE_DBBASE_H #define BITCOIN_OMNICORE_DBBASE_H +#include #include #include #include +#include #include /** Base class for LevelDB based storage. @@ -93,5 +95,48 @@ class CDBBase void Clear(); }; +class CDBaseIterator +{ +private: + std::unique_ptr it; + +public: + explicit CDBaseIterator(leveldb::Iterator* i, const std::string& first = {}) : it(i) + { + assert(it); + first.empty() ? it->SeekToFirst() : it->Seek(first); + } + + CDBaseIterator& operator=(CDBaseIterator&& i) + { + it = std::move(i.it); + assert(it); + return *this; + } + + CDBaseIterator& operator++() + { + assert(it->Valid()); + it->Next(); + return *this; + } + + CDBaseIterator& operator--() + { + assert(it->Valid()); + it->Prev(); + return *this; + } + + operator bool() const + { + return it->Valid(); + } + + leveldb::Iterator* operator->() + { + return it.get(); + } +}; #endif // BITCOIN_OMNICORE_DBBASE_H diff --git a/src/omnicore/nftdb.cpp b/src/omnicore/nftdb.cpp index 5886484383a04..51471baf231f8 100644 --- a/src/omnicore/nftdb.cpp +++ b/src/omnicore/nftdb.cpp @@ -12,48 +12,41 @@ #include +#include +#include #include +#include #include #include typedef std::underlying_type::type StorageType; -/* Extracts the property ID from a DB key - */ -uint32_t CMPNonFungibleTokensDB::GetPropertyIdFromKey(const std::string& key) +static std::string createNFTKey(uint32_t propertyId, NonFungibleStorage type = NonFungibleStorage::None, int64_t tokenIdStart = 0, int64_t tokenIdEnd = 0) { - std::vector vPropertyId; - boost::split(vPropertyId, key, boost::is_any_of("_"), boost::token_compress_on); - assert(vPropertyId.size() == 3); // if size !=3 then we cannot trust the data in the DB and we must halt - return boost::lexical_cast(vPropertyId[0]); + return strprintf("%010d_%u_%020d-%020d", propertyId, static_cast(type), tokenIdStart, tokenIdEnd); } -/* Extracts the storage type from a DB key - */ -NonFungibleStorage CMPNonFungibleTokensDB::GetTypeFromKey(const std::string& key) +static std::tuple parseNFTKey(const std::string& key) { std::vector vPropertyId; boost::split(vPropertyId, key, boost::is_any_of("_"), boost::token_compress_on); assert(vPropertyId.size() == 3); // if size !=3 then we cannot trust the data in the DB and we must halt - auto ch = boost::lexical_cast(vPropertyId[1]); - return static_cast(ch); -} -/* Extracts the range from a DB key - */ -void CMPNonFungibleTokensDB::GetRangeFromKey(const std::string& key, int64_t *start, int64_t *end) -{ - std::vector vPropertyId; - boost::split(vPropertyId, key, boost::is_any_of("_"), boost::token_compress_on); - assert(vPropertyId.size() == 3); // if size !=2 then we cannot trust the data in the DB and we must halt + std::vector vRanges; + boost::split(vRanges, vPropertyId[2], boost::is_any_of("-"), boost::token_compress_on); + assert(vRanges.size() == 2); // if size !=2 then we cannot trust the data in the DB and we must halt - std::vector vRanges; - boost::split(vRanges, vPropertyId[2], boost::is_any_of("-"), boost::token_compress_on); - assert(vRanges.size() == 2); // if size !=2 then we cannot trust the data in the DB and we must halt + return std::make_tuple(boost::lexical_cast(vPropertyId[0]), + static_cast(boost::lexical_cast(vPropertyId[1])), + boost::lexical_cast(vRanges[0]), + boost::lexical_cast(vRanges[1])); +} - *start = boost::lexical_cast(vRanges[0]); - *end = boost::lexical_cast(vRanges[1]); +template +auto to_pair(std::tuple& t) -> decltype(std::make_pair(std::get(t), std::get(t))) +{ + return std::make_pair(std::get(t), std::get(t)); } /* Gets the range a non-fungible token is in @@ -61,51 +54,39 @@ void CMPNonFungibleTokensDB::GetRangeFromKey(const std::string& key, int64_t *st std::pair CMPNonFungibleTokensDB::GetRange(const uint32_t &propertyId, const int64_t &tokenId, const NonFungibleStorage type) { assert(pdb); - leveldb::Iterator* it = NewIterator(); + CDBaseIterator it{NewIterator(), createNFTKey(propertyId, type)}; - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (propertyId != GetPropertyIdFromKey(it->key().ToString()) || - GetTypeFromKey(it->key().ToString()) != type) continue; - - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - if (tokenId >= start && tokenId <= end) { - delete it; - return std::make_pair(start, end); + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (to_pair<0, 1>(nkey) != std::make_pair(propertyId, type)) { + break; + } + if (tokenId >= std::get<2>(nkey) && tokenId <= std::get<3>(nkey)) { + return to_pair<2, 3>(nkey); } } - delete it; - return std::make_pair(0,0); // token not found, return zero'd range + return std::make_pair(0, 0); // token not found, return zero'd range } /* Checks if the range of tokens is contiguous (ie owned by a single address) */ -bool CMPNonFungibleTokensDB::IsRangeContiguous(const uint32_t &propertyId, const int64_t &rangeStart, const int64_t &rangeEnd) +std::string CMPNonFungibleTokensDB::GetNonFungibleTokenValueInRange(const uint32_t &propertyId, const int64_t &rangeStart, const int64_t &rangeEnd) { - assert(pdb); - leveldb::Iterator* it = NewIterator(); - - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (propertyId != GetPropertyIdFromKey(it->key().ToString()) || - GetTypeFromKey(it->key().ToString()) != NonFungibleStorage::RangeIndex) continue; - - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - - if (rangeStart >= start && rangeStart <= end) { - delete it; - if (rangeEnd >= rangeStart && rangeEnd <= end) { - return true; - } else { - return false; // the start ID falls within this range but the end ID does not - not owned by a single address - } + auto rangeIndex = NonFungibleStorage::RangeIndex; + CDBaseIterator it{NewIterator(), createNFTKey(propertyId, rangeIndex)}; + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (to_pair<0, 1>(nkey) != std::make_pair(propertyId, rangeIndex)) { + break; + } + if (rangeStart >= std::get<2>(nkey) && rangeEnd <= std::get<3>(nkey)) { + return it->value().ToString(); } } - delete it; - return false; // range doesn't exist + return {}; // range doesn't exist } /* Moves a range of tokens (returns false if not able to move) @@ -117,10 +98,10 @@ bool CMPNonFungibleTokensDB::MoveNonFungibleTokens(const uint32_t &propertyId, c assert(pdb); // check that 'from' owns both the start and end token and that the range is contiguous (owns the entire range) - std::string startOwner = GetNonFungibleTokenOwner(propertyId, tokenIdStart); - std::string endOwner = GetNonFungibleTokenOwner(propertyId, tokenIdEnd); - bool contiguous = IsRangeContiguous(propertyId, tokenIdStart, tokenIdEnd); - if (startOwner != from || endOwner != from || !contiguous) return false; + std::string startOwner = GetNonFungibleTokenValueInRange(propertyId, tokenIdStart, tokenIdEnd); + if (startOwner != from) { + return false; + } // are we moving the complete range from 'from'? // we know the range is contiguous (above) so we can use a single GetRange call @@ -133,8 +114,8 @@ bool CMPNonFungibleTokensDB::MoveNonFungibleTokens(const uint32_t &propertyId, c // does 'to' have adjacent ranges that need to be merged? bool bToAdjacentRangeBefore = false; bool bToAdjacentRangeAfter = false; - std::string rangeBelowOwner = GetNonFungibleTokenOwner(propertyId, tokenIdStart-1); - std::string rangeAfterOwner = GetNonFungibleTokenOwner(propertyId, tokenIdEnd+1); + std::string rangeBelowOwner = GetNonFungibleTokenValue(propertyId, tokenIdStart - 1, NonFungibleStorage::RangeIndex); + std::string rangeAfterOwner = GetNonFungibleTokenValue(propertyId, tokenIdEnd + 1, NonFungibleStorage::RangeIndex); if (rangeBelowOwner == to) { bToAdjacentRangeBefore = true; } @@ -200,8 +181,8 @@ bool CMPNonFungibleTokensDB::ChangeNonFungibleTokenData(const uint32_t &property // If we have previous ranges rewrite if needed if (!ranges.empty()) { // Get data on before and after ranges we are writing over - auto beforeData = GetNonFungibleTokenData(propertyId, ranges.begin()->first, type); - auto afterData = GetNonFungibleTokenData(propertyId, ranges.rbegin()->first, type); + auto beforeData = GetNonFungibleTokenValue(propertyId, ranges.begin()->first, type); + auto afterData = GetNonFungibleTokenValue(propertyId, ranges.rbegin()->first, type); // Delete all ranges for (const auto& tokenRange : ranges) { @@ -230,19 +211,16 @@ int64_t CMPNonFungibleTokensDB::GetHighestRangeEnd(const uint32_t &propertyId) assert(pdb); int64_t tokenCount = 0; - leveldb::Iterator* it = NewIterator(); - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (propertyId != GetPropertyIdFromKey(it->key().ToString()) || - GetTypeFromKey(it->key().ToString()) != NonFungibleStorage::RangeIndex) continue; - - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); + auto rangeIndex = NonFungibleStorage::RangeIndex; + CDBaseIterator it{NewIterator(), createNFTKey(propertyId, rangeIndex)}; - if (end > tokenCount) { - tokenCount = end; + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (to_pair<0, 1>(nkey) != std::make_pair(propertyId, rangeIndex)) { + break; } + tokenCount = std::max(tokenCount, std::max(std::get<2>(nkey), std::get<3>(nkey))); } - delete it; return tokenCount; } @@ -251,7 +229,7 @@ int64_t CMPNonFungibleTokensDB::GetHighestRangeEnd(const uint32_t &propertyId) void CMPNonFungibleTokensDB::DeleteRange(const uint32_t &propertyId, const int64_t &tokenIdStart, const int64_t &tokenIdEnd, const NonFungibleStorage type) { assert(pdb); - const std::string key = strprintf("%010d_%u_%020d-%020d", propertyId, static_cast(type), tokenIdStart, tokenIdEnd); + std::string key = createNFTKey(propertyId, type, tokenIdStart, tokenIdEnd); pdb->Delete(leveldb::WriteOptions(), key); if (msc_debug_nftdb) PrintToLog("%s():%s, line %d, file: %s\n", __FUNCTION__, key, __LINE__, __FILE__); @@ -263,7 +241,7 @@ void CMPNonFungibleTokensDB::AddRange(const uint32_t &propertyId, const int64_t { assert(pdb); - const std::string key = strprintf("%010d_%u_%020d-%020d", propertyId, static_cast(type), tokenIdStart, tokenIdEnd); + std::string key = createNFTKey(propertyId, type, tokenIdStart, tokenIdEnd); leveldb::Status status = pdb->Put(writeoptions, key, info); ++nWritten; @@ -276,12 +254,16 @@ std::pair CMPNonFungibleTokensDB::CreateNonFungibleTokens(const { if (msc_debug_nftdb) PrintToLog("%s(): %d:%d:%s, line %d, file: %s\n", __FUNCTION__, propertyId, amount, owner, __LINE__, __FILE__); + // negative amount will result in incorrect work + if (amount < 0) { + return {}; + } + int64_t highestId = GetHighestRangeEnd(propertyId); int64_t newTokenStartId = highestId + 1; int64_t newTokenEndId = 0; - if ( ((amount > 0) && (highestId > (std::numeric_limits::max()-amount))) || /* overflow */ - ((amount < 0) && (highestId < (std::numeric_limits::min()-amount))) ) { /* underflow */ + if ( (highestId > (std::numeric_limits::max() - amount))) { /* overflow */ newTokenEndId = std::numeric_limits::max(); } else { newTokenEndId = highestId + amount; @@ -291,7 +273,7 @@ std::pair CMPNonFungibleTokensDB::CreateNonFungibleTokens(const std::pair newRange = std::make_pair(newTokenStartId, newTokenEndId); - std::string highestRangeOwner = GetNonFungibleTokenOwner(propertyId, highestId); + std::string highestRangeOwner = GetNonFungibleTokenValue(propertyId, highestId, NonFungibleStorage::RangeIndex); if (highestRangeOwner == owner) { std::pair oldRange = GetRange(propertyId, highestId, NonFungibleStorage::RangeIndex); DeleteRange(propertyId, oldRange.first, oldRange.second, NonFungibleStorage::RangeIndex); @@ -303,54 +285,24 @@ std::pair CMPNonFungibleTokensDB::CreateNonFungibleTokens(const return newRange; } -/* Gets the owner of a range of non-fungible tokens - */ -std::string CMPNonFungibleTokensDB::GetNonFungibleTokenOwner(const uint32_t &propertyId, const int64_t &tokenId) -{ - assert(pdb); - leveldb::Iterator* it = NewIterator(); - - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (propertyId != GetPropertyIdFromKey(it->key().ToString()) || - GetTypeFromKey(it->key().ToString()) != NonFungibleStorage::RangeIndex) continue; - - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - - if (tokenId >= start && tokenId <= end) { - std::string retval = it->value().ToString(); - delete it; - return retval; - } - } - - delete it; - return ""; // not found -} - /* Gets the info set in a non-fungible token */ -std::string CMPNonFungibleTokensDB::GetNonFungibleTokenData(const uint32_t &propertyId, const int64_t &tokenId, const NonFungibleStorage type) +std::string CMPNonFungibleTokensDB::GetNonFungibleTokenValue(const uint32_t &propertyId, const int64_t &tokenId, const NonFungibleStorage type) { assert(pdb); - leveldb::Iterator* it = NewIterator(); + CDBaseIterator it{NewIterator(), createNFTKey(propertyId, type)}; - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (propertyId != GetPropertyIdFromKey(it->key().ToString()) || - GetTypeFromKey(it->key().ToString()) != type) continue; - - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - - if (tokenId >= start && tokenId <= end) { - std::string retval = it->value().ToString(); - delete it; - return retval; + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (to_pair<0, 1>(nkey) != std::make_pair(propertyId, type)) { + break; + } + if (tokenId >= std::get<2>(nkey) && tokenId <= std::get<3>(nkey)) { + return it->value().ToString(); } } - delete it; - return ""; // not found + return {}; // not found } /* Gets the ranges of non-fungible tokens owned by an address @@ -359,45 +311,44 @@ std::map>> CMPNonFungibleToken { std::map>> uniqueMap; assert(pdb); - leveldb::Iterator* it = NewIterator(); - for (it->SeekToFirst(); it->Valid(); it->Next()) { + auto rangeIndex = NonFungibleStorage::RangeIndex; + CDBaseIterator it{NewIterator(), createNFTKey(propertyId, rangeIndex)}; + + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (propertyId != 0 && to_pair<0, 1>(nkey) != std::make_pair(propertyId, rangeIndex)) { + break; + } + if (std::get<1>(nkey) != rangeIndex) { + continue; + } std::string value = it->value().ToString(); if (value != address) continue; - const auto id = GetPropertyIdFromKey(it->key().ToString()); - if ((propertyId != 0 && propertyId != id) || - GetTypeFromKey(it->key().ToString()) != NonFungibleStorage::RangeIndex) continue; - - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - - uniqueMap[id].emplace_back(start, end); + uniqueMap[std::get<0>(nkey)].emplace_back(std::get<2>(nkey), std::get<3>(nkey)); } - delete it; + return uniqueMap; } /* Gets the ranges of non-fungible tokens for a property */ -std::vector > > CMPNonFungibleTokensDB::GetNonFungibleTokenRanges(const uint32_t &propertyId) +std::vector>> CMPNonFungibleTokensDB::GetNonFungibleTokenRanges(const uint32_t &propertyId) { - std::vector > > rangeMap; + std::vector>> rangeMap; assert(pdb); + auto rangeIndex = NonFungibleStorage::RangeIndex; + CDBaseIterator it{NewIterator(), createNFTKey(propertyId, rangeIndex)}; - leveldb::Iterator* it = NewIterator(); - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (propertyId != GetPropertyIdFromKey(it->key().ToString()) || - GetTypeFromKey(it->key().ToString()) != NonFungibleStorage::RangeIndex) continue; - - std::string address = it->value().ToString(); - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - - rangeMap.push_back(std::make_pair(address,std::make_pair(start, end))); + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (to_pair<0, 1>(nkey) != std::make_pair(propertyId, rangeIndex)) { + break; + } + rangeMap.emplace_back(it->value().ToString(), to_pair<2, 3>(nkey)); } - delete it; return rangeMap; } @@ -405,28 +356,25 @@ void CMPNonFungibleTokensDB::SanityCheck() { assert(pdb); - std::string result = ""; + std::string result; std::map totals; - leveldb::Iterator* it = NewIterator(); - for (it->SeekToFirst(); it->Valid(); it->Next()) { - if (GetTypeFromKey(it->key().ToString()) != NonFungibleStorage::RangeIndex) continue; - uint32_t propertyId = GetPropertyIdFromKey(it->key().ToString()); - int64_t start, end; - GetRangeFromKey(it->key().ToString(), &start, &end); - if (end > totals[propertyId]) { - totals[propertyId] = end; - } + CDBaseIterator it{NewIterator()}; + for (; it; ++it) { + auto nkey = parseNFTKey(it->key().ToString()); + if (std::get<1>(nkey) != NonFungibleStorage::RangeIndex) continue; + + auto& prop = totals[std::get<0>(nkey)]; + prop = std::max(prop, std::get<3>(nkey)); } - delete it; for (std::map::iterator it = totals.begin(); it != totals.end(); ++it) { if (mastercore::getTotalTokens(it->first) != it->second) { std::string abortMsg = strprintf("Failed sanity check on property %d (%d != %d)\n", it->first, mastercore::getTotalTokens(it->first), it->second); AbortNode(abortMsg); - } else { - result = result + strprintf("%d:%d=%d,", it->first, mastercore::getTotalTokens(it->first), it->second); + } else if (msc_debug_nftdb) { + result += strprintf("%d:%d=%d,", it->first, mastercore::getTotalTokens(it->first), it->second); } } @@ -441,17 +389,14 @@ void CMPNonFungibleTokensDB::printStats() void CMPNonFungibleTokensDB::printAll() { int count = 0; - leveldb::Slice skey, svalue; - leveldb::Iterator* it = NewIterator(); + CDBaseIterator it{NewIterator()}; - for(it->SeekToFirst(); it->Valid(); it->Next()) { - skey = it->key(); - svalue = it->value(); + for(; it; ++it) { + auto skey = it->key().ToString(); + auto svalue = it->value().ToString(); ++count; - PrintToConsole("entry #%8d= %s:%s\n", count, skey.ToString(), svalue.ToString()); + PrintToConsole("entry #%8d= %s:%s\n", count, skey, svalue); // PrintToLog("entry #%8d= %s:%s\n", count, skey.ToString(), svalue.ToString()); } - - delete it; } diff --git a/src/omnicore/nftdb.h b/src/omnicore/nftdb.h index cb171405c7eb6..1a41703e6fe95 100644 --- a/src/omnicore/nftdb.h +++ b/src/omnicore/nftdb.h @@ -38,19 +38,10 @@ class CMPNonFungibleTokensDB : public CDBBase void printStats(); void printAll(); - // Helper to extract the property ID from a DB key - uint32_t GetPropertyIdFromKey(const std::string& key); - // Extracts the storage type from a DB key - NonFungibleStorage GetTypeFromKey(const std::string& key); - // Helper to extracts the range from a DB key - void GetRangeFromKey(const std::string& key, int64_t *start, int64_t *end); - - // Gets the owner of a range of non-fungible tokens - std::string GetNonFungibleTokenOwner(const uint32_t &propertyId, const int64_t &tokenId); // Gets the data set in a non-fungible token - std::string GetNonFungibleTokenData(const uint32_t &propertyId, const int64_t &tokenId, const NonFungibleStorage type); + std::string GetNonFungibleTokenValue(const uint32_t &propertyId, const int64_t &tokenId, const NonFungibleStorage type); // Checks if the range of tokens is contiguous (ie owned by a single address) - bool IsRangeContiguous(const uint32_t &propertyId, const int64_t &rangeStart, const int64_t &rangeEnd); + std::string GetNonFungibleTokenValueInRange(const uint32_t &propertyId, const int64_t &rangeStart, const int64_t &rangeEnd); // Counts the highest token range end (which is thus the total number of tokens) int64_t GetHighestRangeEnd(const uint32_t &propertyId); // Creates a range of non-fungible tokens @@ -68,7 +59,7 @@ class CMPNonFungibleTokensDB : public CDBBase // Gets the non-fungible token ranges for a property ID and address std::map>> GetAddressNonFungibleTokens(const uint32_t &propertyId, const std::string &address); // Gets the non-fungible token ranges for a property ID - std::vector > > GetNonFungibleTokenRanges(const uint32_t &propertyId); + std::vector>> GetNonFungibleTokenRanges(const uint32_t &propertyId); // Sanity checks the token counts void SanityCheck(); }; diff --git a/src/omnicore/rpc.cpp b/src/omnicore/rpc.cpp index 4dfc5feb3593d..290f2257eea44 100644 --- a/src/omnicore/rpc.cpp +++ b/src/omnicore/rpc.cpp @@ -317,10 +317,10 @@ UniValue omni_getnonfungibletokendata(const JSONRPCRequest& request) UniValue result(UniValue::VARR); for (; start <= end; ++start) { - auto owner = pDbNFT->GetNonFungibleTokenOwner(propertyId, start); - auto grantData = pDbNFT->GetNonFungibleTokenData(propertyId, start, NonFungibleStorage::GrantData); - auto issuerData = pDbNFT->GetNonFungibleTokenData(propertyId, start, NonFungibleStorage::IssuerData); - auto holderData = pDbNFT->GetNonFungibleTokenData(propertyId, start, NonFungibleStorage::HolderData); + auto owner = pDbNFT->GetNonFungibleTokenValue(propertyId, start, NonFungibleStorage::RangeIndex); + auto grantData = pDbNFT->GetNonFungibleTokenValue(propertyId, start, NonFungibleStorage::GrantData); + auto issuerData = pDbNFT->GetNonFungibleTokenValue(propertyId, start, NonFungibleStorage::IssuerData); + auto holderData = pDbNFT->GetNonFungibleTokenValue(propertyId, start, NonFungibleStorage::HolderData); UniValue rpcObj(UniValue::VOBJ); rpcObj.pushKV("index", start); diff --git a/src/omnicore/rpcrequirements.cpp b/src/omnicore/rpcrequirements.cpp index 840de8fc3707d..f304fdabb3f5f 100644 --- a/src/omnicore/rpcrequirements.cpp +++ b/src/omnicore/rpcrequirements.cpp @@ -256,10 +256,8 @@ void RequireHeightInChain(int blockHeight) void RequireNonFungibleTokenOwner(const std::string& address, uint32_t propertyId, int64_t tokenStart, int64_t tokenEnd) { - std::string rangeStartOwner = mastercore::pDbNFT->GetNonFungibleTokenOwner(propertyId, tokenStart); - std::string rangeEndOwner = mastercore::pDbNFT->GetNonFungibleTokenOwner(propertyId, tokenEnd); - bool contiguous = mastercore::pDbNFT->IsRangeContiguous(propertyId, tokenStart, tokenEnd); - if (rangeStartOwner != address || rangeEndOwner != address || !contiguous) { + std::string rangeStartOwner = mastercore::pDbNFT->GetNonFungibleTokenValueInRange(propertyId, tokenStart, tokenEnd); + if (rangeStartOwner != address) { throw JSONRPCError(RPC_TYPE_ERROR, "Sender does not own the range"); } } diff --git a/src/omnicore/rpctx.cpp b/src/omnicore/rpctx.cpp index ffdb24bd0e511..14a43a522bcbe 100644 --- a/src/omnicore/rpctx.cpp +++ b/src/omnicore/rpctx.cpp @@ -393,12 +393,12 @@ static UniValue omni_setnonfungibledata(const JSONRPCRequest& request) RequireNonFungibleProperty(propertyId); RequireSaneNonFungibleRange(tokenStart, tokenEnd); - if (!issuer && !pDbNFT->IsRangeContiguous(propertyId, tokenStart, tokenEnd)) { + std::string fromAddress = pDbNFT->GetNonFungibleTokenValueInRange(propertyId, tokenStart, tokenEnd); + + if (!issuer && fromAddress.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Range set owned by multiple addresses, set data one owner a time"); } - std::string fromAddress = pDbNFT->GetNonFungibleTokenOwner(propertyId, tokenStart); - if (issuer) { CMPSPInfo::Entry sp; { diff --git a/src/omnicore/rules.cpp b/src/omnicore/rules.cpp index 5d91364be9898..729c133689c65 100644 --- a/src/omnicore/rules.cpp +++ b/src/omnicore/rules.cpp @@ -354,7 +354,7 @@ CRegTestConsensusParams::CRegTestConsensusParams() MSC_NONFUNGIBLE_BLOCK = 0; MSC_DELEGATED_ISSUANCE_BLOCK = 0; MSC_SEND_TO_MANY_BLOCK = 0; - MSC_NFT_BLOCK = 999999; + MSC_NFT_BLOCK = 0; // Other feature activations: GRANTEFFECTS_FEATURE_BLOCK = 999999; DEXMATH_FEATURE_BLOCK = 999999; diff --git a/src/omnicore/test/nftdb_tests.cpp b/src/omnicore/test/nftdb_tests.cpp index e86e4938b51dc..0bdfe2e50cc48 100644 --- a/src/omnicore/test/nftdb_tests.cpp +++ b/src/omnicore/test/nftdb_tests.cpp @@ -16,7 +16,7 @@ BOOST_FIXTURE_TEST_SUITE(omnicore_nftdb_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(nftdb_test_sequence) { LOCK(cs_tally); - auto UITDb = new CMPNonFungibleTokensDB(GetDataDir() / "OMNI_nftdb", true); + std::unique_ptr UITDb{new CMPNonFungibleTokensDB(GetDataDir() / "OMNI_nftdb", true)}; /* Test A: * - Create 1000 tokens of prop 50 and assign them to address 'Alice' @@ -24,13 +24,18 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) * - Check that the owner of token ID 1 of prop 50 is address 'Alice' * - Check that the owner of token ID 1000 of prop 50 is address 'Alice' * - Check that the owner of token ID 454 of prop 50 is address 'Alice' + * - Check that range of token IDs is not negative */ std::pair testA = UITDb->CreateNonFungibleTokens(50, 1000, "Alice", ""); BOOST_CHECK_EQUAL(1, testA.first); BOOST_CHECK_EQUAL(1000, testA.second); - BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenOwner(50, 1)); - BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenOwner(50, 1000)); - BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenOwner(50, 454)); + BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenValueInRange(50, 1, 1000)); + BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenValue(50, 1000, NonFungibleStorage::RangeIndex)); + BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenValue(50, 454, NonFungibleStorage::RangeIndex)); + + std::pair testA1 = UITDb->CreateNonFungibleTokens(50, -1001, "Bob", ""); + BOOST_CHECK_EQUAL(0, testA1.first); + BOOST_CHECK_EQUAL(0, testA1.second); /* Test B: * - Create another 1000 tokens of prop 50 and assign them to address 'Alice' @@ -42,9 +47,7 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) std::pair testB = UITDb->CreateNonFungibleTokens(50, 1000, "Alice", ""); BOOST_CHECK_EQUAL(1001, testB.first); BOOST_CHECK_EQUAL(2000, testB.second); - BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenOwner(50, 1001)); - BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenOwner(50, 2000)); - BOOST_CHECK(UITDb->IsRangeContiguous(50, 1, 2000)); + BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenValueInRange(50, 1001, 2000)); /* Test C: * - Create another 1000 tokens of prop 50 and assign them to address 'Bob' @@ -56,9 +59,8 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) std::pair testC = UITDb->CreateNonFungibleTokens(50, 1000, "Bob", ""); BOOST_CHECK_EQUAL(3000, testC.second); BOOST_CHECK_EQUAL(2001, testC.first); - BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenOwner(50, 2001)); - BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenOwner(50, 3000)); - BOOST_CHECK(!UITDb->IsRangeContiguous(50, 2000, 2001)); + BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenValueInRange(50, 2001, 3000)); + BOOST_CHECK(UITDb->GetNonFungibleTokenValueInRange(50, 2000, 2001).empty()); /* Test D: * - Move the entire token range 2001 to 3000 of prop 50 from address 'Bob' to address 'Charles' @@ -67,8 +69,7 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) * - Check that the owner of token ID 3000 of prop 50 is address 'Charles' */ BOOST_CHECK(UITDb->MoveNonFungibleTokens(50, 2001, 3000, "Bob", "Charles")); - BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenOwner(50, 2001)); - BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenOwner(50, 3000)); + BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenValueInRange(50, 2001, 3000)); /* Test E: * - Create another 1000 tokens of prop 50 and assign them to address 'David' @@ -85,13 +86,10 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) std::pair testE = UITDb->CreateNonFungibleTokens(50, 1000, "David", ""); BOOST_CHECK_EQUAL(3001, testE.first); BOOST_CHECK_EQUAL(4000, testE.second); - BOOST_CHECK_EQUAL("David", UITDb->GetNonFungibleTokenOwner(50, 3001)); - BOOST_CHECK_EQUAL("David", UITDb->GetNonFungibleTokenOwner(50, 4000)); - BOOST_CHECK(!UITDb->IsRangeContiguous(50, 3000, 3001)); + BOOST_CHECK_EQUAL("David", UITDb->GetNonFungibleTokenValueInRange(50, 3001, 4000)); + BOOST_CHECK(UITDb->GetNonFungibleTokenValueInRange(50, 3000, 3001).empty()); BOOST_CHECK(UITDb->MoveNonFungibleTokens(50, 3001, 4000, "David", "Charles")); - BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenOwner(50, 3001)); - BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenOwner(50, 4000)); - BOOST_CHECK(UITDb->IsRangeContiguous(50, 3000, 3001)); + BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenValueInRange(50, 3001, 4000)); /* Test F: * - Check that the owner of token ID 500 of prop 50 is 'Alice' @@ -101,9 +99,9 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) * - TODO: Check that address 'Alice' now owns 1-499 and 501-2000 via dump * - TODO: Check that address 'Bob' now owns 500 and 2001-3000 via dump */ - BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenOwner(50, 500)); + BOOST_CHECK_EQUAL("Alice", UITDb->GetNonFungibleTokenValue(50, 500, NonFungibleStorage::RangeIndex)); BOOST_CHECK(UITDb->MoveNonFungibleTokens(50, 500, 500, "Alice", "Bob")); - BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenOwner(50, 500)); + BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenValue(50, 500, NonFungibleStorage::RangeIndex)); /* Test G: * - Check that the owner of token ID 2300 of prop 50 is 'Charles' @@ -115,15 +113,9 @@ BOOST_AUTO_TEST_CASE(nftdb_test_sequence) * - Check that the owner of token ID 2400 of prop 50 is 'Bob' * - Check that the range 2300-2400 is contiguous */ - BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenOwner(50, 2300)); - BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenOwner(50, 2400)); - BOOST_CHECK(UITDb->IsRangeContiguous(50, 2300, 2400)); + BOOST_CHECK_EQUAL("Charles", UITDb->GetNonFungibleTokenValueInRange(50, 2300, 2400)); BOOST_CHECK(UITDb->MoveNonFungibleTokens(50, 2300, 2400, "Charles", "Bob")); - BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenOwner(50, 2300)); - BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenOwner(50, 2400)); - BOOST_CHECK(UITDb->IsRangeContiguous(50, 2300, 2400)); - - delete UITDb; + BOOST_CHECK_EQUAL("Bob", UITDb->GetNonFungibleTokenValueInRange(50, 2300, 2400)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/omnicore/tx.cpp b/src/omnicore/tx.cpp index deb01cce484e2..02df155deacda 100644 --- a/src/omnicore/tx.cpp +++ b/src/omnicore/tx.cpp @@ -1573,10 +1573,8 @@ int CMPTransaction::logicMath_SendNonFungible() return (PKT_ERROR_NFT -32); } - std::string rangeStartOwner = pDbNFT->GetNonFungibleTokenOwner(property, nonfungible_token_start); - std::string rangeEndOwner = pDbNFT->GetNonFungibleTokenOwner(property, nonfungible_token_end); - bool contiguous = pDbNFT->IsRangeContiguous(property, nonfungible_token_start, nonfungible_token_end); - if (rangeStartOwner != sender || rangeEndOwner != sender || !contiguous) { + std::string rangeStartOwner = pDbNFT->GetNonFungibleTokenValueInRange(property, nonfungible_token_start, nonfungible_token_end); + if (rangeStartOwner != sender) { PrintToLog("%s(): rejected: sender %s does not own the range being sent\n", __func__, sender); @@ -2980,10 +2978,8 @@ int CMPTransaction::logicMath_NonFungibleData() if (type == NonFungibleStorage::HolderData) { - std::string rangeStartOwner = pDbNFT->GetNonFungibleTokenOwner(property, nonfungible_token_start); - std::string rangeEndOwner = pDbNFT->GetNonFungibleTokenOwner(property, nonfungible_token_end); - bool contiguous = pDbNFT->IsRangeContiguous(property, nonfungible_token_start, nonfungible_token_end); - if (rangeStartOwner != sender || rangeEndOwner != sender || !contiguous) { + std::string rangeStartOwner = pDbNFT->GetNonFungibleTokenValueInRange(property, nonfungible_token_start, nonfungible_token_end); + if (rangeStartOwner != sender) { PrintToLog("%s(): rejected: sender %s does not own the range data is being set on\n", __func__, sender); diff --git a/test/functional/omni_nonfungibletokens.py b/test/functional/omni_nonfungibletokens.py index 27759b8fc3ad8..82c2be72788ce 100644 --- a/test/functional/omni_nonfungibletokens.py +++ b/test/functional/omni_nonfungibletokens.py @@ -670,7 +670,7 @@ def run_test(self): unspent = utxos break rawtx = self.nodes[1].createrawtransaction([{"txid":unspent['txid'], "vout":unspent['vout']}], [{unspent['address']:unspent['amount'] - Decimal('0.00001')}]) - payload = self.nodes[1].omni_createpayload_setnonfungibledata(property_id, 102, 102, True, "Test non-issuer update") + payload = self.nodes[1].omni_createpayload_setnonfungibledata(property_id, 102, 102, False, "Test non-issuer update") rawtx = self.nodes[1].omni_createrawtx_opreturn(rawtx, payload) signed_rawtx = self.nodes[1].signrawtransactionwithwallet(rawtx) txid = self.nodes[1].sendrawtransaction(signed_rawtx['hex']) @@ -682,7 +682,7 @@ def run_test(self): # Check issuer data set by non-issuer result = self.nodes[1].omni_getnonfungibletokendata(property_id, 102) - assert_equal(result[0]['issuerdata'], 'Test non-issuer update') + assert_equal(result[0]['holderdata'], 'Test non-issuer update') # Fund activation address activation_address = self.nodes[1].getnewaddress("", "legacy") @@ -719,7 +719,7 @@ def run_test(self): # Check issuer data same as before by non-issuer result = self.nodes[1].omni_getnonfungibletokendata(property_id, 102) - assert_equal(result[0]['issuerdata'], 'Test non-issuer update') + assert_equal(result[0]['holderdata'], 'Test non-issuer update') if __name__ == '__main__': OmniNonFungibleTokensTest().main()