first tests ran, working to make takeover height unnecessary

This commit is contained in:
Brannon King 2019-10-24 13:09:56 -06:00
parent 3e16229318
commit c2d2e4befd
12 changed files with 1075 additions and 1098 deletions

View file

@ -1,4 +1,3 @@
#include <claimtrie.h> #include <claimtrie.h>
#include <hash.h> #include <hash.h>
#include <logging.h> #include <logging.h>
@ -54,8 +53,7 @@ CClaimTrie::CClaimTrie(bool fMemory, bool fWipe, int height, int proportionalDel
_db.define("merkle_pair", [](const std::vector<unsigned char>& blob1, const std::vector<unsigned char>& blob2) { return Hash(blob1.begin(), blob1.end(), blob2.begin(), blob2.end()); }); _db.define("merkle_pair", [](const std::vector<unsigned char>& blob1, const std::vector<unsigned char>& blob2) { return Hash(blob1.begin(), blob1.end(), blob2.begin(), blob2.end()); });
_db.define("merkle", [](const std::vector<unsigned char>& blob1) { return Hash(blob1.begin(), blob1.end()); }); _db.define("merkle", [](const std::vector<unsigned char>& blob1) { return Hash(blob1.begin(), blob1.end()); });
_db << "CREATE TABLE IF NOT EXISTS nodes (name TEXT NOT NULL PRIMARY KEY, parent TEXT, " _db << "CREATE TABLE IF NOT EXISTS nodes (name TEXT NOT NULL PRIMARY KEY, parent TEXT, hash BLOB)";
"lastTakeoverHeight INTEGER NOT NULL DEFAULT 0, hash BLOB)";
_db << "CREATE INDEX nodes_hash ON nodes (hash)"; _db << "CREATE INDEX nodes_hash ON nodes (hash)";
_db << "CREATE INDEX nodes_parent ON nodes (parent)"; _db << "CREATE INDEX nodes_parent ON nodes (parent)";
@ -110,15 +108,17 @@ bool CClaimTrie::empty() {
bool CClaimTrieCacheBase::haveClaim(const std::string& name, const COutPoint& outPoint) const bool CClaimTrieCacheBase::haveClaim(const std::string& name, const COutPoint& outPoint) const
{ {
auto query = base->_db << "SELECT 1 FROM claims WHERE nodeName = ? AND txID = ? AND txN = ? AND validHeight < ? LIMIT 1" auto query = base->_db << "SELECT 1 FROM claims WHERE nodeName = ? AND txID = ? AND txN = ? "
<< name << outPoint.hash << outPoint.n << nNextHeight; "AND validHeight < ? AND expirationHeight >= ? LIMIT 1"
<< name << outPoint.hash << outPoint.n << nNextHeight << nNextHeight;
return query.begin() != query.end(); return query.begin() != query.end();
} }
bool CClaimTrieCacheBase::haveSupport(const std::string& name, const COutPoint& outPoint) const bool CClaimTrieCacheBase::haveSupport(const std::string& name, const COutPoint& outPoint) const
{ {
auto query = base->_db << "SELECT 1 FROM supports WHERE nodeName = ? AND txID = ? AND txN = ? AND validHeight < ? LIMIT 1" auto query = base->_db << "SELECT 1 FROM supports WHERE nodeName = ? AND txID = ? AND txN = ? "
<< name << outPoint.hash << outPoint.n << nNextHeight; "AND validHeight < ? AND expirationHeight >= ? LIMIT 1"
<< name << outPoint.hash << outPoint.n << nNextHeight << nNextHeight;
return query.begin() != query.end(); return query.begin() != query.end();
} }
@ -126,7 +126,7 @@ supportEntryType CClaimTrieCacheBase::getSupportsForName(const std::string& name
{ {
// includes values that are not yet valid // includes values that are not yet valid
auto query = base->_db << "SELECT supportedClaimID, txID, txN, blockHeight, validHeight, amount " auto query = base->_db << "SELECT supportedClaimID, txID, txN, blockHeight, validHeight, amount "
"FROM supports WHERE nodeName = ?" << name; "FROM supports WHERE nodeName = ? AND expirationHeight >= ?" << name << nNextHeight;
supportEntryType ret; supportEntryType ret;
for (auto&& row: query) { for (auto&& row: query) {
CSupportValue value; CSupportValue value;
@ -139,8 +139,9 @@ supportEntryType CClaimTrieCacheBase::getSupportsForName(const std::string& name
bool CClaimTrieCacheBase::haveClaimInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const bool CClaimTrieCacheBase::haveClaimInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const
{ {
auto query = base->_db << "SELECT validHeight FROM claims WHERE nodeName = ? AND txID = ? AND txN = ? AND validHeight >= ? LIMIT 1" auto query = base->_db << "SELECT validHeight FROM claims WHERE nodeName = ? AND txID = ? AND txN = ? "
<< name << outPoint.hash << outPoint.n << nNextHeight; "AND validHeight >= ? AND expirationHeight >= ? LIMIT 1"
<< name << outPoint.hash << outPoint.n << nNextHeight << nNextHeight;
for (auto&& row: query) { for (auto&& row: query) {
row >> nValidAtHeight; row >> nValidAtHeight;
return true; return true;
@ -150,8 +151,9 @@ bool CClaimTrieCacheBase::haveClaimInQueue(const std::string& name, const COutPo
bool CClaimTrieCacheBase::haveSupportInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const bool CClaimTrieCacheBase::haveSupportInQueue(const std::string& name, const COutPoint& outPoint, int& nValidAtHeight) const
{ {
auto query = base->_db << "SELECT validHeight FROM supports WHERE nodeName = ? AND txID = ? AND txN = ? AND validHeight >= ? LIMIT 1" auto query = base->_db << "SELECT validHeight FROM supports WHERE nodeName = ? AND txID = ? AND txN = ? "
<< name << outPoint.hash << outPoint.n << nNextHeight; "AND validHeight >= ? AND expirationHeight >= ? LIMIT 1"
<< name << outPoint.hash << outPoint.n << nNextHeight << nNextHeight;
for (auto&& row: query) { for (auto&& row: query) {
row >> nValidAtHeight; row >> nValidAtHeight;
return true; return true;
@ -172,7 +174,8 @@ bool CClaimTrieCacheBase::deleteNodeIfPossible(const std::string& name, std::str
if (name.empty()) return false; if (name.empty()) return false;
// to remove a node it must have one or less children and no claims // to remove a node it must have one or less children and no claims
vector_builder<std::string, std::string> claimsBuilder; vector_builder<std::string, std::string> claimsBuilder;
base->_db << "SELECT name FROM claims WHERE name = ?" << name >> claimsBuilder; base->_db << "SELECT name FROM claims WHERE name = ? AND validHeight < ? AND expirationHeight >= ? "
<< name << nNextHeight << nNextHeight >> claimsBuilder;
claims = std::move(claimsBuilder); claims = std::move(claimsBuilder);
if (!claims.empty()) return false; // still has claims if (!claims.empty()) return false; // still has claims
// we now know it has no claims, but we need to check its children // we now know it has no claims, but we need to check its children
@ -210,9 +213,10 @@ void CClaimTrieCacheBase::ensureTreeStructureIsUpToDate() {
// should we do the same to remove nodes? no; we need their last takeover height if they come back // should we do the same to remove nodes? no; we need their last takeover height if they come back
//float time = 0; //float time = 0;
// assume parents are not set correctly here:
auto parentQuery = base->_db << "SELECT name FROM nodes WHERE parent IS NOT NULL AND " auto parentQuery = base->_db << "SELECT name FROM nodes WHERE parent IS NOT NULL AND "
"name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL " "name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL "
"SELECT SUBSTR(p, 0, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) " "SELECT SUBSTR(p, 1, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) "
"ORDER BY LENGTH(name) DESC LIMIT 1"; "ORDER BY LENGTH(name) DESC LIMIT 1";
for (auto& name: names) { for (auto& name: names) {
@ -276,40 +280,50 @@ void CClaimTrieCacheBase::ensureTreeStructureIsUpToDate() {
if (splitPos == 0) if (splitPos == 0)
base->_db << "UPDATE nodes SET hash = NULL WHERE name = ?" << parent; base->_db << "UPDATE nodes SET hash = NULL WHERE name = ?" << parent;
} }
// now we need to percolate the nulls up the tree
// parents should all be set right
base->_db << "UPDATE nodes SET hash = NULL WHERE name IN (WITH RECURSIVE prefix(p) AS ("
"SELECT parent WHERE hash IS NULL ORDER BY name DESC UNION "
"SELECT parent FROM prefix,nodes WHERE name = p AND p IS NOT NULL)";
} }
std::size_t CClaimTrieCacheBase::getTotalNamesInTrie() const std::size_t CClaimTrieCacheBase::getTotalNamesInTrie() const
{ {
// you could do this select from the nodes table, but you would have to ensure it is not dirty first // you could do this select from the nodes table, but you would have to ensure it is not dirty first
std::size_t ret; std::size_t ret;
base->_db << "SELECT COUNT(DISTINCT nodeName) FROM claims" >> ret; base->_db << "SELECT COUNT(DISTINCT nodeName) FROM claims WHERE validHeight < ? AND expirationHeight >= ?"
<< nNextHeight << nNextHeight >> ret;
return ret; return ret;
} }
std::size_t CClaimTrieCacheBase::getTotalClaimsInTrie() const std::size_t CClaimTrieCacheBase::getTotalClaimsInTrie() const
{ {
std::size_t ret; std::size_t ret;
base->_db << "SELECT COUNT(*) FROM claims" >> ret; base->_db << "SELECT COUNT(*) FROM claims WHERE validHeight < ? AND expirationHeight >= ?"
<< nNextHeight << nNextHeight >> ret;
return ret; return ret;
} }
CAmount CClaimTrieCacheBase::getTotalValueOfClaimsInTrie(bool fControllingOnly) const CAmount CClaimTrieCacheBase::getTotalValueOfClaimsInTrie(bool fControllingOnly) const
{ {
CAmount ret = 0; CAmount ret = 0;
std::string query("SELECT c.amount + SUM(SELECT s.amount FROM supports s WHERE s.supportedClaimID = c.claimID AND s.validHeight < ?)" std::string query("SELECT c.amount + SUM(SELECT s.amount FROM supports s "
" FROM claims c WHERE c.validHeight < ?"); "WHERE s.supportedClaimID = c.claimID AND s.validHeight < ? AND s.expirationHeight >= ?) "
"FROM claims c WHERE c.validHeight < ? AND s.expirationHeight >= ?");
if (fControllingOnly) if (fControllingOnly)
throw std::runtime_error("not implemented yet"); // TODO: finish this throw std::runtime_error("not implemented yet"); // TODO: finish this
base->_db << query << nNextHeight << nNextHeight >> ret; base->_db << query << nNextHeight << nNextHeight << nNextHeight << nNextHeight >> ret;
return ret; return ret;
} }
bool CClaimTrieCacheBase::getInfoForName(const std::string& name, CClaimValue& claim) const bool CClaimTrieCacheBase::getInfoForName(const std::string& name, CClaimValue& claim) const
{ {
auto query = base->_db << "SELECT c.claimID, c.txID, c.txN, c.blockHeight, c.validHeight, c.amount, c.amount + " auto query = base->_db << "SELECT c.claimID, c.txID, c.txN, c.blockHeight, c.validHeight, c.amount, "
"SUM(SELECT s.amount FROM supports s WHERE s.supportedClaimID = c.claimID AND s.validHeight < ?) as effectiveAmount" "(SELECT TOTAL(s.amount)+c.amount FROM supports s WHERE s.supportedClaimID = c.claimID AND s.validHeight < ? AND s.expirationHeight >= ?) as effectiveAmount "
"FROM claims c WHERE c.nodeName = ? AND c.validHeight < ? " "FROM claims c WHERE c.nodeName = ? AND c.validHeight < ? AND c.expirationHeight >= ? "
"ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN LIMIT 1" << nNextHeight << name << nNextHeight; "ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN LIMIT 1"
<< nNextHeight << nNextHeight << name << nNextHeight << nNextHeight;
for (auto&& row: query) { for (auto&& row: query) {
row >> claim.claimId >> claim.outPoint.hash >> claim.outPoint.n row >> claim.claimId >> claim.outPoint.hash >> claim.outPoint.n
>> claim.nHeight >> claim.nValidAtHeight >> claim.nAmount >> claim.nEffectiveAmount; >> claim.nHeight >> claim.nValidAtHeight >> claim.nAmount >> claim.nEffectiveAmount;
@ -325,8 +339,8 @@ CClaimSupportToName CClaimTrieCacheBase::getClaimsForName(const std::string& nam
auto supports = getSupportsForName(name); auto supports = getSupportsForName(name);
auto query = base->_db << "SELECT claimID, txID, txN, blockHeight, validHeight, amount " auto query = base->_db << "SELECT claimID, txID, txN, blockHeight, validHeight, amount "
"FROM claims WHERE nodeName = ?" "FROM claims WHERE nodeName = ? AND expirationHeight >= ?"
<< name; << name << nNextHeight;
for (auto&& row: query) { for (auto&& row: query) {
CClaimValue claim; CClaimValue claim;
row >> claim.claimId >> claim.outPoint.hash >> claim.outPoint.n row >> claim.claimId >> claim.outPoint.hash >> claim.outPoint.n
@ -369,19 +383,18 @@ void completeHash(uint256& partialHash, const std::string& key, std::size_t to)
.Finalize(partialHash.begin()); .Finalize(partialHash.begin());
} }
uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly) uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(const std::string& name, bool checkOnly)
{ {
std::vector<uint8_t> vchToHash; std::vector<uint8_t> vchToHash;
const auto pos = name.size(); const auto pos = name.size();
auto query = base->_db << "SELECT name, hash, lastTakeoverHeight FROM nodes WHERE parent = ? ORDER BY name" << name; auto query = base->_db << "SELECT name, hash FROM nodes WHERE parent = ? ORDER BY name" << name;
for (auto&& row : query) { for (auto&& row : query) {
std::string key; std::string key;
int keyLastTakeoverHeight;
std::unique_ptr<uint256> hash; std::unique_ptr<uint256> hash;
row >> key >> hash >> keyLastTakeoverHeight; row >> key >> hash;
if (hash == nullptr) hash = std::make_unique<uint256>(); if (hash == nullptr) hash = std::make_unique<uint256>();
if (hash->IsNull()) { if (hash->IsNull()) {
*hash = recursiveComputeMerkleHash(key, keyLastTakeoverHeight, checkOnly); *hash = recursiveComputeMerkleHash(key, checkOnly);
} }
completeHash(*hash, key, pos); completeHash(*hash, key, pos);
vchToHash.push_back(key[pos]); vchToHash.push_back(key[pos]);
@ -390,11 +403,11 @@ uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(const std::string& name,
CClaimValue claim; CClaimValue claim;
if (getInfoForName(name, claim)) { if (getInfoForName(name, claim)) {
uint256 valueHash = getValueHash(claim.outPoint, lastTakeoverHeight); uint256 valueHash = getValueHash(claim.outPoint, claim.nValidAtHeight);
vchToHash.insert(vchToHash.end(), valueHash.begin(), valueHash.end()); vchToHash.insert(vchToHash.end(), valueHash.begin(), valueHash.end());
} }
auto computedHash = Hash(vchToHash.begin(), vchToHash.end()); auto computedHash = vchToHash.empty() ? one : Hash(vchToHash.begin(), vchToHash.end());
if (!checkOnly) if (!checkOnly)
base->_db << "UPDATE nodes SET hash = ? WHERE name = ?" << computedHash << name; base->_db << "UPDATE nodes SET hash = ? WHERE name = ?" << computedHash << name;
return computedHash; return computedHash;
@ -404,13 +417,12 @@ bool CClaimTrieCacheBase::checkConsistency()
{ {
// verify that all claims hash to the values on the nodes // verify that all claims hash to the values on the nodes
auto query = base->_db << "SELECT name, hash, lastTakeoverHeight FROM nodes"; auto query = base->_db << "SELECT name, hash FROM nodes";
for (auto&& row: query) { for (auto&& row: query) {
std::string name; std::string name;
uint256 hash; uint256 hash;
int takeoverHeight; row >> name >> hash;
row >> name >> hash >> takeoverHeight; auto computedHash = recursiveComputeMerkleHash(name, true);
auto computedHash = recursiveComputeMerkleHash(name, takeoverHeight, true);
if (computedHash != hash) if (computedHash != hash)
return false; return false;
} }
@ -475,11 +487,10 @@ int CClaimTrieCacheBase::expirationTime() const
uint256 CClaimTrieCacheBase::getMerkleHash() uint256 CClaimTrieCacheBase::getMerkleHash()
{ {
ensureTreeStructureIsUpToDate(); ensureTreeStructureIsUpToDate();
int lastTakeover;
std::unique_ptr<uint256> hash; std::unique_ptr<uint256> hash;
base->_db << "SELECT hash, lastTakeoverHeight FROM nodes WHERE name = ''" >> std::tie(hash, lastTakeover); base->_db << "SELECT hash FROM nodes WHERE name = ''" >> hash;
if (hash == nullptr || hash->IsNull()) if (hash == nullptr || hash->IsNull())
return recursiveComputeMerkleHash("", lastTakeover, false); return recursiveComputeMerkleHash("", false);
return *hash; return *hash;
} }
@ -501,7 +512,9 @@ bool CClaimTrieCacheBase::addClaim(const std::string& name, const COutPoint& out
auto expires = expirationTime() + nHeight; auto expires = expirationTime() + nHeight;
auto validHeight = nHeight + delay; auto validHeight = nHeight + delay;
base->_db << "INSERT INTO claims(claimID, name, nodeName, txID, txN, amount, blockHeight, validHeight, expirationHeight, metadata) " base->_db << "INSERT INTO claims(claimID, name, nodeName, txID, txN, amount, blockHeight, validHeight, expirationHeight, metadata) "
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" << claimId << name << nodeName "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(claimID) DO UPDATE SET name = excluded.name, "
"nodeName = excluded.nodeName, txID = excluded.txID, txN = excluded.txN, amount = excluded.amount, "
"expirationHeight = excluded.expirationHeight, metadata = excluded.metadata" << claimId << name << nodeName
<< outPoint.hash << outPoint.n << nAmount << nHeight << validHeight << expires << metadata; << outPoint.hash << outPoint.n << nAmount << nHeight << validHeight << expires << metadata;
base->_db << "INSERT INTO nodes(name) VALUES(?) ON CONFLICT(name) DO UPDATE SET hash = NULL" << nodeName; base->_db << "INSERT INTO nodes(name) VALUES(?) ON CONFLICT(name) DO UPDATE SET hash = NULL" << nodeName;
@ -837,7 +850,7 @@ static const boost::container::flat_map<std::pair<int, std::string>, int> takeov
{{ 653524, "celtic-folk-music-full-live-concert-mps" }, 589762}, {{ 653524, "celtic-folk-music-full-live-concert-mps" }, 589762},
}; };
bool CClaimTrieCacheBase::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo) bool CClaimTrieCacheBase::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
{ {
// the plan: // the plan:
// for every claim and support that becomes active this block set its node hash to null (aka, dirty) // for every claim and support that becomes active this block set its node hash to null (aka, dirty)
@ -863,8 +876,6 @@ bool CClaimTrieCacheBase::incrementBlock(insertUndoType& insertUndo, claimQueueR
expireUndo.emplace_back(name, value); expireUndo.emplace_back(name, value);
} }
} }
base->_db << "UPDATE claims SET active = 0 WHERE expirationHeight = ?"
<< nNextHeight;
base->_db << "UPDATE nodes SET hash = NULL WHERE name IN (SELECT nodeName FROM claims WHERE expirationHeight = ?)" base->_db << "UPDATE nodes SET hash = NULL WHERE name IN (SELECT nodeName FROM claims WHERE expirationHeight = ?)"
<< nNextHeight; << nNextHeight;
@ -880,54 +891,26 @@ bool CClaimTrieCacheBase::incrementBlock(insertUndoType& insertUndo, claimQueueR
expireSupportUndo.emplace_back(name, value); expireSupportUndo.emplace_back(name, value);
} }
} }
base->_db << "UPDATE supports SET active = 0 WHERE expirationHeight = ?"
<< nNextHeight;
base->_db << "UPDATE nodes SET hash = NULL WHERE name IN (SELECT nodeName FROM supports WHERE expirationHeight = ?)" base->_db << "UPDATE nodes SET hash = NULL WHERE name IN (SELECT nodeName FROM supports WHERE expirationHeight = ?)"
<< nNextHeight; << nNextHeight;
// takeover handling: // takeover handling:
std::vector<std::pair<std::string, int>> takeovers; vector_builder<std::string, std::string> takeovers;
base->_db << "SELECT name, lastTakeoverHeight FROM nodes WHERE hash IS NULL" >> takeovers; base->_db << "SELECT name FROM nodes WHERE hash IS NULL" >> takeovers;
for (const auto& takeover : takeovers) {
// the plan: select the old and new bests
// if they are different, record the valid heights of the current claims that are not active
// then make them all active
for (const auto& nameWithTakeover : takeovers) {
if (nNextHeight >= 496856 && nNextHeight <= 653524) { if (nNextHeight >= 496856 && nNextHeight <= 653524) {
auto wit = takeoverWorkarounds.find(std::make_pair(nNextHeight, takeover.first)); auto wit = takeoverWorkarounds.find(std::make_pair(nNextHeight, nameWithTakeover));
if (wit != takeoverWorkarounds.end()) { if (wit != takeoverWorkarounds.end()) {
activateAllFor(insertUndo, insertSupportUndo, takeover.first); activateAllFor(insertUndo, insertSupportUndo, nameWithTakeover);
base->_db << "UPDATE nodes SET lastTakeoverHeight = ? WHERE nodeName = ?" << wit->second << takeover.first;
takeoverHeightUndo.emplace_back(takeover.first, takeover.second);
continue; continue;
} }
} }
int lastTakeoverHeight = 0; // if somebody activates on this block and they are the new best, then everybody activates on this block
auto findBestValid = base->_db << "SELECT c.validHeight, c.amount + " CClaimValue value;
"SUM(SELECT s.amount FROM supports s WHERE s.supportedClaimID = c.claimID AND s.validHeight < ?) as effectiveAmount" if (getInfoForName(nameWithTakeover, value) && value.nValidAtHeight == nNextHeight - 1)
"FROM claims c WHERE c.nodeName = ? AND c.validHeight < ? " activateAllFor(insertUndo, insertSupportUndo, nameWithTakeover);
"ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN LIMIT 1"
<< nNextHeight + 1 << takeover.first << nNextHeight + 1;
auto lit = findBestValid.begin();
if (lit == findBestValid.end()) {
takeoverHeightUndo.emplace_back(takeover.first, takeover.second);
continue;
}
*lit >> lastTakeoverHeight;
if (lastTakeoverHeight == takeover.second)
continue; // no takeover happened
activateAllFor(insertUndo, insertSupportUndo, takeover.first);
// now get the best again:
findBestValid++;
lit = findBestValid.begin();
*lit >> lastTakeoverHeight;
base->_db << "UPDATE nodes SET lastTakeoverHeight = ? WHERE nodeName = ?" << lastTakeoverHeight << takeover.first;
takeoverHeightUndo.emplace_back(takeover.first, takeover.second);
} }
nNextHeight++; nNextHeight++;
@ -938,8 +921,8 @@ void CClaimTrieCacheBase::activateAllFor(insertUndoType& insertUndo, insertUndoT
const std::string& name) { const std::string& name) {
// now that we know a takeover is happening, we bring everybody in: // now that we know a takeover is happening, we bring everybody in:
{ {
auto query = base->_db << "SELECT txID, txN, validHeight FROM claims WHERE nodeName = ? AND validHeight > ?" auto query = base->_db << "SELECT txID, txN, validHeight FROM claims WHERE nodeName = ? AND validHeight > ? AND expirationHeight >= ?"
<< name << nNextHeight; << name << nNextHeight << nNextHeight;
for (auto &&row: query) { for (auto &&row: query) {
uint256 hash; uint256 hash;
uint32_t n; uint32_t n;
@ -949,12 +932,13 @@ void CClaimTrieCacheBase::activateAllFor(insertUndoType& insertUndo, insertUndoT
} }
} }
// and then update them all to activate now: // and then update them all to activate now:
base->_db << "UPDATE claims SET validHeight = ? WHERE nodeName = ? AND validHeight > ?" << nNextHeight << name << nNextHeight; base->_db << "UPDATE claims SET validHeight = ? WHERE nodeName = ? AND validHeight > ? AND expirationHeight >= ?"
<< nNextHeight << name << nNextHeight << nNextHeight;
// then do the same for supports: // then do the same for supports:
{ {
auto query = base->_db << "SELECT txID, txN, validHeight FROM supports WHERE nodeName = ? AND validHeight > ?" auto query = base->_db << "SELECT txID, txN, validHeight FROM supports WHERE nodeName = ? AND validHeight > ? AND expirationHeight >= ?"
<< name << nNextHeight; << name << nNextHeight << nNextHeight;
for (auto &&row: query) { for (auto &&row: query) {
uint256 hash; uint256 hash;
uint32_t n; uint32_t n;
@ -964,7 +948,8 @@ void CClaimTrieCacheBase::activateAllFor(insertUndoType& insertUndo, insertUndoT
} }
} }
// and then update them all to activate now: // and then update them all to activate now:
base->_db << "UPDATE supports SET validHeight = ? WHERE nodeName = ? AND validHeight > ?" << nNextHeight << name << nNextHeight; base->_db << "UPDATE supports SET validHeight = ? WHERE nodeName = ? AND validHeight > ? AND expirationHeight >= ?"
<< nNextHeight << name << nNextHeight << nNextHeight;
} }
bool CClaimTrieCacheBase::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo) bool CClaimTrieCacheBase::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
@ -998,12 +983,8 @@ bool CClaimTrieCacheBase::decrementBlock(insertUndoType& insertUndo, claimQueueR
return true; return true;
} }
bool CClaimTrieCacheBase::finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo) bool CClaimTrieCacheBase::finalizeDecrement()
{ {
for (auto it = takeoverHeightUndo.crbegin(); it != takeoverHeightUndo.crend(); ++it)
base->_db << "UPDATE nodes SET lastTakeoverHeight = ?, hash = NULL WHERE name = ?"
<< it->second << it->first;
return true; return true;
} }
@ -1173,11 +1154,11 @@ int CClaimTrieCacheBase::getNumBlocksOfContinuousOwnership(const std::string& na
if (nNextHeight <= 646584 && ownershipWorkaround.find(std::make_pair(nNextHeight, name)) != ownershipWorkaround.end()) if (nNextHeight <= 646584 && ownershipWorkaround.find(std::make_pair(nNextHeight, name)) != ownershipWorkaround.end())
return 0; return 0;
int lastTakeover = -1; CClaimValue value;
auto query = base->_db << "SELECT lastTakeoverHeight FROM nodes WHERE name = ?" << name; if (getInfoForName(name, value))
for (auto&& row: query) return nNextHeight - value.nValidAtHeight;
row >> lastTakeover;
return lastTakeover > 0 ? nNextHeight - lastTakeover : 0; return 0;
} }
int CClaimTrieCacheBase::getDelayForName(const std::string& name) const int CClaimTrieCacheBase::getDelayForName(const std::string& name) const
@ -1207,19 +1188,18 @@ bool CClaimTrieCacheBase::getProofForName(const std::string& name, const uint160
// cache the parent nodes // cache the parent nodes
getMerkleHash(); getMerkleHash();
proof = CClaimTrieProof(); proof = CClaimTrieProof();
auto nodeQuery = base->_db << "SELECT name, lastTakeoverHeight FROM nodes WHERE " auto nodeQuery = base->_db << "SELECT name FROM nodes WHERE "
"name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL " "name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL "
"SELECT SUBSTR(p, 0, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) " "SELECT SUBSTR(p, 1, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) "
"ORDER BY LENGTH(name)" << name; "ORDER BY LENGTH(name)" << name;
for (auto&& row: nodeQuery) { for (auto&& row: nodeQuery) {
CClaimValue claim; CClaimValue claim;
std::string key; std::string key;
int lastTakeoverHeight; row >> key;
row >> key >> lastTakeoverHeight;
bool fNodeHasValue = getInfoForName(key, claim); bool fNodeHasValue = getInfoForName(key, claim);
uint256 valueHash; uint256 valueHash;
if (fNodeHasValue) if (fNodeHasValue)
valueHash = getValueHash(claim.outPoint, lastTakeoverHeight); valueHash = getValueHash(claim.outPoint, claim.nValidAtHeight);
const auto pos = key.size(); const auto pos = key.size();
std::vector<std::pair<unsigned char, uint256>> children; std::vector<std::pair<unsigned char, uint256>> children;
@ -1246,7 +1226,7 @@ bool CClaimTrieCacheBase::getProofForName(const std::string& name, const uint160
proof.hasValue = fNodeHasValue && claim.claimId == finalClaim; proof.hasValue = fNodeHasValue && claim.claimId == finalClaim;
if (proof.hasValue) { if (proof.hasValue) {
proof.outPoint = claim.outPoint; proof.outPoint = claim.outPoint;
proof.nHeightOfLastTakeover = lastTakeoverHeight; proof.nHeightOfLastTakeover = claim.nValidAtHeight;
} }
valueHash.SetNull(); valueHash.SetNull();
} }
@ -1256,12 +1236,14 @@ bool CClaimTrieCacheBase::getProofForName(const std::string& name, const uint160
} }
bool CClaimTrieCacheBase::findNameForClaim(const std::vector<unsigned char>& claim, CClaimValue& value, std::string& name) { bool CClaimTrieCacheBase::findNameForClaim(const std::vector<unsigned char>& claim, CClaimValue& value, std::string& name) {
auto query = base->_db << "SELECT nodeName, claimId, txID, txN, amount, block_height FROM claims WHERE SUBSTR(claimID, 1, ?) = ?" << claim.size() + 1 << claim; auto query = base->_db << "SELECT nodeName, claimId, txID, txN, amount, validHeight, block_height "
"FROM claims WHERE SUBSTR(claimID, 1, ?) = ? AND validHeight < ? AND expirationHeight >= ?"
<< claim.size() + 1 << claim << nNextHeight << nNextHeight;
auto hit = false; auto hit = false;
for (auto&& row: query) { for (auto&& row: query) {
if (hit) return false; if (hit) return false;
row >> name >> value.claimId >> value.outPoint.hash >> value.outPoint.n row >> name >> value.claimId >> value.outPoint.hash >> value.outPoint.n
>> value.nAmount >> value.nHeight; >> value.nAmount >> value.nValidAtHeight >> value.nHeight;
hit = true; hit = true;
} }
return true; return true;
@ -1269,7 +1251,8 @@ bool CClaimTrieCacheBase::findNameForClaim(const std::vector<unsigned char>& cla
void CClaimTrieCacheBase::getNamesInTrie(std::function<void(const std::string&)> callback) void CClaimTrieCacheBase::getNamesInTrie(std::function<void(const std::string&)> callback)
{ {
auto query = base->_db << "SELECT DISTINCT nodeName FROM claims"; auto query = base->_db << "SELECT DISTINCT nodeName FROM claims WHERE validHeight < ? AND expirationHeight >= ?"
<< nNextHeight << nNextHeight;
for (auto&& row: query) { for (auto&& row: query) {
std::string name; std::string name;
row >> name; row >> name;

View file

@ -49,19 +49,21 @@ namespace sqlite {
struct has_sqlite_type<uint160, SQLITE_BLOB, void> : std::true_type {}; struct has_sqlite_type<uint160, SQLITE_BLOB, void> : std::true_type {};
inline uint160 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint160>) { inline uint160 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint160>) {
int bytes = sqlite3_column_bytes(stmt, inx);
uint160 ret; uint160 ret;
assert(bytes == ret.size());
auto ptr = sqlite3_column_blob(stmt, inx); auto ptr = sqlite3_column_blob(stmt, inx);
if (!ptr) return ret;
int bytes = sqlite3_column_bytes(stmt, inx);
assert(bytes == ret.size());
std::memcpy(ret.begin(), ptr, bytes); std::memcpy(ret.begin(), ptr, bytes);
return ret; return ret;
} }
inline uint256 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint256>) { inline uint256 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<uint256>) {
int bytes = sqlite3_column_bytes(stmt, inx);
uint256 ret; uint256 ret;
assert(bytes == ret.size());
auto ptr = sqlite3_column_blob(stmt, inx); auto ptr = sqlite3_column_blob(stmt, inx);
if (!ptr) return ret;
int bytes = sqlite3_column_bytes(stmt, inx);
assert(bytes == ret.size());
std::memcpy(ret.begin(), ptr, bytes); std::memcpy(ret.begin(), ptr, bytes);
return ret; return ret;
} }
@ -373,8 +375,7 @@ public:
virtual bool incrementBlock(insertUndoType& insertUndo, virtual bool incrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo, claimQueueRowType& expireUndo,
insertUndoType& insertSupportUndo, insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo, supportQueueRowType& expireSupportUndo);
std::vector<std::pair<std::string, int>>& takeoverHeightUndo);
virtual bool decrementBlock(insertUndoType& insertUndo, virtual bool decrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo, claimQueueRowType& expireUndo,
@ -386,7 +387,7 @@ public:
virtual int expirationTime() const; virtual int expirationTime() const;
virtual bool finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo); virtual bool finalizeDecrement();
virtual CClaimSupportToName getClaimsForName(const std::string& name) const; virtual CClaimSupportToName getClaimsForName(const std::string& name) const;
virtual std::string adjustNameForValidHeight(const std::string& name, int validHeight) const; virtual std::string adjustNameForValidHeight(const std::string& name, int validHeight) const;
@ -403,7 +404,7 @@ protected:
CClaimTrie* base; CClaimTrie* base;
bool dirtyNodes; bool dirtyNodes;
virtual uint256 recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly); virtual uint256 recursiveComputeMerkleHash(const std::string& name, bool checkOnly);
supportEntryType getSupportsForName(const std::string& name) const; supportEntryType getSupportsForName(const std::string& name) const;
@ -435,13 +436,12 @@ public:
int expirationTime() const override; int expirationTime() const override;
virtual void initializeIncrement(); virtual void initializeIncrement();
bool finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo) override; bool finalizeDecrement() override;
bool incrementBlock(insertUndoType& insertUndo, bool incrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo, claimQueueRowType& expireUndo,
insertUndoType& insertSupportUndo, insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo, supportQueueRowType& expireSupportUndo) override;
std::vector<std::pair<std::string, int>>& takeoverHeightUndo) override;
bool decrementBlock(insertUndoType& insertUndo, bool decrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo, claimQueueRowType& expireUndo,
@ -470,8 +470,7 @@ public:
bool incrementBlock(insertUndoType& insertUndo, bool incrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo, claimQueueRowType& expireUndo,
insertUndoType& insertSupportUndo, insertUndoType& insertSupportUndo,
supportQueueRowType& expireSupportUndo, supportQueueRowType& expireSupportUndo) override;
std::vector<std::pair<std::string, int>>& takeoverHeightUndo) override;
bool decrementBlock(insertUndoType& insertUndo, bool decrementBlock(insertUndoType& insertUndo,
claimQueueRowType& expireUndo, claimQueueRowType& expireUndo,
@ -497,12 +496,12 @@ public:
bool getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof) override; bool getProofForName(const std::string& name, const uint160& finalClaim, CClaimTrieProof& proof) override;
void initializeIncrement() override; void initializeIncrement() override;
bool finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo) override; bool finalizeDecrement() override;
bool allowSupportMetadata() const; bool allowSupportMetadata() const;
protected: protected:
uint256 recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly) override; uint256 recursiveComputeMerkleHash(const std::string& name, bool checkOnly) override;
}; };
typedef CClaimTrieCacheHashFork CClaimTrieCache; typedef CClaimTrieCacheHashFork CClaimTrieCache;

View file

@ -25,9 +25,9 @@ int CClaimTrieCacheExpirationFork::expirationTime() const
return nExpirationTime; return nExpirationTime;
} }
bool CClaimTrieCacheExpirationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo) bool CClaimTrieCacheExpirationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
{ {
if (CClaimTrieCacheBase::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo)) { if (CClaimTrieCacheBase::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo)) {
setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight)); setExpirationTime(Params().GetConsensus().GetExpirationTime(nNextHeight));
return true; return true;
} }
@ -52,9 +52,9 @@ void CClaimTrieCacheExpirationFork::initializeIncrement()
forkForExpirationChange(true); forkForExpirationChange(true);
} }
bool CClaimTrieCacheExpirationFork::finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo) bool CClaimTrieCacheExpirationFork::finalizeDecrement()
{ {
auto ret = CClaimTrieCacheBase::finalizeDecrement(takeoverHeightUndo); auto ret = CClaimTrieCacheBase::finalizeDecrement();
if (ret && nNextHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight) if (ret && nNextHeight == Params().GetConsensus().nExtendedClaimExpirationForkHeight)
forkForExpirationChange(false); forkForExpirationChange(false);
return ret; return ret;
@ -74,7 +74,7 @@ bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment)
if (!increment) extension = -extension; if (!increment) extension = -extension;
base->_db << "UPDATE claims SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?" << extension << nNextHeight; base->_db << "UPDATE claims SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?" << extension << nNextHeight;
base->_db << "UPDATE supports SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?" << extension << nNextHeight; base->_db << "UPDATE supports SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?" << extension << nNextHeight;
base->_db << "UPDATE nodes SET hash = NULL, claimHash = NULL"; // recompute all hashes (as there aren't that many at this point) base->_db << "UPDATE nodes SET hash = NULL"; // recompute all hashes (as there aren't that many at this point)
dirtyNodes = true; dirtyNodes = true;
return true; return true;
} }
@ -135,7 +135,7 @@ bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(bool f
base->_db.define("NORMALIZED", [this](const std::string& str) { return normalizeClaimName(str, true); }); base->_db.define("NORMALIZED", [this](const std::string& str) { return normalizeClaimName(str, true); });
auto query = base->_db << "SELECT NORMALIZED(name), name, claimID as nn FROM claims HAVING nodeName != nn"; auto query = base->_db << "SELECT NORMALIZED(name) as nn, name, claimID FROM claims HAVING nodeName != nn";
for(auto&& row: query) { for(auto&& row: query) {
std::string newName, oldName; std::string newName, oldName;
uint160 claimID; uint160 claimID;
@ -143,17 +143,17 @@ bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(bool f
if (!forward) std::swap(newName, oldName); if (!forward) std::swap(newName, oldName);
base->_db << "UPDATE claims SET nodeName = ? WHERE claimID = ?" << newName << claimID; base->_db << "UPDATE claims SET nodeName = ? WHERE claimID = ?" << newName << claimID;
base->_db << "DELETE FROM nodes WHERE name = ?" << oldName; base->_db << "DELETE FROM nodes WHERE name = ?" << oldName;
base->_db << "INSERT INTO nodes(name) VALUES(?) ON CONFLICT DO UPDATE hash = NULL, claimHash = NULL" << newName; base->_db << "INSERT INTO nodes(name) VALUES(?) ON CONFLICT DO UPDATE hash = NULL" << newName;
} }
dirtyNodes = true; dirtyNodes = true;
return true; return true;
} }
bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo, std::vector<std::pair<std::string, int>>& takeoverHeightUndo) bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
{ {
normalizeAllNamesInTrieIfNecessary(true); normalizeAllNamesInTrieIfNecessary(true);
return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo); return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo);
} }
bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo) bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, claimQueueRowType& expireUndo, insertUndoType& insertSupportUndo, supportQueueRowType& expireSupportUndo)
@ -196,36 +196,37 @@ CClaimTrieCacheHashFork::CClaimTrieCacheHashFork(CClaimTrie* base) : CClaimTrieC
static const uint256 leafHash = uint256S("0000000000000000000000000000000000000000000000000000000000000002"); static const uint256 leafHash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
static const uint256 emptyHash = uint256S("0000000000000000000000000000000000000000000000000000000000000003"); static const uint256 emptyHash = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(const std::string& name, int lastTakeoverHeight, bool checkOnly) uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(const std::string& name, bool checkOnly)
{ {
if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight) if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight)
return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(name, lastTakeoverHeight, checkOnly); return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(name, checkOnly);
auto childQuery = base->_db << "SELECT name, hash, lastTakeoverHeight FROM nodes WHERE parent = ? ORDER BY name" << name; auto childQuery = base->_db << "SELECT name, hash FROM nodes WHERE parent = ? ORDER BY name" << name;
std::vector<uint256> childHashes; std::vector<uint256> childHashes;
for (auto&& row: childQuery) { for (auto&& row: childQuery) {
std::string key; std::string key;
int keyLastTakeoverHeight;
std::unique_ptr<uint256> hash; std::unique_ptr<uint256> hash;
row >> key >> hash >> keyLastTakeoverHeight; row >> key >> hash;
if (hash == nullptr) hash = std::make_unique<uint256>(); if (hash == nullptr) hash = std::make_unique<uint256>();
if (hash->IsNull()) { if (hash->IsNull()) {
*hash = recursiveComputeMerkleHash(key, keyLastTakeoverHeight, checkOnly); *hash = recursiveComputeMerkleHash(key, checkOnly);
} }
childHashes.push_back(*hash); childHashes.push_back(*hash);
} }
auto claimQuery = base->_db << "SELECT c.txID, c.txN, c.validHeight, c.amount + " auto claimQuery = base->_db << "SELECT c.txID, c.txN, c.validHeight, c.amount + "
"SUM(SELECT s.amount FROM supports s WHERE s.supportedClaimID = c.claimID AND s.validHeight < ?) as effectiveAmount" "SUM(SELECT s.amount FROM supports s WHERE s.supportedClaimID = c.claimID "
"FROM claims c WHERE c.nodeName = ? AND c.validHeight < ? " "AND s.validHeight < ? AND s.expirationHeight >= ?) as effectiveAmount"
"ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN" << nNextHeight << name << nNextHeight; "FROM claims c WHERE c.nodeName = ? AND c.validHeight < ? AND c.expirationHeight >= ? "
"ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN" << nNextHeight << nNextHeight << name << nNextHeight << nNextHeight;
std::vector<uint256> claimHashes; std::vector<uint256> claimHashes;
for (auto&& row: claimQuery) { for (auto&& row: claimQuery) {
COutPoint p; COutPoint p;
row >> p.hash >> p.n; int validHeight;
auto claimHash = getValueHash(p, lastTakeoverHeight); row >> p.hash >> p.n >> validHeight;
auto claimHash = getValueHash(p, validHeight);
claimHashes.push_back(claimHash); claimHashes.push_back(claimHash);
} }
@ -307,13 +308,13 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uin
// cache the parent nodes // cache the parent nodes
getMerkleHash(); getMerkleHash();
proof = CClaimTrieProof(); proof = CClaimTrieProof();
auto nodeQuery = base->_db << "SELECT name, lastTakeoverHeight FROM nodes WHERE " auto nodeQuery = base->_db << "SELECT name FROM nodes WHERE "
"name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL " "name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL "
"SELECT SUBSTR(p, 0, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) " "SELECT SUBSTR(p, 1, LENGTH(p)) FROM prefix WHERE p != '') SELECT p FROM prefix) "
"ORDER BY LENGTH(name)" << name; "ORDER BY LENGTH(name)" << name;
for (auto&& row: nodeQuery) { for (auto&& row: nodeQuery) {
std::string key; int lastTakeover; std::string key;;
row >> key >> lastTakeover; row >> key;
std::vector<uint256> childHashes; std::vector<uint256> childHashes;
uint32_t nextCurrentIdx = 0; uint32_t nextCurrentIdx = 0;
auto childQuery = base->_db << "SELECT name, hash FROM nodes WHERE parent = ?" << key; auto childQuery = base->_db << "SELECT name, hash FROM nodes WHERE parent = ?" << key;
@ -332,7 +333,7 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uin
COutPoint finalOutPoint; COutPoint finalOutPoint;
for (uint32_t i = 0; i < cns.claimsNsupports.size(); ++i) { for (uint32_t i = 0; i < cns.claimsNsupports.size(); ++i) {
auto& child = cns.claimsNsupports[i].claim; auto& child = cns.claimsNsupports[i].claim;
claimHashes.push_back(getValueHash(child.outPoint, lastTakeover)); claimHashes.push_back(getValueHash(child.outPoint, child.nValidAtHeight));
if (child.claimId == finalClaim) { if (child.claimId == finalClaim) {
finalClaimIdx = i; finalClaimIdx = i;
finalOutPoint = child.outPoint; finalOutPoint = child.outPoint;
@ -344,7 +345,7 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uin
// else it will be hash(x, claims) // else it will be hash(x, claims)
if (key == name) { if (key == name) {
proof.outPoint = finalOutPoint; proof.outPoint = finalOutPoint;
proof.nHeightOfLastTakeover = lastTakeover; proof.nHeightOfLastTakeover = cns.nLastTakeoverHeight;
proof.hasValue = true; proof.hasValue = true;
auto hash = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes); auto hash = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes);
proof.pairs.emplace_back(true, hash); proof.pairs.emplace_back(true, hash);
@ -369,9 +370,9 @@ void CClaimTrieCacheHashFork::initializeIncrement()
base->_db << "UPDATE nodes SET hash = NULL"; base->_db << "UPDATE nodes SET hash = NULL";
} }
bool CClaimTrieCacheHashFork::finalizeDecrement(std::vector<std::pair<std::string, int>>& takeoverHeightUndo) bool CClaimTrieCacheHashFork::finalizeDecrement()
{ {
auto ret = CClaimTrieCacheNormalizationFork::finalizeDecrement(takeoverHeightUndo); auto ret = CClaimTrieCacheNormalizationFork::finalizeDecrement();
if (ret && nNextHeight == Params().GetConsensus().nAllClaimsInMerkleForkHeight - 1) if (ret && nNextHeight == Params().GetConsensus().nAllClaimsInMerkleForkHeight - 1)
base->_db << "UPDATE nodes SET hash = NULL"; base->_db << "UPDATE nodes SET hash = NULL";
return ret; return ret;

View file

@ -59,7 +59,6 @@ void blockToCache(const CBlock* pblock, CClaimTrieCache& trieCache, int nHeight)
claimQueueRowType dummyExpireUndo; claimQueueRowType dummyExpireUndo;
insertUndoType dummyInsertSupportUndo; insertUndoType dummyInsertSupportUndo;
supportQueueRowType dummyExpireSupportUndo; supportQueueRowType dummyExpireSupportUndo;
std::vector<std::pair<std::string, int> > dummyTakeoverHeightUndo;
CUpdateCacheCallbacks callbacks = { CUpdateCacheCallbacks callbacks = {
.findScriptKey = [&pblock](const COutPoint& point) { .findScriptKey = [&pblock](const COutPoint& point) {
@ -79,7 +78,7 @@ void blockToCache(const CBlock* pblock, CClaimTrieCache& trieCache, int nHeight)
if (!tx->IsCoinBase()) if (!tx->IsCoinBase())
UpdateCache(*tx, trieCache, view, nHeight, callbacks); UpdateCache(*tx, trieCache, view, nHeight, callbacks);
trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo, dummyInsertSupportUndo, dummyExpireSupportUndo, dummyTakeoverHeightUndo); trieCache.incrementBlock(dummyInsertUndo, dummyExpireUndo, dummyInsertSupportUndo, dummyExpireSupportUndo);
} }
BlockAssembler::Options::Options() { BlockAssembler::Options::Options() {

View file

@ -17,7 +17,7 @@ CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stop
CScheduler::~CScheduler() CScheduler::~CScheduler()
{ {
assert(nThreadsServicingQueue == 0); assert(!AreThreadsServicingQueue());
} }

File diff suppressed because it is too large Load diff

View file

@ -7,24 +7,25 @@
namespace sqlite { namespace sqlite {
class sqlite_exception: public std::runtime_error { class sqlite_exception: public std::runtime_error {
public: public:
sqlite_exception(const char* msg, str_ref sql, int code = -1): runtime_error(msg), code(code), sql(sql) {} sqlite_exception(const char* msg, str_ref sql, int code = -1): runtime_error(msg), code(code), sql(sql) {}
sqlite_exception(int code, str_ref sql): runtime_error(sqlite3_errstr(code)), code(code), sql(sql) {} sqlite_exception(int code, str_ref sql, const char *msg = nullptr): runtime_error(msg ? msg : sqlite3_errstr(code)), code(code), sql(sql) {}
int get_code() const {return code & 0xFF;} int get_code() const {return code & 0xFF;}
int get_extended_code() const {return code;} int get_extended_code() const {return code;}
std::string get_sql() const {return sql;} std::string get_sql() const {return sql;}
private: const char *errstr() const {return code == -1 ? "Unknown error" : sqlite3_errstr(code);}
int code; private:
std::string sql; int code;
}; std::string sql;
};
namespace errors { namespace errors {
//One more or less trivial derived error class for each SQLITE error. //One more or less trivial derived error class for each SQLITE error.
//Note the following are not errors so have no classes: //Note the following are not errors so have no classes:
//SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE //SQLITE_OK, SQLITE_NOTICE, SQLITE_WARNING, SQLITE_ROW, SQLITE_DONE
// //
//Note these names are exact matches to the names of the SQLITE error codes. //Note these names are exact matches to the names of the SQLITE error codes.
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \ #define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\ class name: public sqlite_exception { using sqlite_exception::sqlite_exception; };\
derived derived
@ -34,15 +35,15 @@ namespace sqlite {
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED #undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE #undef SQLITE_MODERN_CPP_ERROR_CODE
//Some additional errors are here for the C++ interface //Some additional errors are here for the C++ interface
class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class more_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class no_rows: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement class more_statements: public sqlite_exception { using sqlite_exception::sqlite_exception; }; // Prepared statements can only contain one statement
class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class invalid_utf16: public sqlite_exception { using sqlite_exception::sqlite_exception; };
class unknown_binding: public sqlite_exception { using sqlite_exception::sqlite_exception; }; class unknown_binding: public sqlite_exception { using sqlite_exception::sqlite_exception; };
static void throw_sqlite_error(const int& error_code, str_ref sql = "") { static void throw_sqlite_error(const int& error_code, str_ref sql = "", const char *errmsg = nullptr) {
switch(error_code & 0xFF) { switch(error_code & 0xFF) {
#define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \ #define SQLITE_MODERN_CPP_ERROR_CODE(NAME,name,derived) \
case SQLITE_ ## NAME: switch(error_code) { \ case SQLITE_ ## NAME: switch(error_code) { \
derived \ derived \
@ -51,19 +52,19 @@ namespace sqlite {
} }
#if SQLITE_VERSION_NUMBER < 3010000 #if SQLITE_VERSION_NUMBER < 3010000
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8)) #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
#endif #endif
#define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \ #define SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED(BASE,SUB,base,sub) \
case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql); case SQLITE_ ## BASE ## _ ## SUB: throw base ## _ ## sub(error_code, sql, errmsg);
#include "lists/error_codes.h" #include "lists/error_codes.h"
#undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED #undef SQLITE_MODERN_CPP_ERROR_CODE_EXTENDED
#undef SQLITE_MODERN_CPP_ERROR_CODE #undef SQLITE_MODERN_CPP_ERROR_CODE
default: throw sqlite_exception(error_code, sql); default: throw sqlite_exception(error_code, sql, errmsg);
} }
} }
} }
namespace exceptions = errors; namespace exceptions = errors;
} }

View file

@ -12,7 +12,7 @@
#ifdef __has_include #ifdef __has_include
#if __cplusplus > 201402 && __has_include(<optional>) #if __cplusplus > 201402 && __has_include(<optional>)
#define MODERN_SQLITE_STD_OPTIONAL_SUPPORT #define MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#elif __has_include(<experimental/optional>) #elif __has_include(<experimental/optional>) && __apple_build_version__ < 11000000
#define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #define MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
#endif #endif
#endif #endif
@ -45,130 +45,130 @@ namespace sqlite
#else #else
namespace sqlite namespace sqlite
{ {
typedef const std::string& str_ref; typedef const std::string& str_ref;
typedef const std::u16string& u16str_ref; typedef const std::u16string& u16str_ref;
} }
#endif #endif
#include <sqlite/sqlite3.h> #include <sqlite/sqlite3.h>
#include "errors.h" #include "errors.h"
namespace sqlite { namespace sqlite {
template<class T, int Type, class = void> template<class T, int Type, class = void>
struct has_sqlite_type : std::false_type {}; struct has_sqlite_type : std::false_type {};
template<class T> template<class T>
using is_sqlite_value = std::integral_constant<bool, false using is_sqlite_value = std::integral_constant<bool, false
|| has_sqlite_type<T, SQLITE_NULL>::value || has_sqlite_type<T, SQLITE_NULL>::value
|| has_sqlite_type<T, SQLITE_INTEGER>::value || has_sqlite_type<T, SQLITE_INTEGER>::value
|| has_sqlite_type<T, SQLITE_FLOAT>::value || has_sqlite_type<T, SQLITE_FLOAT>::value
|| has_sqlite_type<T, SQLITE_TEXT>::value || has_sqlite_type<T, SQLITE_TEXT>::value
|| has_sqlite_type<T, SQLITE_BLOB>::value || has_sqlite_type<T, SQLITE_BLOB>::value
>; >;
template<class T, int Type> template<class T, int Type>
struct has_sqlite_type<T&, Type> : has_sqlite_type<T, Type> {}; struct has_sqlite_type<T&, Type> : has_sqlite_type<T, Type> {};
template<class T, int Type> template<class T, int Type>
struct has_sqlite_type<const T, Type> : has_sqlite_type<T, Type> {}; struct has_sqlite_type<const T, Type> : has_sqlite_type<T, Type> {};
template<class T, int Type> template<class T, int Type>
struct has_sqlite_type<volatile T, Type> : has_sqlite_type<T, Type> {}; struct has_sqlite_type<volatile T, Type> : has_sqlite_type<T, Type> {};
template<class T> template<class T>
struct result_type { struct result_type {
using type = T; using type = T;
constexpr result_type() = default; constexpr result_type() = default;
template<class U, class = typename std::enable_if<std::is_assignable<U, T>::value>> template<class U, class = typename std::enable_if<std::is_assignable<U, T>::value>>
constexpr result_type(result_type<U>) { } constexpr result_type(result_type<U>) { }
}; };
// int // int
template<> template<>
struct has_sqlite_type<int, SQLITE_INTEGER> : std::true_type {}; struct has_sqlite_type<int, SQLITE_INTEGER> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const int& val) {
return sqlite3_bind_int(stmt, inx, val); return sqlite3_bind_int(stmt, inx, val);
} }
inline void store_result_in_db(sqlite3_context* db, const int& val) { inline void store_result_in_db(sqlite3_context* db, const int& val) {
sqlite3_result_int(db, val); sqlite3_result_int(db, val);
} }
inline int get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<int>) { inline int get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<int>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_int(stmt, inx); sqlite3_column_int(stmt, inx);
} }
inline int get_val_from_db(sqlite3_value *value, result_type<int>) { inline int get_val_from_db(sqlite3_value *value, result_type<int>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 : return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_int(value); sqlite3_value_int(value);
} }
// sqlite_int64 // sqlite_int64
template<> template<>
struct has_sqlite_type<sqlite_int64, SQLITE_INTEGER, void> : std::true_type {}; struct has_sqlite_type<sqlite_int64, SQLITE_INTEGER, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const sqlite_int64& val) {
return sqlite3_bind_int64(stmt, inx, val); return sqlite3_bind_int64(stmt, inx, val);
} }
inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) { inline void store_result_in_db(sqlite3_context* db, const sqlite_int64& val) {
sqlite3_result_int64(db, val); sqlite3_result_int64(db, val);
} }
inline sqlite_int64 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<sqlite_int64 >) { inline sqlite_int64 get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<sqlite_int64 >) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_int64(stmt, inx); sqlite3_column_int64(stmt, inx);
} }
inline sqlite3_int64 get_val_from_db(sqlite3_value *value, result_type<sqlite3_int64>) { inline sqlite3_int64 get_val_from_db(sqlite3_value *value, result_type<sqlite3_int64>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 : return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_int64(value); sqlite3_value_int64(value);
} }
// float // float
template<> template<>
struct has_sqlite_type<float, SQLITE_FLOAT, void> : std::true_type {}; struct has_sqlite_type<float, SQLITE_FLOAT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const float& val) {
return sqlite3_bind_double(stmt, inx, double(val)); return sqlite3_bind_double(stmt, inx, double(val));
} }
inline void store_result_in_db(sqlite3_context* db, const float& val) { inline void store_result_in_db(sqlite3_context* db, const float& val) {
sqlite3_result_double(db, val); sqlite3_result_double(db, val);
} }
inline float get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<float>) { inline float get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<float>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_double(stmt, inx); sqlite3_column_double(stmt, inx);
} }
inline float get_val_from_db(sqlite3_value *value, result_type<float>) { inline float get_val_from_db(sqlite3_value *value, result_type<float>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 : return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_double(value); sqlite3_value_double(value);
} }
// double // double
template<> template<>
struct has_sqlite_type<double, SQLITE_FLOAT, void> : std::true_type {}; struct has_sqlite_type<double, SQLITE_FLOAT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const double& val) {
return sqlite3_bind_double(stmt, inx, val); return sqlite3_bind_double(stmt, inx, val);
} }
inline void store_result_in_db(sqlite3_context* db, const double& val) { inline void store_result_in_db(sqlite3_context* db, const double& val) {
sqlite3_result_double(db, val); sqlite3_result_double(db, val);
} }
inline double get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<double>) { inline double get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<double>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 : return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? 0 :
sqlite3_column_double(stmt, inx); sqlite3_column_double(stmt, inx);
} }
inline double get_val_from_db(sqlite3_value *value, result_type<double>) { inline double get_val_from_db(sqlite3_value *value, result_type<double>) {
return sqlite3_value_type(value) == SQLITE_NULL ? 0 : return sqlite3_value_type(value) == SQLITE_NULL ? 0 :
sqlite3_value_double(value); sqlite3_value_double(value);
} }
/* for nullptr support */ /* for nullptr support */
template<> template<>
struct has_sqlite_type<std::nullptr_t, SQLITE_NULL, void> : std::true_type {}; struct has_sqlite_type<std::nullptr_t, SQLITE_NULL, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::nullptr_t) {
return sqlite3_bind_null(stmt, inx); return sqlite3_bind_null(stmt, inx);
} }
inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) { inline void store_result_in_db(sqlite3_context* db, std::nullptr_t) {
sqlite3_result_null(db); sqlite3_result_null(db);
} }
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
template<> template<>
struct has_sqlite_type<std::monostate, SQLITE_NULL, void> : std::true_type {}; struct has_sqlite_type<std::monostate, SQLITE_NULL, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::monostate) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, std::monostate) {
@ -185,184 +185,184 @@ namespace sqlite {
} }
#endif #endif
// str_ref // str_ref
template<> template<>
struct has_sqlite_type<std::string, SQLITE3_TEXT, void> : std::true_type {}; struct has_sqlite_type<std::string, SQLITE3_TEXT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, str_ref val) {
return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_STATIC); return sqlite3_bind_text(stmt, inx, val.data(), val.length(), SQLITE_TRANSIENT);
} }
// Convert char* to string_view to trigger op<<(..., const str_ref ) // Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) { template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char(&STR)[N]) {
return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_STATIC); return sqlite3_bind_text(stmt, inx, &STR[0], N-1, SQLITE_TRANSIENT);
} }
inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::string>) { inline std::string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::string>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::string() : return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::string() :
std::string(reinterpret_cast<char const *>(sqlite3_column_text(stmt, inx)), sqlite3_column_bytes(stmt, inx)); std::string(reinterpret_cast<char const *>(sqlite3_column_text(stmt, inx)), sqlite3_column_bytes(stmt, inx));
} }
inline std::string get_val_from_db(sqlite3_value *value, result_type<std::string >) { inline std::string get_val_from_db(sqlite3_value *value, result_type<std::string >) {
return sqlite3_value_type(value) == SQLITE_NULL ? std::string() : return sqlite3_value_type(value) == SQLITE_NULL ? std::string() :
std::string(reinterpret_cast<char const *>(sqlite3_value_text(value)), sqlite3_value_bytes(value)); std::string(reinterpret_cast<char const *>(sqlite3_value_text(value)), sqlite3_value_bytes(value));
} }
inline void store_result_in_db(sqlite3_context* db, str_ref val) { inline void store_result_in_db(sqlite3_context* db, str_ref val) {
sqlite3_result_text(db, val.data(), val.length(), SQLITE_TRANSIENT); sqlite3_result_text(db, val.data(), val.length(), SQLITE_TRANSIENT);
} }
// u16str_ref // u16str_ref
template<> template<>
struct has_sqlite_type<std::u16string, SQLITE3_TEXT, void> : std::true_type {}; struct has_sqlite_type<std::u16string, SQLITE3_TEXT, void> : std::true_type {};
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, u16str_ref val) {
return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_STATIC); return sqlite3_bind_text16(stmt, inx, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
} }
// Convert char* to string_view to trigger op<<(..., const str_ref ) // Convert char* to string_view to trigger op<<(..., const str_ref )
template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) { template<std::size_t N> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const char16_t(&STR)[N]) {
return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_STATIC); return sqlite3_bind_text16(stmt, inx, &STR[0], sizeof(char16_t) * (N-1), SQLITE_TRANSIENT);
} }
inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::u16string>) { inline std::u16string get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::u16string>) {
return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::u16string() : return sqlite3_column_type(stmt, inx) == SQLITE_NULL ? std::u16string() :
std::u16string(reinterpret_cast<char16_t const *>(sqlite3_column_text16(stmt, inx)), sqlite3_column_bytes16(stmt, inx)); std::u16string(reinterpret_cast<char16_t const *>(sqlite3_column_text16(stmt, inx)), sqlite3_column_bytes16(stmt, inx));
} }
inline std::u16string get_val_from_db(sqlite3_value *value, result_type<std::u16string>) { inline std::u16string get_val_from_db(sqlite3_value *value, result_type<std::u16string>) {
return sqlite3_value_type(value) == SQLITE_NULL ? std::u16string() : return sqlite3_value_type(value) == SQLITE_NULL ? std::u16string() :
std::u16string(reinterpret_cast<char16_t const *>(sqlite3_value_text16(value)), sqlite3_value_bytes16(value)); std::u16string(reinterpret_cast<char16_t const *>(sqlite3_value_text16(value)), sqlite3_value_bytes16(value));
} }
inline void store_result_in_db(sqlite3_context* db, u16str_ref val) { inline void store_result_in_db(sqlite3_context* db, u16str_ref val) {
sqlite3_result_text16(db, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT); sqlite3_result_text16(db, val.data(), sizeof(char16_t) * val.length(), SQLITE_TRANSIENT);
} }
// Other integer types // Other integer types
template<class Integral> template<class Integral>
struct has_sqlite_type<Integral, SQLITE_INTEGER, typename std::enable_if<std::is_integral<Integral>::value>::type> : std::true_type {}; struct has_sqlite_type<Integral, SQLITE_INTEGER, typename std::enable_if<std::is_integral<Integral>::value>::type> : std::true_type {};
template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type> template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val) { inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const Integral& val) {
return bind_col_in_db(stmt, inx, static_cast<sqlite3_int64>(val)); return bind_col_in_db(stmt, inx, static_cast<sqlite3_int64>(val));
} }
template<class Integral, class = std::enable_if<std::is_integral<Integral>::type>> template<class Integral, class = std::enable_if<std::is_integral<Integral>::type>>
inline void store_result_in_db(sqlite3_context* db, const Integral& val) { inline void store_result_in_db(sqlite3_context* db, const Integral& val) {
store_result_in_db(db, static_cast<sqlite3_int64>(val)); store_result_in_db(db, static_cast<sqlite3_int64>(val));
} }
template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type> template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline Integral get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<Integral>) { inline Integral get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<Integral>) {
return get_col_from_db(stmt, inx, result_type<sqlite3_int64>()); return get_col_from_db(stmt, inx, result_type<sqlite3_int64>());
} }
template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type> template<class Integral, class = typename std::enable_if<std::is_integral<Integral>::value>::type>
inline Integral get_val_from_db(sqlite3_value *value, result_type<Integral>) { inline Integral get_val_from_db(sqlite3_value *value, result_type<Integral>) {
return get_val_from_db(value, result_type<sqlite3_int64>()); return get_val_from_db(value, result_type<sqlite3_int64>());
} }
// vector<T, A> // vector<T, A>
template<typename T, typename A> template<typename T, typename A>
struct has_sqlite_type<std::vector<T, A>, SQLITE_BLOB, void> : std::true_type {}; struct has_sqlite_type<std::vector<T, A>, SQLITE_BLOB, void> : std::true_type {};
template<typename T, typename A> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector<T, A>& vec) { template<typename T, typename A> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::vector<T, A>& vec) {
void const* buf = reinterpret_cast<void const *>(vec.data()); void const* buf = reinterpret_cast<void const *>(vec.data());
int bytes = vec.size() * sizeof(T); int bytes = vec.size() * sizeof(T);
return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_STATIC); return sqlite3_bind_blob(stmt, inx, buf, bytes, SQLITE_TRANSIENT);
} }
template<typename T, typename A> inline void store_result_in_db(sqlite3_context* db, const std::vector<T, A>& vec) { template<typename T, typename A> inline void store_result_in_db(sqlite3_context* db, const std::vector<T, A>& vec) {
void const* buf = reinterpret_cast<void const *>(vec.data()); void const* buf = reinterpret_cast<void const *>(vec.data());
int bytes = vec.size() * sizeof(T); int bytes = vec.size() * sizeof(T);
sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT); sqlite3_result_blob(db, buf, bytes, SQLITE_TRANSIENT);
} }
template<typename T, typename A> inline std::vector<T, A> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::vector<T, A>>) { template<typename T, typename A> inline std::vector<T, A> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::vector<T, A>>) {
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return {}; return {};
} }
int bytes = sqlite3_column_bytes(stmt, inx); int bytes = sqlite3_column_bytes(stmt, inx);
T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(stmt, inx)); T const* buf = reinterpret_cast<T const *>(sqlite3_column_blob(stmt, inx));
return std::vector<T, A>(buf, buf + bytes/sizeof(T)); return std::vector<T, A>(buf, buf + bytes/sizeof(T));
} }
template<typename T, typename A> inline std::vector<T, A> get_val_from_db(sqlite3_value *value, result_type<std::vector<T, A>>) { template<typename T, typename A> inline std::vector<T, A> get_val_from_db(sqlite3_value *value, result_type<std::vector<T, A>>) {
if(sqlite3_value_type(value) == SQLITE_NULL) { if(sqlite3_value_type(value) == SQLITE_NULL) {
return {}; return {};
} }
int bytes = sqlite3_value_bytes(value); int bytes = sqlite3_value_bytes(value);
T const* buf = reinterpret_cast<T const *>(sqlite3_value_blob(value)); T const* buf = reinterpret_cast<T const *>(sqlite3_value_blob(value));
return std::vector<T, A>(buf, buf + bytes/sizeof(T)); return std::vector<T, A>(buf, buf + bytes/sizeof(T));
} }
/* for unique_ptr<T> support */ /* for unique_ptr<T> support */
template<typename T, int Type> template<typename T, int Type>
struct has_sqlite_type<std::unique_ptr<T>, Type, void> : has_sqlite_type<T, Type> {}; struct has_sqlite_type<std::unique_ptr<T>, Type, void> : has_sqlite_type<T, Type> {};
template<typename T> template<typename T>
struct has_sqlite_type<std::unique_ptr<T>, SQLITE_NULL, void> : std::true_type {}; struct has_sqlite_type<std::unique_ptr<T>, SQLITE_NULL, void> : std::true_type {};
template<typename T> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr<T>& val) { template<typename T> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const std::unique_ptr<T>& val) {
return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr); return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr);
} }
template<typename T> inline std::unique_ptr<T> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::unique_ptr<T>>) { template<typename T> inline std::unique_ptr<T> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<std::unique_ptr<T>>) {
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return nullptr; return nullptr;
} }
return std::make_unique<T>(get_col_from_db(stmt, inx, result_type<T>())); return std::make_unique<T>(get_col_from_db(stmt, inx, result_type<T>()));
} }
template<typename T> inline std::unique_ptr<T> get_val_from_db(sqlite3_value *value, result_type<std::unique_ptr<T>>) { template<typename T> inline std::unique_ptr<T> get_val_from_db(sqlite3_value *value, result_type<std::unique_ptr<T>>) {
if(sqlite3_value_type(value) == SQLITE_NULL) { if(sqlite3_value_type(value) == SQLITE_NULL) {
return nullptr; return nullptr;
} }
return std::make_unique<T>(get_val_from_db(value, result_type<T>())); return std::make_unique<T>(get_val_from_db(value, result_type<T>()));
} }
// std::optional support for NULL values // std::optional support for NULL values
#ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT #ifdef MODERN_SQLITE_STD_OPTIONAL_SUPPORT
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
template<class T> template<class T>
using optional = std::experimental::optional<T>; using optional = std::experimental::optional<T>;
#else #else
template<class T> template<class T>
using optional = std::optional<T>; using optional = std::optional<T>;
#endif #endif
template<typename T, int Type> template<typename T, int Type>
struct has_sqlite_type<optional<T>, Type, void> : has_sqlite_type<T, Type> {}; struct has_sqlite_type<optional<T>, Type, void> : has_sqlite_type<T, Type> {};
template<typename T> template<typename T>
struct has_sqlite_type<optional<T>, SQLITE_NULL, void> : std::true_type {}; struct has_sqlite_type<optional<T>, SQLITE_NULL, void> : std::true_type {};
template <typename OptionalT> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional<OptionalT>& val) { template <typename OptionalT> inline int bind_col_in_db(sqlite3_stmt* stmt, int inx, const optional<OptionalT>& val) {
return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr); return val ? bind_col_in_db(stmt, inx, *val) : bind_col_in_db(stmt, inx, nullptr);
} }
template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const optional<OptionalT>& val) { template <typename OptionalT> inline void store_result_in_db(sqlite3_context* db, const optional<OptionalT>& val) {
if(val) if(val)
store_result_in_db(db, *val); store_result_in_db(db, *val);
else else
sqlite3_result_null(db); sqlite3_result_null(db);
} }
template <typename OptionalT> inline optional<OptionalT> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<optional<OptionalT>>) { template <typename OptionalT> inline optional<OptionalT> get_col_from_db(sqlite3_stmt* stmt, int inx, result_type<optional<OptionalT>>) {
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return std::experimental::nullopt; return std::experimental::nullopt;
} }
return std::experimental::make_optional(get_col_from_db(stmt, inx, result_type<OptionalT>())); return std::experimental::make_optional(get_col_from_db(stmt, inx, result_type<OptionalT>()));
#else #else
if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) { if(sqlite3_column_type(stmt, inx) == SQLITE_NULL) {
return std::nullopt; return std::nullopt;
} }
return std::make_optional(get_col_from_db(stmt, inx, result_type<OptionalT>())); return std::make_optional(get_col_from_db(stmt, inx, result_type<OptionalT>()));
#endif #endif
} }
template <typename OptionalT> inline optional<OptionalT> get_val_from_db(sqlite3_value *value, result_type<optional<OptionalT>>) { template <typename OptionalT> inline optional<OptionalT> get_val_from_db(sqlite3_value *value, result_type<optional<OptionalT>>) {
#ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT #ifdef MODERN_SQLITE_EXPERIMENTAL_OPTIONAL_SUPPORT
if(sqlite3_value_type(value) == SQLITE_NULL) { if(sqlite3_value_type(value) == SQLITE_NULL) {
return std::experimental::nullopt; return std::experimental::nullopt;
} }
return std::experimental::make_optional(get_val_from_db(value, result_type<OptionalT>())); return std::experimental::make_optional(get_val_from_db(value, result_type<OptionalT>()));
#else #else
if(sqlite3_value_type(value) == SQLITE_NULL) { if(sqlite3_value_type(value) == SQLITE_NULL) {
return std::nullopt; return std::nullopt;
} }
return std::make_optional(get_val_from_db(value, result_type<OptionalT>())); return std::make_optional(get_val_from_db(value, result_type<OptionalT>()));
#endif #endif
} }
#endif #endif
#ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT #ifdef MODERN_SQLITE_STD_VARIANT_SUPPORT
namespace detail { namespace detail {
template<class T, class U> template<class T, class U>
struct tag_trait : U { using tag = T; }; struct tag_trait : U { using tag = T; };
} }

View file

@ -303,8 +303,7 @@ BOOST_AUTO_TEST_CASE(takeover_workaround_triggers)
CClaimTrieCacheTest cache(&trie); CClaimTrieCacheTest cache(&trie);
insertUndoType icu, isu; claimQueueRowType ecu; supportQueueRowType esu; insertUndoType icu, isu; claimQueueRowType ecu; supportQueueRowType esu;
std::vector<std::pair<std::string, int>> thu; BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu));
CClaimValue value; CClaimValue value;
value.nHeight = 1; value.nHeight = 1;
@ -317,9 +316,9 @@ BOOST_AUTO_TEST_CASE(takeover_workaround_triggers)
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value)); BOOST_CHECK(cache.insertClaimIntoTrie("cc", value));
BOOST_CHECK(cache.insertSupportIntoMap("aa", CSupportValue())); BOOST_CHECK(cache.insertSupportIntoMap("aa", CSupportValue()));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu)); BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu));
BOOST_CHECK(cache.flush()); BOOST_CHECK(cache.flush());
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu)); BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu));
BOOST_CHECK_EQUAL(0, cache.getTotalNamesInTrie()); BOOST_CHECK_EQUAL(0, cache.getTotalNamesInTrie());
CSupportValue temp; CSupportValue temp;
@ -336,7 +335,7 @@ BOOST_AUTO_TEST_CASE(takeover_workaround_triggers)
BOOST_CHECK(cache.insertClaimIntoTrie("bb", value)); BOOST_CHECK(cache.insertClaimIntoTrie("bb", value));
BOOST_CHECK(cache.insertClaimIntoTrie("cc", value)); BOOST_CHECK(cache.insertClaimIntoTrie("cc", value));
BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu, thu)); BOOST_CHECK(cache.incrementBlock(icu, ecu, isu, esu));
BOOST_CHECK(cache.getInfoForName("aa", cv)); BOOST_CHECK(cache.getInfoForName("aa", cv));
BOOST_CHECK_EQUAL(3, cv.nValidAtHeight); BOOST_CHECK_EQUAL(3, cv.nValidAtHeight);

View file

@ -227,8 +227,7 @@ BOOST_AUTO_TEST_CASE(claimtriecache_normalization)
claimQueueRowType expireUndo; claimQueueRowType expireUndo;
insertUndoType insertSupportUndo; insertUndoType insertSupportUndo;
supportQueueRowType expireSupportUndo; supportQueueRowType expireSupportUndo;
std::vector<std::pair<std::string, int> > takeoverHeightUndo; BOOST_CHECK(trieCache.incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo));
BOOST_CHECK(trieCache.incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo));
BOOST_CHECK(trieCache.shouldNormalize()); BOOST_CHECK(trieCache.shouldNormalize());
} }
@ -314,14 +313,13 @@ BOOST_AUTO_TEST_CASE(normalization_removal_test)
claimQueueRowType expireUndo; claimQueueRowType expireUndo;
insertUndoType insertSupportUndo; insertUndoType insertSupportUndo;
supportQueueRowType expireSupportUndo; supportQueueRowType expireSupportUndo;
std::vector<std::pair<std::string, int> > takeoverHeightUndo; BOOST_CHECK(cache.incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo));
BOOST_CHECK(cache.incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverHeightUndo));
BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports.size() == 3U); BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports.size() == 3U);
BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[0].supports.size() == 1U); BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[0].supports.size() == 1U);
BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[1].supports.size() == 0U); BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[1].supports.size() == 0U);
BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[2].supports.size() == 1U); BOOST_CHECK(cache.getClaimsForName("ab").claimsNsupports[2].supports.size() == 1U);
BOOST_CHECK(cache.decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo)); BOOST_CHECK(cache.decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo));
BOOST_CHECK(cache.finalizeDecrement(takeoverHeightUndo)); BOOST_CHECK(cache.finalizeDecrement());
std::string unused; std::string unused;
BOOST_CHECK(cache.removeSupport(COutPoint(sx1.GetHash(), 0), unused, height)); BOOST_CHECK(cache.removeSupport(COutPoint(sx1.GetHash(), 0), unused, height));
BOOST_CHECK(cache.removeSupport(COutPoint(sx2.GetHash(), 0), unused, height)); BOOST_CHECK(cache.removeSupport(COutPoint(sx2.GetHash(), 0), unused, height));

View file

@ -78,7 +78,6 @@ public:
claimQueueRowType expireUndo; // any claims that expired claimQueueRowType expireUndo; // any claims that expired
insertUndoType insertSupportUndo; // any supports that went from the support queue to the support map insertUndoType insertSupportUndo; // any supports that went from the support queue to the support map
supportQueueRowType expireSupportUndo; // any supports that expired supportQueueRowType expireSupportUndo; // any supports that expired
std::vector<std::pair<std::string, int> > takeoverHeightUndo; // for any name that was taken over, the previous time that name was taken over
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
@ -89,7 +88,6 @@ public:
READWRITE(expireUndo); READWRITE(expireUndo);
READWRITE(insertSupportUndo); READWRITE(insertSupportUndo);
READWRITE(expireSupportUndo); READWRITE(expireSupportUndo);
READWRITE(takeoverHeightUndo);
} }
}; };

View file

@ -1559,7 +1559,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
// move best block pointer to prevout block // move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash()); view.SetBestBlock(pindex->pprev->GetBlockHash());
assert(trieCache.finalizeDecrement(blockUndo.takeoverHeightUndo)); assert(trieCache.finalizeDecrement());
auto merkleHash = trieCache.getMerkleHash(); auto merkleHash = trieCache.getMerkleHash();
if (merkleHash != pindex->pprev->hashClaimTrie) { if (merkleHash != pindex->pprev->hashClaimTrie) {
LogPrintf("Hash comparison failure at block %d\n", pindex->nHeight); LogPrintf("Hash comparison failure at block %d\n", pindex->nHeight);
@ -2033,7 +2033,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
} }
// TODO: if the "just check" flag is set, we should reduce the work done here. Incrementing blocks twice per mine is not efficient. // TODO: if the "just check" flag is set, we should reduce the work done here. Incrementing blocks twice per mine is not efficient.
const auto incremented = trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo, blockundo.insertSupportUndo, blockundo.expireSupportUndo, blockundo.takeoverHeightUndo); const auto incremented = trieCache.incrementBlock(blockundo.insertUndo, blockundo.expireUndo, blockundo.insertSupportUndo, blockundo.expireSupportUndo);
assert(incremented); assert(incremented);
if (trieCache.getMerkleHash() != block.hashClaimTrie) if (trieCache.getMerkleHash() != block.hashClaimTrie)