/// <exception cref="BitcoinSharp.Blockchain.Store.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.BlockHeader)) { // 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 walletToTransactionMap = new Dictionary<IDefaultWallet, List<Transaction>>(); if (block.Transactions != null) { ScanTransactions(block, walletToTransactionMap); contentsImportant = walletToTransactionMap.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 previousStoredBlock = _blockStore.Get(block.PreviousBlockHash); if (previousStoredBlock == 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 = previousStoredBlock.Build(block); CheckDifficultyTransitions(previousStoredBlock, newStoredBlock); _blockStore.Put(newStoredBlock); ConnectBlock(newStoredBlock, previousStoredBlock, walletToTransactionMap); if (tryConnecting) TryConnectingUnconnected(); _statsBlocksAdded++; return true; } }