Use memory mapped file for claim data allocations

Signed-off-by: Anthony Fieroni <bvbfan@abv.bg>

match previous serialization


tweaks


added check for RC's data
This commit is contained in:
Brannon King 2019-09-30 12:13:05 -06:00
parent 8932d90a9e
commit 7afebb02f4
15 changed files with 473 additions and 521 deletions

View file

@ -208,15 +208,15 @@ COptional<const std::vector<queueEntryType<T>>> CClaimTrieCacheBase::getQueueCac
}
template <>
queueNameRowType* CClaimTrieCacheBase::getQueueCacheNameRow<CClaimValue>(const std::string& name, bool createIfNotExists)
queueNameRowType* CClaimTrieCacheBase::getQueueCacheNameRow<CClaimValue>(const std::string& name, bool createIfNoExists)
{
return getQueue(*(base->db), CLAIM_QUEUE_NAME_ROW, name, claimQueueNameCache, createIfNotExists);
return getQueue(*(base->db), CLAIM_QUEUE_NAME_ROW, name, claimQueueNameCache, createIfNoExists);
}
template <>
queueNameRowType* CClaimTrieCacheBase::getQueueCacheNameRow<CSupportValue>(const std::string& name, bool createIfNotExists)
queueNameRowType* CClaimTrieCacheBase::getQueueCacheNameRow<CSupportValue>(const std::string& name, bool createIfNoExists)
{
return getQueue(*(base->db), SUPPORT_QUEUE_NAME_ROW, name, supportQueueNameCache, createIfNotExists);
return getQueue(*(base->db), SUPPORT_QUEUE_NAME_ROW, name, supportQueueNameCache, createIfNoExists);
}
template <typename T>
@ -246,15 +246,15 @@ COptional<const queueNameRowType> CClaimTrieCacheBase::getQueueCacheNameRow(cons
}
template <>
expirationQueueRowType* CClaimTrieCacheBase::getExpirationQueueCacheRow<CClaimValue>(int nHeight, bool createIfNotExists)
expirationQueueRowType* CClaimTrieCacheBase::getExpirationQueueCacheRow<CClaimValue>(int nHeight, bool createIfNoExists)
{
return getQueue(*(base->db), CLAIM_EXP_QUEUE_ROW, nHeight, expirationQueueCache, createIfNotExists);
return getQueue(*(base->db), CLAIM_EXP_QUEUE_ROW, nHeight, expirationQueueCache, createIfNoExists);
}
template <>
expirationQueueRowType* CClaimTrieCacheBase::getExpirationQueueCacheRow<CSupportValue>(int nHeight, bool createIfNotExists)
expirationQueueRowType* CClaimTrieCacheBase::getExpirationQueueCacheRow<CSupportValue>(int nHeight, bool createIfNoExists)
{
return getQueue(*(base->db), SUPPORT_EXP_QUEUE_ROW, nHeight, supportExpirationQueueCache, createIfNotExists);
return getQueue(*(base->db), SUPPORT_EXP_QUEUE_ROW, nHeight, supportExpirationQueueCache, createIfNoExists);
}
template <typename T>
@ -266,13 +266,8 @@ expirationQueueRowType* CClaimTrieCacheBase::getExpirationQueueCacheRow(int, boo
bool CClaimTrieCacheBase::haveClaim(const std::string& name, const COutPoint& outPoint) const
{
auto it = nodesToAddOrUpdate.find(name);
if (it && it->haveClaim(outPoint))
return true;
if (it || nodesToDelete.count(name))
return false;
CClaimTrieData data;
return base->find(name, data) && data.haveClaim(outPoint);
auto it = find(name);
return it && it->haveClaim(outPoint);
}
bool CClaimTrieCacheBase::haveSupport(const std::string& name, const COutPoint& outPoint) const
@ -325,70 +320,39 @@ bool CClaimTrieCacheBase::haveSupportInQueue(const std::string& name, const COut
return haveInQueue<CSupportValue>(name, outPoint, nValidAtHeight);
}
void CClaimTrie::recurseNodes(const std::string& name, const CClaimTrieDataNode& current, std::function<recurseNodesCB> function) const {
CClaimTrieData data;
find(name, data);
data.hash = current.hash;
data.flags |= current.children.empty() ? 0 : CClaimTrieDataFlags::POTENTIAL_CHILDREN;
function(name, data, current.children);
for (auto& child: current.children) {
CClaimTrieDataNode node;
auto childName = name + child;
if (find(childName, node))
recurseNodes(childName, node, function);
}
}
std::size_t CClaimTrie::getTotalNamesInTrie() const
{
std::size_t count = 0;
CClaimTrieDataNode node;
if (find({}, node))
recurseNodes({}, node, [&count](const std::string &name, const CClaimTrieData &data, const std::vector<std::string>& children) {
count += !data.empty();
});
for (auto it = begin(); it != end(); ++it)
if (!it->empty()) ++count;
return count;
}
std::size_t CClaimTrie::getTotalClaimsInTrie() const
{
std::size_t count = 0;
CClaimTrieDataNode node;
if (find({}, node))
recurseNodes({}, node, [&count]
(const std::string &name, const CClaimTrieData &data, const std::vector<std::string>& children) {
count += data.claims.size();
});
for (auto it = begin(); it != end(); ++it)
count += it->claims.size();
return count;
}
CAmount CClaimTrie::getTotalValueOfClaimsInTrie(bool fControllingOnly) const
{
CAmount value_in_subtrie = 0;
CClaimTrieDataNode node;
if (find({}, node))
recurseNodes({}, node, [&value_in_subtrie, fControllingOnly]
(const std::string &name, const CClaimTrieData &data, const std::vector<std::string>& children) {
for (const auto &claim : data.claims) {
for (auto it = begin(); it != end(); ++it) {
for (auto& claim : it->claims) {
value_in_subtrie += claim.nAmount;
if (fControllingOnly)
break;
}
});
}
return value_in_subtrie;
}
bool CClaimTrieCacheBase::getInfoForName(const std::string& name, CClaimValue& claim) const
{
auto it = nodesToAddOrUpdate.find(name);
if (it && it->getBestClaim(claim))
return true;
if (it || nodesToDelete.count(name))
return false;
CClaimTrieData claims;
return base->find(name, claims) && claims.getBestClaim(claim);
auto it = find(name);
return it && it->getBestClaim(claim);
}
template <typename T>
@ -410,15 +374,9 @@ CClaimSupportToName CClaimTrieCacheBase::getClaimsForName(const std::string& nam
auto supports = getSupportsForName(name);
insertRowsFromQueue(supports, name);
if (auto it = nodesToAddOrUpdate.find(name)) {
if (auto it = find(name)) {
claims = it->claims;
nLastTakeoverHeight = it->nHeightOfLastTakeover;
} else if (!nodesToDelete.count(name)) {
CClaimTrieData data;
if (base->find(name, data)) {
claims = data.claims;
nLastTakeoverHeight = data.nHeightOfLastTakeover;
}
}
insertRowsFromQueue(claims, name);
@ -453,94 +411,79 @@ void completeHash(uint256& partialHash, const std::string& key, std::size_t to)
.Finalize(partialHash.begin());
}
bool CClaimTrie::checkConsistency(const uint256& rootHash) const
template <typename T>
using iCbType = std::function<void(T&)>;
template <typename TIterator>
uint256 recursiveMerkleHash(TIterator& it, const iCbType<TIterator>& process)
{
CClaimTrieDataNode node;
if (!find({}, node) || node.hash != rootHash) {
if (rootHash == one)
return true;
return error("Mismatched root claim trie hashes. This may happen when there is not a clean process shutdown. Please run with -reindex.");
}
bool success = true;
recurseNodes({}, node, [&success, this](const std::string &name, const CClaimTrieData &data, const std::vector<std::string>& children) {
if (!success) return;
std::vector<uint8_t> vchToHash;
const auto pos = name.size();
for (auto &child : children) {
auto key = name + child;
CClaimTrieDataNode node;
success &= find(key, node);
auto hash = node.hash;
const auto pos = it.key().size();
for (auto& child : it.children()) {
process(child);
auto& key = child.key();
auto hash = child->hash;
completeHash(hash, key, pos);
vchToHash.push_back(key[pos]);
vchToHash.insert(vchToHash.end(), hash.begin(), hash.end());
}
CClaimValue claim;
if (data.getBestClaim(claim)) {
uint256 valueHash = getValueHash(claim.outPoint, data.nHeightOfLastTakeover);
if (it->getBestClaim(claim)) {
uint256 valueHash = getValueHash(claim.outPoint, it->nHeightOfLastTakeover);
vchToHash.insert(vchToHash.end(), valueHash.begin(), valueHash.end());
} else {
success &= !children.empty(); // we disallow leaf nodes without claims
}
success &= data.hash == Hash(vchToHash.begin(), vchToHash.end());
});
return success;
} else if (!it.hasChildren()) {
return {};
}
std::vector<std::pair<std::string, CClaimTrieDataNode>> CClaimTrie::nodes(const std::string &key) const {
std::vector<std::pair<std::string, CClaimTrieDataNode>> ret;
CClaimTrieDataNode node;
if (!find({}, node))
return ret;
ret.emplace_back(std::string{}, node);
std::string partialKey = key;
while (!node.children.empty()) {
// auto it = node.children.lower_bound(partialKey); // for using a std::map
auto it = std::lower_bound(node.children.begin(), node.children.end(), partialKey);
if (it != node.children.end() && *it == partialKey) {
// we're completely done
if (find(key, node))
ret.emplace_back(key, node);
break;
}
if (it != node.children.begin()) --it;
const auto count = match(partialKey, *it);
if (count != it->size()) break;
if (count == partialKey.size()) break;
partialKey = partialKey.substr(count);
auto frontKey = key.substr(0, key.size() - partialKey.size());
if (find(frontKey, node))
ret.emplace_back(frontKey, node);
else break;
return Hash(vchToHash.begin(), vchToHash.end());
}
return ret;
bool CClaimTrieCacheBase::recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const
{
struct CRecursiveBreak {};
using iterator = CClaimTrie::const_iterator;
iCbType<iterator> process = [&failed, &process](iterator& it) {
if (it->hash.IsNull() || it->hash != recursiveMerkleHash(it, process)) {
failed = it.key();
throw CRecursiveBreak();
}
};
try {
process(it);
} catch (const CRecursiveBreak&) {
return false;
}
return true;
}
bool CClaimTrie::contains(const std::string &key) const {
return db->Exists(std::make_pair(TRIE_NODE_CHILDREN, key));
}
bool CClaimTrieCacheBase::checkConsistency() const
{
if (base->empty())
return true;
bool CClaimTrie::empty() const {
return !contains({});
auto it = base->cbegin();
std::string failed;
auto consistent = recursiveCheckConsistency(it, failed);
if (!consistent) {
LogPrintf("\nPrinting base tree from its parent:\n");
auto basePath = base->nodes(failed);
if (basePath.size() > 1) basePath.pop_back();
dumpToLog(basePath.back(), false);
auto cachePath = nodesToAddOrUpdate.nodes(failed);
if (!cachePath.empty()) {
LogPrintf("\nPrinting %s's parent from cache:\n", failed);
if (cachePath.size() > 1) cachePath.pop_back();
dumpToLog(cachePath.back(), false);
}
bool CClaimTrie::find(const std::string& key, CClaimTrieDataNode &node) const {
return db->Read(std::make_pair(TRIE_NODE_CHILDREN, key), node);
if (!nodesToDelete.empty()) {
std::string joined;
for (const auto &piece : nodesToDelete) joined += ", " + piece;
LogPrintf("Nodes to be deleted: %s\n", joined.substr(2));
}
bool CClaimTrie::find(const std::string& key, CClaimTrieData &data) const {
return db->Read(std::make_pair(TRIE_NODE_CLAIMS, key), data);
}
return consistent;
}
template <typename K, typename T>
@ -579,26 +522,24 @@ bool CClaimTrieCacheBase::flush()
getMerkleHash();
for (const auto& nodeName : nodesToDelete) {
if (nodesToAddOrUpdate.contains(nodeName))
continue;
auto nodes = base->nodes(nodeName);
base->erase(nodeName);
for (auto& node : nodes)
if (!node)
batch.Erase(std::make_pair(TRIE_NODE, node.key()));
}
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it) {
bool removed = forDeleteFromBase.erase(it.key());
if (it->flags & CClaimTrieDataFlags::HASH_DIRTY) {
CClaimTrieDataNode node;
node.hash = it->hash;
for (auto &child: it.children()) // ordering here is important
node.children.push_back(child.key().substr(it.key().size()));
batch.Write(std::make_pair(TRIE_NODE_CHILDREN, it.key()), node);
if (removed || (it->flags & CClaimTrieDataFlags::CLAIMS_DIRTY))
batch.Write(std::make_pair(TRIE_NODE_CLAIMS, it.key()), it.data());
auto old = base->find(it.key());
if (!old || old.data() != it.data()) {
base->copy(it);
batch.Write(std::make_pair(TRIE_NODE, it.key()), it.data());
}
}
for (auto& name: forDeleteFromBase) {
batch.Erase(std::make_pair(TRIE_NODE_CHILDREN, name));
batch.Erase(std::make_pair(TRIE_NODE_CLAIMS, name));
}
BatchWriteQueue(batch, SUPPORT, supportCache);
BatchWriteQueue(batch, CLAIM_QUEUE_ROW, claimQueueCache);
@ -620,32 +561,70 @@ bool CClaimTrieCacheBase::flush()
return ret;
}
bool CClaimTrieCacheBase::validateTrieConsistency(const CBlockIndex* tip)
bool CClaimTrieCacheBase::ReadFromDisk(const CBlockIndex* tip)
{
if (!tip || tip->nHeight < 1)
return true;
LogPrintf("Loading the claim trie from disk...\n");
base->nNextHeight = nNextHeight = tip ? tip->nHeight + 1 : 0;
if (tip && base->db->Exists(std::make_pair(TRIE_NODE_CHILDREN, std::string()))) {
LogPrintf("The claim trie database contains deprecated data and will need to be rebuilt.\n");
return false;
}
clear();
base->clear();
boost::scoped_ptr<CDBIterator> pcursor(base->db->NewIterator());
for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
std::pair<uint8_t, std::string> key;
if (!pcursor->GetKey(key) || key.first != TRIE_NODE)
continue;
CClaimTrieData data;
if (pcursor->GetValue(data)) {
if (data.empty()) {
// we have a situation where our old trie had many empty nodes
// we don't want to automatically throw those all into our prefix trie
// we'll run a second pass to clean them up
continue;
}
// nEffectiveAmount isn't serialized but it needs to be initialized (as done in reorderClaims):
auto supports = getSupportsForName(key.second);
data.reorderClaims(supports);
base->insert(key.second, std::move(data));
} else {
return error("%s(): error reading claim trie from disk", __func__);
}
}
for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
std::pair<uint8_t, std::string> key;
if (!pcursor->GetKey(key) || key.first != TRIE_NODE)
continue;
auto hit = base->find(key.second);
if (hit) {
CClaimTrieData data;
if (pcursor->GetValue(data))
hit->hash = data.hash;
}
else {
base->db->Erase(key); // this uses a lot of memory and it's 1-time upgrade from 12.4 so we aren't going to batch it
}
}
LogPrintf("Checking claim trie consistency... ");
if (base->checkConsistency(tip->hashClaimTrie)) {
if (checkConsistency()) {
LogPrintf("consistent\n");
if (tip && tip->hashClaimTrie != getMerkleHash())
return error("%s(): hashes don't match when reading claimtrie from disk", __func__);
return true;
}
LogPrintf("inconsistent!\n");
return false;
}
bool CClaimTrieCacheBase::ReadFromDisk(const CBlockIndex* tip)
{
base->nNextHeight = nNextHeight = tip ? tip->nHeight + 1 : 0;
clear();
if (tip && base->db->Exists(std::make_pair(TRIE_NODE, std::string()))) {
LogPrintf("The claim trie database contains deprecated data and will need to be rebuilt\n");
return false;
}
return validateTrieConsistency(tip);
}
CClaimTrieCacheBase::CClaimTrieCacheBase(CClaimTrie* base) : base(base)
{
assert(base);
@ -657,92 +636,65 @@ int CClaimTrieCacheBase::expirationTime() const
return Params().GetConsensus().nOriginalClaimExpirationTime;
}
uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(CClaimPrefixTrie::iterator& it)
uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(CClaimTrie::iterator& it)
{
if (!it->hash.IsNull())
using iterator = CClaimTrie::iterator;
iCbType<iterator> process = [&process](iterator& it) {
if (it->hash.IsNull())
it->hash = recursiveMerkleHash(it, process);
assert(!it->hash.IsNull());
};
process(it);
return it->hash;
std::vector<uint8_t> vchToHash;
const auto pos = it.key().size();
for (auto& child : it.children()) {
auto hash = recursiveComputeMerkleHash(child);
auto& key = child.key();
completeHash(hash, key, pos);
vchToHash.push_back(key[pos]);
vchToHash.insert(vchToHash.end(), hash.begin(), hash.end());
}
CClaimValue claim;
if (it->getBestClaim(claim)) {
uint256 valueHash = getValueHash(claim.outPoint, it->nHeightOfLastTakeover);
vchToHash.insert(vchToHash.end(), valueHash.begin(), valueHash.end());
}
return it->hash = Hash(vchToHash.begin(), vchToHash.end());
}
uint256 CClaimTrieCacheBase::getMerkleHash()
{
if (auto it = nodesToAddOrUpdate.begin())
return recursiveComputeMerkleHash(it);
if (nodesToDelete.empty() && nodesAlreadyCached.empty()) {
CClaimTrieDataNode node;
if (base->find({}, node))
return node.hash; // it may be valuable to have base cache its current root hash
}
return one; // we have no data or we deleted everything
auto it = nodesToAddOrUpdate.begin();
if (!it && nodesToDelete.empty())
it = base->begin();
return !it ? one : recursiveComputeMerkleHash(it);
}
CClaimPrefixTrie::const_iterator CClaimTrieCacheBase::begin() const
CClaimTrie::const_iterator CClaimTrieCacheBase::find(const std::string& name) const
{
return nodesToAddOrUpdate.begin();
}
CClaimPrefixTrie::const_iterator CClaimTrieCacheBase::end() const
{
return nodesToAddOrUpdate.end();
auto it = nodesToAddOrUpdate.find(name);
if (it || nodesToDelete.count(name))
return it;
return base->find(name);
}
bool CClaimTrieCacheBase::empty() const
{
return nodesToAddOrUpdate.empty();
return nodesToAddOrUpdate.empty(); // only used with the dump method, and we don't want to dump base
}
CClaimPrefixTrie::iterator CClaimTrieCacheBase::cacheData(const std::string& name, bool create)
CClaimTrie::iterator CClaimTrieCacheBase::cacheData(const std::string& name, bool create)
{
// get data from the cache. if no data, create empty one
const auto insert = [this](CClaimTrie::iterator& it) {
auto& key = it.key();
// we only ever cache nodes once per cache instance
if (!nodesAlreadyCached.count(key)) {
// do not insert nodes that are already present
nodesAlreadyCached.insert(key);
nodesToAddOrUpdate.insert(key, it.data());
}
};
// we need all parent nodes and their one level deep children
// to calculate merkle hash
auto nodes = base->nodes(name);
for (auto& node: nodes) {
if (nodesAlreadyCached.insert(node.first).second) {
// do not insert nodes that are already present
CClaimTrieData data;
base->find(node.first, data);
data.hash = node.second.hash;
data.flags = node.second.children.empty() ? 0 : CClaimTrieDataFlags::POTENTIAL_CHILDREN;
nodesToAddOrUpdate.insert(node.first, data);
}
for (auto& child : node.second.children) {
auto childKey = node.first + child;
if (nodesAlreadyCached.insert(childKey).second) {
CClaimTrieData childData;
if (!base->find(childKey, childData))
childData = {};
CClaimTrieDataNode childNode;
if (base->find(childKey, childNode)) {
childData.hash = childNode.hash;
childData.flags = childNode.children.empty() ? 0 : CClaimTrieDataFlags::POTENTIAL_CHILDREN;
}
nodesToAddOrUpdate.insert(childKey, childData);
}
}
for (auto& child : node.children())
if (!nodesAlreadyCached.count(child.key()))
nodesToAddOrUpdate.copy(child);
insert(node);
}
auto it = nodesToAddOrUpdate.find(name);
if (!it && create) {
it = nodesToAddOrUpdate.insert(name, CClaimTrieData{});
// if (it.hasChildren()) any children should be in the trie (not base alone)
// it->flags |= CClaimTrieDataFlags::POTENTIAL_CHILDREN;
confirmTakeoverWorkaroundNeeded(name);
}
@ -763,11 +715,10 @@ bool CClaimTrieCacheBase::getLastTakeoverForName(const std::string& name, uint16
std::tie(claimId, takeoverHeight) = cit->second;
return true;
}
CClaimTrieData data;
if (base->find(name, data)) {
takeoverHeight = data.nHeightOfLastTakeover;
if (auto it = base->find(name)) {
takeoverHeight = it->nHeightOfLastTakeover;
CClaimValue claim;
if (data.getBestClaim(claim)) {
if (it->getBestClaim(claim)) {
claimId = claim.claimId;
return true;
}
@ -777,12 +728,8 @@ bool CClaimTrieCacheBase::getLastTakeoverForName(const std::string& name, uint16
void CClaimTrieCacheBase::markAsDirty(const std::string& name, bool fCheckTakeover)
{
for (auto& node : nodesToAddOrUpdate.nodes(name)) {
node->flags |= CClaimTrieDataFlags::HASH_DIRTY;
for (auto& node : nodesToAddOrUpdate.nodes(name))
node->hash.SetNull();
if (node.key() == name)
node->flags |= CClaimTrieDataFlags::CLAIMS_DIRTY;
}
if (fCheckTakeover)
namesToCheckForTakeover.insert(name);
@ -816,9 +763,6 @@ bool CClaimTrieCacheBase::removeClaimFromTrie(const std::string& name, const COu
for (auto& child: it.children())
cacheData(child.key(), false);
for (auto& node : nodesToAddOrUpdate.nodes(name))
forDeleteFromBase.emplace(node.key());
nodesToAddOrUpdate.erase(name);
nodesToDelete.insert(name);
@ -1055,13 +999,11 @@ bool CClaimTrieCacheBase::removeSupportFromMap(const std::string& name, const CO
return false;
}
void CClaimTrieCacheBase::dumpToLog(CClaimPrefixTrie::const_iterator it, bool diffFromBase) const
void CClaimTrieCacheBase::dumpToLog(CClaimTrie::const_iterator it, bool diffFromBase) const
{
if (!it) return;
if (diffFromBase) {
CClaimTrieDataNode node;
if (base->find(it.key(), node) && node.hash == it->hash)
auto hit = base->find(it.key());
if (hit && hit->hash == it->hash)
return;
}
@ -1375,12 +1317,8 @@ int CClaimTrieCacheBase::getNumBlocksOfContinuousOwnership(const std::string& na
that->removalWorkaround.erase(hit);
return 0;
}
if (auto it = nodesToAddOrUpdate.find(name))
return it->empty() ? 0 : nNextHeight - it->nHeightOfLastTakeover;
CClaimTrieData data;
if (base->find(name, data) && !data.empty())
return nNextHeight - data.nHeightOfLastTakeover;
return 0;
auto it = nodesToAddOrUpdate.find(name);
return (it || (it = base->find(name))) && !it->empty() ? nNextHeight - it->nHeightOfLastTakeover : 0;
}
int CClaimTrieCacheBase::getDelayForName(const std::string& name) const
@ -1407,23 +1345,22 @@ std::string CClaimTrieCacheBase::adjustNameForValidHeight(const std::string& nam
bool CClaimTrieCacheBase::clear()
{
forDeleteFromBase.clear();
nodesToAddOrUpdate.clear();
claimsToAddToByIdIndex.clear();
supportCache.clear();
nodesToDelete.clear();
claimsToDeleteFromByIdIndex.clear();
takeoverCache.clear();
claimQueueCache.clear();
supportQueueCache.clear();
removalWorkaround.clear();
nodesToAddOrUpdate.clear();
nodesAlreadyCached.clear();
takeoverWorkaround.clear();
removalWorkaround.clear();
claimQueueNameCache.clear();
expirationQueueCache.clear();
supportQueueNameCache.clear();
claimsToAddToByIdIndex.clear();
namesToCheckForTakeover.clear();
supportExpirationQueueCache.clear();
claimsToDeleteFromByIdIndex.clear();
return true;
}
@ -1433,7 +1370,7 @@ bool CClaimTrieCacheBase::getProofForName(const std::string& name, CClaimTriePro
cacheData(name, false);
getMerkleHash();
proof = CClaimTrieProof();
for (auto& it : static_cast<const CClaimPrefixTrie&>(nodesToAddOrUpdate).nodes(name)) {
for (auto& it : static_cast<const CClaimTrie&>(nodesToAddOrUpdate).nodes(name)) {
CClaimValue claim;
const auto& key = it.key();
bool fNodeHasValue = it->getBestClaim(claim);
@ -1449,7 +1386,7 @@ bool CClaimTrieCacheBase::getProofForName(const std::string& name, CClaimTriePro
for (auto i = pos; i + 1 < childKey.size(); ++i) {
children.emplace_back(childKey[i], uint256{});
proof.nodes.emplace_back(children, fNodeHasValue, valueHash);
children.clear(); // move promises to leave it in a valid state only
children.clear();
valueHash.SetNull();
fNodeHasValue = false;
}
@ -1473,33 +1410,22 @@ bool CClaimTrieCacheBase::getProofForName(const std::string& name, CClaimTriePro
return true;
}
void CClaimTrieCacheBase::recurseNodes(const std::string &name,
std::function<void(const std::string &, const CClaimTrieData &)> function) const {
std::function<CClaimTrie::recurseNodesCB> baseFunction = [this, &function]
(const std::string& name, const CClaimTrieData& data, const std::vector<std::string>&) {
if (nodesToDelete.find(name) == nodesToDelete.end())
function(name, data);
};
if (empty()) {
CClaimTrieDataNode node;
if (base->find(name, node))
base->recurseNodes(name, node, baseFunction);
}
else {
for (auto it = begin(); it != end(); ++it) {
function(it.key(), it.data());
if ((it->flags & CClaimTrieDataFlags::POTENTIAL_CHILDREN) && !it.hasChildren()) {
CClaimTrieDataNode node;
if (base->find(it.key(), node))
for (auto& partialKey: node.children) {
auto childKey = it.key() + partialKey;
CClaimTrieDataNode childNode;
if (base->find(childKey, childNode))
base->recurseNodes(childKey, childNode, baseFunction);
}
}
void CClaimTrieCacheBase::iterate(std::function<void(const std::string&, const CClaimTrieData&)> callback) const
{
if (nodesToAddOrUpdate.empty()) {
for (auto it = base->cbegin(); it != base->cend(); ++it)
if (!nodesToDelete.count(it.key()))
callback(it.key(), it.data());
return;
}
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it) {
callback(it.key(), it.data());
if (it.hasChildren() || nodesToDelete.count(it.key()))
continue;
auto children = base->find(it.key()).children();
for (auto& child : children)
for (; child; ++child)
if (!nodesToDelete.count(child.key()))
callback(child.key(), child.data());
}
}

View file

@ -18,9 +18,8 @@
#include <unordered_set>
// leveldb keys
#define TRIE_NODE 'n' // deprecated
#define TRIE_NODE 'n'
#define TRIE_NODE_CHILDREN 'b'
#define TRIE_NODE_CLAIMS 'c'
#define CLAIM_BY_ID 'i'
#define CLAIM_QUEUE_ROW 'r'
#define CLAIM_QUEUE_NAME_ROW 'm'
@ -63,7 +62,6 @@ struct CClaimValue
READWRITE(nAmount);
READWRITE(nHeight);
READWRITE(nValidAtHeight);
READWRITE(nEffectiveAmount);
}
bool operator<(const CClaimValue& other) const
@ -136,21 +134,12 @@ struct CSupportValue
typedef std::vector<CClaimValue> claimEntryType;
typedef std::vector<CSupportValue> supportEntryType;
enum CClaimTrieDataFlags: uint32_t {
HASH_DIRTY = 1U,
CLAIMS_DIRTY = 2U,
POTENTIAL_CHILDREN = 4U, // existing on disk
};
struct CClaimTrieData
{
uint256 hash;
claimEntryType claims;
int nHeightOfLastTakeover = 0;
// non-serialized data:
uint32_t flags = 0;
uint256 hash;
CClaimTrieData() = default;
CClaimTrieData(CClaimTrieData&&) = default;
CClaimTrieData(const CClaimTrieData&) = default;
@ -168,13 +157,25 @@ struct CClaimTrieData
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(hash);
if (ser_action.ForRead()) {
if (s.eof()) {
claims.clear();
nHeightOfLastTakeover = 0;
return;
}
}
else if (claims.empty())
return;
READWRITE(claims);
READWRITE(nHeightOfLastTakeover);
}
bool operator==(const CClaimTrieData& other) const
{
return nHeightOfLastTakeover == other.nHeightOfLastTakeover && claims == other.claims;
return hash == other.hash && nHeightOfLastTakeover == other.nHeightOfLastTakeover && claims == other.claims;
}
bool operator!=(const CClaimTrieData& other) const
@ -188,28 +189,6 @@ struct CClaimTrieData
}
};
struct CClaimTrieDataNode {
uint256 hash;
// we're using a vector to avoid RAM thrashing and for faster serialization ops.
// We're assuming its data is inserted in order and never modified.
std::vector<std::string> children;
CClaimTrieDataNode() = default;
CClaimTrieDataNode(CClaimTrieDataNode&&) = default;
CClaimTrieDataNode(const CClaimTrieDataNode&) = default;
CClaimTrieDataNode& operator=(CClaimTrieDataNode&&) = default;
CClaimTrieDataNode& operator=(const CClaimTrieDataNode& d) = default;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(hash);
READWRITE(children);
}
};
struct COutPointHeightType
{
COutPoint outPoint;
@ -364,7 +343,7 @@ struct CClaimSupportToName
const std::vector<CSupportValue> unmatchedSupports;
};
class CClaimTrie
class CClaimTrie : public CPrefixTrie<std::string, CClaimTrieData>
{
public:
CClaimTrie() = default;
@ -387,23 +366,12 @@ public:
std::size_t getTotalNamesInTrie() const;
std::size_t getTotalClaimsInTrie() const;
virtual bool checkConsistency(const uint256& rootHash) const;
CAmount getTotalValueOfClaimsInTrie(bool fControllingOnly) const;
bool contains(const std::string& key) const;
bool empty() const;
bool find(const std::string& key, CClaimTrieDataNode& node) const;
bool find(const std::string& key, CClaimTrieData& claims) const;
std::vector<std::pair<std::string, CClaimTrieDataNode>> nodes(const std::string& key) const;
protected:
int nNextHeight = 0;
int nProportionalDelayFactor = 0;
std::unique_ptr<CDBWrapper> db;
using recurseNodesCB = void(const std::string&, const CClaimTrieData&, const std::vector<std::string>&);
void recurseNodes(const std::string& name, const CClaimTrieDataNode& current, std::function<recurseNodesCB> function) const;
};
struct CClaimTrieProofNode
@ -508,8 +476,6 @@ typedef std::map<int, expirationQueueRowType> expirationQueueType;
typedef std::set<CClaimValue> claimIndexClaimListType;
typedef std::vector<CClaimIndexElement> claimIndexElementListType;
typedef CPrefixTrie<std::string, CClaimTrieData> CClaimPrefixTrie;
class CClaimTrieCacheBase
{
public:
@ -520,6 +486,7 @@ public:
bool flush();
bool empty() const;
bool checkConsistency() const;
bool ReadFromDisk(const CBlockIndex* tip);
bool haveClaim(const std::string& name, const COutPoint& outPoint) const;
@ -560,21 +527,20 @@ public:
virtual CClaimSupportToName getClaimsForName(const std::string& name) const;
CClaimPrefixTrie::const_iterator begin() const;
CClaimPrefixTrie::const_iterator end() const;
CClaimTrie::const_iterator find(const std::string& name) const;
void iterate(std::function<void(const std::string&, const CClaimTrieData&)> callback) const;
void dumpToLog(CClaimPrefixTrie::const_iterator it, bool diffFromBase = true) const;
void dumpToLog(CClaimTrie::const_iterator it, bool diffFromBase = true) const;
virtual std::string adjustNameForValidHeight(const std::string& name, int validHeight) const;
void recurseNodes(const std::string& name, std::function<void(const std::string&, const CClaimTrieData&)> function) const;
protected:
CClaimTrie* base;
CClaimPrefixTrie nodesToAddOrUpdate; // nodes pulled in from base (and possibly modified thereafter), written to base on flush
CClaimTrie nodesToAddOrUpdate; // nodes pulled in from base (and possibly modified thereafter), written to base on flush
std::unordered_set<std::string> nodesAlreadyCached; // set of nodes already pulled into cache from base
std::unordered_set<std::string> namesToCheckForTakeover; // takeover numbers are updated on increment
virtual uint256 recursiveComputeMerkleHash(CClaimPrefixTrie::iterator& it);
virtual uint256 recursiveComputeMerkleHash(CClaimTrie::iterator& it);
virtual bool recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const;
virtual bool insertClaimIntoTrie(const std::string& name, const CClaimValue& claim, bool fCheckTakeover);
virtual bool removeClaimFromTrie(const std::string& name, const COutPoint& outPoint, CClaimValue& claim, bool fCheckTakeover);
@ -587,7 +553,7 @@ public:
int getDelayForName(const std::string& name) const;
virtual int getDelayForName(const std::string& name, const uint160& claimId) const;
CClaimPrefixTrie::iterator cacheData(const std::string& name, bool create = true);
CClaimTrie::iterator cacheData(const std::string& name, bool create = true);
bool getLastTakeoverForName(const std::string& name, uint160& claimId, int& takeoverHeight) const;
@ -618,7 +584,6 @@ private:
std::unordered_set<std::string> nodesToDelete; // to be removed from base (and disk) on flush
std::unordered_map<std::string, bool> takeoverWorkaround;
std::unordered_set<std::string> removalWorkaround;
std::unordered_set<std::string> forDeleteFromBase;
bool shouldUseTakeoverWorkaround(const std::string& key) const;
void addTakeoverWorkaroundPotential(const std::string& key);
@ -630,8 +595,6 @@ private:
bool removeSupport(const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight, bool fCheckTakeover);
bool removeClaim(const std::string& name, const COutPoint& outPoint, int nHeight, int& nValidAtHeight, bool fCheckTakeover);
bool validateTrieConsistency(const CBlockIndex* tip);
template <typename T>
void insertRowsFromQueue(std::vector<T>& result, const std::string& name) const;
@ -781,20 +744,13 @@ public:
bool allowSupportMetadata() const;
protected:
uint256 recursiveComputeMerkleHash(CClaimPrefixTrie::iterator& it) override;
uint256 recursiveComputeMerkleHash(CClaimTrie::iterator& it) override;
bool recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const override;
private:
void copyAllBaseToCache();
};
class CClaimTrieHashFork : public CClaimTrie
{
public:
using CClaimTrie::CClaimTrie;
protected:
bool checkConsistency(const uint256& rootHash) const override;
};
typedef CClaimTrieCacheHashFork CClaimTrieCache;
#endif // BITCOIN_CLAIMTRIE_H

View file

@ -4,8 +4,6 @@
#include <claimtrie.h>
#include <hash.h>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/locale.hpp>
#include <boost/locale/conversion.hpp>
#include <boost/locale/localization_backend.hpp>
@ -174,17 +172,12 @@ bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(insert
// 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
boost::scoped_ptr<CDBIterator> pcursor(base->db->NewIterator());
for (pcursor->SeekToFirst(); pcursor->Valid(); pcursor->Next()) {
std::pair<uint8_t, std::string> key;
if (!pcursor->GetKey(key) || key.first != TRIE_NODE_CHILDREN)
continue;
const auto& name = key.second;
const std::string normalized = normalizeClaimName(name, true);
if (normalized == key.second)
for (auto it = base->cbegin(); it != base->cend(); ++it) {
const std::string normalized = normalizeClaimName(it.key(), true);
if (normalized == it.key())
continue;
auto& name = it.key();
auto supports = getSupportsForName(name);
for (auto support : supports) {
// if it's already going to expire just skip it
@ -279,23 +272,17 @@ std::vector<uint256> getClaimHashes(const CClaimTrieData& data)
template <typename T>
using iCbType = std::function<uint256(T&)>;
template <typename T>
using decay = typename std::decay<T>::type;
template <typename Vector, typename T>
uint256 recursiveMerkleHash(const CClaimTrieData& data, Vector&& children, const iCbType<T>& childHash)
template <typename TIterator>
uint256 recursiveBinaryTreeHash(TIterator& it, const iCbType<TIterator>& process)
{
static_assert(std::is_same<decay<Vector>, std::vector<decay<T>>>::value, "Vector should be std vector");
static_assert(std::is_same<decltype(children[0]), T&>::value, "Vector element type should match callback type");
std::vector<uint256> childHashes;
for (auto& child : children)
childHashes.emplace_back(childHash(child));
for (auto& child : it.children())
childHashes.emplace_back(process(child));
std::vector<uint256> claimHashes;
if (!data.empty())
claimHashes = getClaimHashes(data);
else if (children.empty())
if (!it->empty())
claimHashes = getClaimHashes(it.data());
else if (!it.hasChildren())
return {};
auto left = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes);
@ -304,53 +291,44 @@ uint256 recursiveMerkleHash(const CClaimTrieData& data, Vector&& children, const
return Hash(left.begin(), left.end(), right.begin(), right.end());
}
extern const uint256 one;
bool CClaimTrieHashFork::checkConsistency(const uint256& rootHash) const
{
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrie::checkConsistency(rootHash);
CClaimTrieDataNode node;
if (!find({}, node) || node.hash != rootHash) {
if (rootHash == one)
return true;
return error("Mismatched root claim trie hashes. This may happen when there is not a clean process shutdown. Please run with -reindex.");
}
bool success = true;
recurseNodes({}, node, [&success, this](const std::string& name, const CClaimTrieData& data, const std::vector<std::string>& children) {
if (!success) return;
iCbType<const std::string> callback = [&success, &name, this](const std::string& child) -> uint256 {
auto key = name + child;
CClaimTrieDataNode node;
success &= find(key, node);
return node.hash;
};
success &= !data.hash.IsNull();
success &= data.hash == recursiveMerkleHash(data, children, callback);
});
return success;
}
uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(CClaimPrefixTrie::iterator& it)
uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(CClaimTrie::iterator& it)
{
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(it);
using iterator = CClaimPrefixTrie::iterator;
using iterator = CClaimTrie::iterator;
iCbType<iterator> process = [&process](iterator& it) -> uint256 {
if (it->hash.IsNull())
it->hash = recursiveMerkleHash(it.data(), it.children(), process);
it->hash = recursiveBinaryTreeHash(it, process);
assert(!it->hash.IsNull());
return it->hash;
};
return process(it);
}
bool CClaimTrieCacheHashFork::recursiveCheckConsistency(CClaimTrie::const_iterator& it, std::string& failed) const
{
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::recursiveCheckConsistency(it, failed);
struct CRecursiveBreak {};
using iterator = CClaimTrie::const_iterator;
iCbType<iterator> process = [&failed, &process](iterator& it) -> uint256 {
if (it->hash.IsNull() || it->hash != recursiveBinaryTreeHash(it, process)) {
failed = it.key();
throw CRecursiveBreak();
}
return it->hash;
};
try {
process(it);
} catch (const CRecursiveBreak&) {
return false;
}
return true;
}
std::vector<uint256> ComputeMerklePath(const std::vector<uint256>& hashes, uint32_t idx)
{
uint32_t count = 0;
@ -426,7 +404,7 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, CClaimTri
cacheData(name, false);
getMerkleHash();
proof = CClaimTrieProof();
for (auto& it : static_cast<const CClaimPrefixTrie&>(nodesToAddOrUpdate).nodes(name)) {
for (auto& it : static_cast<const CClaimTrie&>(nodesToAddOrUpdate).nodes(name)) {
std::vector<uint256> childHashes;
uint32_t nextCurrentIdx = 0;
for (auto& child : it.children()) {
@ -469,15 +447,12 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, CClaimTri
void CClaimTrieCacheHashFork::copyAllBaseToCache()
{
recurseNodes({}, [this](const std::string& name, const CClaimTrieData& data) {
if (nodesAlreadyCached.insert(name).second)
nodesToAddOrUpdate.insert(name, data);
});
for (auto it = base->cbegin(); it != base->cend(); ++it)
if (nodesAlreadyCached.insert(it.key()).second)
nodesToAddOrUpdate.insert(it.key(), it.data());
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it) {
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it)
it->hash.SetNull();
it->flags |= CClaimTrieDataFlags::HASH_DIRTY;
}
}
void CClaimTrieCacheHashFork::initializeIncrement()
@ -500,6 +475,7 @@ bool CClaimTrieCacheHashFork::finalizeDecrement(std::vector<std::pair<std::strin
return ret;
}
bool CClaimTrieCacheHashFork::allowSupportMetadata() const {
bool CClaimTrieCacheHashFork::allowSupportMetadata() const
{
return nNextHeight >= Params().GetConsensus().nAllClaimsInMerkleForkHeight;
}

View file

@ -22,6 +22,7 @@
#include <httprpc.h>
#include <index/txindex.h>
#include <key.h>
#include <lbry.h>
#include <validation.h>
#include <miner.h>
#include <netbase.h>
@ -398,6 +399,7 @@ void SetupServerArgs()
hidden_args.emplace_back("-sysperms");
#endif
gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS);
gArgs.AddArg("-memfile=<GiB>", "Use a memory mapped file for the claimtrie allocations (default: use RAM instead)", false, OptionsCategory::OPTIONS);
gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", false, OptionsCategory::CONNECTION);
gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION);
@ -1441,6 +1443,8 @@ bool AppInitMain()
LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
g_memfileSize = gArgs.GetArg("-memfile", 0u);
bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) {
bool fReset = fReindex;
@ -1465,7 +1469,7 @@ bool AppInitMain()
int64_t trieCacheMB = gArgs.GetArg("-claimtriecache", nDefaultDbCache);
trieCacheMB = std::min(trieCacheMB, nMaxDbCache);
trieCacheMB = std::max(trieCacheMB, nMinDbCache);
pclaimTrie = new CClaimTrieHashFork(false, fReindex || fReindexChainState, 32, trieCacheMB);
pclaimTrie = new CClaimTrie(false, fReindex || fReindexChainState, 32, trieCacheMB);
if (fReset) {
pblocktree->WriteReindexing(true);

View file

@ -3,6 +3,8 @@
#include <cstdio>
uint32_t g_memfileSize = 0;
unsigned int CalculateLbryNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
if (params.fPowNoRetargeting)

View file

@ -4,6 +4,7 @@
#include <chain.h>
#include <chainparams.h>
extern uint32_t g_memfileSize;
unsigned int CalculateLbryNextWorkRequired(const CBlockIndex* pindexLast, int64_t nLastRetargetTime, const Consensus::Params& params);
#endif

View file

@ -208,7 +208,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
if (!trieCache.empty())
trieCache.dumpToLog(trieCache.begin());
trieCache.dumpToLog(trieCache.find({}));
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
}
int64_t nTime2 = GetTimeMicros();

View file

@ -1,6 +1,78 @@
#include "prefixtrie.h"
#include "claimtrie.h"
#include <claimtrie.h>
#include <fs.h>
#include <lbry.h>
#include <limits>
#include <memory>
#include <prefixtrie.h>
#include <boost/interprocess/allocators/private_node_allocator.hpp>
#include <boost/interprocess/indexes/null_index.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
namespace bip = boost::interprocess;
typedef bip::basic_managed_mapped_file <
char,
bip::rbtree_best_fit<bip::null_mutex_family, bip::offset_ptr<void>>,
bip::null_index
> managed_mapped_file;
template <typename T>
using node_allocator = bip::private_node_allocator<T, managed_mapped_file::segment_manager>;
static managed_mapped_file::segment_manager* segmentManager()
{
struct CSharedMemoryFile
{
CSharedMemoryFile() : file(GetDataDir() / "shared.mem")
{
std::remove(file.c_str());
auto size = (uint64_t)g_memfileSize * 1024ULL * 1024ULL * 1024ULL;
menaged_file.reset(new managed_mapped_file(bip::create_only, file.c_str(), size));
}
~CSharedMemoryFile()
{
menaged_file.reset();
std::remove(file.c_str());
}
managed_mapped_file::segment_manager* segmentManager()
{
return menaged_file->get_segment_manager();
}
const fs::path file;
std::unique_ptr<managed_mapped_file> menaged_file;
};
static CSharedMemoryFile shem;
return shem.segmentManager();
}
template <typename T>
static node_allocator<T>& nodeAllocator()
{
static node_allocator<T> allocator(segmentManager());
return allocator;
}
template <typename T, class... Args>
static std::shared_ptr<T> nodeAllocate(Args&&... args)
{
return std::allocate_shared<T>(nodeAllocator<T>(), std::forward<Args>(args)...);
}
template <typename T, class... Args>
static std::shared_ptr<T> allocateShared(Args&&... args)
{
static auto allocate = g_memfileSize ? nodeAllocate<T, Args...> : std::make_shared<T, Args...>;
try {
return allocate(std::forward<Args>(args)...);
}
catch (const bip::bad_alloc&) {
allocate = std::make_shared<T, Args...>; // in case we fill up the memfile
LogPrint(BCLog::BENCH, "WARNING: The memfile is full; reverting to the RAM allocator for %s.\n", typeid(T).name());
return allocate(std::forward<Args>(args)...);
}
}
template <typename TKey, typename TData>
template <bool IsConst>
@ -18,7 +90,7 @@ typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>& CPrefixTrie<TKey,
stack.clear();
stack.reserve(o.stack.size());
for (auto& i : o.stack)
stack.push_back(Bookmark{i.name, i.parent, i.it, i.end});
stack.push_back(Bookmark{i.name, i.it, i.end});
return *this;
}
@ -48,7 +120,7 @@ typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>& CPrefixTrie<TKey,
if (!shared->children.empty()) {
auto& children = shared->children;
auto it = children.begin();
stack.emplace_back(Bookmark{name, shared, it, children.end()});
stack.emplace_back(Bookmark{name, it, children.end()});
auto& postfix = it->first;
name.insert(name.end(), postfix.begin(), postfix.end());
node = it->second;
@ -106,18 +178,30 @@ bool CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator!=(const Iterator& o)
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*() const
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*()
{
assert(!node.expired());
return TPair(name, node.lock()->data);
return reference{name, data()};
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->() const
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::const_reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*() const
{
assert(!node.expired());
return &(node.lock()->data);
return const_reference{name, data()};
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->()
{
return &(data());
}
template <typename TKey, typename TData>
template <bool IsConst>
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::const_pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->() const
{
return &(data());
}
template <typename TKey, typename TData>
@ -129,18 +213,20 @@ const TKey& CPrefixTrie<TKey, TData>::Iterator<IsConst>::key() const
template <typename TKey, typename TData>
template <bool IsConst>
TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data()
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::data_reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::data()
{
assert(!node.expired());
return node.lock()->data;
auto shared = node.lock();
assert(shared);
return *(shared->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;
auto shared = node.lock();
assert(shared);
return *(shared->data);
}
template <typename TKey, typename TData>
@ -240,19 +326,20 @@ std::shared_ptr<typename CPrefixTrie<TKey, TData>::Node>& CPrefixTrie<TKey, TDat
}
if (count == 0) {
++size;
it = children.emplace(key, std::make_shared<Node>()).first;
it = children.emplace(key, allocateShared<Node>()).first;
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());
TKey prefix(key.begin(), key.begin() + count);
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 = children.emplace(std::move(prefix), allocateShared<Node>()).first;
it->second->children.emplace(std::move(postfix), std::move(nodes));
if (key.size() == count)
return it->second;
it->second->data = allocateShared<TData>();
}
return insert(TKey(key.begin() + count, key.end()), it->second);
}
@ -269,12 +356,12 @@ void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& nod
if (!find(key, node, cb))
return;
nodes.back().second->data = {};
nodes.back().second->data = allocateShared<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();
auto noData = cNode->data->empty();
if (onlyOneChild && noData) {
auto child = cNode->children.begin();
auto& prefix = nodes.back().first;
@ -282,7 +369,7 @@ void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& nod
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.emplace(std::move(newKey), std::move(child->second));
pNode->children.erase(prefix);
--size;
continue;
@ -300,8 +387,9 @@ void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& nod
}
template <typename TKey, typename TData>
CPrefixTrie<TKey, TData>::CPrefixTrie() : size(0), root(std::make_shared<Node>())
CPrefixTrie<TKey, TData>::CPrefixTrie() : size(0), root(allocateShared<Node>())
{
root->data = allocateShared<TData>();
}
template <typename TKey, typename TData>
@ -309,7 +397,7 @@ 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::forward<TDataUni>(data);
node->data = allocateShared<TData>(std::forward<TDataUni>(data));
return key.empty() ? begin() : iterator{key, node};
}
@ -333,9 +421,9 @@ typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(CPr
auto name = it.key();
name.insert(name.end(), key.begin(), key.end());
auto& node = insert(key, shared);
copy = iterator{name, node};
copy = iterator{std::move(name), node};
}
copy.node.lock()->data = std::forward<TDataUni>(data);
copy.node.lock()->data = allocateShared<TData>(std::forward<TDataUni>(data));
return copy;
}
@ -404,7 +492,7 @@ bool CPrefixTrie<TKey, TData>::erase(const TKey& key)
{
auto size_was = height();
if (key.empty()) {
root->data = {};
root->data = allocateShared<TData>();
} else {
erase(key, root);
}
@ -415,7 +503,7 @@ template <typename TKey, typename TData>
void CPrefixTrie<TKey, TData>::clear()
{
size = 0;
root->data = {};
root->data = allocateShared<TData>();
root->children.clear();
}
@ -428,7 +516,7 @@ bool CPrefixTrie<TKey, TData>::empty() const
template <typename TKey, typename TData>
std::size_t CPrefixTrie<TKey, TData>::height() const
{
return size + (root->data.empty() ? 0 : 1);
return size + (root->data->empty() ? 0 : 1);
}
template <typename TKey, typename TData>

View file

@ -27,9 +27,9 @@ class CPrefixTrie
Node() = default;
Node(const Node&) = delete;
Node(Node&& o) noexcept = default;
Node& operator=(Node&& o) noexcept = default;
Node& operator=(Node&&) noexcept = default;
Node& operator=(const Node&) = delete;
TData data;
std::shared_ptr<TData> data;
};
using TChildren = decltype(Node::children);
@ -42,15 +42,15 @@ class CPrefixTrie
friend class CPrefixTrie<TKey, TData>;
using TKeyRef = std::reference_wrapper<const TKey>;
using TDataRef = std::reference_wrapper<TData>;
using TDataRef = std::reference_wrapper<typename std::conditional<IsConst, const TData, TData>::type>;
using TPair = std::pair<TKeyRef, TDataRef>;
using ConstTPair = std::pair<TKeyRef, const TData>;
TKey name;
std::weak_ptr<Node> node;
struct Bookmark {
TKey name;
std::weak_ptr<Node> parent;
typename TChildren::iterator it;
typename TChildren::iterator end;
};
@ -60,8 +60,11 @@ class CPrefixTrie
public:
// Iterator traits
using value_type = TPair;
using const_pointer = const TData* const;
using const_reference = ConstTPair;
using data_reference = typename std::conditional<IsConst, const TData&, TData&>::type;
using pointer = typename std::conditional<IsConst, const TData* const, TData* const>::type;
using reference = typename std::conditional<IsConst, const TPair, TPair>::type;
using reference = typename std::conditional<IsConst, ConstTPair, TPair>::type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
@ -89,12 +92,15 @@ class CPrefixTrie
bool operator==(const Iterator& o) const;
bool operator!=(const Iterator& o) const;
reference operator*() const;
pointer operator->() const;
reference operator*();
const_reference operator*() const;
pointer operator->();
const_pointer operator->() const;
const TKey& key() const;
TData& data();
data_reference data();
const TData& data() const;
std::size_t depth() const;

View file

@ -307,7 +307,7 @@ static UniValue getclaimsintrie(const JSONRPCRequest& request)
}
UniValue ret(UniValue::VARR);
trieCache.recurseNodes({}, [&ret, &trieCache, &coinsCache] (const std::string& name, const CClaimTrieData& data) {
trieCache.iterate([&ret, &trieCache, &coinsCache] (const std::string& name, const CClaimTrieData& data) {
if (ShutdownRequested())
throw JSONRPCError(RPC_INTERNAL_ERROR, "Shutdown requested");
@ -348,7 +348,7 @@ static UniValue getnamesintrie(const JSONRPCRequest& request)
}
UniValue ret(UniValue::VARR);
trieCache.recurseNodes({}, [&ret](const std::string &name, const CClaimTrieData &data) {
trieCache.iterate([&ret](const std::string &name, const CClaimTrieData &data) {
if (!data.empty())
ret.push_back(escapeNonUtf8(name));
if (ShutdownRequested())

View file

@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(claim_replace_test) {
fixture.Spend(tx1);
CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "bassfisher", "one", 1);
fixture.IncrementBlocks(1);
BOOST_CHECK(pclaimTrie->checkConsistency(fixture.getMerkleHash()));
BOOST_CHECK(fixture.checkConsistency());
BOOST_CHECK(!fixture.is_best_claim("bass", tx1));
BOOST_CHECK(fixture.is_best_claim("bassfisher", tx2));
}
@ -1852,17 +1852,18 @@ BOOST_AUTO_TEST_CASE(update_on_support2_test)
CMutableTransaction s2 = fixture.MakeSupport(fixture.GetCoinbase(), tx1, name, 1);
fixture.IncrementBlocks(1);
CClaimTrieData node;
BOOST_CHECK(pclaimTrie->find(name, node));
BOOST_CHECK_EQUAL(node.nHeightOfLastTakeover, height + 1);
auto it = pclaimTrie->find(name);
BOOST_CHECK(it);
BOOST_CHECK_EQUAL(it->nHeightOfLastTakeover, height + 1);
fixture.Spend(s1);
fixture.Spend(s2);
CMutableTransaction u1 = fixture.MakeUpdate(tx1, name, value, ClaimIdHash(tx1.GetHash(), 0), 3);
fixture.IncrementBlocks(1);
BOOST_CHECK(pclaimTrie->find(name, node));
BOOST_CHECK_EQUAL(node.nHeightOfLastTakeover, height + 1);
it = pclaimTrie->find(name);
BOOST_CHECK(it);
BOOST_CHECK_EQUAL(it->nHeightOfLastTakeover, height + 1);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -37,7 +37,7 @@ public:
return nodesToAddOrUpdate.height();
}
CClaimPrefixTrie::iterator getCache(const std::string& key)
CClaimTrie::iterator getCache(const std::string& key)
{
return nodesToAddOrUpdate.find(key);
}
@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency(hash2));
BOOST_CHECK(ntState.checkConsistency());
CClaimTrieCacheTest ntState1(pclaimTrie);
ntState1.removeClaimFromTrie(std::string("test"), tx1OutPoint, unused, true);
@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState2.getMerkleHash(), hash3);
BOOST_CHECK(pclaimTrie->checkConsistency(hash3));
BOOST_CHECK(ntState2.checkConsistency());
CClaimTrieCacheTest ntState3(pclaimTrie);
ntState3.insertClaimIntoTrie(std::string("test"), CClaimValue(tx1OutPoint, hash160, 50, 100, 200), true);
@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
ntState3.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState3.getMerkleHash(), hash4);
BOOST_CHECK(pclaimTrie->checkConsistency(hash4));
BOOST_CHECK(ntState3.checkConsistency());
CClaimTrieCacheTest ntState4(pclaimTrie);
ntState4.removeClaimFromTrie(std::string("abab"), tx6OutPoint, unused, true);
@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
ntState4.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState4.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency(hash2));
BOOST_CHECK(ntState4.checkConsistency());
CClaimTrieCacheTest ntState5(pclaimTrie);
ntState5.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
ntState5.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState5.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency(hash2));
BOOST_CHECK(ntState5.checkConsistency());
CClaimTrieCacheTest ntState6(pclaimTrie);
ntState6.insertClaimIntoTrie(std::string("test"), CClaimValue(tx3OutPoint, hash160, 50, 101, 201), true);
@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
ntState6.flush();
BOOST_CHECK(!pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState6.getMerkleHash(), hash2);
BOOST_CHECK(pclaimTrie->checkConsistency(hash2));
BOOST_CHECK(ntState6.checkConsistency());
CClaimTrieCacheTest ntState7(pclaimTrie);
ntState7.removeClaimFromTrie(std::string("test"), tx3OutPoint, unused, true);
@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(merkle_hash_multiple_test)
ntState7.flush();
BOOST_CHECK(pclaimTrie->empty());
BOOST_CHECK_EQUAL(ntState7.getMerkleHash(), hash0);
BOOST_CHECK(pclaimTrie->checkConsistency(hash0));
BOOST_CHECK(ntState7.checkConsistency());
}
BOOST_AUTO_TEST_CASE(basic_insertion_info_test)
@ -281,14 +281,12 @@ BOOST_AUTO_TEST_CASE(iteratetrie_test)
ctc.insertClaimIntoTrie("test", claimVal, true);
BOOST_CHECK(ctc.flush());
CClaimTrieDataNode node;
BOOST_CHECK(pclaimTrie->find("", node));
BOOST_CHECK_EQUAL(node.children.size(), 1U);
BOOST_CHECK(pclaimTrie->find("test", node));
BOOST_CHECK_EQUAL(node.children.size(), 0U);
CClaimTrieData data;
BOOST_CHECK(pclaimTrie->find("test", data));
BOOST_CHECK_EQUAL(data.claims.size(), 1);
auto hit = pclaimTrie->find("");
BOOST_CHECK(hit);
BOOST_CHECK_EQUAL(hit.children().size(), 1U);
BOOST_CHECK(hit = pclaimTrie->find("test"));
BOOST_CHECK_EQUAL(hit.children().size(), 0U);
BOOST_CHECK_EQUAL(hit.data().claims.size(), 1);
}
BOOST_AUTO_TEST_CASE(trie_stays_consistent_test)
@ -305,13 +303,13 @@ BOOST_AUTO_TEST_CASE(trie_stays_consistent_test)
BOOST_CHECK(cache.insertClaimIntoTrie(name, value, false));
cache.flush();
BOOST_CHECK(trie.checkConsistency(cache.getMerkleHash()));
BOOST_CHECK(cache.checkConsistency());
for (auto& name: names) {
CClaimValue temp;
BOOST_CHECK(cache.removeClaimFromTrie(name, COutPoint(), temp, false));
cache.flush();
BOOST_CHECK(trie.checkConsistency(cache.getMerkleHash()));
BOOST_CHECK(cache.checkConsistency());
}
BOOST_CHECK(trie.empty());
}
@ -384,7 +382,6 @@ BOOST_AUTO_TEST_CASE(verify_basic_serialization)
ssData >> cv2;
BOOST_CHECK_EQUAL(cv, cv2);
BOOST_CHECK_EQUAL(cv.nEffectiveAmount, cv2.nEffectiveAmount);
}
BOOST_AUTO_TEST_CASE(claimtrienode_serialize_unserialize)

View file

@ -93,8 +93,7 @@ BOOST_AUTO_TEST_CASE(claimtriebranching_normalization)
BOOST_CHECK(fixture.is_best_claim("normalizetest", tx1));
BOOST_CHECK(fixture.best_claim_effective_amount_equals("normalizetest", 3));
CClaimTrieData data;
BOOST_CHECK(!pclaimTrie->find("normalizeTest", data));
BOOST_CHECK(!pclaimTrie->find("normalizeTest"));
// Check equivalence of normalized claim names
BOOST_CHECK(fixture.is_best_claim("normalizetest", tx1)); // collapsed tx2
@ -223,8 +222,7 @@ BOOST_AUTO_TEST_CASE(claimtriecache_normalization)
BOOST_CHECK(!trieCache.spendClaim(name_normd, COutPoint(tx2.GetHash(), 0), currentHeight, amelieValidHeight));
BOOST_CHECK(trieCache.spendClaim(name_upper, COutPoint(tx2.GetHash(), 0), currentHeight, amelieValidHeight));
CClaimTrieData data;
BOOST_CHECK(!pclaimTrie->find(name, data));
BOOST_CHECK(!pclaimTrie->find(name));
BOOST_CHECK(trieCache.getInfoForName(name, nval1));
BOOST_CHECK(trieCache.addClaim(name, COutPoint(tx1.GetHash(), 0), ClaimIdHash(tx1.GetHash(), 0), CAmount(2), currentHeight + 1));
BOOST_CHECK(trieCache.getInfoForName(name, nval1));
@ -236,8 +234,9 @@ BOOST_AUTO_TEST_CASE(claimtriecache_normalization)
BOOST_CHECK(trieCache.incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo));
BOOST_CHECK(trieCache.shouldNormalize());
CClaimTrieDataNode node;
BOOST_CHECK(!pclaimTrie->find(name, node));
// we cannot use getXXXForName cause they will normalized name
for (auto it = pclaimTrie->cbegin(); it != pclaimTrie->cend(); ++it)
BOOST_CHECK(it.key() != name);
}
BOOST_AUTO_TEST_CASE(undo_normalization_does_not_kill_claim_order)

View file

@ -144,7 +144,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
pclaimTrie = new CClaimTrieHashFork(true, false, 1);
pclaimTrie = new CClaimTrie(true, false, 1);
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}

View file

@ -1562,12 +1562,8 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
assert(trieCache.finalizeDecrement(blockUndo.takeoverHeightUndo));
auto merkleHash = trieCache.getMerkleHash();
if (merkleHash != pindex->pprev->hashClaimTrie) {
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());
}
if (!trieCache.empty())
trieCache.dumpToLog(trieCache.find({}));
LogPrintf("Hash comparison failure at block %d\n", pindex->nHeight);
assert(merkleHash == pindex->pprev->hashClaimTrie);
}
@ -2045,7 +2041,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (trieCache.getMerkleHash() != block.hashClaimTrie)
{
if (!trieCache.empty()) // we could run checkConsistency here, but it would take a while
trieCache.dumpToLog(trieCache.begin());
trieCache.dumpToLog(trieCache.find({}));
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");
@ -2188,7 +2184,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->SyncToDisk())
if (mode == FlushStateMode::ALWAYS && !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())