flattening prefix trie work

put getclaimsintrie back as deprecated


added test for adding a lot of data to the claimtrie


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

View file

@ -59,12 +59,13 @@ def get_obj_from_dirty_text(full_object: str):
last_name = property_name
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()

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

@ -71,6 +71,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 \
@ -151,7 +152,6 @@ test_test_lbrycrd_fuzzy_LDADD = \
$(LIBSECP256K1)
test_test_lbrycrd_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(ICU_LIBS)
#
nodist_test_test_lbrycrd_SOURCES = $(GENERATED_TEST_FILES)

View file

@ -143,6 +143,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
@ -258,6 +260,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
@ -361,6 +365,8 @@ public:
consensus.nAllowMinDiffMinHeight = -1;
consensus.nAllowMinDiffMaxHeight = -1;
consensus.nNormalizedNameForkHeight = 250; // SDK depends upon this number
consensus.nMinTakeoverWorkaroundHeight = -1;
consensus.nMaxTakeoverWorkaroundHeight = -1;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

@ -1503,12 +1503,6 @@ bool AppInitMain()
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 mapBlockIndex!
@ -1541,6 +1535,13 @@ bool AppInitMain()
assert(chainActive.Tip() != nullptr);
}
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
@ -1702,7 +1703,7 @@ bool AppInitMain()
LogPrintf("nBestHeight = %d\n", chain_active_height);
const Consensus::Params& consensusParams = Params().GetConsensus();
pclaimTrie->setExpirationTime(consensusParams.GetExpirationTime(chain_active_height));
CClaimTrieCache(pclaimTrie).setExpirationTime(consensusParams.GetExpirationTime(chain_active_height));
if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl();

492
src/prefixtrie.cpp Normal file
View file

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

196
src/prefixtrie.h Normal file
View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -222,6 +222,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
{
BOOST_CHECK(!pblock->hashClaimTrie.IsNull());
LOCK(cs_main);
pblock->hashPrevBlock = chainActive.Tip()->GetBlockHash();
pblock->nVersion = 5;
@ -373,16 +374,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;
pcoinsTip->SetBestBlock(next->GetBlockHash());
trieCache.setBestBlock(next->GetBlockHash());
trieCache.flush();
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@ -394,9 +391,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;
pcoinsTip->SetBestBlock(next->GetBlockHash());
trieCache.setBestBlock(next->GetBlockHash());
trieCache.flush();
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
@ -427,8 +423,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* del = chainActive.Tip();
chainActive.SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
trieCache.setBestBlock(del->pprev->GetBlockHash());
trieCache.flush();
delete del->phashBlock;
delete del;
}

View file

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

View file

@ -60,9 +60,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;
}
@ -77,6 +77,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_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{

View file

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

View file

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

View file

@ -1416,7 +1416,6 @@ static bool AbortNode(const std::string& strMessage, const std::string& userMess
{
SetMiscWarning(strMessage);
LogPrintf("*** %s\n", strMessage);
throw "nasty";
uiInterface.ThreadSafeMessageBox(
userMessage.empty() ? "Error: A fatal internal error occurred, see debug.log for details. System message: " + strMessage : userMessage,
"", CClientUIInterface::MSG_ERROR);
@ -1457,6 +1456,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
@ -1486,7 +1495,11 @@ int ApplyTxInUndo(unsigned int index, CTxUndo& txUndo, CCoinsViewCache& view, CC
DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, CClaimTrieCache& trieCache)
{
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;
@ -1501,7 +1514,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
@ -1557,14 +1570,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);
}
@ -1776,7 +1800,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)
@ -1784,7 +1812,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (!fJustCheck)
{
view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
}
/* return true; */
}
@ -1926,7 +1953,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);
}
@ -2059,15 +2086,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;
@ -2101,7 +2130,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);
@ -2208,7 +2236,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space");
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 (!pcoinsTip->Flush())
@ -4183,7 +4211,6 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
}
cache.SetBestBlock(pindexNew->GetBlockHash());
trieCache.setBestBlock(pindexNew->GetBlockHash());
cache.Flush();
trieCache.flush();
uiInterface.ShowProgress("", 100, false);

View file

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

View file

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