/// <exception cref="IOException"/> private void BlockChainDownload(Sha256Hash toHash) { // This may run in ANY thread. // The block chain download process is a bit complicated. Basically, we start with zero or more blocks in a // chain that we have from a previous session. We want to catch up to the head of the chain BUT we don't know // where that chain is up to or even if the top block we have is even still in the chain - we // might have got ourselves onto a fork that was later resolved by the network. // // To solve this, we send the peer a block locator which is just a list of block hashes. It contains the // blocks we know about, but not all of them, just enough of them so the peer can figure out if we did end up // on a fork and if so, what the earliest still valid block we know about is likely to be. // // Once it has decided which blocks we need, it will send us an inv with up to 500 block messages. We may // have some of them already if we already have a block chain and just need to catch up. Once we request the // last block, if there are still more to come it sends us an "inv" containing only the hash of the head // block. // // That causes us to download the head block but then we find (in processBlock) that we can't connect // it to the chain yet because we don't have the intermediate blocks. So we rerun this function building a // new block locator describing where we're up to. // // The getblocks with the new locator gets us another inv with another bunch of blocks. We download them once // again. This time when the peer sends us an inv with the head block, we already have it so we won't download // it again - but we recognize this case as special and call back into blockChainDownload to continue the // process. // // So this is a complicated process but it has the advantage that we can download a chain of enormous length // in a relatively stateless manner and with constant/bounded memory usage. _log.InfoFormat("blockChainDownload({0})", toHash); // TODO: Block locators should be abstracted out rather than special cased here. var blockLocator = new LinkedList <Sha256Hash>(); // We don't do the exponential thinning here, so if we get onto a fork of the chain we will end up // re-downloading the whole thing again. blockLocator.AddLast(_params.GenesisBlock.Hash); var topBlock = _blockChain.ChainHead.Header; if (!topBlock.Equals(_params.GenesisBlock)) { blockLocator.AddFirst(topBlock.Hash); } var message = new GetBlocksMessage(_params, blockLocator.ToList(), toHash); _conn.WriteMessage(message); }