From dfc5ebf3f62af99de4d9e4e9086791288bcfa4af Mon Sep 17 00:00:00 2001 From: Brannon King <countprimes@gmail.com> Date: Thu, 7 Nov 2019 11:29:38 -0700 Subject: [PATCH] It passes 200k --- src/claimscriptop.cpp | 60 ++++++----- src/claimtrie.cpp | 142 +++++++++++++++----------- src/claimtrie.h | 2 + src/claimtrieforks.cpp | 48 ++++----- src/test/claimtriebranching_tests.cpp | 19 ++++ 5 files changed, 162 insertions(+), 109 deletions(-) diff --git a/src/claimscriptop.cpp b/src/claimscriptop.cpp index 43f68c58f..9b733607e 100644 --- a/src/claimscriptop.cpp +++ b/src/claimscriptop.cpp @@ -14,12 +14,17 @@ CClaimScriptAddOp::CClaimScriptAddOp(const COutPoint& point, CAmount nValue, int bool CClaimScriptAddOp::claimName(CClaimTrieCache& trieCache, const std::string& name, const std::vector<unsigned char>& metadata) { - return addClaim(trieCache, name, ClaimIdHash(point.hash, point.n), -1, metadata); + auto claimId = ClaimIdHash(point.hash, point.n); + LogPrint(BCLog::CLAIMS, "+++ Claim added: %s, c: %.6s, t: %.6s:%d, h: %.6d, a: %du\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValue); + return addClaim(trieCache, name, claimId, -1, metadata); } bool CClaimScriptAddOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { + LogPrint(BCLog::CLAIMS, "+++ Claim updated: %s, c: %.6s, t: %.6s:%d, h: %.6d, a: %du\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValue); return addClaim(trieCache, name, claimId, -1, metadata); } @@ -32,6 +37,8 @@ bool CClaimScriptAddOp::addClaim(CClaimTrieCache& trieCache, const std::string& bool CClaimScriptAddOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { + LogPrint(BCLog::CLAIMS, "+++ Support added: %s, c: %.6s, t: %.6s:%d, h: %.6d, a: %du\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValue); return trieCache.addSupport(name, point, nValue, claimId, nHeight, -1, metadata); } @@ -43,14 +50,16 @@ bool CClaimScriptUndoAddOp::claimName(CClaimTrieCache& trieCache, const std::str const std::vector<unsigned char>& metadata) { auto claimId = ClaimIdHash(point.hash, point.n); - LogPrint(BCLog::CLAIMS, "--- [%lu]: Undoing OP_CLAIM_NAME \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n); + LogPrint(BCLog::CLAIMS, "--- Undoing claim add: %s, c: %.6s, t: %.6s:%d, h: %.6d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight); return undoAddClaim(trieCache, name, claimId); } bool CClaimScriptUndoAddOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { - LogPrint(BCLog::CLAIMS, "--- [%lu]: Undoing OP_UPDATE_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n); + LogPrint(BCLog::CLAIMS, "--- Undoing claim update: %s, c: %.6s, t: %.6s:%d, h: %.6d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight); return undoAddClaim(trieCache, name, claimId); } @@ -60,22 +69,20 @@ bool CClaimScriptUndoAddOp::undoAddClaim(CClaimTrieCache& trieCache, const std:: int validHeight; bool res = trieCache.removeClaim(claimId, point, nodeName, validHeight); if (!res) - LogPrint(BCLog::CLAIMS, "%s: Removing claim fails\n", __func__); + LogPrint(BCLog::CLAIMS, "Remove claim failed for %s with claimid %s\n", name, claimId.GetHex().substr(0, 6)); return res; } bool CClaimScriptUndoAddOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { - if (LogAcceptCategory(BCLog::CLAIMS)) { - LogPrintf("--- [%lu]: Undoing OP_SUPPORT_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, - claimId.GetHex(), point.hash.ToString(), point.n); - } + LogPrint(BCLog::CLAIMS, "--- Undoing support add: %s, c: %.6s, t: %.6s:%d, h: %.6d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight); std::string nodeName; int validHeight; bool res = trieCache.removeSupport(point, nodeName, validHeight); if (!res) - LogPrint(BCLog::CLAIMS, "%s: Removing support fails\n", __func__); + LogPrint(BCLog::CLAIMS, "Remove support failed for %s with claimid %s\n", name, claimId.GetHex().substr(0, 6)); return res; } @@ -88,15 +95,19 @@ bool CClaimScriptSpendOp::claimName(CClaimTrieCache& trieCache, const std::strin const std::vector<unsigned char>& metadata) { auto claimId = ClaimIdHash(point.hash, point.n); - LogPrint(BCLog::CLAIMS, "+++ [%lu]: Spending OP_CLAIM_NAME \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n); - return spendClaim(trieCache, name, claimId); + auto ret = spendClaim(trieCache, name, claimId); + LogPrint(BCLog::CLAIMS, "--- Spending original claim: %s, c: %.6s, t: %.6s:%d, h: %.6d, vh: %d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValidHeight); + return ret; } bool CClaimScriptSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { - LogPrint(BCLog::CLAIMS, "+++ [%lu]: Spending OP_UPDATE_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, claimId.GetHex(), point.hash.ToString(), point.n); - return spendClaim(trieCache, name, claimId); + auto ret = spendClaim(trieCache, name, claimId); + LogPrint(BCLog::CLAIMS, "--- Spending updated claim: %s, c: %.6s, t: %.6s:%d, h: %.6d, vh: %d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValidHeight); + return ret; } bool CClaimScriptSpendOp::spendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId) @@ -104,21 +115,19 @@ bool CClaimScriptSpendOp::spendClaim(CClaimTrieCache& trieCache, const std::stri std::string nodeName; bool res = trieCache.removeClaim(claimId, point, nodeName, nValidHeight); if (!res) - LogPrint(BCLog::CLAIMS, "%s: Removing fails\n", __func__); + LogPrint(BCLog::CLAIMS, "Remove claim failed for %s with claimid %s\n", name, claimId.GetHex().substr(0, 6)); return res; } bool CClaimScriptSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { - if (LogAcceptCategory(BCLog::CLAIMS)) { - LogPrintf("+++ [%lu]: Spending OP_SUPPORT_CLAIM \"%s\" with claimId %s and tx prevout %s at index %d\n", nHeight, name, - claimId.GetHex(), point.hash.ToString(), point.n); - } std::string nodeName; bool res = trieCache.removeSupport(point, nodeName, nValidHeight); + LogPrint(BCLog::CLAIMS, "--- Spending support: %s, c: %.6s, t: %.6s:%d, h: %.6d, vh: %d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValidHeight); if (!res) - LogPrint(BCLog::CLAIMS, "%s: Removing support fails\n", __func__); + LogPrint(BCLog::CLAIMS, "Remove support failed for %s with claimid %s\n", name, claimId.GetHex().substr(0, 6)); return res; } @@ -130,26 +139,31 @@ CClaimScriptUndoSpendOp::CClaimScriptUndoSpendOp(const COutPoint& point, CAmount bool CClaimScriptUndoSpendOp::claimName(CClaimTrieCache& trieCache, const std::string& name, const std::vector<unsigned char>& metadata) { - return undoSpendClaim(trieCache, name, ClaimIdHash(point.hash, point.n), metadata); + auto claimId = ClaimIdHash(point.hash, point.n); + LogPrint(BCLog::CLAIMS, "+++ Undoing original claim spend: %s, c: %.6s, t: %.6s:%d, h: %.6d, vh: %d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValidHeight); + return undoSpendClaim(trieCache, name, claimId, metadata); } bool CClaimScriptUndoSpendOp::updateClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { + LogPrint(BCLog::CLAIMS, "+++ Undoing updated claim spend: %s, c: %.6s, t: %.6s:%d, h: %.6d, vh: %d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValidHeight); return undoSpendClaim(trieCache, name, claimId, metadata); } bool CClaimScriptUndoSpendOp::undoSpendClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { - LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Undoing spend of %s, claimId: %s, to the claim trie due to block disconnect\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString()); return trieCache.addClaim(name, point, claimId, nValue, nHeight, nValidHeight, metadata); } bool CClaimScriptUndoSpendOp::supportClaim(CClaimTrieCache& trieCache, const std::string& name, const uint160& claimId, const std::vector<unsigned char>& metadata) { - LogPrint(BCLog::CLAIMS, "%s: (txid: %s, nOut: %d) Restoring support for %s, claimId: %s, to the claim trie due to block disconnect\n", __func__, point.hash.ToString(), point.n, name, claimId.ToString()); + LogPrint(BCLog::CLAIMS, "+++ Undoing support spend: %s, c: %.6s, t: %.6s:%d, h: %.6d, vh: %d\n", + name, claimId.GetHex(), point.hash.GetHex(), point.n, nHeight, nValidHeight); return trieCache.addSupport(name, point, nValue, claimId, nHeight, nValidHeight, metadata); } @@ -233,7 +247,7 @@ void UpdateCache(const CTransaction& tx, CClaimTrieCache& trieCache, const CCoin const std::vector<unsigned char>& metadata) override { if (callback(name, claimId)) - return CClaimScriptAddOp::addClaim(trieCache, name, claimId, -1, metadata); + return CClaimScriptAddOp::updateClaim(trieCache, name, claimId, metadata); return false; } std::function<bool(const std::string& name, const uint160& claimId)> callback; diff --git a/src/claimtrie.cpp b/src/claimtrie.cpp index 01d48fd74..b6b97e34e 100644 --- a/src/claimtrie.cpp +++ b/src/claimtrie.cpp @@ -56,7 +56,8 @@ CClaimTrie::CClaimTrie(bool fWipe, int height, int proportionalDelayFactor) // 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 << "CREATE TABLE IF NOT EXISTS nodes (name TEXT NOT NULL PRIMARY KEY, parent TEXT, hash BLOB COLLATE BINARY, takeoverHeight INTEGER, takeoverID BLOB COLLATE BINARY)"; + db << "CREATE TABLE IF NOT EXISTS nodes (name TEXT NOT NULL PRIMARY KEY, parent TEXT REFERENCES nodes(name) DEFERRABLE INITIALLY DEFERRED, " + "hash BLOB COLLATE BINARY, takeoverHeight INTEGER, takeoverID BLOB COLLATE BINARY)"; db << "CREATE INDEX IF NOT EXISTS nodes_hash ON nodes (hash)"; db << "CREATE INDEX IF NOT EXISTS nodes_parent ON nodes (parent)"; @@ -109,23 +110,23 @@ bool CClaimTrie::SyncToDisk() bool CClaimTrie::empty() { int64_t count; - db << "SELECT COUNT(*) FROM claims WHERE validHeight < ? AND expirationHeight >= ?" << nNextHeight << nNextHeight >> count; + db << "SELECT COUNT(*) FROM claims WHERE validHeight < ?1 AND expirationHeight >= ?1" << nNextHeight >> count; return count == 0; } bool CClaimTrieCacheBase::haveClaim(const std::string& name, const COutPoint& outPoint) const { - auto query = db << "SELECT 1 FROM claims WHERE nodeName = ? AND txID = ? AND txN = ? " - "AND validHeight < ? AND expirationHeight >= ? LIMIT 1" - << name << outPoint.hash << outPoint.n << nNextHeight << nNextHeight; + auto query = db << "SELECT 1 FROM claims WHERE nodeName = ?1 AND txID = ?2 AND txN = ?3 " + "AND validHeight < ?4 AND expirationHeight >= ?4 LIMIT 1" + << name << outPoint.hash << outPoint.n << nNextHeight; return query.begin() != query.end(); } bool CClaimTrieCacheBase::haveSupport(const std::string& name, const COutPoint& outPoint) const { - auto query = db << "SELECT 1 FROM supports WHERE nodeName = ? AND txID = ? AND txN = ? " - "AND validHeight < ? AND expirationHeight >= ? LIMIT 1" - << name << outPoint.hash << outPoint.n << nNextHeight << nNextHeight; + auto query = db << "SELECT 1 FROM supports WHERE nodeName = ?1 AND txID = ?2 AND txN = ?3 " + "AND validHeight < ?4 AND expirationHeight >= ?4 LIMIT 1" + << name << outPoint.hash << outPoint.n << nNextHeight; return query.begin() != query.end(); } @@ -181,8 +182,8 @@ bool CClaimTrieCacheBase::deleteNodeIfPossible(const std::string& name, std::str if (name.empty()) return false; // to remove a node it must have one or less children and no claims vector_builder<std::string, std::string> claimsBuilder; - db << "SELECT name FROM claims WHERE nodeName = ? AND validHeight < ? AND expirationHeight >= ? " - << name << nNextHeight << nNextHeight >> claimsBuilder; + db << "SELECT name FROM claims WHERE nodeName = ?1 AND validHeight < ?2 AND expirationHeight >= ?2 " + << name << nNextHeight >> claimsBuilder; claims = std::move(claimsBuilder); if (!claims.empty()) return false; // still has claims // we now know it has no claims, but we need to check its children @@ -193,6 +194,7 @@ bool CClaimTrieCacheBase::deleteNodeIfPossible(const std::string& name, std::str // alternately: SELECT COUNT(DISTINCT nodeName) FROM claims WHERE SUBSTR(nodeName, 1, len(?)) == ? AND LENGTH(nodeName) > len(?) db << "SELECT COUNT(*),MAX(name) FROM nodes WHERE parent = ?" << name >> std::tie(count, childName); if (count > 1) return false; // still has multiple children + LogPrint(BCLog::CLAIMS, "Removing node %s with %d children\n", name, count); // okay. it's going away auto query = db << "SELECT parent FROM nodes WHERE name = ?" << name; auto it = query.begin(); @@ -271,6 +273,7 @@ void CClaimTrieCacheBase::ensureTreeStructureIsUpToDate() { break; } // insert the split node: + LogPrint(BCLog::CLAIMS, "Inserting split node %s, parent %s\n", newNodeName, parent); db << "INSERT INTO nodes(name, parent, hash) VALUES(?, ?, NULL) " "ON CONFLICT(name) DO UPDATE SET parent = excluded.parent, hash = NULL" << newNodeName << parent; @@ -279,11 +282,15 @@ void CClaimTrieCacheBase::ensureTreeStructureIsUpToDate() { } } + LogPrint(BCLog::CLAIMS, "Inserting or updating node %s, parent %s\n", name, parent); db << "INSERT INTO nodes(name, parent, hash) VALUES(?, ?, NULL) " "ON CONFLICT(name) DO UPDATE SET parent = excluded.parent, hash = NULL" << name << parent; if (splitPos == 0) db << "UPDATE nodes SET hash = NULL WHERE name = ?" << parent; + + db << "UPDATE nodes SET parent = ?1 WHERE SUBSTR(name, 1, ?2) = ?1 AND LENGTH(parent) < ?2 AND name != ?1" + << name << name.size(); } // now we need to percolate the nulls up the tree @@ -297,16 +304,16 @@ 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 std::size_t ret; - db << "SELECT COUNT(DISTINCT nodeName) FROM claims WHERE validHeight < ? AND expirationHeight >= ?" - << nNextHeight << nNextHeight >> ret; + db << "SELECT COUNT(DISTINCT nodeName) FROM claims WHERE validHeight < ?1 AND expirationHeight >= ?1" + << nNextHeight >> ret; return ret; } std::size_t CClaimTrieCacheBase::getTotalClaimsInTrie() const { std::size_t ret; - db << "SELECT COUNT(*) FROM claims WHERE validHeight < ? AND expirationHeight >= ?" - << nNextHeight << nNextHeight >> ret; + db << "SELECT COUNT(*) FROM claims WHERE validHeight < ?1 AND expirationHeight >= ?1" + << nNextHeight >> ret; return ret; } @@ -314,28 +321,26 @@ CAmount CClaimTrieCacheBase::getTotalValueOfClaimsInTrie(bool fControllingOnly) { CAmount ret = 0; std::string query("SELECT (SELECT TOTAL(s.amount)+c.amount FROM supports s " - "WHERE s.supportedClaimID = c.claimID AND s.validHeight < ? AND s.expirationHeight >= ?) " - "FROM claims c WHERE c.validHeight < ? AND s.expirationHeight >= ?"); + "WHERE s.supportedClaimID = c.claimID AND s.validHeight < ?1 AND s.expirationHeight >= ?1) " + "FROM claims c WHERE c.validHeight < ?1 AND s.expirationHeight >= ?1"); if (fControllingOnly) throw std::runtime_error("not implemented yet"); // TODO: finish this - db << query << nNextHeight << nNextHeight << nNextHeight << nNextHeight >> ret; + db << query << nNextHeight >> ret; return ret; } bool CClaimTrieCacheBase::getInfoForName(const std::string& name, CClaimValue& claim, int heightOffset) const { auto nextHeight = nNextHeight + heightOffset; - auto query = db << "SELECT c.claimID, c.txID, c.txN, c.blockHeight, c.validHeight, c.amount, " - "(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 < ? AND c.expirationHeight >= ? " - "ORDER BY effectiveAmount DESC, c.blockHeight, REVERSE_BYTES(c.txID), c.txN LIMIT 1" - << nextHeight << nextHeight << name << nextHeight << nextHeight; - for (auto&& row: query) { - row >> claim.claimId >> claim.outPoint.hash >> claim.outPoint.n + auto ret = false; + for (auto&& row: claimHashQuery << nextHeight << name) { + row >> claim.outPoint.hash >> claim.outPoint.n >> claim.claimId >> claim.nHeight >> claim.nValidAtHeight >> claim.nAmount >> claim.nEffectiveAmount; - return true; + ret = true; + break; } - return false; + claimHashQuery++; + return ret; } CClaimSupportToName CClaimTrieCacheBase::getClaimsForName(const std::string& name) const @@ -398,19 +403,23 @@ uint256 CClaimTrieCacheBase::recursiveComputeMerkleHash(const std::string& name, { std::vector<uint8_t> vchToHash; const auto pos = name.size(); - auto query = db << "SELECT name, hash, IFNULL(takeoverHeight,0) FROM nodes WHERE parent = ? ORDER BY name" << name; - for (auto&& row : query) { - std::string key; - int childTakeoverHeight; - std::unique_ptr<uint256> hash; - row >> key >> hash >> childTakeoverHeight; - if (hash == nullptr) hash = std::make_unique<uint256>(); - if (hash->IsNull()) { - *hash = recursiveComputeMerkleHash(key, childTakeoverHeight, checkOnly); + // we have to free up the hash query so it can be reused by a child + struct Triple { std::string name; std::unique_ptr<uint256> hash; int takeoverHeight; }; + std::vector<Triple> children; + for (auto&& row : childHashQuery << name) { + children.emplace_back(); + auto& b = children.back(); + row >> b.name >> b.hash >> b.takeoverHeight; + } + childHashQuery++; + for (auto& child: children) { + if (child.hash == nullptr) child.hash = std::make_unique<uint256>(); + if (child.hash->IsNull()) { + *child.hash = recursiveComputeMerkleHash(child.name, child.takeoverHeight, checkOnly); } - completeHash(*hash, key, pos); - vchToHash.push_back(key[pos]); - vchToHash.insert(vchToHash.end(), hash->begin(), hash->end()); + completeHash(*child.hash, child.name, pos); + vchToHash.push_back(child.name[pos]); + vchToHash.insert(vchToHash.end(), child.hash->begin(), child.hash->end()); } CClaimValue claim; @@ -488,7 +497,13 @@ bool CClaimTrieCacheBase::ValidateTipMatches(const CBlockIndex* tip) } CClaimTrieCacheBase::CClaimTrieCacheBase(CClaimTrie* base) - : base(base), db(base->dbPath, sharedConfig), transacting(false) + : base(base), db(base->dbPath, sharedConfig), transacting(false), + childHashQuery(db << "SELECT name, hash, IFNULL(takeoverHeight, 0) FROM nodes WHERE parent = ? ORDER BY name"), + claimHashQuery(db << "SELECT c.txID, c.txN, c.claimID, c.blockHeight, c.validHeight, c.amount, " + "(SELECT TOTAL(s.amount)+c.amount FROM supports s WHERE s.supportedClaimID = c.claimID " + "AND s.validHeight < ?1 AND s.expirationHeight >= ?1) as effectiveAmount " + "FROM claims c WHERE c.nodeName = ?2 AND c.validHeight < ?1 AND c.expirationHeight >= ?1 " + "ORDER BY effectiveAmount DESC, c.blockHeight, c.txID, c.txN") { assert(base); nNextHeight = base->nNextHeight; @@ -498,8 +513,6 @@ CClaimTrieCacheBase::CClaimTrieCacheBase(CClaimTrie* base) db << "PRAGMA journal_mode=MEMORY"; db << "PRAGMA temp_store=MEMORY"; db << "PRAGMA case_sensitive_like=true"; - - db.define("REVERSE_BYTES", [](std::vector<unsigned char> blob) { std::reverse(blob.begin(), blob.end()); return blob; }); } int CClaimTrieCacheBase::expirationTime() const @@ -512,6 +525,7 @@ uint256 CClaimTrieCacheBase::getMerkleHash() ensureTreeStructureIsUpToDate(); std::unique_ptr<uint256> hash; int takeoverHeight; + // can't use childHashQuery here because "IS NULL" must be used instead of parent = NULL db << "SELECT hash, IFNULL(takeoverHeight, 0) FROM nodes WHERE name = ''" >> std::tie(hash, takeoverHeight); if (hash == nullptr || hash->IsNull()) { assert(transacting); // no data changed but we didn't have the root hash there already? @@ -576,7 +590,7 @@ bool CClaimTrieCacheBase::addSupport(const std::string& name, const COutPoint& o return true; } -bool CClaimTrieCacheBase::removeClaim(const uint160& claimId, const COutPoint& outPoint, std::string& nodeName, int& validHeight) + bool CClaimTrieCacheBase::removeClaim(const uint160& claimId, const COutPoint& outPoint, std::string& nodeName, int& validHeight) { if (!transacting) { transacting = true; db << "begin"; } @@ -592,6 +606,8 @@ bool CClaimTrieCacheBase::removeClaim(const uint160& claimId, const COutPoint& o if (it == query.end()) return false; *it >> nodeName >> validHeight; db << "DELETE FROM claims WHERE claimID = ? AND txID = ? and txN = ?" << claimId << outPoint.hash << outPoint.n; + if (!db.rows_modified()) + return false; db << "UPDATE nodes SET hash = NULL WHERE name = ?" << nodeName; return true; } @@ -946,6 +962,9 @@ bool CClaimTrieCacheBase::incrementBlock(insertUndoType& insertUndo, claimUndoTy // takeover handling: vector_builder<std::string, std::string> takeovers; db << "SELECT name FROM nodes WHERE hash IS NULL" >> takeovers; + auto getTakeoverQuery = db << "SELECT IFNULL(takeoverHeight, 0), takeoverID FROM nodes WHERE name = ?"; + auto hasCandidateQuery = db << "UPDATE nodes SET takeoverHeight = ?, takeoverID = ? WHERE name = ?"; + auto noCandidateQuery = db << "UPDATE nodes SET takeoverHeight = NULL, takeoverID = NULL WHERE name = ?"; for (const auto& nameWithTakeover : takeovers) { auto needsActivate = false; @@ -960,21 +979,26 @@ bool CClaimTrieCacheBase::incrementBlock(insertUndoType& insertUndo, claimUndoTy // now that they're all in get the winner: int existingHeight; std::unique_ptr<uint160> existingID; - db << "SELECT IFNULL(takeoverHeight, 0), takeoverID FROM nodes WHERE name = ?" - << nameWithTakeover >> std::tie(existingHeight, existingID); + getTakeoverQuery << nameWithTakeover >> std::tie(existingHeight, existingID); + getTakeoverQuery++; // reset it auto hasBeenSetBefore = existingID != nullptr && !existingID->IsNull(); auto takeoverHappening = needsActivate || !hasCandidate || (hasBeenSetBefore && *existingID != candidateValue.claimId); if (takeoverHappening && activateAllFor(insertUndo, insertSupportUndo, nameWithTakeover)) hasCandidate = getInfoForName(nameWithTakeover, candidateValue, 1); + LogPrint(BCLog::CLAIMS, "Takeover on %s at %d, happening: %d, set before: %d\n", nameWithTakeover, nNextHeight, takeoverHappening, hasBeenSetBefore); + if (takeoverHappening || !hasBeenSetBefore) { takeoverUndo.emplace_back(nameWithTakeover, std::make_pair(existingHeight, hasBeenSetBefore ? *existingID : uint160())); - if (hasCandidate) - db << "UPDATE nodes SET takeoverHeight = ?, takeoverID = ? WHERE name = ?" - << nNextHeight << candidateValue.claimId << nameWithTakeover; - else - db << "UPDATE nodes SET takeoverHeight = NULL, takeoverID = NULL WHERE name = ?" << nameWithTakeover; + if (hasCandidate) { + hasCandidateQuery << nNextHeight << candidateValue.claimId << nameWithTakeover; + hasCandidateQuery++; + } + else { + noCandidateQuery << nameWithTakeover; + noCandidateQuery++; + } assert(db.rows_modified()); } } @@ -988,36 +1012,38 @@ bool CClaimTrieCacheBase::activateAllFor(insertUndoType& insertUndo, insertUndoT // now that we know a takeover is happening, we bring everybody in: auto ret = false; { - auto query = db << "SELECT txID, txN, validHeight FROM claims WHERE nodeName = ? AND validHeight > ? AND expirationHeight > ?" - << name << nNextHeight << nNextHeight; + auto query = db << "SELECT txID, txN, validHeight FROM claims WHERE nodeName = ?1 AND validHeight > ?2 AND expirationHeight > ?2" + << name << nNextHeight; for (auto &&row: query) { uint256 hash; uint32_t n; int oldValidHeight; row >> hash >> n >> oldValidHeight; insertUndo.emplace_back(name, COutPoint(hash, n), oldValidHeight); + LogPrint(BCLog::CLAIMS, "Early activation of claim %s, t: %s:%d at %d\n", name, hash.GetHex().substr(0, 6), n, nNextHeight); } } // and then update them all to activate now: - db << "UPDATE claims SET validHeight = ? WHERE nodeName = ? AND validHeight > ? AND expirationHeight > ?" - << nNextHeight << name << nNextHeight << nNextHeight; + db << "UPDATE claims SET validHeight = ?1 WHERE nodeName = ?2 AND validHeight > ?1 AND expirationHeight > ?1" + << nNextHeight << name; ret |= db.rows_modified() > 0; // then do the same for supports: { - auto query = db << "SELECT txID, txN, validHeight FROM supports WHERE nodeName = ? AND validHeight > ? AND expirationHeight > ?" - << name << nNextHeight << nNextHeight; + auto query = db << "SELECT txID, txN, validHeight FROM supports WHERE nodeName = ?1 AND validHeight > ?2 AND expirationHeight > ?2" + << name << nNextHeight; for (auto &&row: query) { uint256 hash; uint32_t n; int oldValidHeight; row >> hash >> n >> oldValidHeight; insertSupportUndo.emplace_back(name, COutPoint(hash, n), oldValidHeight); + LogPrint(BCLog::CLAIMS, "Early activation of support %s, t: %s:%d at %d\n", name, hash.GetHex().substr(0, 6), n, nNextHeight); } } // and then update them all to activate now: - db << "UPDATE supports SET validHeight = ? WHERE nodeName = ? AND validHeight > ? AND expirationHeight > ?" - << nNextHeight << name << nNextHeight << nNextHeight; + db << "UPDATE supports SET validHeight = ?1 WHERE nodeName = ?2 AND validHeight > ?1 AND expirationHeight > ?1" + << nNextHeight << name; ret |= db.rows_modified() > 0; return ret; } @@ -1312,7 +1338,7 @@ bool CClaimTrieCacheBase::findNameForClaim(std::vector<unsigned char> claim, CCl std::reverse(claim.begin(), claim.end()); auto query = db << "SELECT nodeName, claimId, txID, txN, amount, validHeight, blockHeight " "FROM claims WHERE SUBSTR(claimID, ?) = ? AND validHeight < ? AND expirationHeight >= ?" - << -claim.size() << claim << nNextHeight << nNextHeight; + << -int(claim.size()) << claim << nNextHeight << nNextHeight; auto hit = false; for (auto&& row: query) { if (hit) return false; diff --git a/src/claimtrie.h b/src/claimtrie.h index 67ba3c341..47cb0c6a3 100644 --- a/src/claimtrie.h +++ b/src/claimtrie.h @@ -385,6 +385,8 @@ protected: bool transacting; // one greater than the height of the chain's tip + mutable sqlite::database_binder claimHashQuery, childHashQuery; + virtual uint256 recursiveComputeMerkleHash(const std::string& name, int takeoverHeight, bool checkOnly); supportEntryType getSupportsForName(const std::string& name) const; diff --git a/src/claimtrieforks.cpp b/src/claimtrieforks.cpp index e24afa028..9ca88d2d9 100644 --- a/src/claimtrieforks.cpp +++ b/src/claimtrieforks.cpp @@ -245,35 +245,33 @@ uint256 CClaimTrieCacheHashFork::recursiveComputeMerkleHash(const std::string& n if (nNextHeight < Params().GetConsensus().nAllClaimsInMerkleForkHeight) return CClaimTrieCacheNormalizationFork::recursiveComputeMerkleHash(name, takeoverHeight, checkOnly); - auto childQuery = db << "SELECT name, hash, IFNULL(takeoverHeight, 0) FROM nodes WHERE parent = ? ORDER BY name" << name; + // it may be that using RAM for this is more expensive than preparing a new query statement in each recursive call + struct Triple { std::string name; std::unique_ptr<uint256> hash; int takeoverHeight; }; + std::vector<Triple> children; + for (auto&& row : childHashQuery << name) { + children.emplace_back(); + auto& b = children.back(); + row >> b.name >> b.hash >> b.takeoverHeight; + } + childHashQuery++; std::vector<uint256> childHashes; - for (auto&& row: childQuery) { - std::string key; - std::unique_ptr<uint256> hash; - int childTakeoverHeight; - row >> key >> hash >> childTakeoverHeight; - if (hash == nullptr) hash = std::make_unique<uint256>(); - if (hash->IsNull()) { - *hash = recursiveComputeMerkleHash(key, childTakeoverHeight, checkOnly); + for (auto& child: children) { + if (child.hash == nullptr) child.hash = std::make_unique<uint256>(); + if (child.hash->IsNull()) { + *child.hash = recursiveComputeMerkleHash(child.name, child.takeoverHeight, checkOnly); } - childHashes.push_back(*hash); + childHashes.push_back(*child.hash); } - auto claimQuery = db << "SELECT c.txID, c.txN, " - "(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 < ? AND c.expirationHeight >= ? " - "ORDER BY effectiveAmount DESC, c.blockHeight, REVERSE_BYTES(c.txID), c.txN" - << nNextHeight << nNextHeight << name << nNextHeight << nNextHeight; - std::vector<uint256> claimHashes; - for (auto&& row: claimQuery) { + for (auto&& row: claimHashQuery << nNextHeight << name) { COutPoint p; row >> p.hash >> p.n; auto claimHash = getValueHash(p, takeoverHeight); claimHashes.push_back(claimHash); } + claimHashQuery++; auto left = childHashes.empty() ? leafHash : ComputeMerkleRoot(childHashes); auto right = claimHashes.empty() ? emptyHash : ComputeMerkleRoot(claimHashes); @@ -363,8 +361,7 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uin row >> key >> takeoverHeight; std::vector<uint256> childHashes; uint32_t nextCurrentIdx = 0; - auto childQuery = db << "SELECT name, hash FROM nodes WHERE parent = ? ORDER BY name" << key; - for (auto&& child : childQuery) { + for (auto&& child : childHashQuery << key) { std::string childKey; uint256 childHash; child >> childKey >> childHash; @@ -372,17 +369,11 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uin nextCurrentIdx = uint32_t(childHashes.size()); childHashes.push_back(childHash); } - - auto claimQuery = db << "SELECT c.txID, c.txN, c.claimID, " - "(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 < ? AND c.expirationHeight >= ? " - "ORDER BY effectiveAmount DESC, c.blockHeight, REVERSE_BYTES(c.txID), c.txN" - << nNextHeight << nNextHeight << key << nNextHeight << nNextHeight; + childHashQuery++; std::vector<uint256> claimHashes; uint32_t finalClaimIdx = 0; - for (auto&& child: claimQuery) { + for (auto&& child: claimHashQuery << nNextHeight << key) { COutPoint childOutPoint; uint160 childClaimID; child >> childOutPoint.hash >> childOutPoint.n >> childClaimID; @@ -393,6 +384,7 @@ bool CClaimTrieCacheHashFork::getProofForName(const std::string& name, const uin } claimHashes.push_back(getValueHash(childOutPoint, takeoverHeight)); } + claimHashQuery++; // 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) diff --git a/src/test/claimtriebranching_tests.cpp b/src/test/claimtriebranching_tests.cpp index a119128a4..c8d0d2c0e 100644 --- a/src/test/claimtriebranching_tests.cpp +++ b/src/test/claimtriebranching_tests.cpp @@ -23,6 +23,25 @@ BOOST_AUTO_TEST_CASE(claim_replace_test) { BOOST_CHECK(fixture.is_best_claim("bassfisher", tx2)); } +BOOST_AUTO_TEST_CASE(takeover_stability_test) { + // no competing bids + ClaimTrieChainFixture fixture; + CMutableTransaction tx1 = fixture.MakeClaim(fixture.GetCoinbase(), "@bass", "one", 1); + CMutableTransaction tx2 = fixture.MakeClaim(fixture.GetCoinbase(), "@bass", "two", 2); + fixture.IncrementBlocks(1); + BOOST_CHECK(fixture.is_best_claim("@bass", tx2)); + uint160 id; int takeover; + BOOST_REQUIRE(fixture.getLastTakeoverForName("@bass", id, takeover)); + auto height = chainActive.Tip()->nHeight; + BOOST_CHECK_EQUAL(takeover, height); + CMutableTransaction tx3 = fixture.MakeClaim(fixture.GetCoinbase(), "@bass", "three", 3); + fixture.Spend(tx3); + fixture.IncrementBlocks(1); + BOOST_CHECK(fixture.is_best_claim("@bass", tx2)); + BOOST_REQUIRE(fixture.getLastTakeoverForName("@bass", id, takeover)); + BOOST_CHECK_EQUAL(takeover, height); +} + /* claims no competing bids