Buffer block downloading and commit/check blocks in parallel #1233

Closed
wants to merge 37 commits into
from
Select commit
+1,301 −560
View
4 bitcoin-qt.pro
@@ -115,6 +115,8 @@ HEADERS += src/qt/bitcoingui.h \
src/serialize.h \
src/strlcpy.h \
src/main.h \
+ src/hub.h \
+ src/blockstore.h \
src/net.h \
src/key.h \
src/db.h \
@@ -180,6 +182,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
src/key.cpp \
src/script.cpp \
src/main.cpp \
+ src/hub.cpp \
+ src/blockstore.cpp \
src/init.cpp \
src/net.cpp \
src/irc.cpp \
View
16 src/bitcoinrpc.cpp
@@ -12,6 +12,7 @@
#include "ui_interface.h"
#include "base58.h"
#include "bitcoinrpc.h"
+#include "hub.h"
#undef printf
#include <boost/asio.hpp>
@@ -2187,10 +2188,10 @@ Value getmemorypool(const Array& params, bool fHelp)
{
// Parse parameters
CDataStream ssBlock(ParseHex(params[0].get_str()), SER_NETWORK, PROTOCOL_VERSION);
- CBlock pblock;
- ssBlock >> pblock;
+ CBlock block;
+ ssBlock >> block;
- return ProcessBlock(NULL, &pblock);
+ return phub->EmitBlock(block);
}
}
@@ -2254,16 +2255,9 @@ Value sendrawtx(const Array& params, bool fHelp)
}
// push to local node
- CTxDB txdb("r");
- if (!tx.AcceptToMemoryPool(txdb))
+ if (!phub->EmitTransaction(tx))
throw JSONRPCError(-22, "TX rejected");
- SyncWithWallets(tx, NULL, true);
-
- // relay to network
- CInv inv(MSG_TX, tx.GetHash());
- RelayInventory(inv);
-
return tx.GetHash().GetHex();
}
View
91 src/blockstore.cpp
@@ -0,0 +1,91 @@
+#include "blockstore.h"
+#include "util.h"
+#include "net.h"
+#include "main.h"
+
+void CBlockStore::CallbackCommitBlock(const CBlock &block)
+{
+ {
+ LOCK(cs_mapGetBlockIndexWaits);
+ std::map<uint256, CSemaphore*>::iterator it = mapGetBlockIndexWaits.find(block.GetHash());
+ if (it != mapGetBlockIndexWaits.end() && it->second != NULL)
+ it->second->post_all();
+ }
+ LOCK(sigtable.cs_sigCommitBlock);
+ sigtable.sigCommitBlock(block);
+}
+
+void CBlockStore::SubmitCallbackFinishEmitBlock(CBlock& block, CNode* pNodeDoS)
+{
+ unsigned int nQueueSize;
+ {
+ LOCK(cs_callbacks);
+ nQueueSize = queueFinishEmitBlockCallbacks.size();
+ }
+ while (nQueueSize >= GetArg("-blockbuffersize", 20) && fProcessCallbacks)
+ {
+ Sleep(20);
+ LOCK(cs_callbacks);
+ nQueueSize = queueFinishEmitBlockCallbacks.size();
+ }
+
+ if (pNodeDoS) pNodeDoS->AddRef();
+
+ LOCK(cs_callbacks);
+ queueFinishEmitBlockCallbacks.push(std::make_pair(new CBlock(block), pNodeDoS));
+ sem_callbacks.post();
+}
+
+void CBlockStore::StopProcessCallbacks()
+{
+ {
+ LOCK(cs_callbacks);
+ fProcessCallbacks = false;
+ sem_callbacks.post();
+ }
+ while (fProcessingCallbacks)
+ Sleep(20);
+}
+
+void CBlockStore::ProcessCallbacks()
+{
+ {
+ LOCK(cs_callbacks);
+ if (!fProcessCallbacks)
+ return;
+ fProcessingCallbacks = true;
+ }
+
+ loop
+ {
+ std::pair<CBlock*, CNode*> callback;
+ sem_callbacks.wait();
+ if (fProcessCallbacks)
+ {
+ LOCK(cs_callbacks);
+ assert(queueFinishEmitBlockCallbacks.size() > 0);
+ callback = queueFinishEmitBlockCallbacks.front();
+ queueFinishEmitBlockCallbacks.pop();
+ }
+ else
+ {
+ fProcessingCallbacks = false;
+ return;
+ }
+
+ FinishEmitBlock(*(callback.first), callback.second);
+ delete callback.first;
+ if (callback.second) callback.second->Release();
+ }
+}
+
+void CBlockStoreProcessCallbacks(void* parg)
+{
+ ((CBlockStore*)parg)->ProcessCallbacks();
+}
+
+CBlockStore::CBlockStore() : sem_callbacks(0), fProcessCallbacks(true), fProcessingCallbacks(false)
+{
+ if (!CreateThread(CBlockStoreProcessCallbacks, this))
+ throw std::runtime_error("Couldn't create callback threads");
+}
View
112 src/blockstore.h
@@ -0,0 +1,112 @@
+#ifndef BITCOIN_BLOCKSTORE_H
+#define BITCOIN_BLOCKSTORE_H
+
+// This API is considered stable ONLY for existing bitcoin codebases,
+// any futher uses are not yet supported.
+// This API is subject to change dramatically overnight, do not
+// depend on it for anything.
+
+#include <boost/signals2/signal.hpp>
+#include <queue>
+#include <set>
+
+#include "sync.h"
+#include "uint256.h"
+
+class CBlock;
+class CTxDB;
+class CBlockIndex;
+class CHub;
+class CNode;
+
+class CBlockStoreSignalTable
+{
+public:
+ CCriticalSection cs_sigCommitBlock;
+ boost::signals2::signal<void (const CBlock&)> sigCommitBlock;
+
+ CCriticalSection cs_sigAskForBlocks;
+ boost::signals2::signal<void (const uint256, const uint256)> sigAskForBlocks;
+
+ CCriticalSection cs_sigDoS;
+ boost::function<void (CNode* pNode, const int nDoS)> sigDoS;
+};
+
+class CBlockStore
+{
+private:
+ CCriticalSection cs_setBlocksSeen;
+ std::set<uint256> setBlocksSeen;
+
+ CCriticalSection cs_mapGetBlockIndexWaits;
+ std::map<uint256, CSemaphore*> mapGetBlockIndexWaits;
+
+ CBlockStoreSignalTable sigtable;
+
+ void CallbackCommitBlock(const CBlock &block);
+
+ void CallbackAskForBlocks(const uint256 hashEnd, const uint256 hashOriginator) { LOCK(sigtable.cs_sigAskForBlocks); sigtable.sigAskForBlocks(hashEnd, hashOriginator); }
+
+ void CallbackDoS(CNode* pNode, const int nDoS) { LOCK(sigtable.cs_sigDoS); sigtable.sigDoS(pNode, nDoS); }
+
+ CCriticalSection cs_callbacks;
+ CSemaphore sem_callbacks;
+ bool fProcessCallbacks;
+ bool fProcessingCallbacks;
+
+ std::queue<std::pair<CBlock*, CNode*> > queueFinishEmitBlockCallbacks;
+ void SubmitCallbackFinishEmitBlock(CBlock& block, CNode* pNodeDoS);
+ bool FinishEmitBlock(CBlock& block, CNode* pNodeDoS);
+
+ bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew);
+ bool DisconnectBlock(CBlock& block, CTxDB& txdb, CBlockIndex* pindex);
+ bool ConnectBlock(CBlock& block, CTxDB& txdb, CBlockIndex* pindex);
+ bool SetBestChainInner(CBlock& block, CTxDB& txdb, CBlockIndex *pindexNew);
+ bool SetBestChain(CBlock& block, CTxDB& txdb, CBlockIndex* pindexNew);
+ bool AddToBlockIndex(CBlock& block, unsigned int nFile, unsigned int nBlockPos);
+ bool AcceptBlock(CBlock& block);
+public:
+ // Loops to process callbacks (do not call manually, automatically started in the constructor)
+ void ProcessCallbacks();
+ // Stop callback processing threads
+ void StopProcessCallbacks();
+
+ CBlockStore();
+ ~CBlockStore() { StopProcessCallbacks(); }
+
+ bool LoadBlockIndex(bool fReadOnly=false);
+
+//Register methods
+ // Register a handler (of the form void f(const CBlock& block)) to be called after every block commit
+ void RegisterCommitBlock(boost::function<void (const CBlock&)> func) { LOCK(sigtable.cs_sigCommitBlock); sigtable.sigCommitBlock.connect(func); }
+
+ // Register a handler (of the form void f(const uint256 hashEnd, const uint256 hashOriginator)) to be called when we need to ask for blocks up to hashEnd
+ // Should always start from the best block (GetBestBlockIndex())
+ // The receiver should check if it has a peer which is known to have a block with hash hashOriginator and if it does, it should
+ // send the block query to that node.
+ void RegisterAskForBlocks(boost::function<void (const uint256, const uint256)> func) { LOCK(sigtable.cs_sigAskForBlocks); sigtable.sigAskForBlocks.connect(func); }
+
+ // Register a handler (of the form void f(CNode* pNode, const int nDoS)) that calls pNode->Misbehaving(nDoS)
+ void RegisterDoSHandler(boost::function<void (CNode* pNode, const int nDoS)> func) { LOCK(sigtable.cs_sigDoS); sigtable.sigDoS = func; }
+
+//Blockchain access methods
+ // Emit methods will verify the object, commit it to memory/disk and then place it in queue to
+ // be handled by listeners
+
+ // if (!fBlocking) only initial checks will be performed before returning
+ // This means block.nDoS may not be set to its final value before returning
+ // DoSHandler will be called with the final value of block.nDoS at some point during callbacks.
+ bool EmitBlock(CBlock& block, bool fBlocking=true, CNode* pNodeDoS=NULL);
+
+ // Returns true if we have/have seen a block with the given hash
+ // Does not indicate whether the block is orphan/was invalid/is in the main chain/is waiting to be committed/etc
+ bool HaveSeenBlock(const uint256& hash);
+
+ // Return CBlockIndex* with *phashBlock == hash or NULL if we dont have one
+ // if (fBlocking) wait for the block to be committed (assuming it has already been emitted)
+ // This can still return NULL even if the block has been emitted if the block is invalid
+ // WARNING: DO NOT call this with fBlocking == true if you are holding cs_main
+ const CBlockIndex* GetBlockIndex(const uint256& hash, bool fBlocking=false);
+};
+
+#endif
View
23 src/checkpoints.cpp
@@ -14,6 +14,8 @@ namespace Checkpoints
{
typedef std::map<int, uint256> MapCheckpoints;
+ static const CBlockIndex* lastCheckpoint = NULL;
+
//
// What makes a good checkpoint block?
// + Is surrounded by blocks with reasonable timestamps
@@ -56,17 +58,28 @@ namespace Checkpoints
return checkpoints.rbegin()->first;
}
- CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex)
+ void HandleCommitBlock(const CBlock& block)
{
MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
+ const uint256 blockHash = block.GetHash();
+
BOOST_REVERSE_FOREACH(const MapCheckpoints::value_type& i, checkpoints)
{
const uint256& hash = i.second;
- std::map<uint256, CBlockIndex*>::const_iterator t = mapBlockIndex.find(hash);
- if (t != mapBlockIndex.end())
- return t->second;
+ if (blockHash != hash)
+ continue;
+
+ LOCK(cs_main);
+
+ assert(mapBlockIndex.count(hash));
+ lastCheckpoint = mapBlockIndex[hash];
+ return;
}
- return NULL;
+ }
+
+ const CBlockIndex* GetLastCheckpoint()
+ {
+ return lastCheckpoint;
}
}
View
8 src/checkpoints.h
@@ -8,6 +8,7 @@
class uint256;
class CBlockIndex;
+class CBlock;
/** Block-chain checkpoints are compiled-in sanity checks.
* They are updated every release or three.
@@ -20,8 +21,11 @@ namespace Checkpoints
// Return conservative estimate of total number of blocks, 0 if unknown
int GetTotalBlocksEstimate();
- // Returns last CBlockIndex* in mapBlockIndex that is a checkpoint
- CBlockIndex* GetLastCheckpoint(const std::map<uint256, CBlockIndex*>& mapBlockIndex);
+ // Used to keep last checkpoint cache up to date
+ void HandleCommitBlock(const CBlock& block);
+
+ // Returns last CBlockIndex* that is a checkpoint
+ const CBlockIndex* GetLastCheckpoint();
}
#endif
View
125 src/db.cpp
@@ -575,131 +575,6 @@ bool CTxDB::LoadBlockIndex()
// Load bnBestInvalidWork, OK if it doesn't exist
ReadBestInvalidWork(bnBestInvalidWork);
- // Verify blocks in the best chain
- int nCheckLevel = GetArg("-checklevel", 1);
- int nCheckDepth = GetArg( "-checkblocks", 2500);
- if (nCheckDepth == 0)
- nCheckDepth = 1000000000; // suffices until the year 19000
- if (nCheckDepth > nBestHeight)
- nCheckDepth = nBestHeight;
- printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
- CBlockIndex* pindexFork = NULL;
- map<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos;
- for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
- {
- if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
- break;
- CBlock block;
- if (!block.ReadFromDisk(pindex))
- return error("LoadBlockIndex() : block.ReadFromDisk failed");
- // check level 1: verify block validity
- if (nCheckLevel>0 && !block.CheckBlock())
- {
- printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
- pindexFork = pindex->pprev;
- }
- // check level 2: verify transaction index validity
- if (nCheckLevel>1)
- {
- pair<unsigned int, unsigned int> pos = make_pair(pindex->nFile, pindex->nBlockPos);
- mapBlockPos[pos] = pindex;
- BOOST_FOREACH(const CTransaction &tx, block.vtx)
- {
- uint256 hashTx = tx.GetHash();
- CTxIndex txindex;
- if (ReadTxIndex(hashTx, txindex))
- {
- // check level 3: checker transaction hashes
- if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos)
- {
- // either an error or a duplicate transaction
- CTransaction txFound;
- if (!txFound.ReadFromDisk(txindex.pos))
- {
- printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- else
- if (txFound.GetHash() != hashTx) // not a duplicate tx
- {
- printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- }
- // check level 4: check whether spent txouts were spent within the main chain
- unsigned int nOutput = 0;
- if (nCheckLevel>3)
- {
- BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent)
- {
- if (!txpos.IsNull())
- {
- pair<unsigned int, unsigned int> posFind = make_pair(txpos.nFile, txpos.nBlockPos);
- if (!mapBlockPos.count(posFind))
- {
- printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- // check level 6: check whether spent txouts were spent by a valid transaction that consume them
- if (nCheckLevel>5)
- {
- CTransaction txSpend;
- if (!txSpend.ReadFromDisk(txpos))
- {
- printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput);
- pindexFork = pindex->pprev;
- }
- else if (!txSpend.CheckTransaction())
- {
- printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput);
- pindexFork = pindex->pprev;
- }
- else
- {
- bool fFound = false;
- BOOST_FOREACH(const CTxIn &txin, txSpend.vin)
- if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput)
- fFound = true;
- if (!fFound)
- {
- printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput);
- pindexFork = pindex->pprev;
- }
- }
- }
- }
- nOutput++;
- }
- }
- }
- // check level 5: check whether all prevouts are marked spent
- if (nCheckLevel>4)
- {
- BOOST_FOREACH(const CTxIn &txin, tx.vin)
- {
- CTxIndex txindex;
- if (ReadTxIndex(txin.prevout.hash, txindex))
- if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull())
- {
- printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- }
- }
- }
- }
- }
- if (pindexFork && !fRequestShutdown)
- {
- // Reorg back to the fork
- printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
- CBlock block;
- if (!block.ReadFromDisk(pindexFork))
- return error("LoadBlockIndex() : block.ReadFromDisk failed");
- CTxDB txdb;
- block.SetBestChain(txdb, pindexFork);
- }
-
return true;
}
View
193 src/hub.cpp
@@ -0,0 +1,193 @@
+#include "hub.h"
+#include "main.h"
+
+CHub* phub;
+
+class CHubCallbackCommitBlock : public CHubCallback
+{
+private:
+ CBlock block;
+public:
+ CHubCallbackCommitBlock(const CBlock &blockIn) : block(blockIn) {}
+ void Signal(CHubSignalTable& sigtable) { LOCK(sigtable.cs_sigCommitBlock); sigtable.sigCommitBlock(block); }
+};
+
+class CHubCallbackCommitAlert : public CHubCallback
+{
+private:
+ CAlert alert;
+public:
+ CHubCallbackCommitAlert(const CAlert &alertIn) : alert(alertIn) {}
+ void Signal(CHubSignalTable& sigtable) { LOCK(sigtable.cs_sigCommitAlert); sigtable.sigCommitAlert(alert); }
+};
+
+class CHubCallbackRemoveAlert : public CHubCallback
+{
+private:
+ CAlert alert;
+public:
+ CHubCallbackRemoveAlert(const CAlert &alertIn) : alert(alertIn) {}
+ void Signal(CHubSignalTable& sigtable) { LOCK(sigtable.cs_sigRemoveAlert); sigtable.sigRemoveAlert(alert); }
+};
+
+class CHubCallbackAskForBlocks : public CHubCallback
+{
+private:
+ uint256 hashEnd, hashOrig;
+public:
+ CHubCallbackAskForBlocks(uint256 hashEndIn, uint256 hashOrigIn) : hashEnd(hashEndIn), hashOrig(hashOrigIn) {}
+ void Signal(CHubSignalTable& sigtable) { LOCK(sigtable.cs_sigAskForBlocks); sigtable.sigAskForBlocks(hashEnd, hashOrig); }
+};
+
+class CHubCallbackCommitTransactionToMemoryPool : public CHubCallback
+{
+private:
+ CTransaction tx;
+public:
+ CHubCallbackCommitTransactionToMemoryPool(const CTransaction &txIn) : tx(txIn) {}
+ void Signal(CHubSignalTable& sigtable) { LOCK(sigtable.cs_sigCommitTransactionToMemoryPool); sigtable.sigCommitTransactionToMemoryPool(tx); }
+};
+
+class CHubCallbackDoS : public CHubCallback
+{
+private:
+ CNode* pNode;
+ int nDoS;
+public:
+ CHubCallbackDoS(CNode* pNodeIn, const int nDoSIn) { pNode = pNodeIn; nDoS = nDoSIn; if(pNode) pNode->AddRef(); }
+ void Signal(CHubSignalTable& sigtable) { if (pNode) pNode->Misbehaving(nDoS); }
+ ~CHubCallbackDoS() { if(pNode) pNode->Release(); }
+};
+
+void CHub::SubmitCallbackCommitBlock(const CBlock &block)
+{
+ LOCK(cs_callbacks);
+ queueCallbacks.push(new CHubCallbackCommitBlock(block));
+ sem_callbacks.post();
+}
+
+void CHub::SubmitCallbackCommitAlert(const CAlert &alert)
+{
+ LOCK(cs_callbacks);
+ queueCallbacks.push(new CHubCallbackCommitAlert(alert));
+ sem_callbacks.post();
+}
+
+void CHub::SubmitCallbackRemoveAlert(const CAlert &alert)
+{
+ LOCK(cs_callbacks);
+ queueCallbacks.push(new CHubCallbackRemoveAlert(alert));
+ sem_callbacks.post();
+}
+
+void CHub::AskForBlocks(const uint256 hashEnd, const uint256 hashOriginator)
+{
+ LOCK(cs_callbacks);
+ queueCallbacks.push(new CHubCallbackAskForBlocks(hashEnd, hashOriginator));
+ sem_callbacks.post();
+}
+
+void CHub::SubmitCallbackCommitTransactionToMemoryPool(const CTransaction &tx)
+{
+ LOCK(cs_callbacks);
+ queueCallbacks.push(new CHubCallbackCommitTransactionToMemoryPool(tx));
+ sem_callbacks.post();
+}
+
+bool CHub::ConnectToBlockStore(CBlockStore* pblockstoreIn)
+{
+ if (pblockstore)
+ return false;
+ pblockstore = pblockstoreIn;
+
+ pblockstore->RegisterCommitBlock(boost::bind(&CHub::SubmitCallbackCommitBlock, this, _1));
+
+ pblockstore->RegisterAskForBlocks(boost::bind(&CHub::AskForBlocks, this, _1, _2));
+
+ pblockstore->RegisterDoSHandler(boost::bind(&CHub::SubmitCallbackDoS, this, _1, _2));
+
+ return true;
+}
+
+void CHub::SubmitCallbackDoS(CNode* pNode, const int nDoS)
+{
+ LOCK(cs_callbacks);
+ queueCallbacks.push(new CHubCallbackDoS(pNode, nDoS));
+ sem_callbacks.post();
+}
+
+void CHub::ProcessCallbacks()
+{
+ {
+ LOCK(cs_callbacks);
+ if (fProcessCallbacks)
+ nCallbackThreads++;
+ else
+ return;
+ }
+
+ loop
+ {
+ CHubCallback *pcallback = NULL;
+ sem_callbacks.wait();
+ if (fProcessCallbacks)
+ {
+ LOCK(cs_callbacks);
+ assert(queueCallbacks.size() > 0);
+ pcallback = queueCallbacks.front();
+ queueCallbacks.pop();
+ }
+ else
+ {
+ LOCK(cs_callbacks);
+ nCallbackThreads--;
+ return;
+ }
+
+ pcallback->Signal(sigtable);
+ delete pcallback;
+ }
+}
+
+void CHub::StopProcessCallbacks()
+{
+ {
+ LOCK(cs_callbacks);
+ fProcessCallbacks = false;
+ for (int i = 0; i < nCallbackThreads; i++)
+ sem_callbacks.post();
+ }
+ while (nCallbackThreads > 0)
+ Sleep(20);
+}
+
+void ProcessCallbacks(void* parg)
+{
+ ((CHub*)parg)->ProcessCallbacks();
+}
+
+CHub::CHub() : sem_callbacks(0), fProcessCallbacks(true), nCallbackThreads(0), pblockstore(NULL)
+{
+ for (int i = 0; i < GetArg("-callbackconcurrency", 1); i++)
+ if (!CreateThread(::ProcessCallbacks, this))
+ throw std::runtime_error("Couldn't create callback threads");
+}
+
+
+
+void CHubListener::RegisterWithHub(CHub* phub)
+{
+ phub->RegisterCommitBlock(boost::bind(&CHubListener::HandleCommitBlock, this, _1));
+
+ phub->RegisterCommitTransactionToMemoryPool(boost::bind(&CHubListener::HandleCommitTransactionToMemoryPool, this, _1));
+
+ phub->RegisterCommitAlert(boost::bind(&CHubListener::HandleCommitAlert, this, _1));
+ phub->RegisterRemoveAlert(boost::bind(&CHubListener::HandleRemoveAlert, this, _1));
+
+ phub->RegisterAskForBlocks(boost::bind(&CHubListener::HandleAskForBlocks, this, _1, _2));
+}
+
+void CHubListener::DeregisterFromHub()
+{
+ // TODO: Allow deregistration from CHub callbacks
+}
View
162 src/hub.h
@@ -0,0 +1,162 @@
+#ifndef BITCOIN_HUB_H
+#define BITCOIN_HUB_H
+
+// This API is considered stable ONLY for existing bitcoin codebases,
+// any futher uses are not yet supported.
+// This API is subject to change dramatically overnight, do not
+// depend on it for anything.
+
+#include <boost/signals2/signal.hpp>
+#include <queue>
+
+#include "uint256.h"
+#include "sync.h"
+#include "blockstore.h"
+
+class CBlock;
+class CMerkleTx;
+class CTransaction;
+class CAlert;
+class CInv;
+class CNode;
+
+class CHubSignalTable
+{
+public:
+ CCriticalSection cs_sigCommitBlock;
+ boost::signals2::signal<void (const CBlock&)> sigCommitBlock;
+
+ CCriticalSection cs_sigCommitTransactionToMemoryPool;
+ boost::signals2::signal<void (const CTransaction&)> sigCommitTransactionToMemoryPool;
+
+ CCriticalSection cs_sigCommitAlert;
+ boost::signals2::signal<void (const CAlert&)> sigCommitAlert;
+ CCriticalSection cs_sigRemoveAlert;
+ boost::signals2::signal<void (const CAlert&)> sigRemoveAlert;
+
+ CCriticalSection cs_sigAskForBlocks;
+ boost::signals2::signal<void (const uint256, const uint256)> sigAskForBlocks;
+};
+
+class CHubCallback
+{
+public:
+ virtual ~CHubCallback() {};
+ virtual void Signal(CHubSignalTable& sigtable) =0;
+};
+
+class CHub
+{
+private:
+ CHubSignalTable sigtable;
+
+ CCriticalSection cs_callbacks;
+ std::queue<CHubCallback*> queueCallbacks;
+ CSemaphore sem_callbacks;
+
+ bool fProcessCallbacks;
+ int nCallbackThreads;
+
+ CBlockStore* pblockstore;
+
+ void SubmitCallbackCommitBlock(const CBlock &block);
+
+ bool EmitTransactionInner(CTransaction& tx, bool fCheckInputs);
+ void SubmitCallbackCommitTransactionToMemoryPool(const CTransaction &tx);
+
+ void SubmitCallbackCommitAlert(const CAlert &alert);
+ void SubmitCallbackRemoveAlert(const CAlert &alert);
+
+ void SubmitCallbackDoS(CNode* pNode, const int nDoS);
+public:
+//Util methods
+ // Loops to process callbacks (do not call manually, automatically started in the constructor)
+ void ProcessCallbacks();
+ // Stop callback processing threads
+ void StopProcessCallbacks();
+
+ CHub();
+ ~CHub() { StopProcessCallbacks(); }
+
+ bool ConnectToBlockStore(CBlockStore* pblockstoreIn);
+
+//Register methods
+ // Register a handler (of the form void f(const CBlock& block)) to be called after every block commit
+ void RegisterCommitBlock(boost::function<void (const CBlock&)> func) { LOCK(sigtable.cs_sigCommitBlock); sigtable.sigCommitBlock.connect(func); }
+
+ // Register a handler (of the form void f(const CTransaction& tx)) to be called after every transaction commit to memory pool
+ void RegisterCommitTransactionToMemoryPool(boost::function<void (const CTransaction&)> func) { LOCK(sigtable.cs_sigCommitTransactionToMemoryPool); sigtable.sigCommitTransactionToMemoryPool.connect(func); }
+
+ // Register a handler (of the form void f(const CAlert& alert)) to be called after every alert commit
+ void RegisterCommitAlert(boost::function<void (const CAlert&)> func) { LOCK(sigtable.cs_sigCommitAlert); sigtable.sigCommitAlert.connect(func); }
+ // Register a handler (of the form void f(const CAlert& alert)) to be called after every alert cancel or expire
+ void RegisterRemoveAlert(boost::function<void (const CAlert&)> func) { LOCK(sigtable.cs_sigRemoveAlert); sigtable.sigRemoveAlert.connect(func); }
+
+ // Register a handler (of the form void f(const uint256 hashEnd, const uint256 hashOriginator)) to be called when we need to ask for blocks up to hashEnd
+ // Should always start from the best block (GetBestBlockIndex())
+ // The receiver should check if it has a peer which is known to have a block with hash hashOriginator and if it does, it should
+ // send the block query to that node.
+ void RegisterAskForBlocks(boost::function<void (const uint256, const uint256)> func) { LOCK(sigtable.cs_sigAskForBlocks); sigtable.sigAskForBlocks.connect(func); }
+
+//Blockchain access methods
+ // Emit methods will verify the object, commit it to memory/disk and then place it in queue to
+ // be handled by listeners
+
+ // if (!fBlocking) only initial checks will be performed before returning
+ // This means block.nDoS may not be set to its final value before returning
+ // pNodeDoS->Misbehaving() will be called with the final value of block.nDoS at some point during callbacks.
+ bool EmitBlock(CBlock& block, bool fBlocking=true, CNode* pNodeDoS=NULL) { if (!pblockstore) return false; return pblockstore->EmitBlock(block, fBlocking, pNodeDoS); }
+
+ bool EmitAlert(CAlert& alert);
+
+ // Emitting transactions already in a block is acceptable only if it is a supporting
+ // transaction for one of our own
+ // fCheckInputs is ignored (and set to true) if !IsInitialBlockDownload() && !fClient
+ // Only set fCheckInputs when tx is a supporting transaction for one of our own
+ bool EmitTransaction(CMerkleTx& tx, bool fCheckInputs=true);
+ bool EmitTransaction(CTransaction& tx, bool fCheckInputs=true);
+
+ // Returns true if we haven't seen a given inv and want it
+ bool NeedInv(const CInv& inv);
+
+ // Return CBlockIndex* with *phashBlock == hash or NULL if we dont have one
+ // if (fBlocking) wait for the block to be committed (assuming it has already been emitted)
+ // This can still return NULL even if the block has been emitted if the block is invalid
+ // WARNING: DO NOT call this with fBlocking == true if you are holding cs_main
+ const CBlockIndex* GetBlockIndex(const uint256& hash, bool fBlocking=false) { if (!pblockstore) return NULL; return pblockstore->GetBlockIndex(hash, fBlocking); }
+
+//Connected wallet/etc access methods
+
+ // Ask that any listeners who have access to ask other nodes for blocks
+ // (ie net) ask for all blocks between GetBestBlockIndex() and hashEnd
+ // If hashOriginator is specified, then a node which is known to have a block
+ // with that hash will be the one to get the block request, unless no connected
+ // nodes are known to have this block, in which case a random one will be queried.
+ void AskForBlocks(const uint256 hashEnd, const uint256 hashOriginator);
+};
+
+// A simple generic CHub Listening class which can be extended, if you wish
+class CHubListener
+{
+public:
+ void RegisterWithHub(CHub* phub);
+ void DeregisterFromHub();
+
+ CHubListener() {}
+ CHubListener(CHub* phub) { RegisterWithHub(phub); }
+ ~CHubListener() { DeregisterFromHub(); }
+
+protected:
+ virtual void HandleCommitBlock(const CBlock& block) {}
+
+ virtual void HandleCommitTransactionToMemoryPool(const CTransaction& tx) {}
+
+ virtual void HandleCommitAlert(const CAlert& alert) {}
+ virtual void HandleRemoveAlert(const CAlert& alert) {}
+
+ virtual void HandleAskForBlocks(const uint256, const uint256) {}
+};
+
+extern CHub* phub;
+
+#endif
View
21 src/init.cpp
@@ -9,6 +9,8 @@
#include "init.h"
#include "util.h"
#include "ui_interface.h"
+#include "hub.h"
+#include "checkpoints.h"
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/convenience.hpp>
@@ -23,6 +25,7 @@ using namespace std;
using namespace boost;
CWallet* pwalletMain;
+CBlockStore* pblockstore;
CClientUIInterface uiInterface;
//////////////////////////////////////////////////////////////////////////////
@@ -55,6 +58,8 @@ void Shutdown(void* parg)
if (fFirstThread)
{
fShutdown = true;
+ if (phub) phub->StopProcessCallbacks();
+ if (pblockstore) pblockstore->StopProcessCallbacks();
nTransactionsUpdated++;
bitdb.Flush(false);
StopNode();
@@ -259,6 +264,7 @@ std::string HelpMessage()
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
" -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" +
" -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" +
+ " -blockbuffersize=<n> " + _("The maximum number of blocks to buffer for committing to disk (default: 20)") + "\n";
" -? " + _("This help message") + "\n";
strUsage += string() +
@@ -519,18 +525,26 @@ bool AppInit2()
// ********************************************************* Step 6: load blockchain
+ try {
+ phub = new CHub();
+ } catch (runtime_error& e) {
+ return InitError(_("Unable to create CHub."));
+ }
+ pblockstore = new CBlockStore();
+ phub->ConnectToBlockStore(pblockstore);
+ phub->RegisterCommitBlock(&Checkpoints::HandleCommitBlock);
+
if (GetBoolArg("-loadblockindextest"))
{
- CTxDB txdb("r");
- txdb.LoadBlockIndex();
+ pblockstore->LoadBlockIndex(true);
PrintBlockTree();
return false;
}
uiInterface.InitMessage(_("Loading block index..."));
printf("Loading block index...\n");
nStart = GetTimeMillis();
- if (!LoadBlockIndex())
+ if (!pblockstore->LoadBlockIndex())
strErrors << _("Error loading blkindex.dat") << "\n";
// as LoadBlockIndex can take several minutes, it's possible the user
@@ -629,6 +643,7 @@ bool AppInit2()
printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
RegisterWallet(pwalletMain);
+ pwalletMain->RegisterWithHub(phub);
CBlockIndex *pindexRescan = pindexBest;
if (GetBoolArg("-rescan"))
View
863 src/main.cpp
@@ -8,6 +8,7 @@
#include "net.h"
#include "init.h"
#include "ui_interface.h"
+#include "hub.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -43,8 +44,8 @@ CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
-map<uint256, CDataStream*> mapOrphanTransactions;
-map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
+map<uint256, CTransaction*> mapOrphanTransactions;
+map<uint256, map<uint256, CTransaction*> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
@@ -115,20 +116,6 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
}
-// notify wallets about a new best chain
-void static SetBestChain(const CBlockLocator& loc)
-{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- pwallet->SetBestChain(loc);
-}
-
-// notify wallets about an updated transaction
-void static UpdatedTransaction(const uint256& hashTx)
-{
- BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
- pwallet->UpdatedTransaction(hashTx);
-}
-
// dump all wallets
void static PrintWallets(const CBlock& block)
{
@@ -161,15 +148,13 @@ void static ResendWalletTransactions()
// mapOrphanTransactions
//
-bool AddOrphanTx(const CDataStream& vMsg)
+bool AddOrphanTx(const CTransaction& tx)
{
- CTransaction tx;
- CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
return false;
- CDataStream* pvMsg = new CDataStream(vMsg);
+ CTransaction* pTx = new CTransaction(tx);
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
@@ -178,16 +163,17 @@ bool AddOrphanTx(const CDataStream& vMsg)
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
- if (pvMsg->size() > 5000)
+ unsigned int size = tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION);
+ if (size > 5000)
{
- delete pvMsg;
- printf("ignoring large orphan tx (size: %u, hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str());
+ delete pTx;
+ printf("ignoring large orphan tx (size: %u, hash: %s)\n", size, hash.ToString().substr(0,10).c_str());
return false;
}
- mapOrphanTransactions[hash] = pvMsg;
+ mapOrphanTransactions[hash] = pTx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
+ mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pTx));
printf("stored orphan tx %s (mapsz %u)\n", hash.ToString().substr(0,10).c_str(),
mapOrphanTransactions.size());
@@ -198,16 +184,14 @@ void static EraseOrphanTx(uint256 hash)
{
if (!mapOrphanTransactions.count(hash))
return;
- const CDataStream* pvMsg = mapOrphanTransactions[hash];
- CTransaction tx;
- CDataStream(*pvMsg) >> tx;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ const CTransaction* pTx = mapOrphanTransactions[hash];
+ BOOST_FOREACH(const CTxIn& txin, pTx->vin)
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
- delete pvMsg;
+ delete pTx;
mapOrphanTransactions.erase(hash);
}
@@ -218,7 +202,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
// Evict a random orphan:
uint256 randomhash = GetRandHash();
- map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
+ map<uint256, CTransaction*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
@@ -614,9 +598,75 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
return true;
}
-bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
+bool CHub::EmitTransactionInner(CTransaction& tx, bool fCheckInputs)
+{
+ uint256 hash = tx.GetHash();
+
+ CTxDB* ptxdb = NULL;
+ if (!fClient && fCheckInputs)
+ ptxdb = new CTxDB("r");
+
+ bool fMissingInputs = false;
+ if (mempool.accept(*ptxdb, tx, fCheckInputs, &fMissingInputs))
+ {
+ // Recursively process any orphan transactions that depended on this one
+ for (map<uint256, CTransaction*>::iterator mi = mapOrphanTransactionsByPrev[hash].begin();
+ mi != mapOrphanTransactionsByPrev[hash].end();
+ ++mi)
+ {
+ CTransaction& tx2 = *((*mi).second);
+ CInv inv(MSG_TX, tx2.GetHash());
+
+ if (phub->EmitTransaction(tx2))
+ printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
+ }
+
+ SubmitCallbackCommitTransactionToMemoryPool(tx);
+ return true;
+ }
+ else if (fMissingInputs)
+ {
+ AddOrphanTx(tx);
+
+ // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
+ unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
+ if (nEvicted > 0)
+ printf("mapOrphan overflow, removed %u tx\n", nEvicted);
+
+ return true;
+ }
+ else
+ EraseOrphanTx(hash);
+
+ return false;
+}
+
+bool CHub::EmitTransaction(CTransaction& tx, bool fCheckInputs)
{
- return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
+ LOCK(cs_main);
+
+ if (!fClient && !IsInitialBlockDownload())
+ fCheckInputs = true;
+
+ if (fClient && fCheckInputs)
+ if (!tx.ClientConnectInputs())
+ return false;
+
+ return EmitTransactionInner(tx, fCheckInputs);
+}
+
+bool CHub::EmitTransaction(CMerkleTx& tx, bool fCheckInputs)
+{
+ LOCK(cs_main);
+
+ if (!fClient && !IsInitialBlockDownload())
+ fCheckInputs = true;
+
+ if (fClient)
+ if (!tx.IsInMainChain() && !tx.ClientConnectInputs())
+ return false;
+
+ return EmitTransactionInner(tx, fCheckInputs);
}
bool CTxMemPool::addUnchecked(CTransaction &tx)
@@ -691,53 +741,6 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs)
-{
- if (fClient)
- {
- if (!IsInMainChain() && !ClientConnectInputs())
- return false;
- return CTransaction::AcceptToMemoryPool(txdb, false);
- }
- else
- {
- return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs);
- }
-}
-
-bool CMerkleTx::AcceptToMemoryPool()
-{
- CTxDB txdb("r");
- return AcceptToMemoryPool(txdb);
-}
-
-
-
-bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
-{
-
- {
- LOCK(mempool.cs);
- // Add previous supporting transactions first
- BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
- {
- if (!tx.IsCoinBase())
- {
- uint256 hash = tx.GetHash();
- if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
- tx.AcceptToMemoryPool(txdb, fCheckInputs);
- }
- }
- return AcceptToMemoryPool(txdb, fCheckInputs);
- }
- return false;
-}
-
-bool CWalletTx::AcceptWalletTransaction()
-{
- CTxDB txdb("r");
- return AcceptWalletTransaction(txdb);
-}
int CTxIndex::GetDepthInMainChain() const
{
@@ -960,7 +963,6 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
{
bnBestInvalidWork = pindexNew->bnChainWork;
CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
- uiInterface.NotifyBlocksChanged();
}
printf("InvalidChainFound: invalid block=%s height=%d work=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainWork.ToString().c_str());
printf("InvalidChainFound: current best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str());
@@ -1282,11 +1284,11 @@ bool CTransaction::ClientConnectInputs()
-bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
+bool CBlockStore::DisconnectBlock(CBlock& block, CTxDB& txdb, CBlockIndex* pindex)
{
// Disconnect in reverse order
- for (int i = vtx.size()-1; i >= 0; i--)
- if (!vtx[i].DisconnectInputs(txdb))
+ for (int i = block.vtx.size()-1; i >= 0; i--)
+ if (!block.vtx[i].DisconnectInputs(txdb))
return false;
// Update block index on disk without changing it in memory.
@@ -1302,10 +1304,10 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
return true;
}
-bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
+bool CBlockStore::ConnectBlock(CBlock& block, CTxDB& txdb, CBlockIndex* pindex)
{
// Check it again in case a previous version let a bad block in
- if (!CheckBlock())
+ if (!block.CheckBlock())
return false;
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
@@ -1320,7 +1322,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
// On testnet it is enabled as of februari 20, 2012, 0:00 UTC.
if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000))
{
- BOOST_FOREACH(CTransaction& tx, vtx)
+ BOOST_FOREACH(CTransaction& tx, block.vtx)
{
CTxIndex txindexOld;
if (txdb.ReadTxIndex(tx.GetHash(), txindexOld))
@@ -1337,19 +1339,19 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
//// issue here: it doesn't know the version
- unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size());
+ unsigned int nTxPos = pindex->nBlockPos + GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(block.vtx.size());
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
unsigned int nSigOps = 0;
- BOOST_FOREACH(CTransaction& tx, vtx)
+ BOOST_FOREACH(CTransaction& tx, block.vtx)
{
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
+ return block.DoS(100, error("ConnectBlock() : too many sigops"));
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
- nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
+ nTxPos += GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
MapPrevTx mapInputs;
if (!tx.IsCoinBase())
@@ -1365,7 +1367,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
// an incredibly-expensive-to-validate block.
nSigOps += tx.GetP2SHSigOpCount(mapInputs);
if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
+ return block.DoS(100, error("ConnectBlock() : too many sigops"));
}
nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
@@ -1384,7 +1386,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
return error("ConnectBlock() : UpdateTxIndex failed");
}
- if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
+ if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
return false;
// Update block index on disk without changing it in memory.
@@ -1397,14 +1399,10 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
return error("ConnectBlock() : WriteBlockIndex failed");
}
- // Watch for transactions paying to me
- BOOST_FOREACH(CTransaction& tx, vtx)
- SyncWithWallets(tx, this, true);
-
return true;
}
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool CBlockStore::Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
{
printf("REORGANIZE\n");
@@ -1415,85 +1413,86 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
{
while (plonger->nHeight > pfork->nHeight)
if (!(plonger = plonger->pprev))
- return error("Reorganize() : plonger->pprev is null");
+ return error("CBlockStore::Reorganize() : plonger->pprev is null");
if (pfork == plonger)
break;
if (!(pfork = pfork->pprev))
- return error("Reorganize() : pfork->pprev is null");
+ return error("CBlockStore::Reorganize() : pfork->pprev is null");
}
// List of what to disconnect
- vector<CBlockIndex*> vDisconnect;
+ list<CBlockIndex*> lDisconnect;
for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev)
- vDisconnect.push_back(pindex);
+ lDisconnect.push_back(pindex);
// List of what to connect
- vector<CBlockIndex*> vConnect;
+ list<CBlockIndex*> lConnect;
for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev)
- vConnect.push_back(pindex);
- reverse(vConnect.begin(), vConnect.end());
+ lConnect.push_front(pindex);
- printf("REORGANIZE: Disconnect %i blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
- printf("REORGANIZE: Connect %i blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Disconnect %i blocks; %s..%s\n", lDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Connect %i blocks; %s..%s\n", lConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
// Disconnect shorter branch
- vector<CTransaction> vResurrect;
- BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
+ list<CTransaction> lResurrect;
+ BOOST_FOREACH(CBlockIndex* pindex, lDisconnect)
{
CBlock block;
if (!block.ReadFromDisk(pindex))
- return error("Reorganize() : ReadFromDisk for disconnect failed");
- if (!block.DisconnectBlock(txdb, pindex))
- return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ return error("CBlockStore::Reorganize() : ReadFromDisk for disconnect failed");
+ if (!DisconnectBlock(block, txdb, pindex))
+ return error("CBlockStore::Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!tx.IsCoinBase())
- vResurrect.push_back(tx);
+ lResurrect.push_back(tx);
}
// Connect longer branch
- vector<CTransaction> vDelete;
- for (unsigned int i = 0; i < vConnect.size(); i++)
+ list<CBlock> lCommitted;
+ BOOST_FOREACH(CBlockIndex* pindex, lConnect)
{
- CBlockIndex* pindex = vConnect[i];
CBlock block;
if (!block.ReadFromDisk(pindex))
- return error("Reorganize() : ReadFromDisk for connect failed");
- if (!block.ConnectBlock(txdb, pindex))
+ return error("CBlockStore::Reorganize() : ReadFromDisk for connect failed");
+ if (!ConnectBlock(block, txdb, pindex))
{
// Invalid block
- return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ return error("CBlockStore::Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
}
// Queue memory transactions to delete
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
- vDelete.push_back(tx);
+ lCommitted.push_back(block);
}
if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
- return error("Reorganize() : WriteHashBestChain failed");
+ return error("CBlockStore::Reorganize() : WriteHashBestChain failed");
// Make sure it's successfully written to disk before changing memory structure
if (!txdb.TxnCommit())
- return error("Reorganize() : TxnCommit failed");
+ return error("CBlockStore::Reorganize() : TxnCommit failed");
// Disconnect shorter branch
- BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
+ BOOST_FOREACH(CBlockIndex* pindex, lDisconnect)
if (pindex->pprev)
pindex->pprev->pnext = NULL;
// Connect longer branch
- BOOST_FOREACH(CBlockIndex* pindex, vConnect)
+ BOOST_FOREACH(CBlockIndex* pindex, lConnect)
if (pindex->pprev)
pindex->pprev->pnext = pindex;
// Resurrect memory transactions that were in the disconnected branch
- BOOST_FOREACH(CTransaction& tx, vResurrect)
- tx.AcceptToMemoryPool(txdb, false);
+ BOOST_FOREACH(CTransaction& tx, lResurrect)
+ mempool.accept(txdb, tx, false, NULL);
// Delete redundant memory transactions that are in the connected branch
- BOOST_FOREACH(CTransaction& tx, vDelete)
- mempool.remove(tx);
+ BOOST_FOREACH(CBlock& block, lCommitted)
+ {
+ BOOST_FOREACH(CTransaction& tx, block.vtx)
+ mempool.remove(tx);
+ CallbackCommitBlock(block);
+ }
printf("REORGANIZE: done\n");
@@ -1502,12 +1501,12 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
// Called from inside SetBestChain: attaches a block to the new best chain being built
-bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
+bool CBlockStore::SetBestChainInner(CBlock& block, CTxDB& txdb, CBlockIndex *pindexNew)
{
- uint256 hash = GetHash();
+ uint256 hash = block.GetHash();
// Adding to current best branch
- if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
+ if (!ConnectBlock(block, txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
{
txdb.TxnAbort();
InvalidChainFound(pindexNew);
@@ -1520,15 +1519,17 @@ bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
pindexNew->pprev->pnext = pindexNew;
// Delete redundant memory transactions
- BOOST_FOREACH(CTransaction& tx, vtx)
+ BOOST_FOREACH(CTransaction& tx, block.vtx)
mempool.remove(tx);
+ CallbackCommitBlock(block);
+
return true;
}
-bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
+bool CBlockStore::SetBestChain(CBlock& block, CTxDB& txdb, CBlockIndex* pindexNew)
{
- uint256 hash = GetHash();
+ uint256 hash = block.GetHash();
if (!txdb.TxnBegin())
return error("SetBestChain() : TxnBegin failed");
@@ -1540,9 +1541,9 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
return error("SetBestChain() : TxnCommit failed");
pindexGenesisBlock = pindexNew;
}
- else if (hashPrevBlock == hashBestChain)
+ else if (block.hashPrevBlock == hashBestChain)
{
- if (!SetBestChainInner(txdb, pindexNew))
+ if (!SetBestChainInner(block, txdb, pindexNew))
return error("SetBestChain() : SetBestChainInner failed");
}
else
@@ -1551,18 +1552,18 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
CBlockIndex *pindexIntermediate = pindexNew;
// list of blocks that need to be connected afterwards
- std::vector<CBlockIndex*> vpindexSecondary;
+ std::list<CBlockIndex*> lpindexSecondary;
// Reorganize is costly in terms of db load, as it works in a single db transaction.
// Try to limit how much needs to be done inside
while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
{
- vpindexSecondary.push_back(pindexIntermediate);
+ lpindexSecondary.push_back(pindexIntermediate);
pindexIntermediate = pindexIntermediate->pprev;
}
- if (!vpindexSecondary.empty())
- printf("Postponing %i reconnects\n", vpindexSecondary.size());
+ if (!lpindexSecondary.empty())
+ printf("Postponing %i reconnects\n", lpindexSecondary.size());
// Switch to new best branch
if (!Reorganize(txdb, pindexIntermediate))
@@ -1573,10 +1574,10 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
}
// Connect futher blocks
- BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, lpindexSecondary)
{
- CBlock block;
- if (!block.ReadFromDisk(pindex))
+ CBlock block2;
+ if (!block2.ReadFromDisk(pindex))
{
printf("SetBestChain() : ReadFromDisk failed\n");
break;
@@ -1586,19 +1587,11 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
break;
}
// errors now are not fatal, we still did a reorganisation to a new chain in a valid way
- if (!block.SetBestChainInner(txdb, pindex))
+ if (!SetBestChainInner(block2, txdb, pindex))
break;
}
}
- // Update best block in wallet (so we can detect restored wallets)
- bool fIsInitialDownload = IsInitialBlockDownload();
- if (!fIsInitialDownload)
- {
- const CBlockLocator locator(pindexNew);
- ::SetBestChain(locator);
- }
-
// New best block
hashBestChain = hash;
pindexBest = pindexNew;
@@ -1610,7 +1603,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
std::string strCmd = GetArg("-blocknotify", "");
- if (!fIsInitialDownload && !strCmd.empty())
+ if (!IsInitialBlockDownload() && !strCmd.empty())
{
boost::replace_all(strCmd, "%s", hashBestChain.GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
@@ -1620,20 +1613,20 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
}
-bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
+bool CBlockStore::AddToBlockIndex(CBlock& block, unsigned int nFile, unsigned int nBlockPos)
{
// Check for duplicate
- uint256 hash = GetHash();
+ uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str());
// Construct new block index object
- CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this);
+ CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, block);
if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed");
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
- map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock);
+ map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
@@ -1650,20 +1643,11 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
// New best
if (pindexNew->bnChainWork > bnBestChainWork)
- if (!SetBestChain(txdb, pindexNew))
+ if (!SetBestChain(block, txdb, pindexNew))
return false;
txdb.Close();
- if (pindexNew == pindexBest)
- {
- // Notify UI to display prev block's coinbase if it was ours
- static uint256 hashPrevBestCoinBase;
- UpdatedTransaction(hashPrevBestCoinBase);
- hashPrevBestCoinBase = vtx[0].GetHash();
- }
-
- uiInterface.NotifyBlocksChanged();
return true;
}
@@ -1724,138 +1708,198 @@ bool CBlock::CheckBlock() const
return true;
}
-bool CBlock::AcceptBlock()
+bool CBlockStore::AcceptBlock(CBlock& block)
{
// Check for duplicate
- uint256 hash = GetHash();
+ uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return error("AcceptBlock() : block already in mapBlockIndex");
// Get prev block index
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
+ map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
- return DoS(10, error("AcceptBlock() : prev block not found"));
+ return block.DoS(10, error("AcceptBlock() : prev block not found"));
CBlockIndex* pindexPrev = (*mi).second;
int nHeight = pindexPrev->nHeight+1;
// Check proof of work
- if (nBits != GetNextWorkRequired(pindexPrev, this))
- return DoS(100, error("AcceptBlock() : incorrect proof of work"));
+ if (block.nBits != GetNextWorkRequired(pindexPrev, &block))
+ return block.DoS(100, error("AcceptBlock() : incorrect proof of work"));
// Check timestamp against prev
- if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
+ if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
return error("AcceptBlock() : block's timestamp is too early");
// Check that all transactions are finalized
- BOOST_FOREACH(const CTransaction& tx, vtx)
- if (!tx.IsFinal(nHeight, GetBlockTime()))
- return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
+ BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ if (!tx.IsFinal(nHeight, block.GetBlockTime()))
+ return block.DoS(10, error("AcceptBlock() : contains a non-final transaction"));
// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(nHeight, hash))
- return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight));
+ return block.DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight));
// Write block to history file
- if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
+ if (!CheckDiskSpace(GetSerializeSize(block, SER_DISK, CLIENT_VERSION)))
return error("AcceptBlock() : out of disk space");
unsigned int nFile = -1;
unsigned int nBlockPos = 0;
- if (!WriteToDisk(nFile, nBlockPos))
+ if (!block.WriteToDisk(nFile, nBlockPos))
return error("AcceptBlock() : WriteToDisk failed");
- if (!AddToBlockIndex(nFile, nBlockPos))
+ if (!AddToBlockIndex(block, nFile, nBlockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
- // Relay inventory, but don't relay old inventory during initial block download
- int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
- if (hashBestChain == hash)
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
- pnode->PushInventory(CInv(MSG_BLOCK, hash));
- }
-
return true;
}
-bool ProcessBlock(CNode* pfrom, CBlock* pblock)
+bool CBlockStore::EmitBlock(CBlock& block, bool fBlocking, CNode* pNodeDoS)
{
// Check for duplicate
- uint256 hash = pblock->GetHash();
- if (mapBlockIndex.count(hash))
- return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str());
- if (mapOrphanBlocks.count(hash))
- return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
+ uint256 hash = block.GetHash();
+ {
+ LOCK(cs_setBlocksSeen);
+ if (setBlocksSeen.count(hash) > 0)
+ return error("CHub::EmitBlock() : already seen block %s", hash.ToString().substr(0,20).c_str());
+ }
// Preliminary checks
- if (!pblock->CheckBlock())
- return error("ProcessBlock() : CheckBlock FAILED");
+ if (!block.CheckBlock())
+ {
+ if (block.nDoS && pNodeDoS)
+ CallbackDoS(pNodeDoS, block.nDoS);
+ return error("CBlockStore::EmitBlock() : CheckBlock FAILED");
+ }
- CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
- if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
+ const CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint();
+ if (pcheckpoint && block.hashPrevBlock != hashBestChain)
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
- int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
+ int64 deltaTime = block.GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
- if (pfrom)
- pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with timestamp before last checkpoint");
+ if (pNodeDoS) CallbackDoS(pNodeDoS, 100);
+ return block.DoS(100, error("CBlockStore::EmitBlock() : block with timestamp before last checkpoint"));
}
CBigNum bnNewBlock;
- bnNewBlock.SetCompact(pblock->nBits);
+ bnNewBlock.SetCompact(block.nBits);
CBigNum bnRequired;
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
- if (pfrom)
- pfrom->Misbehaving(100);
- return error("ProcessBlock() : block with too little proof-of-work");
+ if (pNodeDoS) CallbackDoS(pNodeDoS, 100);
+ return block.DoS(100, error("CBlockStore::EmitBlock() : block with too little proof-of-work"));
}
}
+ {
+ LOCK(cs_setBlocksSeen);
+ setBlocksSeen.insert(hash);
+ }
+
+ if (fBlocking)
+ return FinishEmitBlock(block, pNodeDoS);
+ else
+ SubmitCallbackFinishEmitBlock(block, pNodeDoS);
+
+ return true;
+}
+
+bool CBlockStore::FinishEmitBlock(CBlock& block, CNode* pNodeDoS)
+{
+ uint256 hash = block.GetHash();
+
+ LOCK(cs_main);
// If don't already have its previous block, shunt it off to holding area until we get it
- if (!mapBlockIndex.count(pblock->hashPrevBlock))
+ if (!mapBlockIndex.count(block.hashPrevBlock))
{
- printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
- CBlock* pblock2 = new CBlock(*pblock);
- mapOrphanBlocks.insert(make_pair(hash, pblock2));
- mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
+ printf("CBlockStore::FinishEmitBlock: ORPHAN BLOCK, prev=%s\n", block.hashPrevBlock.ToString().substr(0,20).c_str());
@rebroad
rebroad added a line comment Jun 14, 2012

Why make this line longer than it already was?

@sipa
Bitcoin member
sipa added a line comment Jun 14, 2012

Because the name of the function it is in, is being renamed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ CBlock* pblock = new CBlock(block);
+ mapOrphanBlocks.insert(make_pair(hash, pblock));
+ mapOrphanBlocksByPrev.insert(make_pair(pblock->hashPrevBlock, pblock));
// Ask this guy to fill in what we're missing
- if (pfrom)
- pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
+ CallbackAskForBlocks(GetOrphanRoot(pblock), hash);
return true;
}
// Store to disk
- if (!pblock->AcceptBlock())
- return error("ProcessBlock() : AcceptBlock FAILED");
+ if (!AcceptBlock(block))
+ {
+ if (block.nDoS && pNodeDoS)
+ CallbackDoS(pNodeDoS, block.nDoS);
+ return error("CBlockStore::FinishEmitBlock() : AcceptBlock FAILED");
+ }
// Recursively process any orphan blocks that depended on this one
- vector<uint256> vWorkQueue;
- vWorkQueue.push_back(hash);
- for (unsigned int i = 0; i < vWorkQueue.size(); i++)
+ queue<uint256> qWorkQueue;
+ qWorkQueue.push(hash);
+ while (!qWorkQueue.empty())
{
- uint256 hashPrev = vWorkQueue[i];
+ uint256 hashPrev = qWorkQueue.front();
+ qWorkQueue.pop();
for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev);
mi != mapOrphanBlocksByPrev.upper_bound(hashPrev);
++mi)
{
CBlock* pblockOrphan = (*mi).second;
- if (pblockOrphan->AcceptBlock())
- vWorkQueue.push_back(pblockOrphan->GetHash());
- mapOrphanBlocks.erase(pblockOrphan->GetHash());
+ uint256 hashOrphan = pblockOrphan->GetHash();
+ if (AcceptBlock(*pblockOrphan))
+ qWorkQueue.push(hashOrphan);
+ mapOrphanBlocks.erase(hashOrphan);
delete pblockOrphan;
}
mapOrphanBlocksByPrev.erase(hashPrev);
}
- printf("ProcessBlock: ACCEPTED\n");
+ printf("CBlockStore::FinishEmitBlock: ACCEPTED\n");
@rebroad
rebroad added a line comment Jun 14, 2012

Not sure why this line is being changed....

@TheBlueMatt
TheBlueMatt added a line comment Jun 15, 2012

Because the function name has changed.

@rebroad
rebroad added a line comment Jul 2, 2012

ProcessBlock is more visually appealing IMHO.

@TheBlueMatt
TheBlueMatt added a line comment Jul 2, 2012

But if it were ProcessBlock, it would be unclear and searching to code for where the printf was called would be harder (which is the point of prefixing the print with the function that called it)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
return true;
}
+bool CBlockStore::HaveSeenBlock(const uint256& hash)
+{
+ LOCK(cs_setBlocksSeen);
+ if (setBlocksSeen.count(hash))
+ return true;
+ return false;
+}
+
+const CBlockIndex* CBlockStore::GetBlockIndex(const uint256& hash, bool fBlocking)
+{
+ bool fHave;
+ {
+ LOCK(cs_setBlocksSeen);
+ fHave = setBlocksSeen.count(hash) > 0;
+ }
+ CSemaphore* psem;
+ {
+ LOCK(cs_main);
+ if (mapBlockIndex.count(hash) > 0)
+ return mapBlockIndex[hash];
+
+ if (!fBlocking || !fHave)
+ return NULL;
+ else
+ {
+ LOCK(cs_mapGetBlockIndexWaits);
+ std::map<uint256, CSemaphore*>::iterator it = mapGetBlockIndexWaits.find(hash);
+ if (it != mapGetBlockIndexWaits.end() && it->second != NULL)
+ psem = it->second;
+ else
+ psem = mapGetBlockIndexWaits[hash] = new CSemaphore(0);
+ }
+ }
+ psem->wait();
+ {
+ LOCK(cs_main);
+ std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hash);
+ if (it != mapBlockIndex.end())
+ return it->second;
+ else
+ return NULL;
+ }
+}
@@ -1922,7 +1966,7 @@ FILE* AppendBlockFile(unsigned int& nFileRet)
}
}
-bool LoadBlockIndex(bool fAllowNew)
+bool CBlockStore::LoadBlockIndex(bool fReadOnly)
{
if (fTestNet)
{
@@ -1936,17 +1980,143 @@ bool LoadBlockIndex(bool fAllowNew)
//
// Load block index
//
- CTxDB txdb("cr");
+ CTxDB txdb(fReadOnly ? "r" : "cr");
if (!txdb.LoadBlockIndex())
return false;
+
+ // Verify blocks in the best chain
+ int nCheckLevel = GetArg("-checklevel", 1);
+ int nCheckDepth = GetArg( "-checkblocks", 2500);
+ if (nCheckDepth == 0)
+ nCheckDepth = 1000000000; // suffices until the year 19000
+ if (nCheckDepth > nBestHeight)
+ nCheckDepth = nBestHeight;
+ printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
+ CBlockIndex* pindexFork = NULL;
+ map<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos;
+ for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
+ {
+ if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
+ break;
+ CBlock block;
+ if (!block.ReadFromDisk(pindex))
+ return error("LoadBlockIndex() : block.ReadFromDisk failed");
+ // check level 1: verify block validity
+ if (nCheckLevel>0 && !block.CheckBlock())
+ {
+ printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
+ pindexFork = pindex->pprev;
+ }
+ // check level 2: verify transaction index validity
+ if (nCheckLevel>1)
+ {
+ pair<unsigned int, unsigned int> pos = make_pair(pindex->nFile, pindex->nBlockPos);
+ mapBlockPos[pos] = pindex;
+ BOOST_FOREACH(const CTransaction &tx, block.vtx)
+ {
+ uint256 hashTx = tx.GetHash();
+ CTxIndex txindex;
+ if (txdb.ReadTxIndex(hashTx, txindex))
+ {
+ // check level 3: checker transaction hashes
+ if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos)
+ {
+ // either an error or a duplicate transaction
+ CTransaction txFound;
+ if (!txFound.ReadFromDisk(txindex.pos))
+ {
+ printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str());
+ pindexFork = pindex->pprev;
+ }
+ else
+ if (txFound.GetHash() != hashTx) // not a duplicate tx
+ {
+ printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str());
+ pindexFork = pindex->pprev;
+ }
+ }
+ // check level 4: check whether spent txouts were spent within the main chain
+ unsigned int nOutput = 0;
+ if (nCheckLevel>3)
+ {
+ BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent)
+ {
+ if (!txpos.IsNull())
+ {
+ pair<unsigned int, unsigned int> posFind = make_pair(txpos.nFile, txpos.nBlockPos);
+ if (!mapBlockPos.count(posFind))
+ {
+ printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str());
+ pindexFork = pindex->pprev;
+ }
+ // check level 6: check whether spent txouts were spent by a valid transaction that consume them
+ if (nCheckLevel>5)
+ {
+ CTransaction txSpend;
+ if (!txSpend.ReadFromDisk(txpos))
+ {
+ printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput);
+ pindexFork = pindex->pprev;
+ }
+ else if (!txSpend.CheckTransaction())
+ {
+ printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput);
+ pindexFork = pindex->pprev;
+ }
+ else
+ {
+ bool fFound = false;
+ BOOST_FOREACH(const CTxIn &txin, txSpend.vin)
+ if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput)
+ fFound = true;
+ if (!fFound)
+ {
+ printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput);
+ pindexFork = pindex->pprev;
+ }
+ }
+ }
+ }
+ nOutput++;
+ }
+ }
+ }
+ // check level 5: check whether all prevouts are marked spent
+ if (nCheckLevel>4)
+ {
+ BOOST_FOREACH(const CTxIn &txin, tx.vin)
+ {
+ CTxIndex txindex;
+ if (txdb.ReadTxIndex(txin.prevout.hash, txindex))
+ if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull())
+ {
+ printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str());
+ pindexFork = pindex->pprev;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (pindexFork && !fRequestShutdown)
+ {
+ // Reorg back to the fork
+ printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
+ CBlock block;
+ if (!block.ReadFromDisk(pindexFork))
+ return error("LoadBlockIndex() : block.ReadFromDisk failed");
+ CTxDB txdb;
+ SetBestChain(block, txdb, pindexFork);
+ }
+
txdb.Close();
//
// Init with genesis block
//
if (mapBlockIndex.empty())
{
- if (!fAllowNew)
+ if (fReadOnly)
return false;
// Genesis Block:
@@ -1992,10 +2162,17 @@ bool LoadBlockIndex(bool fAllowNew)
unsigned int nBlockPos;
if (!block.WriteToDisk(nFile, nBlockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
- if (!block.AddToBlockIndex(nFile, nBlockPos))
+ if (!AddToBlockIndex(block, nFile, nBlockPos))
return error("LoadBlockIndex() : genesis block not accepted");
}
+ // Init setBlocksSeen and checkpoints TODO this can be much more efficient
+ for (map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++)
+ {
+ setBlocksSeen.insert(it->first);
+ Checkpoints::HandleCommitBlock(it->second->GetBlockHeader());
+ }
+
return true;
}
@@ -2014,15 +2191,15 @@ void PrintBlockTree()
// mapNext[pindex->pprev].push_back(pindex);
}
- vector<pair<int, CBlockIndex*> > vStack;
- vStack.push_back(make_pair(0, pindexGenesisBlock));
+ stack<pair<int, CBlockIndex*> > sStack;
+ sStack.push(make_pair(0, pindexGenesisBlock));
int nPrevCol = 0;
- while (!vStack.empty())
+ while (!sStack.empty())
{
- int nCol = vStack.back().first;
- CBlockIndex* pindex = vStack.back().second;
- vStack.pop_back();
+ int nCol = sStack.top().first;
+ CBlockIndex* pindex = sStack.top().second;
+ sStack.pop();
// print split or gap
if (nCol > nPrevCol)
@@ -2069,15 +2246,14 @@ void PrintBlockTree()
// iterate children
for (unsigned int i = 0; i < vNext.size(); i++)
- vStack.push_back(make_pair(nCol+i, vNext[i]));
+ sStack.push(make_pair(nCol+i, vNext[i]));
}
}
bool LoadExternalBlockFile(FILE* fileIn)
{
int nLoaded = 0;
{
- LOCK(cs_main);
try {
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
unsigned int nPos = 0;
@@ -2114,7 +2290,7 @@ bool LoadExternalBlockFile(FILE* fileIn)
{
CBlock block;
blkdat >> block;
- if (ProcessBlock(NULL,&block))
+ if (phub->EmitBlock(block, false))
{
nLoaded++;
nPos += 4 + nSize;
@@ -2203,29 +2379,29 @@ CAlert CAlert::getAlertByHash(const uint256 &hash)
return retval;
}
-bool CAlert::ProcessAlert()
+bool CHub::EmitAlert(CAlert& alert)
{
- if (!CheckSignature())
+ if (!alert.CheckSignature())
return false;
- if (!IsInEffect())
+ if (!alert.IsInEffect())
return false;
{
LOCK(cs_mapAlerts);
// Cancel previous alerts
for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
{
- const CAlert& alert = (*mi).second;
- if (Cancels(alert))
+ const CAlert& alert2 = (*mi).second;
+ if (alert.Cancels(alert2))
{
- printf("cancelling alert %d\n", alert.nID);
- uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
+ printf("cancelling alert %d\n", alert2.nID);
+ SubmitCallbackRemoveAlert(alert2);
mapAlerts.erase(mi++);
}
- else if (!alert.IsInEffect())
+ else if (!alert2.IsInEffect())
{
- printf("expiring alert %d\n", alert.nID);
- uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
+ printf("expiring alert %d\n", alert2.nID);
+ SubmitCallbackRemoveAlert(alert2);
mapAlerts.erase(mi++);
}
else
@@ -2235,22 +2411,20 @@ bool CAlert::ProcessAlert()
// Check if this alert has been cancelled
BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
{
- const CAlert& alert = item.second;
- if (alert.Cancels(*this))
+ const CAlert& alert2 = item.second;
+ if (alert2.Cancels(alert))
{
- printf("alert already cancelled by %d\n", alert.nID);
+ printf("alert already cancelled by %d\n", alert2.nID);
return false;
}
}