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:
parent
6824576189
commit
5f1c22680e
27 changed files with 8995 additions and 6080 deletions
|
@ -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
|
||||
|
||||
|
@ -274,4 +288,4 @@ def write_api():
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
write_api()
|
||||
write_api()
|
||||
|
|
|
@ -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
|
@ -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 \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
3645
src/claimtrie.cpp
3645
src/claimtrie.cpp
File diff suppressed because it is too large
Load diff
832
src/claimtrie.h
832
src/claimtrie.h
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
|
|
15
src/init.cpp
15
src/init.cpp
|
@ -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
492
src/prefixtrie.cpp
Normal 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
196
src/prefixtrie.h
Normal 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
|
|
@ -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"
|
||||
|
@ -668,7 +658,8 @@ 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
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
221
src/test/prefixtrie_tests.cpp
Normal file
221
src/test/prefixtrie_tests.cpp
Normal 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()
|
|
@ -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))))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue