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 committed by Anthony Fieroni
parent 6824576189
commit 5f1c22680e
27 changed files with 8995 additions and 6080 deletions

View file

@ -59,12 +59,13 @@ def get_obj_from_dirty_text(full_object: str):
last_name = property_name
elif len(left) > 1:
match = re.match(r'^(\[)?"(?P<name>\w.*?)"(\])?.*', left)
last_name = match.group('name')
if match.group(1) is not None and match.group(3) is not None:
left = '['
property_refined_type = 'string'
if 'string' not in line:
raise NotImplementedError('Not implemented: ' + line)
if match is not None:
last_name = match.group('name')
if match.group(1) is not None and match.group(3) is not None:
left = '['
property_refined_type = 'string'
if 'string' not in line:
raise NotImplementedError('Not implemented: ' + line)
if left.endswith('['):
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
if ret is not None:
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
except Exception as e:
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()
if 'array' in arg_type:
return 'array', required, None
if 'numeric' in arg_type:
if 'numeric' in arg_type or 'number' in arg_type:
return 'number', required, None
if 'bool' in arg_type:
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
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)
return None, False, None

View file

@ -13,17 +13,22 @@ def get_type(arg_type, full_line):
if arg_type is None:
return 'string'
arg_type = arg_type.lower()
arg_type = arg_type.lower().split(',')[0].strip()
if 'numeric' in arg_type:
return 'number'
if 'bool' in arg_type:
return 'boolean'
if 'string' in arg_type:
return 'string'
if 'array' in arg_type:
return 'array'
if 'object' in arg_type:
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):
@ -34,7 +39,7 @@ def parse_params(args):
continue
arg_parsed = re_argline.fullmatch(line)
if arg_parsed is None:
raise Exception("Unparsable argument: " + line)
continue
arg_name, arg_type, arg_desc = arg_parsed.group('name', 'type', 'desc')
if not 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

@ -172,6 +172,7 @@ BITCOIN_CORE_H = \
policy/rbf.h \
policy/settings.h \
pow.h \
prefixtrie.h \
protocol.h \
psbt.h \
random.h \
@ -297,6 +298,7 @@ libbitcoin_server_a_SOURCES = \
policy/rbf.cpp \
policy/settings.cpp \
pow.cpp \
prefixtrie.cpp \
rest.cpp \
rpc/blockchain.cpp \
rpc/claimtrie.cpp \

View file

@ -126,6 +126,7 @@ BITCOIN_TESTS =\
test/pmt_tests.cpp \
test/policyestimator_tests.cpp \
test/pow_tests.cpp \
test/prefixtrie_tests.cpp \
test/prevector_tests.cpp \
test/raii_event_tests.cpp \
test/random_tests.cpp \

View file

@ -135,6 +135,8 @@ public:
consensus.nAllowMinDiffMinHeight = -1;
consensus.nAllowMinDiffMaxHeight = -1;
consensus.nNormalizedNameForkHeight = 539940; // targeting 21 March 2019
consensus.nMinTakeoverWorkaroundHeight = 496850;
consensus.nMaxTakeoverWorkaroundHeight = 10000000;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
@ -243,6 +245,8 @@ public:
consensus.nAllowMinDiffMinHeight = 277299;
consensus.nAllowMinDiffMaxHeight = 1100000;
consensus.nNormalizedNameForkHeight = 993380; // targeting, 21 Feb 2019
consensus.nMinTakeoverWorkaroundHeight = 99;
consensus.nMaxTakeoverWorkaroundHeight = 10000000;
consensus.fPowAllowMinDifficultyBlocks = true;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
@ -338,6 +342,8 @@ public:
consensus.nAllowMinDiffMinHeight = -1;
consensus.nAllowMinDiffMaxHeight = -1;
consensus.nNormalizedNameForkHeight = 250; // SDK depends upon this number
consensus.nMinTakeoverWorkaroundHeight = -1;
consensus.nMaxTakeoverWorkaroundHeight = -1;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
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/foreach.hpp>
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/locale/localization_backend.hpp>
#include <boost/locale.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
removeFromExpirationQueue(e->name, e->outPoint, height);
int extend_expiration = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime;
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);
}
}
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
removeSupportFromExpirationQueue(e->name, e->outPoint, height);
int extend_expiration = Params().GetConsensus().nExtendedClaimExpirationTime - Params().GetConsensus().nOriginalClaimExpirationTime;
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);
}
}
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
@ -45,75 +41,40 @@ bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment) cons
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
boost::scoped_ptr<CDBIterator> pcursor(const_cast<CDBWrapper*>(&base->db)->NewIterator());
pcursor->SeekToFirst();
while (pcursor->Valid())
{
std::pair<char, int> key;
if (pcursor->GetKey(key))
{
int height = key.second;
// if we've looked through this in dirtyExprirationQueueRows, don't use it
// because its stale
if ((key.first == EXP_QUEUE_ROW) & (dirtyHeights.count(height) == 0))
{
expirationQueueRowType row;
if (pcursor->GetValue(row))
{
removeAndAddToExpirationQueue(row, height, increment);
}
else
{
return error("%s(): error reading expiration queue rows from disk", __func__);
}
boost::scoped_ptr<CDBIterator> pcursor(base->db->NewIterator());
for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
std::pair<uint8_t, int> key;
if (!pcursor->GetKey(key))
continue;
int height = key.second;
if (key.first == EXP_QUEUE_ROW) {
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))
{
expirationQueueRowType row;
if (pcursor->GetValue(row))
{
removeAndAddSupportToExpirationQueue(row, height, increment);
}
else
{
return error("%s(): error reading support expiration queue rows from disk", __func__);
}
} else if (key.first == SUPPORT_EXP_QUEUE_ROW) {
expirationQueueRowType row;
if (pcursor->GetValue(row)) {
removeAndAddSupportToExpirationQueue(row, height, increment);
} else {
return error("%s(): error reading support expiration queue rows from disk", __func__);
}
}
pcursor->Next();
}
return true;
}
bool CClaimTrieCacheNormalizationFork::shouldNormalize() const {
return nCurrentHeight > Params().GetConsensus().nNormalizedNameForkHeight;
bool CClaimTrieCacheNormalizationFork::shouldNormalize() const
{
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())
return name;
@ -121,7 +82,7 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
static bool initialized = false;
if (!initialized) {
static boost::locale::localization_backend_manager manager =
boost::locale::localization_backend_manager::global();
boost::locale::localization_backend_manager::global();
manager.select("icu");
static boost::locale::generator curLocale(manager);
@ -131,7 +92,6 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
std::string normalized;
try {
// 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
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:
normalized = boost::locale::normalize(normalized, boost::locale::norm_nfd, 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;
}
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());
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());
return name;
}
@ -157,144 +114,121 @@ std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::stri
return normalized;
}
bool CClaimTrieCacheNormalizationFork::insertClaimIntoTrie(const std::string& name, CClaimValue claim,
bool fCheckTakeover) const {
bool CClaimTrieCacheNormalizationFork::insertClaimIntoTrie(const std::string& name, const CClaimValue& claim, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::insertClaimIntoTrie(normalizeClaimName(name, overrideInsertNormalization), claim, fCheckTakeover);
}
bool CClaimTrieCacheNormalizationFork::removeClaimFromTrie(const std::string& name, const COutPoint& outPoint,
CClaimValue& claim, bool fCheckTakeover) const {
bool CClaimTrieCacheNormalizationFork::removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, CClaimValue& claim, bool fCheckTakeover)
{
return CClaimTrieCacheExpirationFork::removeClaimFromTrie(normalizeClaimName(name, overrideRemoveNormalization), outPoint, claim, fCheckTakeover);
}
bool CClaimTrieCacheNormalizationFork::insertSupportIntoMap(const std::string& name, CSupportValue support,
bool fCheckTakeover) const {
bool CClaimTrieCacheNormalizationFork::insertSupportIntoMap(const std::string& name, const CSupportValue& support, bool 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);
}
struct claimsForNormalization: public claimsForNameType {
std::string normalized;
claimsForNormalization(const std::vector<CClaimValue>& claims, const std::vector<CSupportValue>& supports,
int nLastTakeoverHeight, const std::string& name, const std::string& normalized)
: claimsForNameType(claims, supports, nLastTakeoverHeight, name), normalized(normalized) {}
};
bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insertUndoType& insertUndo, claimQueueRowType& removeUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
{
if (nNextHeight != Params().GetConsensus().nNormalizedNameForkHeight)
return false;
bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insertUndoType& insertUndo, claimQueueRowType& removeUndo,
insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo,
std::vector<std::pair<std::string, int> >& takeoverHeightUndo) const {
// run the one-time upgrade of all names that need to change
// it modifies the (cache) trie as it goes, so we need to grab everything to be modified first
struct CNameChangeDetector: public CNodeCallback {
std::vector<claimsForNormalization> hits;
const CClaimTrieCacheNormalizationFork* owner;
CNameChangeDetector(const CClaimTrieCacheNormalizationFork* owner): owner(owner) {}
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;
for (auto it = base->begin(); it != base->end(); ++it) {
const std::string normalized = normalizeClaimName(it.key(), true);
if (normalized == it.key())
continue;
supportMapEntryType supports;
owner->getSupportsForName(name, supports);
const claimsForNormalization cfn(node->claims, supports, node->nHeightOfLastTakeover, name, normalized);
hits.push_back(cfn);
auto supports = getSupportsForName(it.key());
for (auto& support : supports) {
// if it's already going to expire just skip it
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
// it modifies the (cache) trie as it goes, so we need to grab
// everything to be modified first
CNameChangeDetector detector(this);
iterateTrie(detector);
auto cached = cacheData(it.key(), false);
if (!cached || cached->claims.empty())
continue;
for (std::vector<claimsForNormalization>::iterator it = detector.hits.begin(); it != detector.hits.end(); ++it) {
BOOST_FOREACH(CSupportValue support, it->supports) {
// if it's already going to expire just skip it
if (support.nHeight + base->nExpirationTime <= nCurrentHeight)
continue;
for (auto& claim : it->claims) {
if (claim.nHeight + base->nExpirationTime <= nNextHeight)
continue;
bool success = removeSupportFromMap(it->name, support.outPoint, support, false);
assert(success);
expireSupportUndo.push_back(std::make_pair(it->name, support));
success = insertSupportIntoMap(it->normalized, support, false);
assert(success);
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));
CClaimValue removed;
assert(removeClaimFromTrie(it.key(), claim.outPoint, removed, false));
removeUndo.emplace_back(it.key(), removed);
assert(insertClaimIntoTrie(normalized, claim, false));
insertUndo.emplace_back(it.key(), claim.outPoint, -1);
}
return true;
takeoverHeightUndo.emplace_back(it.key(), it->nHeightOfLastTakeover);
}
return false;
return true;
}
bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo,
insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo,
std::vector<std::pair<std::string, int> >& takeoverHeightUndo) {
overrideInsertNormalization = normalizeAllNamesInTrieIfNecessary(insertUndo, expireUndo, insertSupportUndo,
expireSupportUndo, takeoverHeightUndo);
BOOST_SCOPE_EXIT(&overrideInsertNormalization) { overrideInsertNormalization = false; } BOOST_SCOPE_EXIT_END
return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo,
expireSupportUndo, takeoverHeightUndo);
bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo)
{
overrideInsertNormalization = normalizeAllNamesInTrieIfNecessary(insertUndo, expireUndo, insertSupportUndo, 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,
claimQueueRowType& expireUndo,
insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo,
std::vector<std::pair<std::string, int> >& takeoverHeightUndo) {
bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
{
overrideRemoveNormalization = shouldNormalize();
BOOST_SCOPE_EXIT(&overrideRemoveNormalization) { overrideRemoveNormalization = false; } BOOST_SCOPE_EXIT_END
return CClaimTrieCacheExpirationFork::decrementBlock(insertUndo, expireUndo, insertSupportUndo,
expireSupportUndo, takeoverHeightUndo);
BOOST_SCOPE_EXIT(&overrideRemoveNormalization) { overrideRemoveNormalization = false; }
BOOST_SCOPE_EXIT_END
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);
}
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);
}
claimsForNameType CClaimTrieCacheNormalizationFork::getClaimsForName(const std::string& name) const {
CClaimsForNameType CClaimTrieCacheNormalizationFork::getClaimsForName(const std::string& name) const
{
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);
}
void CClaimTrieCacheNormalizationFork::addClaimToQueues(const std::string& name, CClaimValue& claim) const {
return CClaimTrieCacheExpirationFork::addClaimToQueues(normalizeClaimName(name,
claim.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), claim);
bool CClaimTrieCacheNormalizationFork::addClaimToQueues(const std::string& name, const CClaimValue& claim)
{
return CClaimTrieCacheExpirationFork::addClaimToQueues(normalizeClaimName(name, claim.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), claim);
}
bool CClaimTrieCacheNormalizationFork::addSupportToQueues(const std::string& name, CSupportValue& support) const {
return CClaimTrieCacheExpirationFork::addSupportToQueues(normalizeClaimName(name,
support.nValidAtHeight > Params().GetConsensus().nNormalizedNameForkHeight), support);
bool CClaimTrieCacheNormalizationFork::addSupportToQueues(const std::string& name, const CSupportValue& 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);
}

View file

@ -84,6 +84,10 @@ struct Params {
int nAllowMinDiffMinHeight;
int nAllowMinDiffMaxHeight;
int nNormalizedNameForkHeight;
int nMinTakeoverWorkaroundHeight;
int nMaxTakeoverWorkaroundHeight;
int64_t nPowTargetSpacing;
int64_t nPowTargetTimespan;
/** 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)
{
leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
auto write_cache = std::min(nCacheSize / 4, size_t(16) << 20U); // cap write_cache at 16MB (4x default)
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.compression = leveldb::kNoCompression;
options.info_log = new CBitcoinLevelDBLogger();

View file

@ -1534,12 +1534,6 @@ bool AppInitMain(InitInterfaces& interfaces)
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
// block tree into BlockIndex()!
@ -1587,6 +1581,13 @@ bool AppInitMain(InitInterfaces& interfaces)
break;
}
CClaimTrieCache trieCache(pclaimTrie);
if (!trieCache.ReadFromDisk(chainActive.Tip()))
{
strLoadError = _("Error loading the claim trie from disk");
break;
}
if (!fReset) {
// 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
@ -1765,7 +1766,7 @@ bool AppInitMain(InitInterfaces& interfaces)
LogPrintf("nBestHeight = %d\n", chain_active_height);
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))
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 <coins.h>
#include <core_io.h>
#include <logging.h>
#include <nameclaim.h>
#include <rpc/server.h>
#include <shutdown.h>
@ -9,6 +10,8 @@
#include <univalue.h>
#include <validation.h>
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/thread.hpp>
#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)
{
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)
throw std::runtime_error(
"getclaimsintrie\n"
@ -116,96 +184,79 @@ static UniValue getclaimsintrie(const JSONRPCRequest& request)
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);
CClaimsCallback claimsCallback(ret, coinsCache);
trieCache.iterateTrie(claimsCallback);
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())
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;
}
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(
"getclaimtrie\n"
"DEPRECATED. Return the entire claim trie.\n"
"getnamesintrie\n"
"Return all claim names in the trie.\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"
" by this block hash.\n"
" If none is given,\n"
" the latest active\n"
" block will be used.\n"
"Result: \n"
"[\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");
"\"names\" (array) all names in the trie that have claims\n");
LOCK(cs_main);
@ -217,76 +268,24 @@ static UniValue getclaimtrie(const JSONRPCRequest& request)
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);
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;
}
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)
{
if (request.fHelp || request.params.size() > 2)
if (request.fHelp || !validParams(request.params, 1, 1))
throw std::runtime_error(
"getvalueforname \"name\"\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;
CClaimValue targetClaim;
if (pclaimTrie->getClaimById(claim.claimId, targetName, targetClaim))
ret.push_back(Pair("name", targetName));
if (trieCache.getClaimById(claim.claimId, targetName, targetClaim))
ret.pushKV("name", escapeNonUtf8(targetName));
return ret;
}
@ -354,18 +353,18 @@ typedef std::map<uint160, claimAndSupportsType> claimSupportMapType;
UniValue supportToJSON(const CCoinsViewCache& coinsCache, const CSupportValue& support)
{
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("txid", support.outPoint.hash.GetHex()));
ret.push_back(Pair("n", (int)support.outPoint.n));
ret.push_back(Pair("nHeight", support.nHeight));
ret.push_back(Pair("nValidAtHeight", support.nValidAtHeight));
ret.push_back(Pair("nAmount", support.nAmount));
ret.pushKV("txid", support.outPoint.hash.GetHex());
ret.pushKV("n", (int)support.outPoint.n);
ret.pushKV("nHeight", support.nHeight);
ret.pushKV("nValidAtHeight", support.nValidAtHeight);
ret.pushKV("nAmount", support.nAmount);
std::string value;
if (getValueForOutPoint(coinsCache, support.outPoint, value))
ret.push_back(Pair("value", value));
ret.pushKV("value", value);
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 std::vector<CSupportValue>& supports = itClaimsAndSupports->second.second;
@ -389,15 +388,15 @@ UniValue claimAndSupportsToJSON(const CCoinsViewCache& coinsCache, CAmount nEffe
std::string targetName;
CClaimValue targetClaim;
if (pclaimTrie->getClaimById(claim.claimId, targetName, targetClaim))
result.push_back(Pair("name", targetName));
if (trieCache.getClaimById(claim.claimId, targetName, targetClaim))
result.pushKV("name", escapeNonUtf8(targetName));
return result;
}
UniValue getclaimsforname(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 2)
if (request.fHelp || !validParams(request.params, 1, 1))
throw std::runtime_error(
"getclaimsforname\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();
claimsForNameType claimsForName = trieCache.getClaimsForName(name);
auto claimsForName = trieCache.getClaimsForName(name);
UniValue claimObjs(UniValue::VARR);
claimSupportMapType claimSupportMap;
@ -479,13 +478,13 @@ UniValue getclaimsforname(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
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)
{
auto itClaimsAndSupports = claimSupportMap.find(itClaims->claimId);
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);
}
@ -496,7 +495,7 @@ UniValue getclaimsforname(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(
"getclaimbyid\n"
"Get a claim by claim id\n"
@ -526,21 +525,21 @@ UniValue getclaimbyid(const JSONRPCRequest& request)
"}\n");
LOCK(cs_main);
CClaimTrieCache trieCache(pclaimTrie);
uint160 claimId = ParseClaimtrieId(request.params[0], "Claim-id (parameter 1)");
UniValue claim(UniValue::VOBJ);
std::string name;
CClaimValue claimValue;
pclaimTrie->getClaimById(claimId, name, claimValue);
trieCache.getClaimById(claimId, name, claimValue);
if (claimValue.claimId == claimId)
{
std::vector<CSupportValue> supports;
CClaimTrieCache trieCache(pclaimTrie);
CAmount effectiveAmount = trieCache.getEffectiveAmountForClaim(name, claimValue.claimId, &supports);
std::string sValue;
claim.pushKV("name", name);
claim.pushKV("name", escapeNonUtf8(name));
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());
if (getValueForOutPoint(coinsCache, claimValue.outPoint, sValue))
claim.pushKV("value", sValue);
@ -570,7 +569,7 @@ UniValue getclaimbyid(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(
"gettotalclaimednames\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"
);
LOCK(cs_main);
if (!pclaimTrie)
{
return -1;
}
unsigned int num_names = pclaimTrie->getTotalNamesInTrie();
CClaimTrieCache trieCache(pclaimTrie);
auto num_names = trieCache.getTotalNamesInTrie();
return int(num_names);
}
UniValue gettotalclaims(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
if (request.fHelp || !validParams(request.params, 0, 0))
throw std::runtime_error(
"gettotalclaims\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"
);
LOCK(cs_main);
if (!pclaimTrie)
{
return -1;
}
unsigned int num_claims = pclaimTrie->getTotalClaimsInTrie();
CClaimTrieCache trieCache(pclaimTrie);
auto num_claims = trieCache.getTotalClaimsInTrie();
return int(num_claims);
}
UniValue gettotalvalueofclaims(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1)
if (request.fHelp || !validParams(request.params, 0, 1))
throw std::runtime_error(
"gettotalvalueofclaims\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"
);
LOCK(cs_main);
if (!pclaimTrie)
{
return -1;
}
bool controlling_only = false;
if (request.params.size() == 1)
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);
}
UniValue getclaimsfortx(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 1)
if (request.fHelp || !validParams(request.params, 1, 0))
throw std::runtime_error(
"getclaimsfortx\n"
"Return any claims or supports found in a transaction\n"
@ -669,6 +659,7 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
int op;
std::vector<std::vector<unsigned char> > vvchParams;
CClaimTrieCache trieCache(pclaimTrie);
CCoinsViewCache view(pcoinsTip.get());
const Coin& coin = AccessByTxid(view, hash);
std::vector<CTxOut> txouts{ coin.out };
@ -685,7 +676,7 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
{
o.pushKV("nOut", static_cast<int64_t>(i));
std::string sName(vvchParams[0].begin(), vvchParams[0].end());
o.pushKV("name", sName);
o.pushKV("name", escapeNonUtf8(sName));
if (op == OP_CLAIM_NAME)
{
uint160 claimId = ClaimIdHash(hash, i);
@ -710,12 +701,12 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
o.pushKV("depth", chainActive.Height() - nHeight);
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);
if (inClaimTrie)
{
CClaimValue claim;
if (!pclaimTrie->getInfoForName(sName, claim))
if (!trieCache.getInfoForName(sName, claim))
{
LogPrintf("HaveClaim was true but getInfoForName returned false.");
}
@ -724,7 +715,7 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
else
{
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("blocks to valid", nValidAtHeight - chainActive.Height());
@ -737,12 +728,12 @@ UniValue getclaimsfortx(const JSONRPCRequest& request)
}
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);
if (!inSupportMap)
{
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("blocks to valid", nValidAtHeight - chainActive.Height());
@ -811,7 +802,7 @@ UniValue proofToJSON(const CClaimTrieProof& proof)
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(
"getnameproof\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)
{
if (request.fHelp || request.params.size() != 1)
if (request.fHelp || !validParams(request.params, 1, 0))
throw std::runtime_error(
"checknormalization\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
// --------------------- ------------------------ ----------------------- ----------
{ "Claimtrie", "getclaimsintrie", &getclaimsintrie, { "blockhash" } },
{ "Claimtrie", "getclaimtrie", &getclaimtrie, { "blockhash" } },
{ "Claimtrie", "getnamesintrie", &getnamesintrie, { "blockhash" } },
{ "Claimtrie", "getclaimtrie", &getclaimtrie, { "" } },
{ "Claimtrie", "getvalueforname", &getvalueforname, { "name","blockhash" } },
{ "Claimtrie", "getclaimsforname", &getclaimsforname, { "name","blockhash" } },
{ "Claimtrie", "gettotalclaimednames", &gettotalclaimednames, { "" } },

File diff suppressed because it is too large Load diff

View file

@ -5,50 +5,41 @@
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <boost/scope_exit.hpp>
using namespace std;
class CClaimTrieCacheTest : public CClaimTrieCache {
class CClaimTrieCacheTest : public CClaimTrieCacheBase
{
public:
CClaimTrieCacheTest(CClaimTrie* base):
CClaimTrieCache(base, false){}
bool recursiveComputeMerkleHash(CClaimTrieNode* tnCurrent,
std::string sPos) const
explicit CClaimTrieCacheTest(CClaimTrie* base): CClaimTrieCacheBase(base, false)
{
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()
{
return cache.size();
return cache.height();
}
nodeCacheType::iterator getCache(std::string key)
CClaimTrie::iterator getCache(const std::string& 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)
@ -70,7 +61,6 @@ CMutableTransaction BuildTransaction(const uint256& prevhash)
BOOST_FIXTURE_TEST_SUITE(claimtriecache_tests, RegTestingSetup)
BOOST_AUTO_TEST_CASE(merkle_hash_single_test)
{
// check empty trie
@ -78,10 +68,9 @@ BOOST_AUTO_TEST_CASE(merkle_hash_single_test)
CClaimTrieCacheTest cc(pclaimTrie);
BOOST_CHECK_EQUAL(one, cc.getMerkleHash());
// check trie with only root node
CClaimTrieNode base_node;
cc.recursiveComputeMerkleHash(&base_node, "");
BOOST_CHECK_EQUAL(one, cc.getMerkleHash());
// we cannot have leaf root node
auto it = cc.getCache("");
BOOST_CHECK(!it);
}
BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
@ -117,100 +106,101 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ntState(pclaimTrie);
ntState.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200));
ntState.insertClaimIntoTrie(std::string("test2"), CClaimValue(tx2OutPoint, 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), true);
BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK(!ntState.empty());
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);
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);
ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CClaimValue(tx5OutPoint, hash160, 50, 100, 200));
ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5OutPoint, unused);
ntState.insertClaimIntoTrie(std::string("testtesttesttest"), CClaimValue(tx5OutPoint, hash160, 50, 100, 200), true);
ntState.removeClaimFromTrie(std::string("testtesttesttest"), tx5OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
ntState.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
BOOST_CHECK(ntState.checkConsistency(0));
CClaimTrieCacheTest ntState1(pclaimTrie);
ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused);
ntState1.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused);
ntState1.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused);
ntState1.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused);
ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState1.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState1.getMerkleHash(), hash0);
CClaimTrieCacheTest ntState2(pclaimTrie);
ntState2.insertClaimIntoTrie(std::string("abab"), CClaimValue(tx6OutPoint, hash160, 50, 100, 200));
ntState2.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused);
ntState2.insertClaimIntoTrie(std::string("abab"), CClaimValue(tx6OutPoint, hash160, 50, 100, 200), true);
ntState2.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3);
ntState2.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash3);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3);
BOOST_CHECK(ntState2.checkConsistency(0));
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);
ntState3.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash4);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState3.getMerkleHash(), hash4);
BOOST_CHECK(ntState3.checkConsistency(0));
CClaimTrieCacheTest ntState4(pclaimTrie);
ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint, unused);
ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2);
ntState4.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2);
BOOST_CHECK(ntState4.checkConsistency(0));
CClaimTrieCacheTest ntState5(pclaimTrie);
ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused);
ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2);
ntState5.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2);
BOOST_CHECK(ntState5.checkConsistency(0));
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);
ntState6.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState6.getMerkleHash(), hash2);
BOOST_CHECK(ntState6.checkConsistency(0));
CClaimTrieCacheTest ntState7(pclaimTrie);
ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused);
ntState7.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused);
ntState7.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused);
ntState7.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused);
ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("tes"), tx4OutPoint, unused, true);
ntState7.removeClaimFromTrie(std::string("test2"), tx2OutPoint, unused, true);
BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0);
ntState7.flush();
BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK_EQUAL(pclaimTrie->getMerkleHash(), hash0);
BOOST_CHECK(pclaimTrie->checkConsistency());
BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0);
BOOST_CHECK(ntState7.checkConsistency(0));
}
BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
{
// test basic claim insertions and that get methods retreives information properly
BOOST_CHECK_EQUAL(pclaimTrie->empty(), true);
BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ctc(pclaimTrie);
// create and insert claim
CClaimValue unused;
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
CMutableTransaction tx1 = BuildTransaction(hash0);
uint160 claimId = ClaimIdHash(tx1.GetHash(), 0);
@ -219,17 +209,18 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
int height = 0;
int validHeight = 0;
CClaimValue claimVal(claimOutPoint, claimId, amount, height, validHeight);
ctc.insertClaimIntoTrie("test", claimVal);
ctc.insertClaimIntoTrie("test", claimVal, true);
// 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[0], claimVal);
BOOST_CHECK_EQUAL(res.supports.size(), 0);
BOOST_CHECK_EQUAL(10, ctc.getEffectiveAmountForClaim("test", claimId));
CClaimValue claim;
BOOST_CHECK_EQUAL(ctc.getInfoForName("test", claim), true);
BOOST_CHECK(ctc.getInfoForName("test", claim));
BOOST_CHECK_EQUAL(claim, claimVal);
// insert a support
@ -241,6 +232,10 @@ BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
CSupportValue support(supportOutPoint, claimId, supportAmount, height, validHeight);
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
BOOST_CHECK_EQUAL(20, ctc.getEffectiveAmountForClaim("test", claimId));
}
@ -257,35 +252,42 @@ BOOST_AUTO_TEST_CASE(recursive_prune_test)
int validAtHeight = 0;
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.insertClaim(test_claim);
data.insertClaim(test_claim);
cc.insert("", std::move(data));
// node 1 has a claim so it should not be pruned
CClaimTrieNode node_1;
const char c = 't';
base_node.children[c] = &node_1;
node_1.insertClaim(test_claim);
data.insertClaim(test_claim);
// 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
// thus we should find pruned node 1 in cache
CClaimTrieNode node_2;
const char c_2 = 'e';
node_1.children[c_2] = &node_2;
cc.insert("te", CClaimTrieData{});
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());
nodeCacheType::iterator it = cc.getCache(std::string("t"));
BOOST_CHECK_EQUAL(10, it->second->nHeightOfLastTakeover);
BOOST_CHECK_EQUAL(1U, it->second->claims.size());
BOOST_CHECK_EQUAL(0U, it->second->children.size());
BOOST_CHECK(cc.erase(""));
BOOST_CHECK_EQUAL(0, cc.cacheSize());
}
BOOST_AUTO_TEST_CASE(iteratetrie_test)
{
BOOST_CHECK_EQUAL(pclaimTrie->empty(), true);
BOOST_CHECK(pclaimTrie->empty());
CClaimTrieCacheTest ctc(pclaimTrie);
uint256 hash0(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
@ -293,48 +295,108 @@ BOOST_AUTO_TEST_CASE(iteratetrie_test)
const uint256 txhash = tx1.GetHash();
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;
struct TestCallBack : public CNodeCallback
{
TestCallBack(int& count) : count(count)
{
std::size_t count = 0;
for (auto it = pclaimTrie->begin(); it != pclaimTrie->end(); ++it) {
++count;
if (it.key() == "test") {
BOOST_CHECK_EQUAL(it->claims.size(), 1);
}
}
BOOST_CHECK_EQUAL(count, 2);
void visit(const std::string& name, const CClaimTrieNode* node)
{
count++;
if (name == "test")
BOOST_CHECK_EQUAL(node->claims.size(), 1);
count = 0;
for (const auto& it: *pclaimTrie) {
++count;
if (it.first == "test") {
const CClaimTrieData& data = it.second;
BOOST_CHECK_EQUAL(data.claims.size(), 1);
}
}
BOOST_CHECK_EQUAL(count, 2);
int& count;
} testCallback(count);
auto it = pclaimTrie->find("test");
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_CHECK_EQUAL(count, 5);
BOOST_AUTO_TEST_CASE(trie_stays_consistent_test)
{
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
{
TestCallBack2(int& count) : count(count)
{
}
for (auto& name: names)
BOOST_CHECK(cache.insertClaimIntoTrie(name, value, false));
void visit(const std::string& name, const CClaimTrieNode* node)
{
if (--count <= 0)
throw CRecursionInterruptionException(false);
}
cache.flush();
BOOST_CHECK(cache.checkConsistency(0));
int& count;
} testCallback2(count);
for (auto& name: names) {
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_CHECK_EQUAL(count, 0);
BOOST_AUTO_TEST_CASE(takeover_workaround_triggers)
{
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()

View file

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

View file

@ -221,6 +221,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
{
BOOST_CHECK(!pblock->hashClaimTrie.IsNull());
LOCK(cs_main);
pblock->hashPrevBlock = chainActive.Tip()->GetBlockHash();
pblock->nVersion = 5;
@ -372,16 +373,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// subsidy changing
int nHeight = ::ChainActive().Height();
// 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) {
CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
next->hashClaimTrie = pblocktemplate->block.hashClaimTrie;
::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
trieCache.setBestBlock(next->GetBlockHash());
trieCache.flush();
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@ -393,9 +390,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* prev = ::ChainActive().Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
next->hashClaimTrie = pblocktemplate->block.hashClaimTrie;
::ChainstateActive().CoinsTip().SetBestBlock(next->GetBlockHash());
trieCache.setBestBlock(next->GetBlockHash());
trieCache.flush();
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@ -426,8 +422,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* del = ::ChainActive().Tip();
::ChainActive().SetTip(del->pprev);
::ChainstateActive().CoinsTip().SetBestBlock(del->pprev->GetBlockHash());
trieCache.setBestBlock(del->pprev->GetBlockHash());
trieCache.flush();
delete del->phashBlock;
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

@ -54,9 +54,9 @@ std::ostream& operator<<(std::ostream& os, const COutPoint& point)
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;
}
@ -71,6 +71,16 @@ std::ostream& operator<<(std::ostream& os, const CClaimValue& claim)
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)
: m_path_root(fs::temp_directory_path() / "test_common_" PACKAGE_NAME / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{

View file

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

View file

@ -34,7 +34,7 @@ static const int64_t nMaxDbCache = sizeof(void*) > 4 ? 16384 : 1024;
//! min. -dbcache (MiB)
static const int64_t nMinDbCache = 4;
//! 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)
// 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

View file

@ -1721,6 +1721,16 @@ int ApplyTxInUndo(unsigned int index, CTxUndo& txUndo, CCoinsViewCache& view, CC
} else {
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
@ -1750,7 +1760,11 @@ int ApplyTxInUndo(unsigned int index, CTxUndo& txUndo, CCoinsViewCache& view, CC
DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, CClaimTrieCache& trieCache)
{
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;
@ -1765,7 +1779,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
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);
// undo transactions in reverse order
@ -1821,14 +1835,25 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
// move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash());
assert(trieCache.finalizeDecrement());
trieCache.setBestBlock(pindex->pprev->GetBlockHash());
assert(trieCache.getMerkleHash() == pindex->pprev->hashClaimTrie);
assert(trieCache.finalizeDecrement(blockUndo.takeoverHeightUndo));
auto merkleHash = trieCache.getMerkleHash();
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)
{
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);
}
@ -2027,7 +2052,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
assert(hashPrevBlock == view.GetBestBlock());
// 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
// (its coinbase is unspendable)
@ -2035,7 +2064,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (!fJustCheck)
{
view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
}
/* return true; */
}
@ -2182,7 +2210,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (pindex->nHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
{
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);
}
@ -2334,15 +2362,17 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
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);
assert(incremented);
if (trieCache.getMerkleHash() != block.hashClaimTrie)
{
return state.DoS(100,
error("ConnectBlock() : the merkle root of the claim trie does not match "
"(actual=%s vs block=%s)", trieCache.getMerkleHash().GetHex(),
block.hashClaimTrie.GetHex()), REJECT_INVALID, "bad-claim-merkle-hash");
if (trieCache.checkConsistency())
trieCache.dumpToLog(trieCache.begin());
return state.DoS(100, error("ConnectBlock() : the merkle root of the claim trie does not match "
"(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;
@ -2376,7 +2406,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
assert(pindex->phashBlock);
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
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);
@ -2482,7 +2511,7 @@ bool CChainState::FlushStateToDisk(
if (!CheckDiskSpace(GetDataDir(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) {
return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
}
if (!pclaimTrie->WriteToDisk())
if (!pclaimTrie->SyncToDisk())
return state.Error("Failed to write to claim trie database");
// Flush the chainstate (which may refer to block index entries).
if (!CoinsTip().Flush())
@ -4548,7 +4577,6 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
}
cache.SetBestBlock(pindexNew->GetBlockHash());
trieCache.setBestBlock(pindexNew->GetBlockHash());
cache.Flush();
trieCache.flush();
uiInterface.ShowProgress("", 100, false);

View file

@ -536,6 +536,7 @@ UniValue abandonclaim(const JSONRPCRequest& request)
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,
UniValue& ret, const bool include_supports, bool list_spent)
@ -567,7 +568,7 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
continue;
}
std::string sName (vvchParams[0].begin(), vvchParams[0].end());
entry.pushKV("name", sName);
entry.pushKV("name", escapeNonUtf8(sName));
if (op == OP_CLAIM_NAME)
{
uint160 claimId = ClaimIdHash(wtx.GetHash(), s.vout);
@ -597,6 +598,9 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
entry.pushKV("amount", ValueFromAmount(s.amount));
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(nFee));
CClaimTrieCache trieCache(pclaimTrie);
auto it = mapBlockIndex.find(wtx.hashBlock);
if (it != mapBlockIndex.end())
{
@ -604,11 +608,11 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
if (pindex)
{
entry.pushKV("height", pindex->nHeight);
entry.pushKV("expiration height", pindex->nHeight + pclaimTrie->nExpirationTime);
if (pindex->nHeight + pclaimTrie->nExpirationTime > chainActive.Height())
entry.pushKV("expiration height", pindex->nHeight + trieCache.expirationTime());
if (pindex->nHeight + trieCache.expirationTime() > chainActive.Height())
{
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
{
@ -620,11 +624,11 @@ void ListNameClaims(const CWalletTx& wtx, CWallet* const pwallet, const std::str
entry.pushKV("is spent", pwallet->IsSpent(wtx.GetHash(), s.vout));
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)
{
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);
}
@ -1995,7 +1999,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
RPCResult{
"{\n"
" \"transactions\": [\n"
" \"address\":\"address\", (string) The bitcoin address of the transaction.\n"
" \"address\":\"address\", (string) The lbry address of the transaction.\n"
" \"category\": (string) The transaction category.\n"
" \"send\" Transactions sent.\n"
" \"receive\" Non-coinbase transactions received.\n"

View file

@ -35,14 +35,14 @@ class HelpTest(BitcoinTestFramework):
return out, err
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'])
# Node should exit immediately and output help to stdout.
output, _ = self.get_node_output(ret_code_expected=0)
assert b'Options' in output
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'])
# Node should exit immediately and output version to stdout.
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]))
# 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'])
# Node should exit immediately and output an error to stderr
_, output = self.get_node_output(ret_code_expected=1)