/// <exception cref="BlockStoreException"/>
/// <exception cref="VerificationException"/>
/// <exception cref="ScriptException"/>
private bool Add(Block block, bool tryConnecting)
{
lock (this)
{
if (Environment.TickCount - _statsLastTime > 1000)
{
// More than a second passed since last stats logging.
_log.InfoFormat("{0} blocks per second", _statsBlocksAdded);
_statsLastTime = Environment.TickCount;
_statsBlocksAdded = 0;
}
// We check only the chain head for double adds here to avoid potentially expensive block chain misses.
if (block.Equals(_chainHead.Header))
{
// Duplicate add of the block at the top of the chain, can be a natural artifact of the download process.
return true;
}
// Does this block contain any transactions we might care about? Check this up front before verifying the
// blocks validity so we can skip the merkle root verification if the contents aren't interesting. This saves
// a lot of time for big blocks.
var contentsImportant = false;
var walletToTxMap = new Dictionary<Wallet, List<Transaction>>();
if (block.Transactions != null)
{
ScanTransactions(block, walletToTxMap);
contentsImportant = walletToTxMap.Count > 0;
}
// Prove the block is internally valid: hash is lower than target, etc. This only checks the block contents
// if there is a tx sending or receiving coins using an address in one of our wallets. And those transactions
// are only lightly verified: presence in a valid connecting block is taken as proof of validity. See the
// article here for more details: http://code.google.com/p/bitcoinj/wiki/SecurityModel
try
{
block.VerifyHeader();
if (contentsImportant)
block.VerifyTransactions();
}
catch (VerificationException e)
{
_log.Error("Failed to verify block:", e);
_log.Error(block.HashAsString);
throw;
}
// Try linking it to a place in the currently known blocks.
var storedPrev = _blockStore.Get(block.PrevBlockHash);
if (storedPrev == null)
{
// We can't find the previous block. Probably we are still in the process of downloading the chain and a
// block was solved whilst we were doing it. We put it to one side and try to connect it later when we
// have more blocks.
_log.WarnFormat("Block does not connect: {0}", block.HashAsString);
_unconnectedBlocks.Add(block);
return false;
}
// It connects to somewhere on the chain. Not necessarily the top of the best known chain.
//
// Create a new StoredBlock from this block. It will throw away the transaction data so when block goes
// out of scope we will reclaim the used memory.
var newStoredBlock = storedPrev.Build(block);
CheckDifficultyTransitions(storedPrev, newStoredBlock);
_blockStore.Put(newStoredBlock);
ConnectBlock(newStoredBlock, storedPrev, walletToTxMap);
if (tryConnecting)
TryConnectingUnconnected();
_statsBlocksAdded++;
return true;
}
}