Implement a hard fork for extended/infinite claim expiration times #112

Closed
lbrynaut wants to merge 247 commits from claim-expiration into master
5 changed files with 69 additions and 43 deletions
Showing only changes of commit e7654b8703 - Show all commits

View file

@ -1641,6 +1641,7 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, CNCCTrie
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, CNCCTrieCache& trieCache, bool* pfClean) bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, CNCCTrieCache& trieCache, bool* pfClean)
{ {
assert(pindex->GetBlockHash() == view.GetBestBlock()); assert(pindex->GetBlockHash() == view.GetBestBlock());
assert(pindex->GetBlockHash() == trieCache.getBestBlock());
if (pfClean) if (pfClean)
*pfClean = false; *pfClean = false;
@ -1714,6 +1715,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
// move best block pointer to prevout block // move best block pointer to prevout block
view.SetBestBlock(pindex->pprev->GetBlockHash()); view.SetBestBlock(pindex->pprev->GetBlockHash());
trieCache.setBestBlock(pindex->pprev->GetBlockHash());
assert(trieCache.getMerkleHash() == pindex->pprev->hashNCCTrie); assert(trieCache.getMerkleHash() == pindex->pprev->hashNCCTrie);
if (pfClean) { if (pfClean) {
@ -1773,11 +1775,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash();
assert(hashPrevBlock == view.GetBestBlock()); assert(hashPrevBlock == view.GetBestBlock());
// also verify that the trie cache's current state corresponds to the previous block
assert(hashPrevBlock == trieCache.getBestBlock());
// Special case for the genesis block, skipping connection of its transactions // Special case for the genesis block, skipping connection of its transactions
// (its coinbase is unspendable) // (its coinbase is unspendable)
if (block.GetHash() == Params().HashGenesisBlock()) { if (block.GetHash() == Params().HashGenesisBlock()) {
if (!fJustCheck) if (!fJustCheck)
{
view.SetBestBlock(pindex->GetBlockHash()); view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
}
return true; return true;
} }
@ -1960,6 +1968,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// add this block to the view's block chain // add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash()); view.SetBestBlock(pindex->GetBlockHash());
trieCache.setBestBlock(pindex->GetBlockHash());
int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2;
LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001);

View file

@ -175,7 +175,7 @@ bool CNCCTrie::recursiveCheckConsistency(CNCCTrieNode* node)
return calculatedHash == node->hash; return calculatedHash == node->hash;
} }
bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes) bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlockIn)
{ {
// General strategy: the cache is ordered by length, ensuring child // General strategy: the cache is ordered by length, ensuring child
// nodes are always inserted after their parents. Insert each node // nodes are always inserted after their parents. Insert each node
@ -196,10 +196,6 @@ bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes)
// appending extra data to the normal txundo, which will call the // appending extra data to the normal txundo, which will call the
// normal insert/remove names, but obviously the opposite and in // normal insert/remove names, but obviously the opposite and in
// reverse order (though the order shouldn't ever matter). // reverse order (though the order shouldn't ever matter).
if (cache.empty())
{
return true;
}
bool success = true; bool success = true;
std::vector<std::string> deletedNames; std::vector<std::string> deletedNames;
for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache) for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache)
@ -217,7 +213,8 @@ bool CNCCTrie::update(nodeCacheType& cache, hashMapType& hashes)
return false; return false;
changedNodes[ithash->first] = pNode; changedNodes[ithash->first] = pNode;
} }
BatchWrite(changedNodes, deletedNames); BatchWrite(changedNodes, deletedNames, hashBlockIn);
hashBlock = hashBlockIn;
return true; return true;
} }
@ -308,13 +305,14 @@ void BatchEraseNode(CLevelDBBatch& batch, const std::string& name)
batch.Erase(std::make_pair('n', name)); batch.Erase(std::make_pair('n', name));
} }
bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames) bool CNCCTrie::BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlockIn)
{ {
CLevelDBBatch batch; CLevelDBBatch batch;
for (nodeCacheType::iterator itcache = changedNodes.begin(); itcache != changedNodes.end(); ++itcache) for (nodeCacheType::iterator itcache = changedNodes.begin(); itcache != changedNodes.end(); ++itcache)
BatchWriteNode(batch, itcache->first, itcache->second); BatchWriteNode(batch, itcache->first, itcache->second);
for (std::vector<std::string>::iterator itname = deletedNames.begin(); itname != deletedNames.end(); ++itname) for (std::vector<std::string>::iterator itname = deletedNames.begin(); itname != deletedNames.end(); ++itname)
BatchEraseNode(batch, *itname); BatchEraseNode(batch, *itname);
batch.Write('h', hashBlockIn);
return db.WriteBatch(batch); return db.WriteBatch(batch);
} }
@ -339,6 +337,9 @@ bool CNCCTrie::InsertFromDisk(const std::string& name, CNCCTrieNode* node)
bool CNCCTrie::ReadFromDisk(bool check) bool CNCCTrie::ReadFromDisk(bool check)
{ {
if (!db.Read('h', hashBlock))
LogPrintf("%s: Couldn't read the best block's hash\n", __func__);
boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator()); boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
pcursor->SeekToFirst(); pcursor->SeekToFirst();
@ -459,7 +460,7 @@ uint256 CNCCTrieCache::getMerkleHash() const
bool CNCCTrieCache::empty() const bool CNCCTrieCache::empty() const
{ {
return !base || (base->empty() && cache.empty()); return base->empty() && cache.empty();
} }
bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nHeight) const bool CNCCTrieCache::insertName(const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nHeight) const
@ -673,6 +674,19 @@ bool CNCCTrieCache::recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPo
return true; return true;
} }
uint256 CNCCTrieCache::getBestBlock()
{
if (hashBlock.IsNull())
if (base != NULL)
hashBlock = base->hashBlock;
return hashBlock;
}
void CNCCTrieCache::setBestBlock(const uint256& hashBlockIn)
{
hashBlock = hashBlockIn;
}
bool CNCCTrieCache::clear() const bool CNCCTrieCache::clear() const
{ {
for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache) for (nodeCacheType::iterator itcache = cache.begin(); itcache != cache.end(); ++itcache)
@ -689,9 +703,7 @@ bool CNCCTrieCache::flush()
{ {
if (dirty()) if (dirty())
getMerkleHash(); getMerkleHash();
if (!base) bool success = base->update(cache, cacheHashes, hashBlock);
return true;
bool success = base->update(cache, cacheHashes);
if (success) if (success)
{ {
success = clear(); success = clear();

View file

@ -133,27 +133,30 @@ public:
json_spirit::Object getInfoForName(const std::string& name) const; json_spirit::Object getInfoForName(const std::string& name) const;
friend class CNCCTrieCache; friend class CNCCTrieCache;
private: private:
bool update(nodeCacheType& cache, hashMapType& hashes); bool update(nodeCacheType& cache, hashMapType& hashes, const uint256& hashBlock);
bool updateName(const std::string& name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames); bool updateName(const std::string& name, CNCCTrieNode* updatedNode, std::vector<std::string>& deletedNames);
bool updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet); bool updateHash(const std::string& name, uint256& hash, CNCCTrieNode** pNodeRet);
bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames); bool recursiveNullify(CNCCTrieNode* node, std::string& name, std::vector<std::string>& deletedNames);
bool recursiveCheckConsistency(CNCCTrieNode* node); bool recursiveCheckConsistency(CNCCTrieNode* node);
bool BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames); bool BatchWrite(nodeCacheType& changedNodes, std::vector<std::string>& deletedNames, const uint256& hashBlock);
bool InsertFromDisk(const std::string& name, CNCCTrieNode* node); bool InsertFromDisk(const std::string& name, CNCCTrieNode* node);
bool recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const; bool recursiveDumpToJSON(const std::string& name, const CNCCTrieNode* current, json_spirit::Array& ret) const;
CNCCTrieNode root; CNCCTrieNode root;
uint256 hashBlock;
}; };
class CNCCTrieCache class CNCCTrieCache
{ {
public: public:
CNCCTrieCache(CNCCTrie* base): base(base) {} CNCCTrieCache(CNCCTrie* base): base(base) {assert(base);}
uint256 getMerkleHash() const; uint256 getMerkleHash() const;
bool empty() const; bool empty() const;
bool flush(); bool flush();
bool dirty() const { return !dirtyHashes.empty(); } bool dirty() const { return !dirtyHashes.empty(); }
bool insertName (const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nDepth) const; bool insertName (const std::string name, uint256 txhash, int nOut, CAmount nAmount, int nDepth) const;
bool removeName (const std::string name, uint256 txhash, int nOut) const; bool removeName (const std::string name, uint256 txhash, int nOut) const;
uint256 getBestBlock();
void setBestBlock(const uint256& hashBlock);
~CNCCTrieCache() { clear(); } ~CNCCTrieCache() { clear(); }
private: private:
CNCCTrie* base; CNCCTrie* base;
@ -164,6 +167,7 @@ private:
bool recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const; bool recursiveComputeMerkleHash(CNCCTrieNode* tnCurrent, std::string sPos) const;
bool recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) const; bool recursivePruneName(CNCCTrieNode* tnCurrent, unsigned int nPos, std::string sName, bool* pfNullified = NULL) const;
bool clear() const; bool clear() const;
uint256 hashBlock;
}; };
#endif // BITCOIN_NCCTRIE_H #endif // BITCOIN_NCCTRIE_H

View file

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://opensource.org/licenses/mit-license.php // file COPYING or http://opensource.org/licenses/mit-license.php
#include "main.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
#include "ncctrie.h" #include "ncctrie.h"
#include "coins.h" #include "coins.h"
@ -52,15 +53,13 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
uint256 hash4; uint256 hash4;
hash4.SetHex("a79e8a5b28f7fa5e8836a4b48da9988bdf56ce749f81f413cb754f963a516200"); hash4.SetHex("a79e8a5b28f7fa5e8836a4b48da9988bdf56ce749f81f413cb754f963a516200");
CNCCTrie trie; BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(trie.empty()); CNCCTrieCache ntState(pnccTrie);
CNCCTrieCache ntState(&trie);
ntState.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100); ntState.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100);
ntState.insertName(std::string("test2"), tx2.GetHash(), 0, 50, 100); ntState.insertName(std::string("test2"), tx2.GetHash(), 0, 50, 100);
BOOST_CHECK(trie.empty()); BOOST_CHECK(pnccTrie->empty());
BOOST_CHECK(!ntState.empty()); BOOST_CHECK(!ntState.empty());
BOOST_CHECK(ntState.getMerkleHash() == hash1); BOOST_CHECK(ntState.getMerkleHash() == hash1);
@ -73,11 +72,11 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(ntState.getMerkleHash() == hash2); BOOST_CHECK(ntState.getMerkleHash() == hash2);
ntState.flush(); ntState.flush();
BOOST_CHECK(!trie.empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(trie.getMerkleHash() == hash2); BOOST_CHECK(pnccTrie->getMerkleHash() == hash2);
BOOST_CHECK(trie.checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState1(&trie); CNCCTrieCache ntState1(pnccTrie);
ntState1.removeName(std::string("test"), tx1.GetHash(), 0); ntState1.removeName(std::string("test"), tx1.GetHash(), 0);
ntState1.removeName(std::string("test2"), tx2.GetHash(), 0); ntState1.removeName(std::string("test2"), tx2.GetHash(), 0);
ntState1.removeName(std::string("test"), tx3.GetHash(), 0); ntState1.removeName(std::string("test"), tx3.GetHash(), 0);
@ -85,7 +84,7 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
BOOST_CHECK(ntState1.getMerkleHash() == hash0); BOOST_CHECK(ntState1.getMerkleHash() == hash0);
CNCCTrieCache ntState2(&trie); CNCCTrieCache ntState2(pnccTrie);
ntState2.insertName(std::string("abab"), tx6.GetHash(), 0, 50, 100); ntState2.insertName(std::string("abab"), tx6.GetHash(), 0, 50, 100);
ntState2.removeName(std::string("test"), tx1.GetHash(), 0); ntState2.removeName(std::string("test"), tx1.GetHash(), 0);
@ -93,43 +92,43 @@ BOOST_AUTO_TEST_CASE(ncctrie_create_insert_remove)
ntState2.flush(); ntState2.flush();
BOOST_CHECK(!trie.empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(trie.getMerkleHash() == hash3); BOOST_CHECK(pnccTrie->getMerkleHash() == hash3);
BOOST_CHECK(trie.checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState3(&trie); CNCCTrieCache ntState3(pnccTrie);
ntState3.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100); ntState3.insertName(std::string("test"), tx1.GetHash(), 0, 50, 100);
BOOST_CHECK(ntState3.getMerkleHash() == hash4); BOOST_CHECK(ntState3.getMerkleHash() == hash4);
ntState3.flush(); ntState3.flush();
BOOST_CHECK(!trie.empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(trie.getMerkleHash() == hash4); BOOST_CHECK(pnccTrie->getMerkleHash() == hash4);
BOOST_CHECK(trie.checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState4(&trie); CNCCTrieCache ntState4(pnccTrie);
ntState4.removeName(std::string("abab"), tx6.GetHash(), 0); ntState4.removeName(std::string("abab"), tx6.GetHash(), 0);
BOOST_CHECK(ntState4.getMerkleHash() == hash2); BOOST_CHECK(ntState4.getMerkleHash() == hash2);
ntState4.flush(); ntState4.flush();
BOOST_CHECK(!trie.empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(trie.getMerkleHash() == hash2); BOOST_CHECK(pnccTrie->getMerkleHash() == hash2);
BOOST_CHECK(trie.checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState5(&trie); CNCCTrieCache ntState5(pnccTrie);
ntState5.removeName(std::string("test"), tx3.GetHash(), 0); ntState5.removeName(std::string("test"), tx3.GetHash(), 0);
BOOST_CHECK(ntState5.getMerkleHash() == hash2); BOOST_CHECK(ntState5.getMerkleHash() == hash2);
ntState5.flush(); ntState5.flush();
BOOST_CHECK(!trie.empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(trie.getMerkleHash() == hash2); BOOST_CHECK(pnccTrie->getMerkleHash() == hash2);
BOOST_CHECK(trie.checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
CNCCTrieCache ntState6(&trie); CNCCTrieCache ntState6(pnccTrie);
ntState6.insertName(std::string("test"), tx3.GetHash(), 0, 50, 101); ntState6.insertName(std::string("test"), tx3.GetHash(), 0, 50, 101);
BOOST_CHECK(ntState6.getMerkleHash() == hash2); BOOST_CHECK(ntState6.getMerkleHash() == hash2);
ntState6.flush(); ntState6.flush();
BOOST_CHECK(!trie.empty()); BOOST_CHECK(!pnccTrie->empty());
BOOST_CHECK(trie.getMerkleHash() == hash2); BOOST_CHECK(pnccTrie->getMerkleHash() == hash2);
BOOST_CHECK(trie.checkConsistency()); BOOST_CHECK(pnccTrie->checkConsistency());
} }
BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize) BOOST_AUTO_TEST_CASE(ncctrienode_serialize_unserialize)

View file

@ -42,6 +42,7 @@ struct TestingSetup {
pblocktree = new CBlockTreeDB(1 << 20, true); pblocktree = new CBlockTreeDB(1 << 20, true);
pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true);
pcoinsTip = new CCoinsViewCache(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinsdbview);
pnccTrie = new CNCCTrie();
InitBlockIndex(); InitBlockIndex();
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
bool fFirstRun; bool fFirstRun;
@ -63,6 +64,7 @@ struct TestingSetup {
delete pwalletMain; delete pwalletMain;
pwalletMain = NULL; pwalletMain = NULL;
#endif #endif
delete pnccTrie;
delete pcoinsTip; delete pcoinsTip;
delete pcoinsdbview; delete pcoinsdbview;
delete pblocktree; delete pblocktree;