2019-07-09 17:02:54 +02:00
|
|
|
|
2019-10-22 09:57:05 +02:00
|
|
|
#include <forks.h>
|
|
|
|
#include <hashes.h>
|
|
|
|
#include <log.h>
|
|
|
|
#include <trie.h>
|
2018-08-06 21:40:20 +02:00
|
|
|
|
2019-11-01 17:49:39 +01:00
|
|
|
#include <tuple>
|
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
#include <boost/locale.hpp>
|
2018-08-06 21:40:20 +02:00
|
|
|
#include <boost/locale/conversion.hpp>
|
|
|
|
#include <boost/locale/localization_backend.hpp>
|
2019-08-02 02:35:58 +02:00
|
|
|
#include <boost/scoped_ptr.hpp>
|
2018-08-06 21:40:20 +02:00
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
#define logPrint CLogPrint::global()
|
2019-07-09 17:02:54 +02:00
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
CClaimTrieCacheExpirationFork::CClaimTrieCacheExpirationFork(CClaimTrie* base) : CClaimTrieCacheBase(base)
|
2019-07-09 17:02:54 +02:00
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
expirationHeight = nNextHeight;
|
2019-07-09 17:02:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CClaimTrieCacheExpirationFork::expirationTime() const
|
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
if (expirationHeight < base->nExtendedClaimExpirationForkHeight)
|
|
|
|
return CClaimTrieCacheBase::expirationTime();
|
|
|
|
return base->nExtendedClaimExpirationTime;
|
2019-07-09 17:02:54 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheExpirationFork::incrementBlock(insertUndoType& insertUndo, claimUndoType& expireUndo,
|
|
|
|
insertUndoType& insertSupportUndo, supportUndoType& expireSupportUndo, takeoverUndoType& takeoverUndo)
|
2019-07-09 17:02:54 +02:00
|
|
|
{
|
2019-10-31 06:13:07 +01:00
|
|
|
if (CClaimTrieCacheBase::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverUndo)) {
|
2019-10-09 17:09:30 +02:00
|
|
|
expirationHeight = nNextHeight;
|
2019-07-09 17:02:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheExpirationFork::decrementBlock(insertUndoType& insertUndo, claimUndoType& expireUndo, insertUndoType& insertSupportUndo, supportUndoType& expireSupportUndo)
|
2019-07-09 17:02:54 +02:00
|
|
|
{
|
|
|
|
if (CClaimTrieCacheBase::decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo)) {
|
2019-10-09 17:09:30 +02:00
|
|
|
expirationHeight = nNextHeight;
|
2019-07-09 17:02:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-19 17:36:53 +02:00
|
|
|
void CClaimTrieCacheExpirationFork::initializeIncrement()
|
2019-07-09 17:02:54 +02:00
|
|
|
{
|
2019-07-19 17:36:53 +02:00
|
|
|
// we could do this in the constructor, but that would not allow for multiple increments in a row (as done in unit tests)
|
2019-10-09 17:09:30 +02:00
|
|
|
if (nNextHeight != base->nExtendedClaimExpirationForkHeight)
|
2019-07-19 17:36:53 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
forkForExpirationChange(true);
|
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheExpirationFork::finalizeDecrement(takeoverUndoType& takeoverUndo)
|
2019-07-19 17:36:53 +02:00
|
|
|
{
|
2019-10-31 06:13:07 +01:00
|
|
|
auto ret = CClaimTrieCacheBase::finalizeDecrement(takeoverUndo);
|
2019-10-09 17:09:30 +02:00
|
|
|
if (ret && nNextHeight == base->nExtendedClaimExpirationForkHeight)
|
2019-07-19 17:36:53 +02:00
|
|
|
forkForExpirationChange(false);
|
|
|
|
return ret;
|
2019-07-09 17:02:54 +02:00
|
|
|
}
|
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
bool CClaimTrieCacheExpirationFork::forkForExpirationChange(bool increment)
|
2018-08-06 21:40:20 +02:00
|
|
|
{
|
2019-10-26 07:34:52 +02:00
|
|
|
if (!transacting) { transacting = true; db << "begin"; }
|
|
|
|
|
2018-08-06 21:40:20 +02:00
|
|
|
/*
|
|
|
|
If increment is True, we have forked to extend the expiration time, thus items in the expiration queue
|
|
|
|
will have their expiration extended by "new expiration time - original expiration time"
|
|
|
|
|
|
|
|
If increment is False, we are decremented a block to reverse the fork. Thus items in the expiration queue
|
|
|
|
will have their expiration extension removed.
|
|
|
|
*/
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
auto extension = base->nExtendedClaimExpirationTime - base->nOriginalClaimExpirationTime;
|
2019-11-01 00:02:56 +01:00
|
|
|
if (increment) {
|
|
|
|
db << "UPDATE claims SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?"
|
|
|
|
<< extension << nNextHeight;
|
|
|
|
db << "UPDATE supports SET expirationHeight = expirationHeight + ? WHERE expirationHeight >= ?"
|
|
|
|
<< extension << nNextHeight;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
db << "UPDATE claims SET expirationHeight = expirationHeight - ? WHERE expirationHeight >= ?"
|
|
|
|
<< extension << nNextHeight + extension;
|
|
|
|
db << "UPDATE supports SET expirationHeight = expirationHeight - ? WHERE expirationHeight >= ?"
|
|
|
|
<< extension << nNextHeight + extension;
|
|
|
|
}
|
2018-08-06 21:40:20 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
CClaimTrieCacheNormalizationFork::CClaimTrieCacheNormalizationFork(CClaimTrie* base) : CClaimTrieCacheExpirationFork(base)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
bool CClaimTrieCacheNormalizationFork::shouldNormalize() const
|
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
return nNextHeight > base->nNormalizedNameForkHeight;
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
std::string CClaimTrieCacheNormalizationFork::normalizeClaimName(const std::string& name, bool force) const
|
|
|
|
{
|
2018-08-06 21:40:20 +02:00
|
|
|
if (!force && !shouldNormalize())
|
|
|
|
return name;
|
|
|
|
|
|
|
|
static std::locale utf8;
|
|
|
|
static bool initialized = false;
|
|
|
|
if (!initialized) {
|
|
|
|
static boost::locale::localization_backend_manager manager =
|
2019-07-01 20:42:45 +02:00
|
|
|
boost::locale::localization_backend_manager::global();
|
2018-08-06 21:40:20 +02:00
|
|
|
manager.select("icu");
|
|
|
|
|
|
|
|
static boost::locale::generator curLocale(manager);
|
|
|
|
utf8 = curLocale("en_US.UTF8");
|
|
|
|
initialized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string normalized;
|
|
|
|
try {
|
|
|
|
// Check if it is a valid utf-8 string. If not, it will throw a
|
|
|
|
// boost::locale::conv::conversion_error exception which we catch later
|
|
|
|
normalized = boost::locale::conv::to_utf<char>(name, "UTF-8", boost::locale::conv::stop);
|
|
|
|
if (normalized.empty())
|
|
|
|
return name;
|
|
|
|
|
|
|
|
// these methods supposedly only use the "UTF8" portion of the locale object:
|
|
|
|
normalized = boost::locale::normalize(normalized, boost::locale::norm_nfd, utf8);
|
|
|
|
normalized = boost::locale::fold_case(normalized, utf8);
|
2019-07-01 20:42:45 +02:00
|
|
|
} catch (const boost::locale::conv::conversion_error& e) {
|
2018-08-06 21:40:20 +02:00
|
|
|
return name;
|
2019-07-01 20:42:45 +02:00
|
|
|
} catch (const std::bad_cast& e) {
|
2019-10-09 17:09:30 +02:00
|
|
|
logPrint << "CClaimTrieCacheNormalizationFork::" << __func__ << "() is invalid or dependencies are missing: " << e.what() << Clog::endl;
|
2018-08-06 21:40:20 +02:00
|
|
|
throw;
|
2019-07-01 20:42:45 +02:00
|
|
|
} catch (const std::exception& e) { // TODO: change to use ... with current_exception() in c++11
|
2019-10-09 17:09:30 +02:00
|
|
|
logPrint << "CClaimTrieCacheNormalizationFork::" << __func__ << "() had an unexpected exception: " << e.what() << Clog::endl;
|
2018-08-06 21:40:20 +02:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return normalized;
|
|
|
|
}
|
|
|
|
|
2019-11-01 22:53:11 +01:00
|
|
|
bool CClaimTrieCacheNormalizationFork::normalizeAllNamesInTrieIfNecessary(takeoverUndoType& takeoverUndo)
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2019-10-26 07:34:52 +02:00
|
|
|
if (!transacting) { transacting = true; db << "begin"; }
|
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
// run the one-time upgrade of all names that need to change
|
2019-10-24 23:21:36 +02:00
|
|
|
db.define("NORMALIZED", [this](const std::string& str) { return normalizeClaimName(str, true); });
|
2019-10-23 22:15:37 +02:00
|
|
|
|
2019-11-01 22:53:11 +01:00
|
|
|
// make the new nodes
|
|
|
|
db << "INSERT INTO nodes(name) SELECT NORMALIZED(name) AS nn FROM claims WHERE nn != nodeName "
|
2019-11-19 01:34:07 +01:00
|
|
|
"AND validHeight <= ?1 AND expirationHeight > ?1 ON CONFLICT(name) DO UPDATE SET hash = NULL" << nNextHeight;
|
2019-11-01 22:53:11 +01:00
|
|
|
db << "UPDATE nodes SET hash = NULL WHERE name IN "
|
2019-11-19 01:34:07 +01:00
|
|
|
"(SELECT NORMALIZED(name) AS nn FROM supports WHERE nn != nodeName "
|
|
|
|
"AND validHeight <= ?1 AND expirationHeight > ?1)" << nNextHeight;
|
2019-11-01 22:53:11 +01:00
|
|
|
|
|
|
|
// update the claims and supports
|
2019-11-19 01:34:07 +01:00
|
|
|
db << "UPDATE claims SET nodeName = NORMALIZED(name) WHERE validHeight <= ?1 AND expirationHeight > ?1" << nNextHeight;
|
|
|
|
db << "UPDATE supports SET nodeName = NORMALIZED(name) WHERE validHeight <= ?1 AND expirationHeight > ?1" << nNextHeight;
|
2019-11-01 22:53:11 +01:00
|
|
|
|
|
|
|
// remove the old nodes
|
2019-11-19 01:34:07 +01:00
|
|
|
auto query = db << "SELECT name, IFNULL(takeoverHeight, 0), takeoverID FROM nodes WHERE name NOT IN "
|
|
|
|
"(SELECT nodeName FROM claims WHERE validHeight <= ?1 AND expirationHeight > ?1)";
|
2019-11-01 22:53:11 +01:00
|
|
|
for (auto&& row: query) {
|
|
|
|
std::string name;
|
|
|
|
int takeoverHeight;
|
2019-10-09 17:09:30 +02:00
|
|
|
std::unique_ptr<CUint160> takeoverID;
|
2019-11-01 22:53:11 +01:00
|
|
|
row >> name >> takeoverHeight >> takeoverID;
|
|
|
|
if (name.empty()) continue; // preserve our root node
|
2019-11-19 01:34:07 +01:00
|
|
|
if (takeoverHeight > 0)
|
2019-10-09 17:09:30 +02:00
|
|
|
takeoverUndo.emplace_back(name, std::make_pair(takeoverHeight, takeoverID ? *takeoverID : CUint160()));
|
2019-11-01 22:53:11 +01:00
|
|
|
// we need to let the tree structure method do the actual node delete:
|
|
|
|
db << "UPDATE nodes SET hash = NULL WHERE name = ?" << name;
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
2019-11-19 01:34:07 +01:00
|
|
|
db << "UPDATE nodes SET hash = NULL WHERE takeoverHeight IS NULL";
|
|
|
|
|
|
|
|
// work around a bug in the old implementation:
|
|
|
|
db << "UPDATE claims SET validHeight = ?1 " // force a takeover on these
|
|
|
|
"WHERE blockHeight < ?1 AND validHeight > ?1 AND nodeName != name" << nNextHeight;
|
2019-10-23 22:15:37 +02:00
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
return true;
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
|
|
|
|
2019-11-01 22:53:11 +01:00
|
|
|
bool CClaimTrieCacheNormalizationFork::unnormalizeAllNamesInTrieIfNecessary()
|
|
|
|
{
|
|
|
|
if (!transacting) { transacting = true; db << "begin"; }
|
|
|
|
|
|
|
|
// run the one-time upgrade of all names that need to change
|
|
|
|
db.define("NORMALIZED", [this](const std::string& str) { return normalizeClaimName(str, true); });
|
|
|
|
|
|
|
|
db << "INSERT INTO nodes(name) SELECT name FROM claims WHERE name != nodeName "
|
2019-11-19 01:34:07 +01:00
|
|
|
"AND validHeight < ?1 AND expirationHeight > ?1 ON CONFLICT(name) DO UPDATE SET hash = NULL" << nNextHeight;
|
2019-11-01 22:53:11 +01:00
|
|
|
db << "UPDATE nodes SET hash = NULL WHERE name IN "
|
|
|
|
"(SELECT name FROM supports WHERE name != nodeName UNION "
|
|
|
|
"SELECT nodeName FROM supports WHERE name != nodeName UNION "
|
|
|
|
"SELECT nodeName FROM claims WHERE name != nodeName)";
|
|
|
|
|
|
|
|
db << "UPDATE claims SET nodeName = name";
|
|
|
|
db << "UPDATE supports SET nodeName = name";
|
|
|
|
// we need to let the tree structure method do the actual node delete
|
|
|
|
db << "UPDATE nodes SET hash = NULL WHERE name NOT IN "
|
|
|
|
"(SELECT name FROM claims)";
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheNormalizationFork::incrementBlock(insertUndoType& insertUndo, claimUndoType& expireUndo,
|
|
|
|
insertUndoType& insertSupportUndo, supportUndoType& expireSupportUndo, takeoverUndoType& takeoverUndo)
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
if (nNextHeight == base->nNormalizedNameForkHeight)
|
2019-11-01 22:53:11 +01:00
|
|
|
normalizeAllNamesInTrieIfNecessary(takeoverUndo);
|
2019-10-09 17:09:30 +02:00
|
|
|
return CClaimTrieCacheExpirationFork::incrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo, takeoverUndo);
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheNormalizationFork::decrementBlock(insertUndoType& insertUndo, claimUndoType& expireUndo, insertUndoType& insertSupportUndo, supportUndoType& expireSupportUndo)
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2019-10-23 22:15:37 +02:00
|
|
|
auto ret = CClaimTrieCacheExpirationFork::decrementBlock(insertUndo, expireUndo, insertSupportUndo, expireSupportUndo);
|
2019-10-09 17:09:30 +02:00
|
|
|
if (ret && nNextHeight == base->nNormalizedNameForkHeight)
|
2019-11-01 22:53:11 +01:00
|
|
|
unnormalizeAllNamesInTrieIfNecessary();
|
2019-10-23 22:15:37 +02:00
|
|
|
return ret;
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
bool CClaimTrieCacheNormalizationFork::getProofForName(const std::string& name, const CUint160& claim, CClaimTrieProof& proof)
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
return CClaimTrieCacheExpirationFork::getProofForName(normalizeClaimName(name), claim, proof);
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheNormalizationFork::getInfoForName(const std::string& name, CClaimValue& claim, int offsetHeight) const
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2019-10-31 06:13:07 +01:00
|
|
|
return CClaimTrieCacheExpirationFork::getInfoForName(normalizeClaimName(name), claim, offsetHeight);
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
|
|
|
|
2019-08-08 15:39:57 +02:00
|
|
|
CClaimSupportToName CClaimTrieCacheNormalizationFork::getClaimsForName(const std::string& name) const
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2018-08-06 21:40:20 +02:00
|
|
|
return CClaimTrieCacheExpirationFork::getClaimsForName(normalizeClaimName(name));
|
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
int CClaimTrieCacheNormalizationFork::getDelayForName(const std::string& name, const CUint160& claimId) const
|
2019-07-01 20:42:45 +02:00
|
|
|
{
|
2018-08-06 21:40:20 +02:00
|
|
|
return CClaimTrieCacheExpirationFork::getDelayForName(normalizeClaimName(name), claimId);
|
|
|
|
}
|
|
|
|
|
2019-07-01 20:42:45 +02:00
|
|
|
std::string CClaimTrieCacheNormalizationFork::adjustNameForValidHeight(const std::string& name, int validHeight) const
|
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
return normalizeClaimName(name, validHeight > base->nNormalizedNameForkHeight);
|
2018-08-06 21:40:20 +02:00
|
|
|
}
|
2019-07-19 17:36:53 +02:00
|
|
|
|
|
|
|
CClaimTrieCacheHashFork::CClaimTrieCacheHashFork(CClaimTrie* base) : CClaimTrieCacheNormalizationFork(base)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
static const auto leafHash = CUint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
|
|
|
static const auto emptyHash = CUint256S("0000000000000000000000000000000000000000000000000000000000000003");
|
|
|
|
|
|
|
|
CUint256 ComputeMerkleRoot(std::vector<CUint256> hashes)
|
|
|
|
{
|
|
|
|
while (hashes.size() > 1) {
|
|
|
|
if (hashes.size() & 1)
|
|
|
|
hashes.push_back(hashes.back());
|
|
|
|
|
|
|
|
for (std::size_t i = 0, j = 0; i < hashes.size(); i += 2)
|
|
|
|
hashes[j++] = Hash(hashes[i].begin(), hashes[i].end(),
|
|
|
|
hashes[i+1].begin(), hashes[i+1].end());
|
|
|
|
|
|
|
|
hashes.resize(hashes.size() / 2);
|
|
|
|
}
|
|
|
|
return hashes.empty() ? CUint256{} : hashes[0];
|
|
|
|
}
|
2019-07-19 17:36:53 +02:00
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
CUint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(const std::string& name, int takeoverHeight, bool checkOnly)
|
2019-07-19 17:36:53 +02:00
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
if (nNextHeight < base->nAllClaimsInMerkleForkHeight)
|
2019-10-31 06:13:07 +01:00
|
|
|
return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(name, takeoverHeight, checkOnly);
|
2019-07-19 17:36:53 +02:00
|
|
|
|
2019-11-07 19:29:38 +01:00
|
|
|
// it may be that using RAM for this is more expensive than preparing a new query statement in each recursive call
|
2019-11-01 17:49:39 +01:00
|
|
|
std::vector<std::tuple<std::string, std::unique_ptr<CUint256>, int>> children;
|
|
|
|
childHashQuery << name >> [&children](std::string name, std::unique_ptr<CUint256> hash, int takeoverHeight) {
|
|
|
|
children.push_back(std::make_tuple(std::move(name), std::move(hash), takeoverHeight));
|
|
|
|
};
|
2019-11-07 19:29:38 +01:00
|
|
|
childHashQuery++;
|
2019-10-09 17:09:30 +02:00
|
|
|
std::vector<CUint256> childHashes;
|
2019-11-07 19:29:38 +01:00
|
|
|
for (auto& child: children) {
|
2019-11-01 17:49:39 +01:00
|
|
|
auto& name = std::get<0>(child);
|
|
|
|
auto& hash = std::get<1>(child);
|
|
|
|
if (!hash || hash->IsNull())
|
|
|
|
childHashes.push_back(recursiveComputeMerkleHash(name, std::get<2>(child), checkOnly));
|
|
|
|
else
|
|
|
|
childHashes.push_back(*hash);
|
2019-10-23 22:15:37 +02:00
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
std::vector<CUint256> claimHashes;
|
2019-11-22 18:05:38 +01:00
|
|
|
//if (takeoverHeight > 0) {
|
|
|
|
for (auto &&row: claimHashQuery << nNextHeight << name) {
|
2019-10-09 17:09:30 +02:00
|
|
|
CTxOutPoint p;
|
2019-11-22 18:05:38 +01:00
|
|
|
row >> p.hash >> p.n;
|
|
|
|
auto claimHash = getValueHash(p, takeoverHeight);
|
|
|
|
claimHashes.push_back(claimHash);
|
|
|
|
}
|
|
|
|
claimHashQuery++;
|
|
|
|
//}
|
2019-07-19 17:36:53 +02:00
|
|
|
|
2019-11-01 17:49:39 +01:00
|
|
|
auto left = childHashes.empty() ? leafHash : ComputeMerkleRoot(std::move(childHashes));
|
|
|
|
auto right = claimHashes.empty() ? emptyHash : ComputeMerkleRoot(std::move(claimHashes));
|
2019-07-19 17:36:53 +02:00
|
|
|
|
2019-10-23 22:15:37 +02:00
|
|
|
auto computedHash = Hash(left.begin(), left.end(), right.begin(), right.end());
|
|
|
|
if (!checkOnly)
|
2019-10-24 23:21:36 +02:00
|
|
|
db << "UPDATE nodes SET hash = ? WHERE name = ?" << computedHash << name;
|
2019-10-23 22:15:37 +02:00
|
|
|
return computedHash;
|
2019-09-30 20:13:05 +02:00
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
std::vector<CUint256> ComputeMerklePath(const std::vector<CUint256>& hashes, uint32_t idx)
|
2019-07-19 17:36:53 +02:00
|
|
|
{
|
|
|
|
uint32_t count = 0;
|
|
|
|
int matchlevel = -1;
|
|
|
|
bool matchh = false;
|
2019-10-09 17:09:30 +02:00
|
|
|
CUint256 inner[32], h;
|
2019-07-19 17:36:53 +02:00
|
|
|
const uint32_t one = 1;
|
2019-10-09 17:09:30 +02:00
|
|
|
std::vector<CUint256> res;
|
2019-07-19 17:36:53 +02:00
|
|
|
|
|
|
|
const auto iterateInner = [&](int& level) {
|
|
|
|
for (; !(count & (one << level)); level++) {
|
|
|
|
const auto& ihash = inner[level];
|
|
|
|
if (matchh) {
|
|
|
|
res.push_back(ihash);
|
|
|
|
} else if (matchlevel == level) {
|
|
|
|
res.push_back(h);
|
|
|
|
matchh = true;
|
|
|
|
}
|
|
|
|
h = Hash(ihash.begin(), ihash.end(), h.begin(), h.end());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
while (count < hashes.size()) {
|
|
|
|
h = hashes[count];
|
|
|
|
matchh = count == idx;
|
|
|
|
count++;
|
|
|
|
int level = 0;
|
|
|
|
iterateInner(level);
|
|
|
|
// Store the resulting hash at inner position level.
|
|
|
|
inner[level] = h;
|
|
|
|
if (matchh)
|
|
|
|
matchlevel = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
int level = 0;
|
|
|
|
while (!(count & (one << level)))
|
|
|
|
level++;
|
|
|
|
|
|
|
|
h = inner[level];
|
|
|
|
matchh = matchlevel == level;
|
|
|
|
|
|
|
|
while (count != (one << level)) {
|
|
|
|
// If we reach this point, h is an inner value that is not the top.
|
|
|
|
if (matchh)
|
|
|
|
res.push_back(h);
|
|
|
|
|
|
|
|
h = Hash(h.begin(), h.end(), h.begin(), h.end());
|
|
|
|
// Increment count to the value it would have if two entries at this
|
|
|
|
count += (one << level);
|
|
|
|
level++;
|
|
|
|
iterateInner(level);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const CUint160& claim, CClaimTrieProof& proof)
|
2019-07-19 17:36:53 +02:00
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
if (nNextHeight < base->nAllClaimsInMerkleForkHeight)
|
|
|
|
return CClaimTrieCacheNormalizationFork::getProofForName(name, claim, proof);
|
2019-07-19 17:36:53 +02:00
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
auto fillPairs = [&proof](const std::vector<CUint256>& hashes, uint32_t idx) {
|
2019-07-19 17:36:53 +02:00
|
|
|
auto partials = ComputeMerklePath(hashes, idx);
|
|
|
|
for (int i = partials.size() - 1; i >= 0; --i)
|
|
|
|
proof.pairs.emplace_back((idx >> i) & 1, partials[i]);
|
|
|
|
};
|
|
|
|
|
|
|
|
// cache the parent nodes
|
|
|
|
getMerkleHash();
|
|
|
|
proof = CClaimTrieProof();
|
2019-10-31 06:13:07 +01:00
|
|
|
auto nodeQuery = db << "SELECT name, IFNULL(takeoverHeight, 0) FROM nodes WHERE "
|
2019-10-23 22:15:37 +02:00
|
|
|
"name IN (WITH RECURSIVE prefix(p) AS (VALUES(?) UNION ALL "
|
2019-11-19 01:34:07 +01:00
|
|
|
"SELECT POPS(p) FROM prefix WHERE p != '') SELECT p FROM prefix) "
|
2019-11-01 23:59:47 +01:00
|
|
|
"ORDER BY name" << name;
|
2019-10-23 22:15:37 +02:00
|
|
|
for (auto&& row: nodeQuery) {
|
2019-10-09 17:09:30 +02:00
|
|
|
std::string key;
|
2019-10-31 06:13:07 +01:00
|
|
|
int takeoverHeight;
|
|
|
|
row >> key >> takeoverHeight;
|
2019-07-19 17:36:53 +02:00
|
|
|
uint32_t nextCurrentIdx = 0;
|
2019-10-09 17:09:30 +02:00
|
|
|
std::vector<CUint256> childHashes;
|
2019-11-07 19:29:38 +01:00
|
|
|
for (auto&& child : childHashQuery << key) {
|
2019-10-23 22:15:37 +02:00
|
|
|
std::string childKey;
|
2019-10-09 17:09:30 +02:00
|
|
|
CUint256 childHash;
|
2019-10-23 22:15:37 +02:00
|
|
|
child >> childKey >> childHash;
|
|
|
|
if (name.find(childKey) == 0)
|
2019-07-19 17:36:53 +02:00
|
|
|
nextCurrentIdx = uint32_t(childHashes.size());
|
2019-10-23 22:15:37 +02:00
|
|
|
childHashes.push_back(childHash);
|
2019-07-19 17:36:53 +02:00
|
|
|
}
|
2019-11-07 19:29:38 +01:00
|
|
|
childHashQuery++;
|
2019-11-01 23:59:47 +01:00
|
|
|
|
2019-10-09 17:09:30 +02:00
|
|
|
std::vector<CUint256> claimHashes;
|
|
|
|
uint32_t claimIdx = 0;
|
2019-11-07 19:29:38 +01:00
|
|
|
for (auto&& child: claimHashQuery << nNextHeight << key) {
|
2019-10-09 17:09:30 +02:00
|
|
|
CTxOutPoint childOutPoint;
|
|
|
|
CUint160 childClaimID;
|
2019-11-01 23:59:47 +01:00
|
|
|
child >> childOutPoint.hash >> childOutPoint.n >> childClaimID;
|
2019-10-09 17:09:30 +02:00
|
|
|
if (childClaimID == claim && key == name) {
|
|
|
|
claimIdx = uint32_t(claimHashes.size());
|
2019-11-01 23:59:47 +01:00
|
|
|
proof.outPoint = childOutPoint;
|
|
|
|
proof.hasValue = true;
|
2019-10-23 22:15:37 +02:00
|
|
|
}
|
2019-11-01 23:59:47 +01:00
|
|
|
claimHashes.push_back(getValueHash(childOutPoint, takeoverHeight));
|
2019-10-23 22:15:37 +02:00
|
|
|
}
|
2019-11-07 19:29:38 +01:00
|
|
|
claimHashQuery++;
|
2019-07-19 17:36:53 +02:00
|
|
|
|
|
|
|
// I am on a node; I need a hash(children, claims)
|
|
|
|
// if I am the last node on the list, it will be hash(children, x)
|
|
|
|
// else it will be hash(x, claims)
|
2019-10-23 22:15:37 +02:00
|
|
|
if (key == name) {
|
2019-11-01 23:59:47 +01:00
|
|
|
proof.nHeightOfLastTakeover = takeoverHeight;
|
2019-07-19 17:36:53 +02:00
|
|
|
auto hash = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes);
|
|
|
|
proof.pairs.emplace_back(true, hash);
|
|
|
|
if (!claimHashes.empty())
|
2019-10-09 17:09:30 +02:00
|
|
|
fillPairs(claimHashes, claimIdx);
|
2019-07-19 17:36:53 +02:00
|
|
|
} else {
|
|
|
|
auto hash = claimHashes.empty() ? emptyHash : ComputeMerkleRoot(claimHashes);
|
|
|
|
proof.pairs.emplace_back(false, hash);
|
|
|
|
if (!childHashes.empty())
|
|
|
|
fillPairs(childHashes, nextCurrentIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::reverse(proof.pairs.begin(), proof.pairs.end());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CClaimTrieCacheHashFork::initializeIncrement()
|
|
|
|
{
|
|
|
|
CClaimTrieCacheNormalizationFork::initializeIncrement();
|
|
|
|
// we could do this in the constructor, but that would not allow for multiple increments in a row (as done in unit tests)
|
2019-10-09 17:09:30 +02:00
|
|
|
if (nNextHeight == base->nAllClaimsInMerkleForkHeight - 1) {
|
2019-11-01 23:59:47 +01:00
|
|
|
if (!transacting) { transacting = true; db << "begin"; }
|
2019-10-24 23:21:36 +02:00
|
|
|
db << "UPDATE nodes SET hash = NULL";
|
2019-11-01 23:59:47 +01:00
|
|
|
}
|
2019-07-19 17:36:53 +02:00
|
|
|
}
|
|
|
|
|
2019-10-31 06:13:07 +01:00
|
|
|
bool CClaimTrieCacheHashFork::finalizeDecrement(takeoverUndoType& takeoverUndo)
|
2019-07-19 17:36:53 +02:00
|
|
|
{
|
2019-10-31 06:13:07 +01:00
|
|
|
auto ret = CClaimTrieCacheNormalizationFork::finalizeDecrement(takeoverUndo);
|
2019-10-09 17:09:30 +02:00
|
|
|
if (ret && nNextHeight == base->nAllClaimsInMerkleForkHeight - 1) {
|
2019-11-01 23:59:47 +01:00
|
|
|
if (!transacting) { transacting = true; db << "begin"; }
|
2019-10-24 23:21:36 +02:00
|
|
|
db << "UPDATE nodes SET hash = NULL";
|
2019-11-01 23:59:47 +01:00
|
|
|
}
|
2019-07-19 17:36:53 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2019-09-04 22:27:07 +02:00
|
|
|
|
2019-09-30 20:13:05 +02:00
|
|
|
bool CClaimTrieCacheHashFork::allowSupportMetadata() const
|
|
|
|
{
|
2019-10-09 17:09:30 +02:00
|
|
|
return nNextHeight >= base->nAllClaimsInMerkleForkHeight;
|
2019-09-04 22:27:07 +02:00
|
|
|
}
|