public override void OnBlocksDownloaded(Peer peer, Block block, int blocksLeft) { if (blocksLeft == 0) { DoneDownload(); _done.Release(); } if (blocksLeft < 0 || _originalBlocksLeft <= 0) return; var pct = 100.0 - (100.0*(blocksLeft/(double) _originalBlocksLeft)); if ((int) pct != _lastPercent) { Progress(pct, UnixTime.FromUnixTime(block.TimeSeconds*1000)); _lastPercent = (int) pct; } }
/// <summary> /// Creates a new StoredBlock, calculating the additional fields by adding to the values in this block. /// </summary> /// <exception cref="VerificationException"/> public StoredBlock Build(Block block) { // Stored blocks track total work done in this chain, because the canonical chain is the one that represents // the largest amount of work done not the tallest. var chainWork = _chainWork.Add(block.GetWork()); var height = _height + 1; return new StoredBlock(block.CloneAsHeader(), chainWork, height); }
/// <summary> /// Returns a solved block that builds on top of this one. This exists for unit tests. /// </summary> internal Block CreateNextBlock(Address to, uint time) { var b = new Block(Params); b.DifficultyTarget = _difficultyTarget; b.AddCoinbaseTransaction(_emptyBytes); // Add a transaction paying 50 coins to the "to" address. var t = new Transaction(Params); t.AddOutput(new TransactionOutput(Params, t, Utils.ToNanoCoins(50, 0), to)); // The input does not really need to be a valid signature, as long as it has the right general form. var input = new TransactionInput(Params, t, Script.CreateInputScript(_emptyBytes, _emptyBytes)); // Importantly the outpoint hash cannot be zero as that's how we detect a coinbase transaction in isolation // but it must be unique to avoid 'different' transactions looking the same. var counter = new byte[32]; counter[0] = (byte) _txCounter++; input.Outpoint.Hash = new Sha256Hash(counter); t.AddInput(input); b.AddTransaction(t); b.PrevBlockHash = Hash; b.TimeSeconds = time; b.Solve(); b.VerifyHeader(); return b; }
public StoredBlock(Block header, BigInteger chainWork, uint height) { _header = header; _chainWork = chainWork; _height = height; }
/// <summary> /// Returns a copy of the block, but without any transactions. /// </summary> public Block CloneAsHeader() { var block = new Block(Params); block._nonce = _nonce; block._prevBlockHash = _prevBlockHash.Duplicate(); block._merkleRoot = MerkleRoot.Duplicate(); block._version = _version; block._time = _time; block._difficultyTarget = _difficultyTarget; block.Transactions = null; block._hash = Hash.Duplicate(); return block; }
/// <summary> /// For the transactions in the given block, update the txToWalletMap such that each wallet maps to a list of /// transactions for which it is relevant. /// </summary> /// <exception cref="VerificationException"/> private void ScanTransactions(Block block, IDictionary<Wallet, List<Transaction>> walletToTxMap) { foreach (var tx in block.Transactions) { try { foreach (var wallet in _wallets) { var shouldReceive = false; foreach (var output in tx.Outputs) { // TODO: Handle more types of outputs, not just regular to address outputs. if (output.ScriptPubKey.IsSentToIp) continue; // This is not thread safe as a key could be removed between the call to isMine and receive. if (output.IsMine(wallet)) { shouldReceive = true; break; } } // Coinbase transactions don't have anything useful in their inputs (as they create coins out of thin air). if (!shouldReceive && !tx.IsCoinBase) { foreach (var i in tx.Inputs) { var pubkey = i.ScriptSig.PubKey; // This is not thread safe as a key could be removed between the call to isPubKeyMine and receive. if (wallet.IsPubKeyMine(pubkey)) { shouldReceive = true; } } } if (!shouldReceive) continue; List<Transaction> txList; if (!walletToTxMap.TryGetValue(wallet, out txList)) { txList = new List<Transaction>(); walletToTxMap[wallet] = txList; } txList.Add(tx); } } catch (ScriptException e) { // We don't want scripts we don't understand to break the block chain so just note that this tx was // not scanned here and continue. _log.Warn("Failed to parse a script: " + e); } } }
/// <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; } }
/// <summary> /// Processes a received block and tries to add it to the chain. If there's something wrong with the block an /// exception is thrown. If the block is OK but cannot be connected to the chain at this time, returns false. /// If the block can be connected to the chain, returns true. /// </summary> /// <exception cref="VerificationException"/> /// <exception cref="ScriptException"/> public bool Add(Block block) { lock (this) { return Add(block, true); } }
private static Block CreateGenesis(NetworkParameters n) { var genesisBlock = new Block(n); var t = new Transaction(n); // A script containing the difficulty bits and the following message: // // "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks" var bytes = Hex.Decode("04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73"); t.AddInput(new TransactionInput(n, t, bytes)); using (var scriptPubKeyBytes = new MemoryStream()) { Script.WriteBytes(scriptPubKeyBytes, Hex.Decode("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")); scriptPubKeyBytes.Write(Script.OpCheckSig); t.AddOutput(new TransactionOutput(n, t, scriptPubKeyBytes.ToArray())); } genesisBlock.AddTransaction(t); return genesisBlock; }