/// <summary> /// Asks the connected peer for the block of the given hash, and returns a Future representing the answer. /// If you want the block right away and don't mind waiting for it, just call .get() on the result. Your thread /// will block until the peer answers. You can also use the Future object to wait with a timeout, or just check /// whether it's done later. /// </summary> /// <param name="blockHash">Hash of the block you were requesting.</param> /// <exception cref="IOException"/> public IAsyncResult BeginGetBlock(Sha256Hash blockHash, AsyncCallback callback, object state) { var getdata = new GetDataMessage(_params); var inventoryItem = new InventoryItem(InventoryItem.ItemType.Block, blockHash); getdata.AddItem(inventoryItem); var future = new GetDataFuture <Block>(inventoryItem, callback, state); // Add to the list of things we're waiting for. It's important this come before the network send to avoid // race conditions. lock (_pendingGetBlockFutures) { _pendingGetBlockFutures.Add(future); } _conn.WriteMessage(getdata); return(future); }
/// <exception cref="IOException"/> private void ProcessInv(InventoryMessage inv) { // This should be called in the network loop thread for this peer // The peer told us about some blocks or transactions they have. For now we only care about blocks. // Note that as we don't actually want to store the entire block chain or even the headers of the block // chain, we may end up requesting blocks we already requested before. This shouldn't (in theory) happen // enough to be a problem. var topBlock = _blockChain.UnconnectedBlock; var topHash = (topBlock != null ? topBlock.Hash : null); var items = inv.Items; if (items.Count == 1 && items[0].Type == InventoryItem.ItemType.Block && topHash != null && items[0].Hash.Equals(topHash)) { // An inv with a single hash containing our most recent unconnected block is a special inv, // it's kind of like a tickle from the peer telling us that it's time to download more blocks to catch up to // the block chain. We could just ignore this and treat it as a regular inv but then we'd download the head // block over and over again after each batch of 500 blocks, which is wasteful. BlockChainDownload(topHash); return; } var getdata = new GetDataMessage(_params); var dirty = false; foreach (var item in items) { if (item.Type != InventoryItem.ItemType.Block) { continue; } getdata.AddItem(item); dirty = true; } // No blocks to download. This probably contained transactions instead, but right now we can't prove they are // valid so we don't bother downloading transactions that aren't in blocks yet. if (!dirty) { return; } // This will cause us to receive a bunch of block messages. _conn.WriteMessage(getdata); }