flattening prefix trie work

put getclaimsintrie back as deprecated


added test for adding a lot of data to the claimtrie


updated unit test to dodge expiration fork
This commit is contained in:
Brannon King 2019-07-01 12:42:45 -06:00
parent 8d955fdd22
commit 9a67b514c9
28 changed files with 9020 additions and 6106 deletions

View file

@ -59,12 +59,13 @@ def get_obj_from_dirty_text(full_object: str):
last_name = property_name last_name = property_name
elif len(left) > 1: elif len(left) > 1:
match = re.match(r'^(\[)?"(?P<name>\w.*?)"(\])?.*', left) match = re.match(r'^(\[)?"(?P<name>\w.*?)"(\])?.*', left)
last_name = match.group('name') if match is not None:
if match.group(1) is not None and match.group(3) is not None: last_name = match.group('name')
left = '[' if match.group(1) is not None and match.group(3) is not None:
property_refined_type = 'string' left = '['
if 'string' not in line: property_refined_type = 'string'
raise NotImplementedError('Not implemented: ' + line) if 'string' not in line:
raise NotImplementedError('Not implemented: ' + line)
if left.endswith('['): if left.endswith('['):
object_stack.append({'type': 'array', 'items': {'type': property_refined_type}}) object_stack.append({'type': 'array', 'items': {'type': property_refined_type}})
@ -96,7 +97,15 @@ def get_obj_from_dirty_text(full_object: str):
ret = obj ret = obj
if ret is not None: if ret is not None:
if i + 1 < len(lines) - 1: if i + 1 < len(lines) - 1:
print('Ignoring this data (below the parsed object): ' + "\n".join(lines[i+1:]), file=sys.stderr) print('WARNING: unparsable data...', file=sys.stderr)
lines = lines[i+1:]
if not lines[0]:
lines = lines[1:]
nret = get_obj_from_dirty_text("\n".join(lines))
if not nret:
nret = get_obj_from_dirty_text("\n".join(lines[1:]))
if nret:
ret.update(nret)
return ret return ret
except Exception as e: except Exception as e:
print('Exception: ' + str(e), file=sys.stderr) print('Exception: ' + str(e), file=sys.stderr)
@ -113,7 +122,7 @@ def get_type(arg_type: str, full_line: str):
arg_type = arg_type.lower() arg_type = arg_type.lower()
if 'array' in arg_type: if 'array' in arg_type:
return 'array', required, None return 'array', required, None
if 'numeric' in arg_type: if 'numeric' in arg_type or 'number' in arg_type:
return 'number', required, None return 'number', required, None
if 'bool' in arg_type: if 'bool' in arg_type:
return 'boolean', required, None return 'boolean', required, None
@ -123,6 +132,11 @@ def get_type(arg_type: str, full_line: str):
properties = get_obj_from_dirty_text(full_line) if full_line is not None else None properties = get_obj_from_dirty_text(full_line) if full_line is not None else None
return 'object', required, properties return 'object', required, properties
if arg_type.startswith('optional'):
return 'optional', required, None
if arg_type.startswith('json'):
return 'json', required, None
print('Unable to derive type from: ' + arg_type, file=sys.stderr) print('Unable to derive type from: ' + arg_type, file=sys.stderr)
return None, False, None return None, False, None

View file

@ -13,17 +13,22 @@ def get_type(arg_type, full_line):
if arg_type is None: if arg_type is None:
return 'string' return 'string'
arg_type = arg_type.lower() arg_type = arg_type.lower().split(',')[0].strip()
if 'numeric' in arg_type: if 'numeric' in arg_type:
return 'number' return 'number'
if 'bool' in arg_type: if 'bool' in arg_type:
return 'boolean' return 'boolean'
if 'string' in arg_type: if 'array' in arg_type:
return 'string' return 'array'
if 'object' in arg_type: if 'object' in arg_type:
return 'object' return 'object'
raise Exception('Not implemented: ' + arg_type) supported_types = ['number', 'string', 'object', 'array', 'optional']
if arg_type in supported_types:
return arg_type
print("get_type: WARNING", arg_type, "is not supported type", file=sys.stderr)
return arg_type
def parse_params(args): def parse_params(args):
@ -34,7 +39,7 @@ def parse_params(args):
continue continue
arg_parsed = re_argline.fullmatch(line) arg_parsed = re_argline.fullmatch(line)
if arg_parsed is None: if arg_parsed is None:
raise Exception("Unparsable argument: " + line) continue
arg_name, arg_type, arg_desc = arg_parsed.group('name', 'type', 'desc') arg_name, arg_type, arg_desc = arg_parsed.group('name', 'type', 'desc')
if not arg_type: if not arg_type:
raise Exception('Not implemented: ' + arg_type) raise Exception('Not implemented: ' + arg_type)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -149,6 +149,7 @@ BITCOIN_CORE_H = \
policy/policy.h \ policy/policy.h \
policy/rbf.h \ policy/rbf.h \
pow.h \ pow.h \
prefixtrie.h \
protocol.h \ protocol.h \
random.h \ random.h \
reverse_iterator.h \ reverse_iterator.h \
@ -249,6 +250,7 @@ libbitcoin_server_a_SOURCES = \
policy/policy.cpp \ policy/policy.cpp \
policy/rbf.cpp \ policy/rbf.cpp \
pow.cpp \ pow.cpp \
prefixtrie.cpp \
rest.cpp \ rest.cpp \
rpc/blockchain.cpp \ rpc/blockchain.cpp \
rpc/claimtrie.cpp \ rpc/claimtrie.cpp \

View file

@ -71,6 +71,7 @@ BITCOIN_TESTS =\
test/pmt_tests.cpp \ test/pmt_tests.cpp \
test/policyestimator_tests.cpp \ test/policyestimator_tests.cpp \
test/pow_tests.cpp \ test/pow_tests.cpp \
test/prefixtrie_tests.cpp \
test/prevector_tests.cpp \ test/prevector_tests.cpp \
test/raii_event_tests.cpp \ test/raii_event_tests.cpp \
test/random_tests.cpp \ test/random_tests.cpp \
@ -151,7 +152,6 @@ test_test_lbrycrd_fuzzy_LDADD = \
$(LIBSECP256K1) $(LIBSECP256K1)
test_test_lbrycrd_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(ICU_LIBS) test_test_lbrycrd_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(ICU_LIBS)
#
nodist_test_test_lbrycrd_SOURCES = $(GENERATED_TEST_FILES) nodist_test_test_lbrycrd_SOURCES = $(GENERATED_TEST_FILES)

View file

@ -143,6 +143,8 @@ public:
consensus.nAllowMinDiffMinHeight = -1; consensus.nAllowMinDiffMinHeight = -1;
consensus.nAllowMinDiffMaxHeight = -1; consensus.nAllowMinDiffMaxHeight = -1;
consensus.nNormalizedNameForkHeight = 539940; // targeting 21 March 2019 consensus.nNormalizedNameForkHeight = 539940; // targeting 21 March 2019
consensus.nMinTakeoverWorkaroundHeight = 496850;
consensus.nMaxTakeoverWorkaroundHeight = 10000000;
consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false; consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
@ -258,6 +260,8 @@ public:
consensus.nAllowMinDiffMinHeight = 277299; consensus.nAllowMinDiffMinHeight = 277299;
consensus.nAllowMinDiffMaxHeight = 1100000; consensus.nAllowMinDiffMaxHeight = 1100000;
consensus.nNormalizedNameForkHeight = 993380; // targeting, 21 Feb 2019 consensus.nNormalizedNameForkHeight = 993380; // targeting, 21 Feb 2019
consensus.nMinTakeoverWorkaroundHeight = 99;
consensus.nMaxTakeoverWorkaroundHeight = 10000000;
consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowAllowMinDifficultyBlocks = true;
consensus.fPowNoRetargeting = false; consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
@ -361,6 +365,8 @@ public:
consensus.nAllowMinDiffMinHeight = -1; consensus.nAllowMinDiffMinHeight = -1;
consensus.nAllowMinDiffMaxHeight = -1; consensus.nAllowMinDiffMaxHeight = -1;
consensus.nNormalizedNameForkHeight = 250; // SDK depends upon this number consensus.nNormalizedNameForkHeight = 250; // SDK depends upon this number
consensus.nMinTakeoverWorkaroundHeight = -1;
consensus.nMaxTakeoverWorkaroundHeight = -1;
consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false; consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,40 +2,36 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp> #include <boost/locale/conversion.hpp>
#include <boost/locale/localization_backend.hpp> #include <boost/locale/localization_backend.hpp>
#include <boost/locale.hpp>
#include <boost/scope_exit.hpp> #include <boost/scope_exit.hpp>
void CClaimTrieCacheExpirationFork::removeAndAddToExpirationQueue(expirationQueueRowType &row, int height, bool increment) const void CClaimTrieCacheExpirationFork::removeAndAddToExpirationQueue(expirationQueueRowType& row, int height, bool increment)
{ {
for (expirationQueueRowType::iterator e = row.begin(); e != row.end(); ++e) for (auto e = row.begin(); e != row.end(); ++e) {
{
// remove and insert with new expiration time // remove and insert with new expiration time
removeFromExpirationQueue(e->name, e->outPoint, height); removeFromExpirationQueue(e->name, e->outPoint, height);
int extend_expiration = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime; int extend_expiration = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime;
int new_expiration_height = increment ? height + extend_expiration : height - extend_expiration; int new_expiration_height = increment ? height + extend_expiration : height - extend_expiration;
nameOutPointType entry(e->name, e->outPoint); CNameOutPointType entry(e->name, e->outPoint);
addToExpirationQueue(new_expiration_height, entry); addToExpirationQueue(new_expiration_height, entry);
} }
} }
void CClaimTrieCacheExpirationFork::removeAndAddSupportToExpirationQueue(expirationQueueRowType &row, int height, bool increment) const void CClaimTrieCacheExpirationFork::removeAndAddSupportToExpirationQueue(expirationQueueRowType& row, int height, bool increment)
{ {
for (expirationQueueRowType::iterator e = row.begin(); e != row.end(); ++e) for (auto e = row.begin(); e != row.end(); ++e) {
{
// remove and insert with new expiration time // remove and insert with new expiration time
removeSupportFromExpirationQueue(e->name, e->outPoint, height); removeSupportFromExpirationQueue(e->name, e->outPoint, height);
int extend_expiration = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime; int extend_expiration = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime;
int new_expiration_height = increment ? height + extend_expiration : height - extend_expiration; int new_expiration_height = increment ? height + extend_expiration : height - extend_expiration;
nameOutPointType entry(e->name, e->outPoint); CNameOutPointType entry(e->name, e->outPoint);
addSupportToExpirationQueue(new_expiration_height, entry); addSupportToExpirationQueue(new_expiration_height, entry);
} }
} }
bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment) const bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment)
{ {
/* /*
If increment is True, we have forked to extend the expiration time, thus items in the expiration queue If increment is True, we have forked to extend the expiration time, thus items in the expiration queue
@ -45,75 +41,40 @@ bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment) cons
will have their expiration extension removed. will have their expiration extension removed.
*/ */
// look through dirty expiration queues
std::set<int> dirtyHeights;
for (expirationQueueType::const_iterator i = base->dirtyExpirationQueueRows.begin(); i != base->dirtyExpirationQueueRows.end(); ++i)
{
int height = i->first;
dirtyHeights.insert(height);
expirationQueueRowType row = i->second;
removeAndAddToExpirationQueue(row, height, increment);
}
std::set<int> dirtySupportHeights;
for (expirationQueueType::const_iterator i = base->dirtySupportExpirationQueueRows.begin(); i != base->dirtySupportExpirationQueueRows.end(); ++i)
{
int height = i->first;
dirtySupportHeights.insert(height);
expirationQueueRowType row = i->second;
removeAndAddSupportToExpirationQueue(row, height, increment);
}
//look through db for expiration queues, if we haven't already found it in dirty expiration queue //look through db for expiration queues, if we haven't already found it in dirty expiration queue
boost::scoped_ptr<CDBIterator> pcursor(const_cast<CDBWrapper*>(&base->db)->NewIterator()); boost::scoped_ptr<CDBIterator> pcursor(base->db->NewIterator());
pcursor->SeekToFirst(); for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
while (pcursor->Valid()) std::pair<uint8_t, int> key;
{ if (!pcursor->GetKey(key))
std::pair<char, int> key; continue;
if (pcursor->GetKey(key)) int height = key.second;
{ if (key.first == EXP_QUEUE_ROW) {
int height = key.second; expirationQueueRowType row;
// if we've looked through this in dirtyExprirationQueueRows, don't use it if (pcursor->GetValue(row)) {
// because its stale removeAndAddToExpirationQueue(row, height, increment);
if ((key.first == EXP_QUEUE_ROW) & (dirtyHeights.count(height) == 0)) } else {
{ return error("%s(): error reading expiration queue rows from disk", __func__);
expirationQueueRowType row;
if (pcursor->GetValue(row))
{
removeAndAddToExpirationQueue(row, height, increment);
}
else
{
return error("%s(): error reading expiration queue rows from disk", __func__);
}
} }
else if ((key.first == SUPPORT_EXP_QUEUE_ROW) & (dirtySupportHeights.count(height) == 0)) } else if (key.first == SUPPORT_EXP_QUEUE_ROW) {
{ expirationQueueRowType row;
expirationQueueRowType row; if (pcursor->GetValue(row)) {
if (pcursor->GetValue(row)) removeAndAddSupportToExpirationQueue(row, height, increment);
{ } else {
removeAndAddSupportToExpirationQueue(row, height, increment); return error("%s(): error reading support expiration queue rows from disk", __func__);
}
else
{
return error("%s(): error reading support expiration queue rows from disk", __func__);
}
} }
} }
pcursor->Next();
} }
return true; return true;
} }
bool CClaimTrieCacheNormalizationFork::shouldNormalize() const
bool CClaimTrieCacheNormalizationFork::shouldNormalize() const { {
return nCurrentHeight > Params().GetConsensus().nNormalizedNameForkHeight; return nNextHeight > Params().GetConsensus().nNormalizedNameForkHeight;
} }
std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::string& name, bool force) const { std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::string& name, bool force) const
{
if (!force && !shouldNormalize()) if (!force && !shouldNormalize())
return name; return name;
@ -121,7 +82,7 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
static bool initialized = false; static bool initialized = false;
if (!initialized) { if (!initialized) {
static boost::locale::localization_backend_manager manager = static boost::locale::localization_backend_manager manager =
boost::locale::localization_backend_manager::global(); boost::locale::localization_backend_manager::global();
manager.select("icu"); manager.select("icu");
static boost::locale::generator curLocale(manager); static boost::locale::generator curLocale(manager);
@ -131,7 +92,6 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
std::string normalized; std::string normalized;
try { try {
// Check if it is a valid utf-8 string. If not, it will throw a // Check if it is a valid utf-8 string. If not, it will throw a
// boost::locale::conv::conversion_error exception which we catch later // boost::locale::conv::conversion_error exception which we catch later
normalized = boost::locale::conv::to_utf<char>(name, "UTF-8", boost::locale::conv::stop); normalized = boost::locale::conv::to_utf<char>(name, "UTF-8", boost::locale::conv::stop);
@ -141,15 +101,12 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
// these methods supposedly only use the "UTF8" portion of the locale object: // these methods supposedly only use the "UTF8" portion of the locale object:
normalized = boost::locale::normalize(normalized, boost::locale::norm_nfd, utf8); normalized = boost::locale::normalize(normalized, boost::locale::norm_nfd, utf8);
normalized = boost::locale::fold_case(normalized, utf8); normalized = boost::locale::fold_case(normalized, utf8);
} } catch (const boost::locale::conv::conversion_error& e) {
catch (const boost::locale::conv::conversion_error& e){
return name; return name;
} } catch (const std::bad_cast& e) {
catch (const std::bad_cast& e) {
LogPrintf("%s() is invalid or dependencies are missing: %s\n", __func__, e.what()); LogPrintf("%s() is invalid or dependencies are missing: %s\n", __func__, e.what());
throw; throw;
} } catch (const std::exception& e) { // TODO: change to use ... with current_exception() in c++11
catch (const std::exception& e) { // TODO: change to use ... with current_exception() in c++11
LogPrintf("%s() had an unexpected exception: %s\n", __func__, e.what()); LogPrintf("%s() had an unexpected exception: %s\n", __func__, e.what());
return name; return name;
} }
@ -157,144 +114,121 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
return normalized; return normalized;
} }
bool CClaimTrieCacheNormalizationFork::insertClaimIntoTrie(const std::string& name, CClaimValue claim, bool CClaimTrieCacheNormalizationFork::insertClaimIntoTrie(const std::string& name, const CClaimValue& claim, bool fCheckTakeover)
bool fCheckTakeover) const { {
return CClaimTrieCacheExpirationFork::insertClaimIntoTrie(normalizeClaimName(name, overrideInsertNormalization), claim, fCheckTakeover); return CClaimTrieCacheExpirationFork::insertClaimIntoTrie(normalizeClaimName(name, overrideInsertNormalization), claim, fCheckTakeover);
} }
bool CClaimTrieCacheNormalizationFork::removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, bool CClaimTrieCacheNormalizationFork::removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, CClaimValue& claim, bool fCheckTakeover)
CClaimValue& claim, bool fCheckTakeover) const { {
return CClaimTrieCacheExpirationFork::removeClaimFromTrie(normalizeClaimName(name, overrideRemoveNormalization), outPoint, claim, fCheckTakeover); return CClaimTrieCacheExpirationFork::removeClaimFromTrie(normalizeClaimName(name, overrideRemoveNormalization), outPoint, claim, fCheckTakeover);
} }
bool CClaimTrieCacheNormalizationFork::insertSupportIntoMap(const std::string& name, CSupportValue support, bool CClaimTrieCacheNormalizationFork::insertSupportIntoMap(const std::string& name, const CSupportValue& support, bool fCheckTakeover)
bool fCheckTakeover) const { {
return CClaimTrieCacheExpirationFork::insertSupportIntoMap(normalizeClaimName(name, overrideInsertNormalization), support, fCheckTakeover); return CClaimTrieCacheExpirationFork::insertSupportIntoMap(normalizeClaimName(name, overrideInsertNormalization), support, fCheckTakeover);
} }
bool CClaimTrieCacheNormalizationFork::removeSupportFromMap(const std::string& name, const COutPoint& outPoint,
CSupportValue& support, bool fCheckTakeover) const { bool CClaimTrieCacheNormalizationFork::removeSupportFromMap(const std::string& name, const COutPoint& outPoint, CSupportValue& support, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::removeSupportFromMap(normalizeClaimName(name, overrideRemoveNormalization), outPoint, support, fCheckTakeover); return CClaimTrieCacheExpirationFork::removeSupportFromMap(normalizeClaimName(name, overrideRemoveNormalization), outPoint, support, fCheckTakeover);
} }
struct claimsForNormalization: public claimsForNameType { bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insertUndoType& insertUndo, claimQueueRowType& removeUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
std::string normalized; {
claimsForNormalization(const std::vector<CClaimValue>& claims, const std::vector<CSupportValue>& supports, if (nNextHeight != Params().GetConsensus().nNormalizedNameForkHeight)
int nLastTakeoverHeight, const std::string& name, const std::string& normalized) return false;
: claimsForNameType(claims, supports, nLastTakeoverHeight, name), normalized(normalized) {}
};
bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insertUndoType& insertUndo, claimQueueRowType& removeUndo, // run the one-time upgrade of all names that need to change
insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, // it modifies the (cache) trie as it goes, so we need to grab everything to be modified first
std::vector<std::pair<std::string, int> >& takeoverHeightUndo) const {
struct CNameChangeDetector: public CNodeCallback { for (auto it = base->begin(); it != base->end(); ++it) {
std::vector<claimsForNormalization> hits; const std::string normalized = normalizeClaimName(it.key(), true);
const CClaimTrieCacheNormalizationFork* owner; if (normalized == it.key())
CNameChangeDetector(const CClaimTrieCacheNormalizationFork* owner): owner(owner) {} continue;
void visit(const std::string& name, const CClaimTrieNode* node) {
if (node->claims.empty()) return;
const std::string normalized = owner->normalizeClaimName(name, true);
if (normalized == name) return;
supportMapEntryType supports; auto supports = getSupportsForName(it.key());
owner->getSupportsForName(name, supports); for (auto& support : supports) {
const claimsForNormalization cfn(node->claims, supports, node->nHeightOfLastTakeover, name, normalized); // if it's already going to expire just skip it
hits.push_back(cfn); if (support.nHeight + base->nExpirationTime <= nNextHeight)
continue;
CSupportValue removed;
assert(removeSupportFromMap(it.key(), support.outPoint, removed, false));
expireSupportUndo.emplace_back(it.key(), removed);
assert(insertSupportIntoMap(normalized, support, false));
insertSupportUndo.emplace_back(it.key(), support.outPoint, -1);
} }
};
if (nCurrentHeight == Params().GetConsensus().nNormalizedNameForkHeight) { namesToCheckForTakeover.insert(normalized);
// run the one-time upgrade of all names that need to change auto cached = cacheData(it.key(), false);
// it modifies the (cache) trie as it goes, so we need to grab if (!cached || cached->claims.empty())
// everything to be modified first continue;
CNameChangeDetector detector(this);
iterateTrie(detector);
for (std::vector<claimsForNormalization>::iterator it = detector.hits.begin(); it != detector.hits.end(); ++it) { for (auto& claim : it->claims) {
BOOST_FOREACH(CSupportValue support, it->supports) { if (claim.nHeight + base->nExpirationTime <= nNextHeight)
// if it's already going to expire just skip it continue;
if (support.nHeight + base->nExpirationTime <= nCurrentHeight)
continue;
bool success = removeSupportFromMap(it->name, support.outPoint, support, false); CClaimValue removed;
assert(success); assert(removeClaimFromTrie(it.key(), claim.outPoint, removed, false));
expireSupportUndo.push_back(std::make_pair(it->name, support)); removeUndo.emplace_back(it.key(), removed);
success = insertSupportIntoMap(it->normalized, support, false); assert(insertClaimIntoTrie(normalized, claim, false));
assert(success); insertUndo.emplace_back(it.key(), claim.outPoint, -1);
insertSupportUndo.push_back(nameOutPointHeightType(it->name, support.outPoint, -1));
}
BOOST_FOREACH(CClaimValue claim, it->claims) {
if (claim.nHeight + base->nExpirationTime <= nCurrentHeight)
continue;
bool success = removeClaimFromTrie(it->name, claim.outPoint, claim, false);
assert(success);
removeUndo.push_back(std::make_pair(it->name, claim));
success = insertClaimIntoTrie(it->normalized, claim, true);
assert(success);
insertUndo.push_back(nameOutPointHeightType(it->name, claim.outPoint, -1));
}
takeoverHeightUndo.push_back(std::make_pair(it->name, it->nLastTakeoverHeight));
} }
return true;
takeoverHeightUndo.emplace_back(it.key(), it->nHeightOfLastTakeover);
} }
return false; return true;
} }
bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
claimQueueRowType& expireUndo, {
insertUndoType& insertSupportUndo, overrideInsertNormalization = normalizeAllNamesInTrieIfNecessary(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo);
supportQueueRowType& expireSupportUndo, BOOST_SCOPE_EXIT(&overrideInsertNormalization) { overrideInsertNormalization = false; }
std::vector<std::pair<std::string, int> >& takeoverHeightUndo) { BOOST_SCOPE_EXIT_END
overrideInsertNormalization = normalizeAllNamesInTrieIfNecessary(insertUndo, expireUndo, insertSupportUndo, return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo);
expireSupportUndo, takeoverHeightUndo);
BOOST_SCOPE_EXIT(&overrideInsertNormalization) { overrideInsertNormalization = false; } BOOST_SCOPE_EXIT_END
return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo,
expireSupportUndo, takeoverHeightUndo);
} }
bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
claimQueueRowType& expireUndo, {
insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo,
std::vector<std::pair<std::string, int> >& takeoverHeightUndo) {
overrideRemoveNormalization = shouldNormalize(); overrideRemoveNormalization = shouldNormalize();
BOOST_SCOPE_EXIT(&overrideRemoveNormalization) { overrideRemoveNormalization = false; } BOOST_SCOPE_EXIT_END BOOST_SCOPE_EXIT(&overrideRemoveNormalization) { overrideRemoveNormalization = false; }
return CClaimTrieCacheExpirationFork::decrementBlock(insertUndo, expireUndo, insertSupportUndo, BOOST_SCOPE_EXIT_END
expireSupportUndo, takeoverHeightUndo); return CClaimTrieCacheExpirationFork::decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo);
} }
bool CClaimTrieCacheNormalizationFork::getProofForName(const std::string& name, CClaimTrieProof& proof) const { bool CClaimTrieCacheNormalizationFork::getProofForName(const std::string& name, CClaimTrieProof& proof)
{
return CClaimTrieCacheExpirationFork::getProofForName(normalizeClaimName(name), proof); return CClaimTrieCacheExpirationFork::getProofForName(normalizeClaimName(name), proof);
} }
bool CClaimTrieCacheNormalizationFork::getInfoForName(const std::string& name, CClaimValue& claim) const { bool CClaimTrieCacheNormalizationFork::getInfoForName(const std::string& name, CClaimValue& claim) const
{
return CClaimTrieCacheExpirationFork::getInfoForName(normalizeClaimName(name), claim); return CClaimTrieCacheExpirationFork::getInfoForName(normalizeClaimName(name), claim);
} }
claimsForNameType CClaimTrieCacheNormalizationFork::getClaimsForName(const std::string& name) const { CClaimsForNameType CClaimTrieCacheNormalizationFork::getClaimsForName(const std::string& name) const
{
return CClaimTrieCacheExpirationFork::getClaimsForName(normalizeClaimName(name)); return CClaimTrieCacheExpirationFork::getClaimsForName(normalizeClaimName(name));
} }
int CClaimTrieCacheNormalizationFork::getDelayForName(const std::string& name, const uint160& claimId) const { int CClaimTrieCacheNormalizationFork::getDelayForName(const std::string& name, const uint160& claimId)
{
return CClaimTrieCacheExpirationFork::getDelayForName(normalizeClaimName(name), claimId); return CClaimTrieCacheExpirationFork::getDelayForName(normalizeClaimName(name), claimId);
} }
void CClaimTrieCacheNormalizationFork::addClaimToQueues(const std::string& name, CClaimValue& claim) const { bool CClaimTrieCacheNormalizationFork::addClaimToQueues(const std::string& name, const CClaimValue& claim)
return CClaimTrieCacheExpirationFork::addClaimToQueues(normalizeClaimName(name, {
claim.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), claim); return CClaimTrieCacheExpirationFork::addClaimToQueues(normalizeClaimName(name, claim.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), claim);
} }
bool CClaimTrieCacheNormalizationFork::addSupportToQueues(const std::string& name, CSupportValue& support) const { bool CClaimTrieCacheNormalizationFork::addSupportToQueues(const std::string& name, const CSupportValue& support)
return CClaimTrieCacheExpirationFork::addSupportToQueues(normalizeClaimName(name, {
support.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), support); return CClaimTrieCacheExpirationFork::addSupportToQueues(normalizeClaimName(name, support.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), support);
} }
std::string CClaimTrieCacheNormalizationFork::adjustNameForValidHeight(const std::string& name, int validHeight) const { std::string CClaimTrieCacheNormalizationFork::adjustNameForValidHeight(const std::string& name, int validHeight) const
{
return normalizeClaimName(name, validHeight > Params().GetConsensus().nNormalizedNameForkHeight); return normalizeClaimName(name, validHeight > Params().GetConsensus().nNormalizedNameForkHeight);
} }

View file

@ -77,6 +77,10 @@ struct Params {
int nAllowMinDiffMinHeight; int nAllowMinDiffMinHeight;
int nAllowMinDiffMaxHeight; int nAllowMinDiffMaxHeight;
int nNormalizedNameForkHeight; int nNormalizedNameForkHeight;
int nMinTakeoverWorkaroundHeight;
int nMaxTakeoverWorkaroundHeight;
int64_t nPowTargetSpacing; int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan; int64_t nPowTargetTimespan;
/** how long it took claims to expire before the hard fork */ /** how long it took claims to expire before the hard fork */

View file

@ -100,8 +100,9 @@ static void SetMaxOpenFiles(leveldb::Options *options) {
static leveldb::Options GetOptions(size_t nCacheSize) static leveldb::Options GetOptions(size_t nCacheSize)
{ {
leveldb::Options options; leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); auto write_cache = std::min(nCacheSize / 4, size_t(16) << 20U); // cap write_cache at 16MB (4x default)
options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously options.block_cache = leveldb::NewLRUCache(nCacheSize - write_cache * 2);
options.write_buffer_size = write_cache; // up to two write buffers may be held in memory simultaneously
options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.filter_policy = leveldb::NewBloomFilterPolicy(10);
options.compression = leveldb::kNoCompression; options.compression = leveldb::kNoCompression;
options.info_log = new CBitcoinLevelDBLogger(); options.info_log = new CBitcoinLevelDBLogger();

View file

@ -1503,12 +1503,6 @@ bool AppInitMain()
break; break;
} }
if (!pclaimTrie->ReadFromDisk(true))
{
strLoadError = _("Error loading the claim trie from disk");
break;
}
// At this point we're either in reindex or we've loaded a useful // At this point we're either in reindex or we've loaded a useful
// block tree into mapBlockIndex! // block tree into mapBlockIndex!
@ -1541,6 +1535,13 @@ bool AppInitMain()
assert(chainActive.Tip() != nullptr); assert(chainActive.Tip() != nullptr);
} }
CClaimTrieCache trieCache(pclaimTrie);
if (!trieCache.ReadFromDisk(chainActive.Tip()))
{
strLoadError = _("Error loading the claim trie from disk");
break;
}
if (!fReset) { if (!fReset) {
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate. // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
// It both disconnects blocks based on chainActive, and drops block data in // It both disconnects blocks based on chainActive, and drops block data in
@ -1702,7 +1703,7 @@ bool AppInitMain()
LogPrintf("nBestHeight = %d\n", chain_active_height); LogPrintf("nBestHeight = %d\n", chain_active_height);
const Consensus::Params& consensusParams = Params().GetConsensus(); const Consensus::Params& consensusParams = Params().GetConsensus();
pclaimTrie->setExpirationTime(consensusParams.GetExpirationTime(chain_active_height)); CClaimTrieCache(pclaimTrie).setExpirationTime(consensusParams.GetExpirationTime(chain_active_height));
if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(); StartTorControl();

492
src/prefixtrie.cpp Normal file
View file

@ -0,0 +1,492 @@
#include "prefixtrie.h"
#include "claimtrie.h"
template <typename TKey, typename TData>
template <bool IsConst>
CPrefixTrie<TKey, TData>::Iterator<IsConst>::Iterator(const TKey& name, const std::shared_ptr<Node>& node) noexcept : name(name), node(node)
{
}
template <typename TKey, typename TData>
template <bool IsConst>
template <bool C>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>& CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator=(const CPrefixTrie<TKey, TData>::Iterator<C>& o) noexcept
{
name = o.name;
node = o.node;
stack.clear();
stack.reserve(o.stack.size());
for (auto& i : o.stack)
stack.push_back(Bookmark{i.name, i.parent, i.it, i.end});
return *this;
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::hasNext() const
{
auto shared = node.lock();
if (!shared) return false;
if (!shared->children.empty()) return true;
for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
auto mark = *it; // copy
if (++mark.it != mark.end)
return true;
}
return false;
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>& CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator++()
{
auto shared = node.lock();
assert(shared);
// going in pre-order (NLR). See https://en.wikipedia.org/wiki/Tree_traversal
// if there are any children we have to go there first
if (!shared->children.empty()) {
auto& children = shared->children;
auto it = children.begin();
stack.emplace_back(Bookmark{name, shared, it, children.end()});
auto& postfix = it->first;
name.insert(name.end(), postfix.begin(), postfix.end());
node = it->second;
return *this;
}
// move to next sibling:
while (!stack.empty()) {
auto& back = stack.back();
if (++back.it != back.end) {
name = back.name;
auto& postfix = back.it->first;
name.insert(name.end(), postfix.begin(), postfix.end());
node = back.it->second;
return *this;
}
stack.pop_back();
}
// must be at the end:
node.reset();
name = TKey();
return *this;
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst> CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator++(int x)
{
auto ret = *this;
++(*this);
return ret;
}
template <typename TKey, typename TData>
template <bool IsConst>
CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator bool() const
{
return !node.expired();
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator==(const Iterator& o) const
{
return node.lock() == o.node.lock();
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator!=(const Iterator& o) const
{
return !(*this == o);
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*() const
{
assert(!node.expired());
return TPair(name, *(node.lock()->data));
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->() const
{
assert(!node.expired());
return node.lock()->data.get();
}
template <typename TKey, typename TData>
template <bool IsConst>
const TKey& CPrefixTrie<TKey, TData>::Iterator<IsConst>::key() const
{
return name;
}
template <typename TKey, typename TData>
template <bool IsConst>
TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data()
{
assert(!node.expired());
return *(node.lock()->data);
}
template <typename TKey, typename TData>
template <bool IsConst>
const TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data() const
{
assert(!node.expired());
return *(node.lock()->data);
}
template <typename TKey, typename TData>
template <bool IsConst>
std::size_t CPrefixTrie<TKey, TData>::Iterator<IsConst>::depth() const
{
return stack.size();
}
template <typename TKey, typename TData>
template <bool IsConst>
bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::hasChildren() const
{
auto shared = node.lock();
return shared && !shared->children.empty();
}
template <typename TKey, typename TData>
template <bool IsConst>
std::vector<typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>> CPrefixTrie<TKey, TData>::Iterator<IsConst>::children() const
{
auto shared = node.lock();
if (!shared) return {};
std::vector<Iterator<IsConst>> ret;
ret.reserve(shared->children.size());
for (auto& child : shared->children) {
auto key = name;
key.insert(key.end(), child.first.begin(), child.first.end());
ret.emplace_back(key, child.second);
}
return ret;
}
template <typename TKey>
static std::size_t match(const TKey& a, const TKey& b)
{
std::size_t count = 0;
auto ait = a.cbegin(), aend = a.cend();
auto bit = b.cbegin(), bend = b.cend();
while (ait != aend && bit != bend) {
if (*ait != *bit) break;
++count;
++ait;
++bit;
}
return count;
}
template <typename TKey, typename TData>
template <typename TIterator, typename TNode>
TIterator CPrefixTrie<TKey, TData>::find(const TKey& key, TNode node, TIterator end)
{
TIterator it(key, TNode());
using CBType = callback<TNode>;
CBType cb = [&it](const TKey&, TNode node) {
it.node = node;
};
return find(key, node, cb) ? it : end;
}
template <typename TKey, typename TData>
template <typename TNode>
bool CPrefixTrie<TKey, TData>::find(const TKey& key, TNode node, const callback<TNode>& cb)
{
auto& children = node->children;
if (children.empty()) return false;
auto it = children.lower_bound(key);
if (it != children.end() && it->first == key) {
cb(key, it->second);
return true;
}
if (it != children.begin()) --it;
const auto count = match(key, it->first);
if (count != it->first.size()) return false;
if (count == key.size()) return false;
cb(it->first, it->second);
return find(TKey(key.begin() + count, key.end()), it->second, cb);
}
template <typename TKey, typename TData>
std::shared_ptr<typename CPrefixTrie<TKey, TData>::Node>& CPrefixTrie<TKey, TData>::insert(const TKey& key, std::shared_ptr<typename CPrefixTrie<TKey, TData>::Node>& node)
{
std::size_t count = 0;
auto& children = node->children;
auto it = children.lower_bound(key);
if (it != children.end()) {
if (it->first == key)
return it->second;
count = match(key, it->first);
}
if (count == 0 && it != children.begin()) {
--it;
count = match(key, it->first);
}
if (count == 0) {
++size;
it = children.emplace(key, std::make_shared<Node>()).first;
it->second->data = std::make_shared<TData>();
return it->second;
}
if (count < it->first.size()) {
const TKey prefix(key.begin(), key.begin() + count);
const TKey postfix(it->first.begin() + count, it->first.end());
auto nodes = std::move(it->second);
children.erase(it);
++size;
it = children.emplace(prefix, std::make_shared<Node>()).first;
it->second->children.emplace(postfix, std::move(nodes));
it->second->data = std::make_shared<TData>();
if (key.size() == count)
return it->second;
}
return insert(TKey(key.begin() + count, key.end()), it->second);
}
template <typename TKey, typename TData>
void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& node)
{
std::vector<typename TChildren::value_type> nodes;
nodes.emplace_back(TKey(), node);
using CBType = callback<std::shared_ptr<Node>>;
CBType cb = [&nodes](const TKey& k, std::shared_ptr<Node> n) {
nodes.emplace_back(k, n);
};
if (!find(key, node, cb))
return;
nodes.back().second->data = std::make_shared<TData>();
for (; nodes.size() > 1; nodes.pop_back()) {
// if we have only one child and no data ourselves, bring them up to our level
auto& cNode = nodes.back().second;
auto onlyOneChild = cNode->children.size() == 1;
auto noData = cNode->data->empty();
if (onlyOneChild && noData) {
auto child = cNode->children.begin();
auto& prefix = nodes.back().first;
auto newKey = prefix;
auto& postfix = child->first;
newKey.insert(newKey.end(), postfix.begin(), postfix.end());
auto& pNode = nodes[nodes.size() - 2].second;
pNode->children.emplace(newKey, std::move(child->second));
pNode->children.erase(prefix);
--size;
continue;
}
auto noChildren = cNode->children.empty();
if (noChildren && noData) {
auto& pNode = nodes[nodes.size() - 2].second;
pNode->children.erase(nodes.back().first);
--size;
continue;
}
break;
}
}
template <typename TKey, typename TData>
CPrefixTrie<TKey, TData>::CPrefixTrie() : size(0), root(std::make_shared<Node>())
{
root->data = std::make_shared<TData>();
}
template <typename TKey, typename TData>
template <typename TDataUni>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(const TKey& key, TDataUni&& data)
{
auto& node = key.empty() ? root : insert(key, root);
node->data = std::make_shared<TData>(std::forward<TDataUni>(data));
return key.empty() ? begin() : iterator{key, node};
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::copy(CPrefixTrie<TKey, TData>::const_iterator it)
{
auto& key = it.key();
auto& node = key.empty() ? root : insert(key, root);
node->data = it.node.lock()->data;
return key.empty() ? begin() : iterator{key, node};
}
template <typename TKey, typename TData>
template <typename TDataUni>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(CPrefixTrie<TKey, TData>::iterator& it, const TKey& key, TDataUni&& data)
{
auto shared = it.node.lock();
assert(shared);
auto copy = it;
if (!key.empty()) {
auto name = it.key();
name.insert(name.end(), key.begin(), key.end());
auto& node = insert(key, shared);
copy = iterator{name, node};
}
copy.node.lock()->data = std::make_shared<TData>(std::forward<TDataUni>(data));
return copy;
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::find(const TKey& key)
{
if (empty()) return end();
if (key.empty()) return {key, root};
return find(key, root, end());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::find(const TKey& key) const
{
if (empty()) return end();
if (key.empty()) return {key, root};
return find(key, root, end());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::find(CPrefixTrie<TKey, TData>::iterator& it, const TKey& key)
{
if (key.empty()) return it;
auto shared = it.node.lock();
assert(shared);
return find(key, shared, end());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::find(CPrefixTrie<TKey, TData>::const_iterator& it, const TKey& key) const
{
if (key.empty()) return it;
auto shared = it.node.lock();
assert(shared);
return find(key, shared, end());
}
template <typename TKey, typename TData>
bool CPrefixTrie<TKey, TData>::contains(const TKey& key) const
{
return find(key) != end();
}
template <typename TKey, typename TData>
TData& CPrefixTrie<TKey, TData>::at(const TKey& key)
{
return find(key).data();
}
template <typename TKey, typename TData>
std::vector<typename CPrefixTrie<TKey, TData>::iterator> CPrefixTrie<TKey, TData>::nodes(const TKey& key) const
{
std::vector<iterator> ret;
if (empty()) return ret;
ret.reserve(1 + key.size());
ret.emplace_back(TKey{}, root);
if (key.empty()) return ret;
TKey name;
using CBType = callback<std::shared_ptr<Node>>;
CBType cb = [&name, &ret](const TKey& key, std::shared_ptr<Node> node) {
name.insert(name.end(), key.begin(), key.end());
ret.emplace_back(name, node);
};
find(key, root, cb);
return ret;
}
template <typename TKey, typename TData>
bool CPrefixTrie<TKey, TData>::erase(const TKey& key)
{
auto size_was = height();
if (key.empty()) {
root->data = std::make_shared<TData>();
} else {
erase(key, root);
}
return size_was != height();
}
template <typename TKey, typename TData>
void CPrefixTrie<TKey, TData>::clear()
{
size = 0;
root->data = std::make_shared<TData>();
root->children.clear();
}
template <typename TKey, typename TData>
bool CPrefixTrie<TKey, TData>::empty() const
{
return height() == 0;
}
template <typename TKey, typename TData>
std::size_t CPrefixTrie<TKey, TData>::height() const
{
return size + (root->data->empty() ? 0 : 1);
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::begin()
{
return find(TKey());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::end()
{
return iterator{TKey(), std::shared_ptr<Node>{}};
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::cbegin()
{
return find(TKey());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::cend()
{
return const_iterator{TKey(), std::shared_ptr<Node>{}};
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::begin() const
{
return find(TKey());
}
template <typename TKey, typename TData>
typename CPrefixTrie<TKey, TData>::const_iterator CPrefixTrie<TKey, TData>::end() const
{
return const_iterator{TKey(), std::shared_ptr<Node>{}};
}
using Key = std::string;
using Data = CClaimTrieData;
using Trie = CPrefixTrie<Key, Data>;
using iterator = Trie::iterator;
using const_iterator = Trie::const_iterator;
template class CPrefixTrie<Key, Data>;
template class Trie::Iterator<true>;
template class Trie::Iterator<false>;
template const_iterator& const_iterator::operator=<>(const iterator&) noexcept;
template iterator Trie::insert<>(const Key&, Data&);
template iterator Trie::insert<>(const Key&, Data&&);
template iterator Trie::insert<>(const Key&, const Data&);
template iterator Trie::insert<>(iterator&, const Key&, Data&);
template iterator Trie::insert<>(iterator&, const Key&, Data&&);
template iterator Trie::insert<>(iterator&, const Key&, const Data&);

196
src/prefixtrie.h Normal file
View file

@ -0,0 +1,196 @@
#ifndef BITCOIN_PREFIXTRIE_H
#define BITCOIN_PREFIXTRIE_H
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
template <typename TKey, typename TData>
class CPrefixTrie
{
class Node
{
template <bool>
friend class Iterator;
friend class CPrefixTrie<TKey, TData>;
std::map<TKey, std::shared_ptr<Node>> children;
public:
Node() = default;
Node(const Node&) = delete;
Node(Node&& o) noexcept = default;
Node& operator=(Node&& o) noexcept = default;
Node& operator=(const Node&) = delete;
std::shared_ptr<TData> data;
};
using TChildren = decltype(Node::children);
template <bool IsConst>
class Iterator
{
template <bool>
friend class Iterator;
friend class CPrefixTrie<TKey, TData>;
using TKeyRef = std::reference_wrapper<const TKey>;
using TDataRef = std::reference_wrapper<TData>;
using TPair = std::pair<TKeyRef, TDataRef>;
TKey name;
std::weak_ptr<Node> node;
struct Bookmark {
TKey name;
std::weak_ptr<Node> parent;
typename TChildren::iterator it;
typename TChildren::iterator end;
};
std::vector<Bookmark> stack;
public:
// Iterator traits
using value_type = TPair;
using pointer = typename std::conditional<IsConst, const TData* const, TData* const>::type;
using reference = typename std::conditional<IsConst, const TPair, TPair>::type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
Iterator() = delete;
Iterator(const Iterator&) = default;
Iterator(Iterator&& o) noexcept = default;
Iterator(const TKey& name, const std::shared_ptr<Node>& node) noexcept;
template <bool C>
inline Iterator(const Iterator<C>& o) noexcept
{
*this = o;
}
Iterator& operator=(const Iterator&) = default;
Iterator& operator=(Iterator&& o) = default;
template <bool C>
Iterator& operator=(const Iterator<C>& o) noexcept;
bool hasNext() const;
Iterator& operator++();
Iterator operator++(int);
operator bool() const;
bool operator==(const Iterator& o) const;
bool operator!=(const Iterator& o) const;
reference operator*() const;
pointer operator->() const;
const TKey& key() const;
TData& data();
const TData& data() const;
std::size_t depth() const;
bool hasChildren() const;
std::vector<Iterator> children() const;
};
size_t size;
std::shared_ptr<Node> root;
template <typename TNode>
using callback = std::function<void(const TKey&, TNode)>;
template <typename TIterator, typename TNode>
static TIterator find(const TKey& key, TNode node, TIterator end);
template <typename TNode>
static bool find(const TKey& key, TNode node, const callback<TNode>& cb);
std::shared_ptr<Node>& insert(const TKey& key, std::shared_ptr<Node>& node);
void erase(const TKey& key, std::shared_ptr<Node>& node);
public:
using iterator = Iterator<false>;
using const_iterator = Iterator<true>;
CPrefixTrie();
template <typename TDataUni>
iterator insert(const TKey& key, TDataUni&& data);
template <typename TDataUni>
iterator insert(iterator& it, const TKey& key, TDataUni&& data);
iterator copy(const_iterator it);
iterator find(const TKey& key);
const_iterator find(const TKey& key) const;
iterator find(iterator& it, const TKey& key);
const_iterator find(const_iterator& it, const TKey& key) const;
bool contains(const TKey& key) const;
TData& at(const TKey& key);
std::vector<iterator> nodes(const TKey& key) const;
bool erase(const TKey& key);
void clear();
bool empty() const;
size_t height() const;
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend();
const_iterator begin() const;
const_iterator end() const;
};
template <typename T, typename O>
inline bool operator==(const std::reference_wrapper<T>& ref, const O& obj)
{
return ref.get() == obj;
}
template <typename T, typename O>
inline bool operator!=(const std::reference_wrapper<T>& ref, const O& obj)
{
return !(ref == obj);
}
template <typename T>
inline bool operator==(const std::reference_wrapper<std::shared_ptr<T>>& ref, const T& obj)
{
auto ptr = ref.get();
return ptr && *ptr == obj;
}
template <typename T>
inline bool operator!=(const std::reference_wrapper<std::shared_ptr<T>>& ref, const T& obj)
{
return !(ref == obj);
}
template <typename T>
inline bool operator==(const std::reference_wrapper<std::unique_ptr<T>>& ref, const T& obj)
{
auto ptr = ref.get();
return ptr && *ptr == obj;
}
template <typename T>
inline bool operator!=(const std::reference_wrapper<std::unique_ptr<T>>& ref, const T& obj)
{
return !(ref == obj);
}
#endif // BITCOIN_PREFIXTRIE_H

View file

@ -1,6 +1,7 @@
#include <claimtrie.h> #include <claimtrie.h>
#include <coins.h> #include <coins.h>
#include <core_io.h> #include <core_io.h>
#include <logging.h>
#include <nameclaim.h> #include <nameclaim.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <shutdown.h> #include <shutdown.h>
@ -9,6 +10,8 @@
#include <univalue.h> #include <univalue.h>
#include <validation.h> #include <validation.h>
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <cmath> #include <cmath>
@ -75,8 +78,73 @@ void RollBackTo(const CBlockIndex* targetIndex, CCoinsViewCache& coinsCache, CCl
} }
} }
std::string escapeNonUtf8(const std::string& name)
{
using namespace boost::locale::conv;
try {
return to_utf<char>(name, "UTF-8", stop);
} catch (const conversion_error&) {
std::string result;
result.reserve(name.size() * 2);
for (uint8_t ch : name) {
if (ch < 0x08 || (ch >= 0x0e && ch <= 0x1f) || ch >= 0x7f)
result += tfm::format("\\u%04x", ch);
else if (ch == 0x08) result += "\\b";
else if (ch == 0x09) result += "\\t";
else if (ch == 0x0a) result += "\\n";
else if (ch == 0x0c) result += "\\f";
else if (ch == 0x0d) result += "\\r";
else if (ch == 0x22) result += "\\\"";
else if (ch == 0x5c) result += "\\\\";
else result += ch;
}
return result;
}
}
static bool getValueForOutPoint(const CCoinsViewCache& coinsCache, const COutPoint& out, std::string& sValue)
{
const Coin& coin = coinsCache.AccessCoin(out);
if (coin.IsSpent())
{
return false;
}
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (!DecodeClaimScript(coin.out.scriptPubKey, op, vvchParams))
{
return false;
}
if (op == OP_CLAIM_NAME)
{
sValue = HexStr(vvchParams[1].begin(), vvchParams[1].end());
return true;
}
if (vvchParams.size() > 2) // both UPDATE and SUPPORT
{
sValue = HexStr(vvchParams[2].begin(), vvchParams[2].end());
return true;
}
return false;
}
bool validParams(const UniValue& params, uint8_t required, uint8_t optional)
{
auto count = params.size();
return count == required || count == required + optional;
}
static UniValue getclaimsintrie(const JSONRPCRequest& request) static UniValue getclaimsintrie(const JSONRPCRequest& request)
{ {
if (!IsDeprecatedRPCEnabled("getclaimsintrie")) {
const auto msg = "getclaimsintrie is deprecated and will be removed in v0.18. To use this command, start with -deprecatedrpc=getclaimsintrie";
if (request.fHelp) {
throw std::runtime_error(msg);
}
throw JSONRPCError(RPC_METHOD_DEPRECATED, msg);
}
if (request.fHelp || request.params.size() > 1) if (request.fHelp || request.params.size() > 1)
throw std::runtime_error( throw std::runtime_error(
"getclaimsintrie\n" "getclaimsintrie\n"
@ -116,96 +184,79 @@ static UniValue getclaimsintrie(const JSONRPCRequest& request)
RollBackTo(blockIndex, coinsCache, trieCache); RollBackTo(blockIndex, coinsCache, trieCache);
} }
class CClaimsCallback : public CNodeCallback
{
public:
CClaimsCallback(UniValue& ret, const CCoinsViewCache& coinsCache) : nodes(ret), coinsCache(coinsCache)
{
}
void visit(const std::string& name, const CClaimTrieNode* node)
{
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
boost::this_thread::interruption_point();
if (node->claims.empty())
return;
UniValue claims(UniValue::VARR);
for (std::vector<CClaimValue>::const_iterator itClaims = node->claims.begin(); itClaims != node->claims.end(); ++itClaims) {
UniValue claim(UniValue::VOBJ);
claim.pushKV("claimId", itClaims->claimId.GetHex());
claim.pushKV("txid", itClaims->outPoint.hash.GetHex());
claim.pushKV("n", (int)itClaims->outPoint.n);
claim.pushKV("amount", ValueFromAmount(itClaims->nAmount));
claim.pushKV("height", itClaims->nHeight);
const Coin& coin = coinsCache.AccessCoin(itClaims->outPoint);
if (coin.IsSpent())
{
LogPrintf("%s: the specified txout of %s appears to have been spent\n", __func__, itClaims->outPoint.hash.GetHex());
claim.pushKV("error", "Txout spent");
}
else
{
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (!DecodeClaimScript(coin.out.scriptPubKey, op, vvchParams))
{
LogPrintf("%s: the specified txout of %s does not have an claim command\n", __func__, itClaims->outPoint.hash.GetHex());
}
claim.pushKV("value", HexStr(vvchParams[1].begin(), vvchParams[1].end()));
}
std::string targetName;
CClaimValue targetClaim;
if (pclaimTrie->getClaimById(itClaims->claimId, targetName, targetClaim))
claim.push_back(Pair("name", targetName));
claims.push_back(claim);
}
UniValue nodeObj(UniValue::VOBJ);
nodeObj.pushKV("normalized_name", name);
nodeObj.pushKV("claims", claims);
nodes.push_back(nodeObj);
}
private:
UniValue& nodes;
const CCoinsViewCache& coinsCache;
};
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
CClaimsCallback claimsCallback(ret, coinsCache); for (auto it = trieCache.begin(); it != trieCache.end(); ++it)
trieCache.iterateTrie(claimsCallback); {
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
boost::this_thread::interruption_point();
if (it->empty())
continue;
UniValue claims(UniValue::VARR);
for (auto itClaims = it->claims.cbegin(); itClaims != it->claims.cend(); ++itClaims) {
UniValue claim(UniValue::VOBJ);
claim.pushKV("claimId", itClaims->claimId.GetHex());
claim.pushKV("txid", itClaims->outPoint.hash.GetHex());
claim.pushKV("n", (int)itClaims->outPoint.n);
claim.pushKV("amount", ValueFromAmount(itClaims->nAmount));
claim.pushKV("height", itClaims->nHeight);
const Coin& coin = coinsCache.AccessCoin(itClaims->outPoint);
if (coin.IsSpent())
{
LogPrintf("%s: the specified txout of %s appears to have been spent\n", __func__, itClaims->outPoint.hash.GetHex());
claim.pushKV("error", "Txout spent");
}
else
{
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (!DecodeClaimScript(coin.out.scriptPubKey, op, vvchParams))
{
LogPrintf("%s: the specified txout of %s does not have an claim command\n", __func__, itClaims->outPoint.hash.GetHex());
}
claim.pushKV("value", HexStr(vvchParams[1].begin(), vvchParams[1].end()));
}
std::string targetName;
CClaimValue targetClaim;
if (trieCache.getClaimById(itClaims->claimId, targetName, targetClaim))
claim.push_back(Pair("name", escapeNonUtf8(targetName)));
claims.push_back(claim);
}
UniValue nodeObj(UniValue::VOBJ);
nodeObj.pushKV("normalized_name", escapeNonUtf8(it.key()));
nodeObj.pushKV("claims", claims);
ret.push_back(nodeObj);
}
return ret; return ret;
} }
static UniValue getclaimtrie(const JSONRPCRequest& request) static UniValue getclaimtrie(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() > 1) throw JSONRPCError(RPC_METHOD_DEPRECATED, "getclaimtrie was removed in v0.17.\n"
"Clients should use getnamesintrie.");
}
static UniValue getnamesintrie(const JSONRPCRequest& request)
{
if (request.fHelp || !validParams(request.params, 0, 1))
throw std::runtime_error( throw std::runtime_error(
"getclaimtrie\n" "getnamesintrie\n"
"DEPRECATED. Return the entire claim trie.\n" "Return all claim names in the trie.\n"
"Arguments:\n" "Arguments:\n"
"1. \"blockhash\" (string, optional) get claim in the trie\n" "1. \"blockhash\" (string, optional) get claims in the trie\n"
" at the block specified\n" " at the block specified\n"
" by this block hash.\n" " by this block hash.\n"
" If none is given,\n" " If none is given,\n"
" the latest active\n" " the latest active\n"
" block will be used.\n" " block will be used.\n"
"Result: \n" "Result: \n"
"[\n" "\"names\" (array) all names in the trie that have claims\n");
" {\n"
" \"name\" (string) the name of the node\n"
" \"hash\" (string) the hash of the node\n"
" \"txid\" (string) (if value exists) the hash of the transaction which has successfully claimed this name\n"
" \"n\" (numeric) (if value exists) vout value\n"
" \"value\" (numeric) (if value exists) txout value\n"
" \"height\" (numeric) (if value exists) the height of the block in which this transaction is located\n"
" }\n"
"]\n");
LOCK(cs_main); LOCK(cs_main);
@ -217,76 +268,24 @@ static UniValue getclaimtrie(const JSONRPCRequest& request)
RollBackTo(blockIndex, coinsCache, trieCache); RollBackTo(blockIndex, coinsCache, trieCache);
} }
class CClaimCallback : public CNodeCallback
{
public:
CClaimCallback(UniValue& ret) : nodes(ret)
{
}
void visit(const std::string& name, const CClaimTrieNode* node)
{
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
boost::this_thread::interruption_point();
UniValue nodeObj(UniValue::VOBJ);
nodeObj.pushKV("name", name);
nodeObj.pushKV("hash", node->hash.GetHex());
CClaimValue claim;
if (node->getBestClaim(claim)) {
nodeObj.pushKV("txid", claim.outPoint.hash.GetHex());
nodeObj.pushKV("n", (int)claim.outPoint.n);
nodeObj.pushKV("value", ::ValueFromAmount(claim.nAmount));
nodeObj.pushKV("height", claim.nHeight);
}
nodes.push_back(nodeObj);
}
private:
UniValue& nodes;
};
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
CClaimCallback claimCallback(ret);
trieCache.iterateTrie(claimCallback); for (auto it = trieCache.begin(); it != trieCache.end(); ++it) {
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
boost::this_thread::interruption_point();
if (!it->empty())
ret.push_back(escapeNonUtf8(it.key()));
}
return ret; return ret;
} }
static bool getValueForOutPoint(const CCoinsViewCache& coinsCache, const COutPoint& out, std::string& sValue)
{
const Coin& coin = coinsCache.AccessCoin(out);
if (coin.IsSpent())
{
LogPrintf("%s: the specified txout of %s appears to have been spent\n", __func__, out.hash.GetHex());
return true;
}
int op;
std::vector<std::vector<unsigned char> > vvchParams;
if (!DecodeClaimScript(coin.out.scriptPubKey, op, vvchParams))
{
LogPrintf("%s: the specified txout of %s does not have a name claim command\n", __func__, out.hash.GetHex());
return false;
}
if (op == OP_CLAIM_NAME)
{
sValue = HexStr(vvchParams[1].begin(), vvchParams[1].end());
return true;
}
else if (vvchParams.size() > 2) // both UPDATE and SUPPORT
{
sValue = HexStr(vvchParams[2].begin(), vvchParams[2].end());
return true;
}
return false;
}
static UniValue getvalueforname(const JSONRPCRequest& request) static UniValue getvalueforname(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() > 2) if (request.fHelp || !validParams(request.params, 1, 1))
throw std::runtime_error( throw std::runtime_error(
"getvalueforname \"name\"\n" "getvalueforname \"name\"\n"
"Return the winning value associated with a name, if one exists\n" "Return the winning value associated with a name, if one exists\n"
@ -342,8 +341,8 @@ static UniValue getvalueforname(const JSONRPCRequest& request)
std::string targetName; std::string targetName;
CClaimValue targetClaim; CClaimValue targetClaim;
if (pclaimTrie->getClaimById(claim.claimId, targetName, targetClaim)) if (trieCache.getClaimById(claim.claimId, targetName, targetClaim))
ret.push_back(Pair("name", targetName)); ret.pushKV("name", escapeNonUtf8(targetName));
return ret; return ret;
} }
@ -354,18 +353,18 @@ typedef std::map<uint160, claimAndSupportsType> claimSupportMapType;
UniValue supportToJSON(const CCoinsViewCache& coinsCache, const CSupportValue& support) UniValue supportToJSON(const CCoinsViewCache& coinsCache, const CSupportValue& support)
{ {
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("txid", support.outPoint.hash.GetHex())); ret.pushKV("txid", support.outPoint.hash.GetHex());
ret.push_back(Pair("n", (int)support.outPoint.n)); ret.pushKV("n", (int)support.outPoint.n);
ret.push_back(Pair("nHeight", support.nHeight)); ret.pushKV("nHeight", support.nHeight);
ret.push_back(Pair("nValidAtHeight", support.nValidAtHeight)); ret.pushKV("nValidAtHeight", support.nValidAtHeight);
ret.push_back(Pair("nAmount", support.nAmount)); ret.pushKV("nAmount", support.nAmount);
std::string value; std::string value;
if (getValueForOutPoint(coinsCache, support.outPoint, value)) if (getValueForOutPoint(coinsCache, support.outPoint, value))
ret.push_back(Pair("value", value)); ret.pushKV("value", value);
return ret; return ret;
} }
UniValue claimAndSupportsToJSON(const CCoinsViewCache& coinsCache, CAmount nEffectiveAmount, claimSupportMapType::const_iterator itClaimsAndSupports) UniValue claimAndSupportsToJSON(const CClaimTrieCache& trieCache, const CCoinsViewCache& coinsCache, CAmount nEffectiveAmount, claimSupportMapType::const_iterator itClaimsAndSupports)
{ {
const CClaimValue& claim = itClaimsAndSupports->second.first; const CClaimValue& claim = itClaimsAndSupports->second.first;
const std::vector<CSupportValue>& supports = itClaimsAndSupports->second.second; const std::vector<CSupportValue>& supports = itClaimsAndSupports->second.second;
@ -389,15 +388,15 @@ UniValue claimAndSupportsToJSON(const CCoinsViewCache& coinsCache, CAmount nEffe
std::string targetName; std::string targetName;
CClaimValue targetClaim; CClaimValue targetClaim;
if (pclaimTrie->getClaimById(claim.claimId, targetName, targetClaim)) if (trieCache.getClaimById(claim.claimId, targetName, targetClaim))
result.push_back(Pair("name", targetName)); result.pushKV("name", escapeNonUtf8(targetName));
return result; return result;
} }
UniValue getclaimsforname(const JSONRPCRequest& request) UniValue getclaimsforname(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() > 2) if (request.fHelp || !validParams(request.params, 1, 1))
throw std::runtime_error( throw std::runtime_error(
"getclaimsforname\n" "getclaimsforname\n"
"Return all claims and supports for a name\n" "Return all claims and supports for a name\n"
@ -456,7 +455,7 @@ UniValue getclaimsforname(const JSONRPCRequest& request)
} }
std::string name = request.params[0].get_str(); std::string name = request.params[0].get_str();
claimsForNameType claimsForName = trieCache.getClaimsForName(name); auto claimsForName = trieCache.getClaimsForName(name);
UniValue claimObjs(UniValue::VARR); UniValue claimObjs(UniValue::VARR);
claimSupportMapType claimSupportMap; claimSupportMapType claimSupportMap;
@ -479,13 +478,13 @@ UniValue getclaimsforname(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
result.pushKV("nLastTakeoverHeight", claimsForName.nLastTakeoverHeight); result.pushKV("nLastTakeoverHeight", claimsForName.nLastTakeoverHeight);
result.pushKV("normalized_name", claimsForName.name); result.pushKV("normalized_name", escapeNonUtf8(claimsForName.name));
for (auto itClaims = claimsForName.claims.begin(); itClaims != claimsForName.claims.end(); ++itClaims) for (auto itClaims = claimsForName.claims.begin(); itClaims != claimsForName.claims.end(); ++itClaims)
{ {
auto itClaimsAndSupports = claimSupportMap.find(itClaims->claimId); auto itClaimsAndSupports = claimSupportMap.find(itClaims->claimId);
const auto nEffectiveAmount = trieCache.getEffectiveAmountForClaim(claimsForName, itClaimsAndSupports->first); const auto nEffectiveAmount = trieCache.getEffectiveAmountForClaim(claimsForName, itClaimsAndSupports->first);
UniValue claimObj = claimAndSupportsToJSON(coinsCache, nEffectiveAmount, itClaimsAndSupports); UniValue claimObj = claimAndSupportsToJSON(trieCache, coinsCache, nEffectiveAmount, itClaimsAndSupports);
claimObjs.push_back(claimObj); claimObjs.push_back(claimObj);
} }
@ -496,7 +495,7 @@ UniValue getclaimsforname(const JSONRPCRequest& request)
UniValue getclaimbyid(const JSONRPCRequest& request) UniValue getclaimbyid(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || !validParams(request.params, 1, 0))
throw std::runtime_error( throw std::runtime_error(
"getclaimbyid\n" "getclaimbyid\n"
"Get a claim by claim id\n" "Get a claim by claim id\n"
@ -526,21 +525,21 @@ UniValue getclaimbyid(const JSONRPCRequest& request)
"}\n"); "}\n");
LOCK(cs_main); LOCK(cs_main);
CClaimTrieCache trieCache(pclaimTrie);
uint160 claimId = ParseClaimtrieId(request.params[0], "Claim-id (parameter 1)"); uint160 claimId = ParseClaimtrieId(request.params[0], "Claim-id (parameter 1)");
UniValue claim(UniValue::VOBJ); UniValue claim(UniValue::VOBJ);
std::string name; std::string name;
CClaimValue claimValue; CClaimValue claimValue;
pclaimTrie->getClaimById(claimId, name, claimValue); trieCache.getClaimById(claimId, name, claimValue);
if (claimValue.claimId == claimId) if (claimValue.claimId == claimId)
{ {
std::vector<CSupportValue> supports; std::vector<CSupportValue> supports;
CClaimTrieCache trieCache(pclaimTrie);
CAmount effectiveAmount = trieCache.getEffectiveAmountForClaim(name, claimValue.claimId, &supports); CAmount effectiveAmount = trieCache.getEffectiveAmountForClaim(name, claimValue.claimId, &supports);
std::string sValue; std::string sValue;
claim.pushKV("name", name); claim.pushKV("name", escapeNonUtf8(name));
if (trieCache.shouldNormalize()) if (trieCache.shouldNormalize())
claim.push_back(Pair("normalized_name", trieCache.normalizeClaimName(name, true))); claim.pushKV("normalized_name", escapeNonUtf8(trieCache.normalizeClaimName(name, true)));
CCoinsViewCache coinsCache(pcoinsTip.get()); CCoinsViewCache coinsCache(pcoinsTip.get());
if (getValueForOutPoint(coinsCache, claimValue.outPoint, sValue)) if (getValueForOutPoint(coinsCache, claimValue.outPoint, sValue))
claim.pushKV("value", sValue); claim.pushKV("value", sValue);
@ -570,7 +569,7 @@ UniValue getclaimbyid(const JSONRPCRequest& request)
UniValue gettotalclaimednames(const JSONRPCRequest& request) UniValue gettotalclaimednames(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 0) if (request.fHelp || !validParams(request.params, 0, 0))
throw std::runtime_error( throw std::runtime_error(
"gettotalclaimednames\n" "gettotalclaimednames\n"
"Return the total number of names that have been\n" "Return the total number of names that have been\n"
@ -581,17 +580,14 @@ UniValue gettotalclaimednames(const JSONRPCRequest& request)
" names in the trie\n" " names in the trie\n"
); );
LOCK(cs_main); LOCK(cs_main);
if (!pclaimTrie) CClaimTrieCache trieCache(pclaimTrie);
{ auto num_names = trieCache.getTotalNamesInTrie();
return -1;
}
unsigned int num_names = pclaimTrie->getTotalNamesInTrie();
return int(num_names); return int(num_names);
} }
UniValue gettotalclaims(const JSONRPCRequest& request) UniValue gettotalclaims(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 0) if (request.fHelp || !validParams(request.params, 0, 0))
throw std::runtime_error( throw std::runtime_error(
"gettotalclaims\n" "gettotalclaims\n"
"Return the total number of active claims in the trie\n" "Return the total number of active claims in the trie\n"
@ -601,17 +597,14 @@ UniValue gettotalclaims(const JSONRPCRequest& request)
" of active claims\n" " of active claims\n"
); );
LOCK(cs_main); LOCK(cs_main);
if (!pclaimTrie) CClaimTrieCache trieCache(pclaimTrie);
{ auto num_claims = trieCache.getTotalClaimsInTrie();
return -1;
}
unsigned int num_claims = pclaimTrie->getTotalClaimsInTrie();
return int(num_claims); return int(num_claims);
} }
UniValue gettotalvalueofclaims(const JSONRPCRequest& request) UniValue gettotalvalueofclaims(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() > 1) if (request.fHelp || !validParams(request.params, 0, 1))
throw std::runtime_error( throw std::runtime_error(
"gettotalvalueofclaims\n" "gettotalvalueofclaims\n"
"Return the total value of the claims in the trie\n" "Return the total value of the claims in the trie\n"
@ -623,20 +616,17 @@ UniValue gettotalvalueofclaims(const JSONRPCRequest& request)
" claims in the trie\n" " claims in the trie\n"
); );
LOCK(cs_main); LOCK(cs_main);
if (!pclaimTrie)
{
return -1;
}
bool controlling_only = false; bool controlling_only = false;
if (request.params.size() == 1) if (request.params.size() == 1)
controlling_only = request.params[0].get_bool(); controlling_only = request.params[0].get_bool();
CAmount total_amount = pclaimTrie->getTotalValueOfClaimsInTrie(controlling_only); CClaimTrieCache trieCache(pclaimTrie);
auto total_amount = trieCache.getTotalValueOfClaimsInTrie(controlling_only);
return ValueFromAmount(total_amount); return ValueFromAmount(total_amount);
} }
UniValue getclaimsfortx(const JSONRPCRequest& request) UniValue getclaimsfortx(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || !validParams(request.params, 1, 0))
throw std::runtime_error( throw std::runtime_error(
"getclaimsfortx\n" "getclaimsfortx\n"
"Return any claims or supports found in a transaction\n" "Return any claims or supports found in a transaction\n"
@ -669,6 +659,7 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
int op; int op;
std::vector<std::vector<unsigned char> > vvchParams; std::vector<std::vector<unsigned char> > vvchParams;
CClaimTrieCache trieCache(pclaimTrie);
CCoinsViewCache view(pcoinsTip.get()); CCoinsViewCache view(pcoinsTip.get());
const Coin& coin = AccessByTxid(view, hash); const Coin& coin = AccessByTxid(view, hash);
std::vector<CTxOut> txouts{ coin.out }; std::vector<CTxOut> txouts{ coin.out };
@ -685,7 +676,7 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
{ {
o.pushKV("nOut", static_cast<int64_t>(i)); o.pushKV("nOut", static_cast<int64_t>(i));
std::string sName(vvchParams[0].begin(), vvchParams[0].end()); std::string sName(vvchParams[0].begin(), vvchParams[0].end());
o.pushKV("name", sName); o.pushKV("name", escapeNonUtf8(sName));
if (op == OP_CLAIM_NAME) if (op == OP_CLAIM_NAME)
{ {
uint160 claimId = ClaimIdHash(hash, i); uint160 claimId = ClaimIdHash(hash, i);
@ -710,12 +701,12 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
o.pushKV("depth", chainActive.Height() - nHeight); o.pushKV("depth", chainActive.Height() - nHeight);
if (op == OP_CLAIM_NAME || op == OP_UPDATE_CLAIM) if (op == OP_CLAIM_NAME || op == OP_UPDATE_CLAIM)
{ {
bool inClaimTrie = pclaimTrie->haveClaim(sName, COutPoint(hash, i)); bool inClaimTrie = trieCache.haveClaim(sName, COutPoint(hash, i));
o.pushKV("in claim trie", inClaimTrie); o.pushKV("in claim trie", inClaimTrie);
if (inClaimTrie) if (inClaimTrie)
{ {
CClaimValue claim; CClaimValue claim;
if (!pclaimTrie->getInfoForName(sName, claim)) if (!trieCache.getInfoForName(sName, claim))
{ {
LogPrintf("HaveClaim was true but getInfoForName returned false."); LogPrintf("HaveClaim was true but getInfoForName returned false.");
} }
@ -724,7 +715,7 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
else else
{ {
int nValidAtHeight; int nValidAtHeight;
if (pclaimTrie->haveClaimInQueue(sName, COutPoint(hash, i), nValidAtHeight)) if (trieCache.haveClaimInQueue(sName, COutPoint(hash, i), nValidAtHeight))
{ {
o.pushKV("in queue", true); o.pushKV("in queue", true);
o.pushKV("blocks to valid", nValidAtHeight - chainActive.Height()); o.pushKV("blocks to valid", nValidAtHeight - chainActive.Height());
@ -737,12 +728,12 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
} }
else if (op == OP_SUPPORT_CLAIM) else if (op == OP_SUPPORT_CLAIM)
{ {
bool inSupportMap = pclaimTrie->haveSupport(sName, COutPoint(hash, i)); bool inSupportMap = trieCache.haveSupport(sName, COutPoint(hash, i));
o.pushKV("in support map", inSupportMap); o.pushKV("in support map", inSupportMap);
if (!inSupportMap) if (!inSupportMap)
{ {
int nValidAtHeight; int nValidAtHeight;
if (pclaimTrie->haveSupportInQueue(sName, COutPoint(hash, i), nValidAtHeight)) if (trieCache.haveSupportInQueue(sName, COutPoint(hash, i), nValidAtHeight))
{ {
o.pushKV("in queue", true); o.pushKV("in queue", true);
o.pushKV("blocks to valid", nValidAtHeight - chainActive.Height()); o.pushKV("blocks to valid", nValidAtHeight - chainActive.Height());
@ -811,7 +802,7 @@ UniValue proofToJSON(const CClaimTrieProof& proof)
UniValue getnameproof(const JSONRPCRequest& request) UniValue getnameproof(const JSONRPCRequest& request)
{ {
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2)) if (request.fHelp || !validParams(request.params, 1, 1))
throw std::runtime_error( throw std::runtime_error(
"getnameproof\n" "getnameproof\n"
"Return the cryptographic proof that a name maps to a value\n" "Return the cryptographic proof that a name maps to a value\n"
@ -887,7 +878,7 @@ UniValue getnameproof(const JSONRPCRequest& request)
UniValue checknormalization(const JSONRPCRequest& request) UniValue checknormalization(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || !validParams(request.params, 1, 0))
throw std::runtime_error( throw std::runtime_error(
"checknormalization\n" "checknormalization\n"
"Given an unnormalized name of a claim, return normalized version of it\n" "Given an unnormalized name of a claim, return normalized version of it\n"
@ -907,7 +898,8 @@ static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------ ----------------------- ----------
{ "Claimtrie", "getclaimsintrie", &getclaimsintrie, { "blockhash" } }, { "Claimtrie", "getclaimsintrie", &getclaimsintrie, { "blockhash" } },
{ "Claimtrie", "getclaimtrie", &getclaimtrie, { "blockhash" } }, { "Claimtrie", "getnamesintrie", &getnamesintrie, { "blockhash" } },
{ "Claimtrie", "getclaimtrie", &getclaimtrie, { "" } },
{ "Claimtrie", "getvalueforname", &getvalueforname, { "name","blockhash" } }, { "Claimtrie", "getvalueforname", &getvalueforname, { "name","blockhash" } },
{ "Claimtrie", "getclaimsforname", &getclaimsforname, { "name","blockhash" } }, { "Claimtrie", "getclaimsforname", &getclaimsforname, { "name","blockhash" } },
{ "Claimtrie", "gettotalclaimednames", &gettotalclaimednames, { "" } }, { "Claimtrie", "gettotalclaimednames", &gettotalclaimednames, { "" } },

View file

@ -521,7 +521,7 @@ std::string HelpExampleCli(const std::string& methodname, const std::string& arg
std::string HelpExampleRpc(const std::string& methodname, const std::string& args) std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
{ {
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
"\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:9245/\n";
} }
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)

File diff suppressed because it is too large Load diff

View file

@ -5,50 +5,41 @@
#include <test/test_bitcoin.h> #include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <boost/scope_exit.hpp>
using namespace std; using namespace std;
class CClaimTrieCacheTest : public CClaimTrieCache { class CClaimTrieCacheTest : public CClaimTrieCacheBase
{
public: public:
CClaimTrieCacheTest(CClaimTrie* base): explicit CClaimTrieCacheTest(CClaimTrie* base): CClaimTrieCacheBase(base, false)
CClaimTrieCache(base, false){}
bool recursiveComputeMerkleHash(CClaimTrieNode* tnCurrent,
std::string sPos) const
{ {
return CClaimTrieCache::recursiveComputeMerkleHash(tnCurrent, sPos);
} }
bool recursivePruneName(CClaimTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified) const using CClaimTrieCacheBase::insertSupportIntoMap;
using CClaimTrieCacheBase::removeSupportFromMap;
using CClaimTrieCacheBase::insertClaimIntoTrie;
using CClaimTrieCacheBase::removeClaimFromTrie;
void insert(const std::string& key, CClaimTrieData&& data)
{ {
return CClaimTrieCache::recursivePruneName(tnCurrent,nPos,sName, pfNullified); cache.insert(key, std::move(data));
} }
bool insertSupportIntoMap(const std::string& name, CSupportValue support, bool fCheckTakeover) const bool erase(const std::string& key)
{ {
return CClaimTrieCache::insertSupportIntoMap(name, support, fCheckTakeover); return cache.erase(key);
} }
int cacheSize() int cacheSize()
{ {
return cache.size(); return cache.height();
} }
nodeCacheType::iterator getCache(std::string key) CClaimTrie::iterator getCache(const std::string& key)
{ {
return cache.find(key); return cache.find(key);
} }
bool insertClaimIntoTrie(const std::string& name, CClaimValue claim,
bool fCheckTakeover = false) const
{
return CClaimTrieCache::insertClaimIntoTrie(name, claim, fCheckTakeover);
}
bool removeClaimFromTrie(const std::string& name, const COutPoint& outPoint,
CClaimValue& claim, bool fCheckTakeover = false) const
{
return CClaimTrieCache::removeClaimFromTrie(name, outPoint, claim, fCheckTakeover);
}
}; };
CMutableTransaction BuildTransaction(const uint256& prevhash) CMutableTransaction BuildTransaction(const uint256& prevhash)
@ -70,7 +61,6 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
BOOST_FIXTURE_TEST_SUITE(claimtriecache_tests, RegTestingSetup) BOOST_FIXTURE_TEST_SUITE(claimtriecache_tests, RegTestingSetup)
BOOST_AUTO_TEST_CASE(merkle_hash_single_test) BOOST_AUTO_TEST_CASE(merkle_hash_single_test)
{ {
// check empty trie // check empty trie
@ -78,10 +68,9 @@ BOOST_AUTO_TEST_CASE(merkle_hash_single_test)
CClaimTrieCacheTest cc(pclaimTrie); CClaimTrieCacheTest cc(pclaimTrie);
BOOST_CHECK_EQUAL(one, cc.getMerkleHash()); BOOST_CHECK_EQUAL(one, cc.getMerkleHash());
// check trie with only root node // we cannot have leaf root node
CClaimTrieNode base_node; auto it = cc.getCache("");
cc.recursiveComputeMerkleHash(&base_node, ""); BOOST_CHECK(!it);
BOOST_CHECK_EQUAL(one, cc.getMerkleHash());
} }
BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test) BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
@ -117,100 +106,101 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(pclaimTrie->empty()); BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ntState(pclaimTrie); CClaimTrieCacheTest ntState(pclaimTrie);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200)); ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200), true);
ntState.insertClaimIntoTrie(std::string("test2"), CClaimValue(tx2OutPoint, hash160, 50, 100, 200)); ntState.insertClaimIntoTrie(std::string("test2"), CClaimValue(tx2OutPoint, hash160, 50, 100, 200), true);
BOOST_CHECK(pclaimTrie->empty()); BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK(!ntState.empty()); BOOST_CHECK(!ntState.empty());
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash1); BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash1);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201)); ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201), true);
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash1); BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash1);
ntState.insertClaimIntoTrie(std::string("tes"), CClaimValue(tx4OutPoint, hash160, 50, 100, 200)); ntState.insertClaimIntoTrie(std::string("tes"), CClaimValue(tx4OutPoint, hash160, 50, 100, 200), true);
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CClaimValue(tx5OutPoint, hash160, 50, 100, 200)); ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CClaimValue(tx5OutPoint, hash160, 50, 100, 200), true);
ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5OutPoint, unused); ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
ntState.flush(); ntState.flush();
BOOST_CHECK(!pclaimTrie->empty()); BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState.checkConsistency(0));
CClaimTrieCacheTest ntState1(pclaimTrie); CClaimTrieCacheTest ntState1(pclaimTrie);
ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused); ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused); ntState1.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused); ntState1.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused); ntState1.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState1.getMerkleHash(), hash0); BOOST_CHECK_EQUAL(ntState1.getMerkleHash(), hash0);
CClaimTrieCacheTest ntState2(pclaimTrie); CClaimTrieCacheTest ntState2(pclaimTrie);
ntState2.insertClaimIntoTrie(std::string("abab"), CClaimValue(tx6OutPoint, hash160, 50, 100, 200)); ntState2.insertClaimIntoTrie(std::string("abab"), CClaimValue(tx6OutPoint, hash160, 50, 100, 200), true);
ntState2.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused); ntState2.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3); BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3);
ntState2.flush(); ntState2.flush();
BOOST_CHECK(!pclaimTrie->empty()); BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash3); BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState2.checkConsistency(0));
CClaimTrieCacheTest ntState3(pclaimTrie); CClaimTrieCacheTest ntState3(pclaimTrie);
ntState3.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200)); ntState3.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200), true);
BOOST_CHECK_EQUAL(ntState3.getMerkleHash(), hash4); BOOST_CHECK_EQUAL(ntState3.getMerkleHash(), hash4);
ntState3.flush(); ntState3.flush();
BOOST_CHECK(!pclaimTrie->empty()); BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash4); BOOST_CHECK_EQUAL(ntState3.getMerkleHash(), hash4);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState3.checkConsistency(0));
CClaimTrieCacheTest ntState4(pclaimTrie); CClaimTrieCacheTest ntState4(pclaimTrie);
ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint, unused); ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2);
ntState4.flush(); ntState4.flush();
BOOST_CHECK(!pclaimTrie->empty()); BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState4.checkConsistency(0));
CClaimTrieCacheTest ntState5(pclaimTrie); CClaimTrieCacheTest ntState5(pclaimTrie);
ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused); ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2);
ntState5.flush(); ntState5.flush();
BOOST_CHECK(!pclaimTrie->empty()); BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState5.checkConsistency(0));
CClaimTrieCacheTest ntState6(pclaimTrie); CClaimTrieCacheTest ntState6(pclaimTrie);
ntState6.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201)); ntState6.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201), true);
BOOST_CHECK_EQUAL(ntState6.getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState6.getMerkleHash(), hash2);
ntState6.flush(); ntState6.flush();
BOOST_CHECK(!pclaimTrie->empty()); BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2); BOOST_CHECK_EQUAL(ntState6.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState6.checkConsistency(0));
CClaimTrieCacheTest ntState7(pclaimTrie); CClaimTrieCacheTest ntState7(pclaimTrie);
ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused); ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused); ntState7.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused); ntState7.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused); ntState7.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0); BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0);
ntState7.flush(); ntState7.flush();
BOOST_CHECK(pclaimTrie->empty()); BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash0); BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0);
BOOST_CHECK(pclaimTrie->checkConsistency()); BOOST_CHECK(ntState7.checkConsistency(0));
} }
BOOST_AUTO_TEST_CASE(basic_insertion_info_test) BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
{ {
// test basic claim insertions and that get methods retreives information properly // test basic claim insertions and that get methods retreives information properly
BOOST_CHECK_EQUAL(pclaimTrie->empty(), true); BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ctc(pclaimTrie); CClaimTrieCacheTest ctc(pclaimTrie);
// create and insert claim // create and insert claim
CClaimValue unused;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0); CMutableTransaction tx1 = BuildTransaction(hash0);
uint160 claimId = ClaimIdHash(tx1.GetHash(), 0); uint160 claimId = ClaimIdHash(tx1.GetHash(), 0);
@ -219,17 +209,18 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
int height = 0; int height = 0;
int validHeight = 0; int validHeight = 0;
CClaimValue claimVal(claimOutPoint, claimId, amount, height, validHeight); CClaimValue claimVal(claimOutPoint, claimId, amount, height, validHeight);
ctc.insertClaimIntoTrie("test", claimVal); ctc.insertClaimIntoTrie("test", claimVal, true);
// try getClaimsForName, getEffectiveAmountForClaim, getInfoForName // try getClaimsForName, getEffectiveAmountForClaim, getInfoForName
claimsForNameType res = ctc.getClaimsForName("test"); auto res = ctc.getClaimsForName("test");
BOOST_CHECK_EQUAL(res.claims.size(), 1); BOOST_CHECK_EQUAL(res.claims.size(), 1);
BOOST_CHECK_EQUAL(res.claims[0], claimVal); BOOST_CHECK_EQUAL(res.claims[0], claimVal);
BOOST_CHECK_EQUAL(res.supports.size(), 0);
BOOST_CHECK_EQUAL(10, ctc.getEffectiveAmountForClaim("test", claimId)); BOOST_CHECK_EQUAL(10, ctc.getEffectiveAmountForClaim("test", claimId));
CClaimValue claim; CClaimValue claim;
BOOST_CHECK_EQUAL(ctc.getInfoForName("test", claim), true); BOOST_CHECK(ctc.getInfoForName("test", claim));
BOOST_CHECK_EQUAL(claim, claimVal); BOOST_CHECK_EQUAL(claim, claimVal);
// insert a support // insert a support
@ -241,6 +232,10 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
CSupportValue support(supportOutPoint, claimId, supportAmount, height, validHeight); CSupportValue support(supportOutPoint, claimId, supportAmount, height, validHeight);
ctc.insertSupportIntoMap("test", support, false); ctc.insertSupportIntoMap("test", support, false);
res = ctc.getClaimsForName("test");
BOOST_CHECK_EQUAL(res.claims.size(), 1);
BOOST_CHECK_EQUAL(res.supports.size(), 1);
// try getEffectiveAmount // try getEffectiveAmount
BOOST_CHECK_EQUAL(20, ctc.getEffectiveAmountForClaim("test", claimId)); BOOST_CHECK_EQUAL(20, ctc.getEffectiveAmountForClaim("test", claimId));
} }
@ -257,35 +252,42 @@ BOOST_AUTO_TEST_CASE(recursive_prune_test)
int validAtHeight = 0; int validAtHeight = 0;
CClaimValue test_claim(outpoint, claimId, amount, height, validAtHeight); CClaimValue test_claim(outpoint, claimId, amount, height, validAtHeight);
CClaimTrieNode base_node; CClaimTrieData data;
// base node has a claim, so it should not be pruned // base node has a claim, so it should not be pruned
base_node.insertClaim(test_claim); data.insertClaim(test_claim);
cc.insert("", std::move(data));
// node 1 has a claim so it should not be pruned // node 1 has a claim so it should not be pruned
CClaimTrieNode node_1; data.insertClaim(test_claim);
const char c = 't';
base_node.children[c] = &node_1;
node_1.insertClaim(test_claim);
// set this just to make sure we get the right CClaimTrieNode back // set this just to make sure we get the right CClaimTrieNode back
node_1.nHeightOfLastTakeover = 10; data.nHeightOfLastTakeover = 10;
cc.insert("t", std::move(data));
//node 2 does not have a claim so it should be pruned //node 2 does not have a claim so it should be pruned
// thus we should find pruned node 1 in cache // thus we should find pruned node 1 in cache
CClaimTrieNode node_2; cc.insert("te", CClaimTrieData{});
const char c_2 = 'e';
node_1.children[c_2] = &node_2;
cc.recursivePruneName(&base_node, 0, std::string("te"), NULL); BOOST_CHECK(cc.erase("te"));
BOOST_CHECK_EQUAL(2, cc.cacheSize());
auto it = cc.getCache("t");
BOOST_CHECK_EQUAL(10, it->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(1, it->claims.size());
BOOST_CHECK_EQUAL(2, cc.cacheSize());
cc.insert("te", CClaimTrieData{});
// erasing "t" will make it weak
BOOST_CHECK(cc.erase("t"));
// so now we erase "e" as well as "t"
BOOST_CHECK(cc.erase("te"));
// we have claim in root
BOOST_CHECK_EQUAL(1, cc.cacheSize()); BOOST_CHECK_EQUAL(1, cc.cacheSize());
nodeCacheType::iterator it = cc.getCache(std::string("t")); BOOST_CHECK(cc.erase(""));
BOOST_CHECK_EQUAL(10, it->second->nHeightOfLastTakeover); BOOST_CHECK_EQUAL(0, cc.cacheSize());
BOOST_CHECK_EQUAL(1U, it->second->claims.size());
BOOST_CHECK_EQUAL(0U, it->second->children.size());
} }
BOOST_AUTO_TEST_CASE(iteratetrie_test) BOOST_AUTO_TEST_CASE(iteratetrie_test)
{ {
BOOST_CHECK_EQUAL(pclaimTrie->empty(), true); BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ctc(pclaimTrie); CClaimTrieCacheTest ctc(pclaimTrie);
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
@ -293,48 +295,108 @@ BOOST_AUTO_TEST_CASE(iteratetrie_test)
const uint256 txhash = tx1.GetHash(); const uint256 txhash = tx1.GetHash();
CClaimValue claimVal(COutPoint(txhash, 0), ClaimIdHash(txhash, 0), CAmount(10), 0, 0); CClaimValue claimVal(COutPoint(txhash, 0), ClaimIdHash(txhash, 0), CAmount(10), 0, 0);
ctc.insertClaimIntoTrie("test", claimVal); ctc.insertClaimIntoTrie("test", claimVal, true);
BOOST_CHECK(ctc.flush());
int count = 0; std::size_t count = 0;
for (auto it = pclaimTrie->begin(); it != pclaimTrie->end(); ++it) {
struct TestCallBack : public CNodeCallback ++count;
{ if (it.key() == "test") {
TestCallBack(int& count) : count(count) BOOST_CHECK_EQUAL(it->claims.size(), 1);
{
} }
}
BOOST_CHECK_EQUAL(count, 2);
void visit(const std::string& name, const CClaimTrieNode* node) count = 0;
{ for (const auto& it: *pclaimTrie) {
count++; ++count;
if (name == "test") if (it.first == "test") {
BOOST_CHECK_EQUAL(node->claims.size(), 1); const CClaimTrieData& data = it.second;
BOOST_CHECK_EQUAL(data.claims.size(), 1);
} }
}
BOOST_CHECK_EQUAL(count, 2);
int& count; auto it = pclaimTrie->find("test");
} testCallback(count); BOOST_CHECK(it != pclaimTrie->end());
BOOST_CHECK_EQUAL(it->claims.size(), 1);
BOOST_CHECK_EQUAL(pclaimTrie->height(), 1);
}
BOOST_CHECK_EQUAL(ctc.iterateTrie(testCallback), true); BOOST_AUTO_TEST_CASE(trie_stays_consistent_test)
BOOST_CHECK_EQUAL(count, 5); {
std::vector<std::string> names {
"goodness", "goodnight", "goodnatured", "goods", "go", "goody", "goo"
};
count = 3; CClaimTrie trie(true, false, 1);
CClaimTrieCacheTest cache(&trie);
CClaimValue value;
struct TestCallBack2 : public CNodeCallback for (auto& name: names)
{ BOOST_CHECK(cache.insertClaimIntoTrie(name, value, false));
TestCallBack2(int& count) : count(count)
{
}
void visit(const std::string& name, const CClaimTrieNode* node) cache.flush();
{ BOOST_CHECK(cache.checkConsistency(0));
if (--count <= 0)
throw CRecursionInterruptionException(false);
}
int& count; for (auto& name: names) {
} testCallback2(count); CClaimValue temp;
BOOST_CHECK(cache.removeClaimFromTrie(name, COutPoint(), temp, false));
cache.flush();
BOOST_CHECK(cache.checkConsistency(0));
}
BOOST_CHECK_EQUAL(trie.height(), 0);
}
BOOST_CHECK_EQUAL(ctc.iterateTrie(testCallback2), false); BOOST_AUTO_TEST_CASE(takeover_workaround_triggers)
BOOST_CHECK_EQUAL(count, 0); {
auto& consensus = const_cast<Consensus::Params&>(Params().GetConsensus());
auto currentMax = consensus.nMaxTakeoverWorkaroundHeight;
consensus.nMaxTakeoverWorkaroundHeight = 10000;
BOOST_SCOPE_EXIT(&consensus, currentMax) { consensus.nMaxTakeoverWorkaroundHeight = currentMax; }
BOOST_SCOPE_EXIT_END
CClaimTrie trie(true, false, 1);
CClaimTrieCacheTest cache(&trie);
insertUndoType icu, isu; claimQueueRowType ecu; supportQueueRowType esu;
std::vector<std::pair<std::string, int>> thu;
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
CClaimValue value;
value.nHeight = 1;
BOOST_CHECK(cache.insertClaimIntoTrie("a", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("b", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("c", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("aa", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value, true));
BOOST_CHECK(cache.insertSupportIntoMap("aa", CSupportValue(), false));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
BOOST_CHECK(cache.flush());
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
BOOST_CHECK_EQUAL(0, cache.cacheSize());
CSupportValue temp;
BOOST_CHECK(cache.insertSupportIntoMap("bb", temp, false));
BOOST_CHECK(!cache.getCache("aa"));
BOOST_CHECK(cache.removeSupportFromMap("aa", COutPoint(), temp, false));
BOOST_CHECK(cache.removeClaimFromTrie("aa", COutPoint(), value, false));
BOOST_CHECK(cache.removeClaimFromTrie("bb", COutPoint(), value, false));
BOOST_CHECK(cache.removeClaimFromTrie("cc", COutPoint(), value, false));
BOOST_CHECK(cache.insertClaimIntoTrie("aa", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value, true));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value, true));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
BOOST_CHECK_EQUAL(3, cache.find("aa")->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(3, cache.find("bb")->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(1, cache.find("cc")->nHeightOfLastTakeover);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View file

@ -275,7 +275,7 @@ struct StringContentsSerializer {
BOOST_AUTO_TEST_CASE(iterator_string_ordering) BOOST_AUTO_TEST_CASE(iterator_string_ordering)
{ {
char buf[10]; char buf[12];
fs::path ph = SetDataDir("iterator_string_ordering"); fs::path ph = SetDataDir("iterator_string_ordering");
CDBWrapper dbw(ph, (1 << 20), true, false, false); CDBWrapper dbw(ph, (1 << 20), true, false, false);

View file

@ -222,6 +222,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
CBlock *pblock = &pblocktemplate->block; // pointer for convenience CBlock *pblock = &pblocktemplate->block; // pointer for convenience
{ {
BOOST_CHECK(!pblock->hashClaimTrie.IsNull());
LOCK(cs_main); LOCK(cs_main);
pblock->hashPrevBlock = chainActive.Tip()->GetBlockHash(); pblock->hashPrevBlock = chainActive.Tip()->GetBlockHash();
pblock->nVersion = 5; pblock->nVersion = 5;
@ -373,16 +374,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// subsidy changing // subsidy changing
int nHeight = chainActive.Height(); int nHeight = chainActive.Height();
// Create an actual 209999-long block chain (without valid blocks). // Create an actual 209999-long block chain (without valid blocks).
BOOST_CHECK(pclaimTrie);
CClaimTrieCache trieCache(pclaimTrie);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == trieCache.getBestBlock());
while (chainActive.Tip()->nHeight < 209999) { while (chainActive.Tip()->nHeight < 209999) {
CBlockIndex* prev = chainActive.Tip(); CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex(); CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256()); next->phashBlock = new uint256(InsecureRand256());
next->hashClaimTrie = pblocktemplate->block.hashClaimTrie;
pcoinsTip->SetBestBlock(next->GetBlockHash()); pcoinsTip->SetBestBlock(next->GetBlockHash());
trieCache.setBestBlock(next->GetBlockHash());
trieCache.flush();
next->pprev = prev; next->pprev = prev;
next->nHeight = prev->nHeight + 1; next->nHeight = prev->nHeight + 1;
next->BuildSkip(); next->BuildSkip();
@ -394,9 +391,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = chainActive.Tip(); CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex(); CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256()); next->phashBlock = new uint256(InsecureRand256());
next->hashClaimTrie = pblocktemplate->block.hashClaimTrie;
pcoinsTip->SetBestBlock(next->GetBlockHash()); pcoinsTip->SetBestBlock(next->GetBlockHash());
trieCache.setBestBlock(next->GetBlockHash());
trieCache.flush();
next->pprev = prev; next->pprev = prev;
next->nHeight = prev->nHeight + 1; next->nHeight = prev->nHeight + 1;
next->BuildSkip(); next->BuildSkip();
@ -427,8 +423,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* del = chainActive.Tip(); CBlockIndex* del = chainActive.Tip();
chainActive.SetTip(del->pprev); chainActive.SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash()); pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
trieCache.setBestBlock(del->pprev->GetBlockHash());
trieCache.flush();
delete del->phashBlock; delete del->phashBlock;
delete del; delete del;
} }

View file

@ -0,0 +1,221 @@
#include <claimtrie.h>
#include <prefixtrie.h>
#include <boost/test/unit_test.hpp>
#include <test/test_bitcoin.h>
#include <chrono>
#include <random>
std::vector<std::string> random_strings(std::size_t count)
{
static auto& chrs = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::mt19937 rg(42);
std::uniform_int_distribution<std::string::size_type> pick(1, sizeof(chrs) - 2);
std::unordered_set<std::string> strings;
strings.reserve(count);
while(strings.size() < count) {
auto length = pick(rg);
std::string s;
s.reserve(length);
while (length--)
s += chrs[pick(rg)];
strings.emplace(s);
}
std::vector<std::string> ret(strings.begin(), strings.end());
std::shuffle(ret.begin(), ret.end(), rg);
return ret;
}
using namespace std;
BOOST_FIXTURE_TEST_SUITE(prefixtrie_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(insert_erase_test)
{
CPrefixTrie<std::string, CClaimTrieData> root;
BOOST_CHECK(root.empty());
BOOST_CHECK_EQUAL(root.height(), 0);
CClaimTrieData data;
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("abc", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 1);
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("abd", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 3);
// test expanding on ab
BOOST_CHECK(root.find("ab") != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("a", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 4);
// test ab again
BOOST_CHECK(root.find("ab") != root.end());
BOOST_CHECK(root.erase("abd"));
BOOST_CHECK(root.find("abd") == root.end());
BOOST_CHECK_EQUAL(root.height(), 2);
// collapsing of ab-c to abc so ab does not present any more
BOOST_CHECK(root.find("ab") == root.end());
// a has children so only data is erased
BOOST_CHECK(root.erase("a"));
BOOST_CHECK(root.find("a") == root.end());
// erasing abc will erase all node refers to a
BOOST_CHECK(root.erase("abc"));
BOOST_CHECK(root.empty());
BOOST_CHECK_EQUAL(root.height(), 0);
BOOST_CHECK(root.find("a") == root.end());
BOOST_CHECK(root.find("ab") == root.end());
BOOST_CHECK(root.find("abc") == root.end());
BOOST_CHECK(root.find("abd") == root.end());
}
BOOST_AUTO_TEST_CASE(iterate_test)
{
CPrefixTrie<std::string, CClaimTrieData> root;
BOOST_CHECK(root.empty());
BOOST_CHECK_EQUAL(root.height(), 0);
BOOST_CHECK(root.insert("abc", CClaimTrieData{}) != root.end());
BOOST_CHECK_EQUAL(root.height(), 1);
BOOST_CHECK(root.insert("abd", CClaimTrieData{}) != root.end());
BOOST_CHECK_EQUAL(root.height(), 3);
BOOST_CHECK(root.insert("tdb", CClaimTrieData{}) != root.end());
BOOST_CHECK_EQUAL(root.height(), 4);
for (auto it = root.begin(); it != root.end(); ++it) {
auto& key = it.key();
if (key.empty() ||
key == "ab" ||
key == "abc" ||
key == "abd" ||
key == "tdb") {
} else {
BOOST_CHECK(false); // should not happen
}
}
auto nodes = root.nodes("abd");
BOOST_CHECK_EQUAL(nodes.size(), 3);
BOOST_CHECK_EQUAL(nodes[0].key(), "");
BOOST_CHECK(nodes[0].hasChildren());
BOOST_CHECK_EQUAL(nodes[0].children().size(), 2);
// children of ""
for (auto& child : nodes[0].children()) {
auto& key = child.key();
if (key == "ab" || key == "tdb") {
} else {
BOOST_CHECK(false); // should not happen
}
}
BOOST_CHECK_EQUAL(nodes[1].key(), "ab");
BOOST_CHECK(nodes[1].hasChildren());
BOOST_CHECK_EQUAL(nodes[1].children().size(), 2);
// children of "ab"
for (auto& child : nodes[1].children()) {
auto& key = child.key();
if (key == "abc" || key == "abd") {
} else {
BOOST_CHECK(false); // should not happen
}
}
BOOST_CHECK_EQUAL(nodes[2].key(), "abd");
BOOST_CHECK(!nodes[2].hasChildren());
BOOST_CHECK_EQUAL(nodes[2].children().size(), 0);
}
BOOST_AUTO_TEST_CASE(erase_cleanup_test)
{
CPrefixTrie<std::string, CClaimTrieData> root;
CClaimTrieData data;
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("a", std::move(data)) != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("adata", std::move(data)) != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("adata2", std::move(data)) != root.end());
data.insertClaim(CClaimValue{});
BOOST_CHECK(root.insert("adota", std::move(data)) != root.end());
BOOST_CHECK_EQUAL(root.height(), 5);
BOOST_CHECK(root.erase("adata"));
BOOST_CHECK(root.erase("adata2"));
BOOST_CHECK_EQUAL(root.height(), 2);
}
BOOST_AUTO_TEST_CASE(add_many_nodes) {
// this if for testing performance and making sure erasure goes all the way to zero
CPrefixTrie<std::string, CClaimTrieData> trie;
std::size_t count = 100000; // bump this up to 1M for a better test
auto strings = random_strings(count);
auto s1 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
CClaimTrieData d; d.nHeightOfLastTakeover = i;
trie.insert(strings[i], std::move(d));
}
auto s2 = std::chrono::high_resolution_clock::now();
auto nodes = trie.height();
// run with --log_level=message to see these:
BOOST_TEST_MESSAGE("Nodes:" + std::to_string(nodes));
BOOST_TEST_MESSAGE("Insertion sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s2 - s1).count()));
auto s3 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
auto& value = trie.at(strings[i]);
BOOST_CHECK_EQUAL(value.nHeightOfLastTakeover, i);
}
auto s4 = std::chrono::high_resolution_clock::now();
BOOST_TEST_MESSAGE("Lookup sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s4 - s3).count()));
auto s5 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
auto value = trie.nodes(strings[i]);
BOOST_CHECK(!value.empty());
}
auto s6 = std::chrono::high_resolution_clock::now();
BOOST_TEST_MESSAGE("Parents sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s6 - s5).count()));
auto s7 = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < count; i++) {
trie.erase(strings[i]);
}
BOOST_CHECK_EQUAL(trie.height(), 0);
auto s8 = std::chrono::high_resolution_clock::now();
BOOST_TEST_MESSAGE("Deletion sec: " + std::to_string(std::chrono::duration_cast<std::chrono::duration<float> >(s8 - s7).count()));
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -60,9 +60,9 @@ std::ostream& operator<<(std::ostream& os, const COutPoint& point)
return os; return os;
} }
std::ostream& operator<<(std::ostream& os, const CClaimTrieNode& node) std::ostream& operator<<(std::ostream& os, const CClaimTrieData& data)
{ {
os << node.hash.ToString(); os << data.hash.ToString();
return os; return os;
} }
@ -77,6 +77,16 @@ std::ostream& operator<<(std::ostream& os, const CClaimValue& claim)
return os; return os;
} }
std::ostream& operator<<(std::ostream& os, const CSupportValue& support)
{
os << "support(" << support.outPoint.ToString()
<< ", " << support.supportedClaimId.ToString()
<< ", " << support.nAmount
<< ", " << support.nHeight
<< ", " << support.nValidAtHeight << ')';
return os;
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName) BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
: m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30)))) : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{ {

View file

@ -137,7 +137,10 @@ std::ostream& operator<<(std::ostream& os, const COutPoint& point);
class CClaimValue; class CClaimValue;
std::ostream& operator<<(std::ostream& os, const CClaimValue& claim); std::ostream& operator<<(std::ostream& os, const CClaimValue& claim);
class CClaimTrieNode; class CSupportValue;
std::ostream& operator<<(std::ostream& os, const CClaimTrieNode& node); std::ostream& operator<<(std::ostream& os, const CSupportValue& support);
class CClaimTrieData;
std::ostream& operator<<(std::ostream& os, const CClaimTrieData& data);
#endif #endif

View file

@ -34,7 +34,7 @@ static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache (MiB) //! min. -dbcache (MiB)
static const int64_t nMinDbCache = 4; static const int64_t nMinDbCache = 4;
//! Max memory allocated to block tree DB specific cache, if no -txindex (MiB) //! Max memory allocated to block tree DB specific cache, if no -txindex (MiB)
static const int64_t nMaxBlockDBCache = 2; static const int64_t nMaxBlockDBCache = 8;
//! Max memory allocated to block tree DB specific cache, if -txindex (MiB) //! Max memory allocated to block tree DB specific cache, if -txindex (MiB)
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make // Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991 // a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991

View file

@ -1416,7 +1416,6 @@ static bool AbortNode(const std::string& strMessage, const std::string& userMess
{ {
SetMiscWarning(strMessage); SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage); LogPrintf("*** %s\n", strMessage);
throw "nasty";
uiInterface.ThreadSafeMessageBox( uiInterface.ThreadSafeMessageBox(
userMessage.empty() ? "Error: A fatal internal error occurred, see debug.log for details. System message: " + strMessage : userMessage, userMessage.empty() ? "Error: A fatal internal error occurred, see debug.log for details. System message: " + strMessage : userMessage,
"", CClientUIInterface::MSG_ERROR); "", CClientUIInterface::MSG_ERROR);
@ -1457,6 +1456,16 @@ int ApplyTxInUndo(unsigned int index, CTxUndo& txUndo, CCoinsViewCache& view, CC
} else { } else {
return DISCONNECT_FAILED; // adding output for transaction without known metadata return DISCONNECT_FAILED; // adding output for transaction without known metadata
} }
// TODO: pick the above approach or this:
// what is more correct? the above AccessByTxid or this kind of lookup ?
// for (uint32_t i = index + 1; i < txUndo.vprevout.size(); ++i) {
// if (txUndo.vprevout[i].nHeight > 0) {
// assert(undo.nHeight == txUndo.vprevout[i].nHeight);
// assert(undo.fCoinBase == txUndo.vprevout[i].fCoinBase);
// break;
// }
// }
} }
// restore claim if applicable // restore claim if applicable
@ -1486,7 +1495,11 @@ int ApplyTxInUndo(unsigned int index, CTxUndo& txUndo, CCoinsViewCache& view, CC
DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, CClaimTrieCache& trieCache) DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, CClaimTrieCache& trieCache)
{ {
assert(pindex->GetBlockHash() == view.GetBestBlock()); assert(pindex->GetBlockHash() == view.GetBestBlock());
assert(pindex->GetBlockHash() == trieCache.getBestBlock()); if (pindex->hashClaimTrie != trieCache.getMerkleHash()) {
LogPrintf("%s: Indexed claim hash doesn't match current: %s vs %s\n",
__func__, pindex->hashClaimTrie.ToString(), trieCache.getMerkleHash().ToString());
assert(false);
}
bool fClean = true; bool fClean = true;
@ -1501,7 +1514,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
return DISCONNECT_FAILED; return DISCONNECT_FAILED;
} }
const bool decremented = trieCache.decrementBlock(blockUndo.insertUndo, blockUndo.expireUndo, blockUndo.insertSupportUndo, blockUndo.expireSupportUndo, blockUndo.takeoverHeightUndo); const bool decremented = trieCache.decrementBlock(blockUndo.insertUndo, blockUndo.expireUndo, blockUndo.insertSupportUndo, blockUndo.expireSupportUndo);
assert(decremented); assert(decremented);
// undo transactions in reverse order // undo transactions in reverse order
@ -1557,14 +1570,25 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
// move best block pointer to prevout block // move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash()); view.SetBestBlock(pindex->pprev->GetBlockHash());
assert(trieCache.finalizeDecrement()); assert(trieCache.finalizeDecrement(blockUndo.takeoverHeightUndo));
trieCache.setBestBlock(pindex->pprev->GetBlockHash()); auto merkleHash = trieCache.getMerkleHash();
assert(trieCache.getMerkleHash() == pindex->pprev->hashClaimTrie); if (merkleHash != pindex->pprev->hashClaimTrie) {
if (trieCache.checkConsistency()) {
for (auto cit = trieCache.begin(); cit != trieCache.end(); ++cit) {
if (cit->claims.size() && cit->nHeightOfLastTakeover <= 0)
LogPrintf("Invalid takeover height discovered in cache for %s\n", cit.key());
if (cit->hash.IsNull())
LogPrintf("Invalid hash discovered in cache for %s\n", cit.key());
}
}
LogPrintf("Hash comparison failure at block %d\n", pindex->nHeight);
assert(merkleHash == pindex->pprev->hashClaimTrie);
}
if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight) if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
{ {
LogPrintf("Decremented past the extended claim expiration hard fork height\n"); LogPrintf("Decremented past the extended claim expiration hard fork height\n");
pclaimTrie->setExpirationTime(Params().GetConsensus().GetExpirationTime(pindex->nHeight-1)); trieCache.setExpirationTime(Params().GetConsensus().GetExpirationTime(pindex->nHeight-1));
trieCache.forkForExpirationChange(false); trieCache.forkForExpirationChange(false);
} }
@ -1776,7 +1800,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
assert(hashPrevBlock == view.GetBestBlock()); assert(hashPrevBlock == view.GetBestBlock());
// also verify that the trie cache's current state corresponds to the previous block // also verify that the trie cache's current state corresponds to the previous block
assert(hashPrevBlock == trieCache.getBestBlock()); if (pindex->pprev != nullptr && pindex->pprev->hashClaimTrie != trieCache.getMerkleHash()) {
LogPrintf("%s: Previous block claim hash doesn't match current: %s vs %s\n",
__func__, pindex->pprev->hashClaimTrie.ToString(), trieCache.getMerkleHash().ToString());
assert(false);
}
// Special case for the genesis block, skipping connection of its transactions // Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable) // (its coinbase is unspendable)
@ -1784,7 +1812,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (!fJustCheck) if (!fJustCheck)
{ {
view.SetBestBlock(pindex->GetBlockHash()); view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
} }
/* return true; */ /* return true; */
} }
@ -1926,7 +1953,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight) if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
{ {
LogPrintf("Incremented past the extended claim expiration hard fork height\n"); LogPrintf("Incremented past the extended claim expiration hard fork height\n");
pclaimTrie->setExpirationTime(chainparams.GetConsensus().GetExpirationTime(pindex->nHeight)); trieCache.setExpirationTime(chainparams.GetConsensus().GetExpirationTime(pindex->nHeight));
trieCache.forkForExpirationChange(true); trieCache.forkForExpirationChange(true);
} }
@ -2059,15 +2086,17 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
} }
// TODO: if the "just check" flag is set, we should reduce the work done here. Incrementing blocks twice per mine is not efficient.
const auto incremented = trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo, blockundo.insertSupportUndo, blockundo.expireSupportUndo, blockundo.takeoverHeightUndo); const auto incremented = trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo, blockundo.insertSupportUndo, blockundo.expireSupportUndo, blockundo.takeoverHeightUndo);
assert(incremented); assert(incremented);
if (trieCache.getMerkleHash() != block.hashClaimTrie) if (trieCache.getMerkleHash() != block.hashClaimTrie)
{ {
return state.DoS(100, if (trieCache.checkConsistency())
error("ConnectBlock() : the merkle root of the claim trie does not match " trieCache.dumpToLog(trieCache.begin());
"(actual=%s vs block=%s)", trieCache.getMerkleHash().GetHex(), return state.DoS(100, error("ConnectBlock() : the merkle root of the claim trie does not match "
block.hashClaimTrie.GetHex()), REJECT_INVALID, "bad-claim-merkle-hash"); "(actual=%s vs block=%s on height=%d)", trieCache.getMerkleHash().GetHex(),
block.hashClaimTrie.GetHex(), pindex->nHeight), REJECT_INVALID, "bad-claim-merkle-hash");
} }
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
@ -2101,7 +2130,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
assert(pindex->phashBlock); assert(pindex->phashBlock);
// add this block to the view's block chain // add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash()); view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4; int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal); LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal);
@ -2208,7 +2236,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// overwrite one. Still, use a conservative safety factor of 2. // overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize())) if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space"); return state.Error("out of disk space");
if (!pclaimTrie->WriteToDisk()) if (!pclaimTrie->SyncToDisk())
return state.Error("Failed to write to claim trie database"); return state.Error("Failed to write to claim trie database");
// Flush the chainstate (which may refer to block index entries). // Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush()) if (!pcoinsTip->Flush())
@ -4183,7 +4211,6 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
} }
cache.SetBestBlock(pindexNew->GetBlockHash()); cache.SetBestBlock(pindexNew->GetBlockHash());
trieCache.setBestBlock(pindexNew->GetBlockHash());
cache.Flush(); cache.Flush();
trieCache.flush(); trieCache.flush();
uiInterface.ShowProgress("", 100, false); uiInterface.ShowProgress("", 100, false);

View file

@ -25,6 +25,7 @@
#include <timedata.h> #include <timedata.h>
#include <util.h> #include <util.h>
#include <utilmoneystr.h> #include <utilmoneystr.h>
#include <utiltime.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/feebumper.h> #include <wallet/feebumper.h>
#include <wallet/rpcwallet.h> #include <wallet/rpcwallet.h>
@ -217,9 +218,9 @@ static UniValue getaccountaddress(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts")) { if (!IsDeprecatedRPCEnabled("accounts")) {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("getaccountaddress (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("getaccountaddress (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() != 1)
@ -314,9 +315,9 @@ static UniValue setlabel(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "setaccount") { if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "setaccount") {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("setaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("setaccount (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() != 2) if (request.fHelp || request.params.size() != 2)
@ -380,9 +381,9 @@ static UniValue getaccount(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts")) { if (!IsDeprecatedRPCEnabled("accounts")) {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("getaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("getaccount (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() != 1)
@ -425,9 +426,9 @@ static UniValue getaddressesbyaccount(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts")) { if (!IsDeprecatedRPCEnabled("accounts")) {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("getaddressbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("getaddressbyaccount (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() != 1)
@ -689,6 +690,7 @@ UniValue abandonclaim(const JSONRPCRequest& request)
static void MaybePushAddress(UniValue& entry, const CTxDestination &dest); static void MaybePushAddress(UniValue& entry, const CTxDestination &dest);
extern std::string escapeNonUtf8(const std::string&);
void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::string& strAccount, int nMinDepth, void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::string& strAccount, int nMinDepth,
UniValue& ret, const bool include_supports, bool list_spent) UniValue& ret, const bool include_supports, bool list_spent)
@ -720,7 +722,7 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
continue; continue;
} }
std::string sName (vvchParams[0].begin(), vvchParams[0].end()); std::string sName (vvchParams[0].begin(), vvchParams[0].end());
entry.pushKV("name", sName); entry.pushKV("name", escapeNonUtf8(sName));
if (op == OP_CLAIM_NAME) if (op == OP_CLAIM_NAME)
{ {
uint160 claimId = ClaimIdHash(wtx.GetHash(), s.vout); uint160 claimId = ClaimIdHash(wtx.GetHash(), s.vout);
@ -750,6 +752,9 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
entry.pushKV("amount", ValueFromAmount(s.amount)); entry.pushKV("amount", ValueFromAmount(s.amount));
entry.pushKV("vout", s.vout); entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(nFee)); entry.pushKV("fee", ValueFromAmount(nFee));
CClaimTrieCache trieCache(pclaimTrie);
auto it = mapBlockIndex.find(wtx.hashBlock); auto it = mapBlockIndex.find(wtx.hashBlock);
if (it != mapBlockIndex.end()) if (it != mapBlockIndex.end())
{ {
@ -757,11 +762,11 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
if (pindex) if (pindex)
{ {
entry.pushKV("height", pindex->nHeight); entry.pushKV("height", pindex->nHeight);
entry.pushKV("expiration height", pindex->nHeight + pclaimTrie->nExpirationTime); entry.pushKV("expiration height", pindex->nHeight + trieCache.expirationTime());
if (pindex->nHeight + pclaimTrie->nExpirationTime > chainActive.Height()) if (pindex->nHeight + trieCache.expirationTime() > chainActive.Height())
{ {
entry.pushKV("expired", false); entry.pushKV("expired", false);
entry.pushKV("blocks to expiration", pindex->nHeight + pclaimTrie->nExpirationTime - chainActive.Height()); entry.pushKV("blocks to expiration", pindex->nHeight + trieCache.expirationTime() - chainActive.Height());
} }
else else
{ {
@ -773,11 +778,11 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
entry.pushKV("is spent", pwallet->IsSpent(wtx.GetHash(), s.vout)); entry.pushKV("is spent", pwallet->IsSpent(wtx.GetHash(), s.vout));
if (op == OP_CLAIM_NAME) if (op == OP_CLAIM_NAME)
{ {
entry.pushKV("is in name trie", pclaimTrie->haveClaim(sName, COutPoint(wtx.GetHash(), s.vout))); entry.pushKV("is in name trie", trieCache.haveClaim(sName, COutPoint(wtx.GetHash(), s.vout)));
} }
else if (op == OP_SUPPORT_CLAIM) else if (op == OP_SUPPORT_CLAIM)
{ {
entry.pushKV("is in support map", pclaimTrie->haveSupport(sName, COutPoint(wtx.GetHash(), s.vout))); entry.pushKV("is in support map", trieCache.haveSupport(sName, COutPoint(wtx.GetHash(), s.vout)));
} }
ret.push_back(entry); ret.push_back(entry);
} }
@ -1266,9 +1271,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getreceivedbyaccount") { if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getreceivedbyaccount") {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("getreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("getreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
@ -1348,7 +1353,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
"The server total may be different to the balance in the default \"\" account.\n" "The server total may be different to the balance in the default \"\" account.\n"
"\nArguments:\n" "\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. \n" "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. \n"
" To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. The account string may be given as a\n" " To use this deprecated argument, start with -deprecatedrpc=accounts. The account string may be given as a\n"
" specific account name to find the balance associated with wallet keys in\n" " specific account name to find the balance associated with wallet keys in\n"
" a named account, or as the empty string (\"\") to find the balance\n" " a named account, or as the empty string (\"\") to find the balance\n"
" associated with wallet keys not in any named account, or as \"*\" to find\n" " associated with wallet keys not in any named account, or as \"*\" to find\n"
@ -1454,9 +1459,9 @@ static UniValue movecmd(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts")) { if (!IsDeprecatedRPCEnabled("accounts")) {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("move (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("move (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "move is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "move is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() < 3 || request.params.size() > 5) if (request.fHelp || request.params.size() < 3 || request.params.size() > 5)
@ -1513,9 +1518,9 @@ static UniValue sendfrom(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts")) { if (!IsDeprecatedRPCEnabled("accounts")) {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("sendfrom (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("sendfrom (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "sendfrom is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "sendfrom is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
@ -1949,7 +1954,7 @@ static UniValue addwitnessaddress(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("addwitnessaddress")) { if (!IsDeprecatedRPCEnabled("addwitnessaddress")) {
throw JSONRPCError(RPC_METHOD_DEPRECATED, "addwitnessaddress is deprecated and will be fully removed in v0.17. " throw JSONRPCError(RPC_METHOD_DEPRECATED, "addwitnessaddress is deprecated and will be fully removed in v0.17. "
"To use addwitnessaddress in v0.16, restart bitcoind with -deprecatedrpc=addwitnessaddress.\n" "To use addwitnessaddress in v0.16, restart with -deprecatedrpc=addwitnessaddress.\n"
"Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n"); "Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n");
} }
@ -2209,9 +2214,9 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "listreceivedbyaccount") { if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "listreceivedbyaccount") {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("listreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("listreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "listreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "listreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() > 3) if (request.fHelp || request.params.size() > 3)
@ -2573,9 +2578,9 @@ static UniValue listaccounts(const JSONRPCRequest& request)
if (!IsDeprecatedRPCEnabled("accounts")) { if (!IsDeprecatedRPCEnabled("accounts")) {
if (request.fHelp) { if (request.fHelp) {
throw std::runtime_error("listaccounts (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); throw std::runtime_error("listaccounts (Deprecated, will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts)");
} }
throw JSONRPCError(RPC_METHOD_DEPRECATED, "listaccounts is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); throw JSONRPCError(RPC_METHOD_DEPRECATED, "listaccounts is deprecated and will be removed in V0.18. To use this command, start with -deprecatedrpc=accounts.");
} }
if (request.fHelp || request.params.size() > 2) if (request.fHelp || request.params.size() > 2)
@ -2681,7 +2686,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"transactions\": [\n" " \"transactions\": [\n"
" \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name associated with the transaction. Will be \"\" for the default account.\n" " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start with -deprecatedrpc=accounts. The account name associated with the transaction. Will be \"\" for the default account.\n"
" \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
" \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n"
@ -2830,7 +2835,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
" may be unknown for unconfirmed transactions not in the mempool\n" " may be unknown for unconfirmed transactions not in the mempool\n"
" \"details\" : [\n" " \"details\" : [\n"
" {\n" " {\n"
" \"account\" : \"accountname\", (string) DEPRECATED. This field will be removed in a V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name involved in the transaction, can be \"\" for the default account.\n" " \"account\" : \"accountname\", (string) DEPRECATED. This field will be removed in a V0.18. To see this deprecated field, start with -deprecatedrpc=accounts. The account name involved in the transaction, can be \"\" for the default account.\n"
" \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n"
" \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
" \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n"
@ -3790,7 +3795,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
" \"vout\" : n, (numeric) the vout value\n" " \"vout\" : n, (numeric) the vout value\n"
" \"address\" : \"address\", (string) the bitcoin address\n" " \"address\" : \"address\", (string) the bitcoin address\n"
" \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n" " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n"
" \"account\" : \"account\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The associated account, or \"\" for the default account\n" " \"account\" : \"account\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start with -deprecatedrpc=accounts. The associated account, or \"\" for the default account\n"
" \"scriptPubKey\" : \"key\", (string) the script key\n" " \"scriptPubKey\" : \"key\", (string) the script key\n"
" \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n" " \"confirmations\" : n, (numeric) The number of confirmations\n"
@ -4645,7 +4650,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
" \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n"
" \"iscompressed\" : true|false, (boolean) If the address is compressed\n" " \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
" \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n" " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n"
" \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n" " \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n"
" \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
" \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
" \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n" " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n"

View file

@ -35,14 +35,14 @@ class HelpTest(BitcoinTestFramework):
return out, err return out, err
def run_test(self): def run_test(self):
self.log.info("Start bitcoin with -h for help text") self.log.info("Start lbrycrd with -h for help text")
self.nodes[0].start(extra_args=['-h']) self.nodes[0].start(extra_args=['-h'])
# Node should exit immediately and output help to stdout. # Node should exit immediately and output help to stdout.
output, _ = self.get_node_output(ret_code_expected=0) output, _ = self.get_node_output(ret_code_expected=0)
assert b'Options' in output assert b'Options' in output
self.log.info("Help text received: {} (...)".format(output[0:60])) self.log.info("Help text received: {} (...)".format(output[0:60]))
self.log.info("Start bitcoin with -version for version information") self.log.info("Start lbrycrd with -version for version information")
self.nodes[0].start(extra_args=['-version']) self.nodes[0].start(extra_args=['-version'])
# Node should exit immediately and output version to stdout. # Node should exit immediately and output version to stdout.
output, _ = self.get_node_output(ret_code_expected=0) output, _ = self.get_node_output(ret_code_expected=0)
@ -50,7 +50,7 @@ class HelpTest(BitcoinTestFramework):
self.log.info("Version text received: {} (...)".format(output[0:60])) self.log.info("Version text received: {} (...)".format(output[0:60]))
# Test that arguments not in the help results in an error # Test that arguments not in the help results in an error
self.log.info("Start bitcoind with -fakearg to make sure it does not start") self.log.info("Start lbrycrd with -fakearg to make sure it does not start")
self.nodes[0].start(extra_args=['-fakearg']) self.nodes[0].start(extra_args=['-fakearg'])
# Node should exit immediately and output an error to stderr # Node should exit immediately and output an error to stderr
_, output = self.get_node_output(ret_code_expected=1) _, output = self.get_node_output(ret_code_expected=1)