protected override void Parse() { var numHeaders = base.ReadVarInt(); if (numHeaders > MaxHeaders) { throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " + MaxHeaders); } BlockHeaders = new List<Block>(); for (var i = 0UL; i < numHeaders; ++i) { // Read 80 bytes of the header and one more byte for the transaction list, which is always a 00 because the // transaction list is empty. var blockHeaderBytes = base.ReadBytes(81); if (blockHeaderBytes[80] != 0) { throw new ProtocolException("Block header does not end with a null byte"); } var newBlockHeader = new Block(this.NetworkParameters, blockHeaderBytes); BlockHeaders.Add(newBlockHeader); } if (!Log.IsDebugEnabled) return; foreach (var blockHeader in BlockHeaders) { Log.Debug(blockHeader); } }
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; } }
public void TestBadTransactions() { var block = new Block(_params, _blockBytes); // Re-arrange so the coinbase transaction is not first. var tx1 = block.Transactions[0]; var tx2 = block.Transactions[1]; block.Transactions[0] = tx2; block.Transactions[1] = tx1; try { block.Verify(); Assert.Fail(); } catch (VerificationException) { // We should get here. } }
public void TestJavaSerialiazation() { var block = new Block(_params, _blockBytes); var tx = block.Transactions[1]; // Serialize using Java. byte[] javaBits; using (var bos = new MemoryStream()) { var oos = new BinaryFormatter(); oos.Serialize(bos, tx); javaBits = bos.ToArray(); } // Deserialize again. Transaction tx2; using (var bos = new MemoryStream(javaBits)) { var ois = new BinaryFormatter(); tx2 = (Transaction) ois.Deserialize(bos); } // Note that this will actually check the transactions are equal by doing BitCoin serialization and checking // the byte streams are the same! A true "deep equals" is not implemented for Transaction. The primary purpose // of this test is to ensure no errors occur during the Java serialization/deserialization process. Assert.AreEqual(tx, tx2); }
private static Block CreateGenesis(NetworkParameters networkParameters) { var genesisBlock = new Block(networkParameters); var transaction = new Transaction(networkParameters); // 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"); transaction.AddInput(new TransactionInput(networkParameters, transaction, bytes)); using (var scriptPubKeyBytes = new MemoryStream()) { Script.WriteBytes(scriptPubKeyBytes,Hex.Decode("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f")); scriptPubKeyBytes.Write(Script.OpCheckSig); transaction.AddOutput(new TransactionOutput(networkParameters, transaction, scriptPubKeyBytes.ToArray())); } genesisBlock.AddTransaction(transaction); return genesisBlock; }
public HeadersMessage(NetworkParameters @params, Block headers) : base(@params) { BlockHeaders = new List<Block>() { headers }; }
private static Block GetBlock1() { var b1 = new Block(_testNet); b1.MerkleRoot = new Sha256Hash(Hex.Decode("0e8e58ecdacaa7b3c6304a35ae4ffff964816d2b80b62b58558866ce4e648c10")); b1.Nonce = 236038445; b1.TimeSeconds = 1296734340; b1.PreviousBlockHash = new Sha256Hash(Hex.Decode("00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008")); Assert.AreEqual("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604", b1.HashAsString); b1.VerifyHeader(); return b1; }
public void TestHeaderParse() { var block = new Block(_params, _blockBytes); var header = block.CloneAsHeader(); var reparsed = new Block(_params, header.BitcoinSerialize()); Assert.AreEqual(reparsed, header); }
public void TestBitCoinSerialization() { // We have to be able to re-serialize everything exactly as we found it for hashing to work. This test also // proves that transaction serialization works, along with all its sub-objects like scripts and in/outpoints. // // NB: This tests the BITCOIN proprietary serialization protocol. A different test checks Java serialization // of transactions. var block = new Block(_params, _blockBytes); Assert.IsTrue(_blockBytes.SequenceEqual(block.BitcoinSerialize())); }
/// <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); }
public StoredBlock(Block blockHeader, BigInteger chainWork, uint height) { _blockHeader = blockHeader; _chainWork = chainWork; _height = height; }
/// <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<IDefaultWallet, List<Transaction>> walletToTransactionMap) { foreach (var transaction in block.Transactions) { try { foreach (var wallet in _wallets) { var shouldReceive = transaction.TransactionOutputs.Where(output => !output.ScriptPublicKey.IsSentToIp) .Any(output => output.IsMine(wallet)); // Coinbase transactions don't have anything useful in their inputs (as they create coins out of thin air). if (!shouldReceive && !transaction.IsCoinBase) { foreach (var transactionInput in transaction.TransactionInputs) { var publicKey = transactionInput.ScriptSig.PublicKey; // This is not thread safe as a key could be removed between the call to isPubKeyMine and receive. if (wallet.IsPublicKeyMine(publicKey)) { shouldReceive = true; } } } if (!shouldReceive) continue; List<Transaction> transactions; if (!walletToTransactionMap.TryGetValue(wallet, out transactions)) { transactions = new List<Transaction>(); walletToTransactionMap[wallet] = transactions; } transactions.Add(transaction); } } 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="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; } }
/// <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); } }
public void TestProofOfWork() { // This params accepts any difficulty target. var @params = NetworkParameters.UnitTests(); var block = new Block(@params, _blockBytes); block.Nonce = 12346; try { block.Verify(); Assert.Fail(); } catch (VerificationException) { // Expected. } // Blocks contain their own difficulty target. The BlockChain verification mechanism is what stops real blocks // from containing artificially weak difficulties. block.TargetDifficulty = Block.EasiestDifficultyTarget; // Now it should pass. block.Verify(); // Break the nonce again at the lower difficulty level so we can try solving for it. block.Nonce = 1; try { block.Verify(); Assert.Fail(); } catch (VerificationException) { // Expected to fail as the nonce is no longer correct. } // Should find an acceptable nonce. block.Solve(); block.Verify(); Assert.AreEqual(block.Nonce, 2U); }
public virtual void OnBlocksDownloaded(Peer peer, Block block, int blocksLeft) { }
/// <exception cref="System.IO.IOException"/> /// <exception cref="BlockStoreException"/> private void Load(FileInfo file) { _log.InfoFormat("Reading block store from {0}", file); using (var input = file.OpenRead()) { // Read a version byte. var version = input.Read(); if (version == -1) { // No such file or the file was empty. throw new FileNotFoundException(file.Name + " does not exist or is empty"); } if (version != 1) { throw new BlockStoreException("Bad version number: " + version); } // Chain head pointer is the first thing in the file. var chainHeadHash = new byte[32]; if (StreamExtensions.Read(input, chainHeadHash) < chainHeadHash.Length) throw new BlockStoreException("Truncated block store: cannot read chain head hash"); _chainHead = new Sha256Hash(chainHeadHash); _log.InfoFormat("Read chain head from disk: {0}", _chainHead); var now = Environment.TickCount; // Rest of file is raw block headers. var headerBytes = new byte[Block.HeaderSize]; try { while (true) { // Read a block from disk. if (StreamExtensions.Read(input, headerBytes) < 80) { // End of file. break; } // Parse it. var b = new Block(_params, headerBytes); // Look up the previous block it connects to. var prev = Get(b.PreviousBlockHash); StoredBlock s; if (prev == null) { // First block in the stored chain has to be treated specially. if (b.Equals(_params.GenesisBlock)) { s = new StoredBlock(_params.GenesisBlock.CloneAsHeader(), _params.GenesisBlock.GetWork(), 0); } else { throw new BlockStoreException("Could not connect " + b.Hash + " to " + b.PreviousBlockHash); } } else { // Don't try to verify the genesis block to avoid upsetting the unit tests. b.VerifyHeader(); // Calculate its height and total chain work. s = prev.Build(b); } // Save in memory. _blockMap[b.Hash] = s; } } catch (ProtocolException e) { // Corrupted file. throw new BlockStoreException(e); } catch (VerificationException e) { // Should not be able to happen unless the file contains bad blocks. throw new BlockStoreException(e); } var elapsed = Environment.TickCount - now; _log.InfoFormat("Block chain read complete in {0}ms", elapsed); } }
public void TestBlockVerification() { var block = new Block(_params, _blockBytes); block.Verify(); Assert.AreEqual("00000000a6e5eb79dcec11897af55e90cd571a4335383a3ccfbc12ec81085935", block.HashAsString); }
/// <summary> /// Returns a copy of the block, but without any transactions. /// </summary> public Block CloneAsHeader() { var block = new Block(NetworkParameters) { _nonce = _nonce, _previousBlockHash = _previousBlockHash.Duplicate(), _merkleRoot = MerkleRoot.Duplicate(), _version = _version, _time = _time, _targetDifficulty = _targetDifficulty, Transactions = null, _hash = Hash.Duplicate() }; return block; }
public void TestBadDifficulty() { Assert.IsTrue(_testNetChain.Add(GetBlock1())); var b2 = GetBlock2(); Assert.IsTrue(_testNetChain.Add(b2)); var params2 = NetworkParameters.TestNet(); var bad = new Block(params2); // Merkle root can be anything here, doesn't matter. bad.MerkleRoot = new Sha256Hash(Hex.Decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); // Nonce was just some number that made the hash < difficulty limit set below, it can be anything. bad.Nonce = 140548933; bad.TimeSeconds = 1279242649; bad.PreviousBlockHash = b2.Hash; // We're going to make this block so easy 50% of solutions will pass, and check it gets rejected for having a // bad difficulty target. Unfortunately the encoding mechanism means we cannot make one that accepts all // solutions. bad.TargetDifficulty = Block.EasiestDifficultyTarget; try { _testNetChain.Add(bad); // The difficulty target above should be rejected on the grounds of being easier than the networks // allowable difficulty. Assert.Fail(); } catch (VerificationException e) { Assert.IsTrue(e.Message.IndexOf("Target difficulty is bad") >= 0, e.Message); } // Accept any level of difficulty now. params2.ProofOfWorkLimit = new BigInteger("00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16); try { _testNetChain.Add(bad); // We should not get here as the difficulty target should not be changing at this point. Assert.Fail(); } catch (VerificationException e) { Assert.IsTrue(e.Message.IndexOf("Unexpected change in difficulty") >= 0, e.Message); } // TODO: Test difficulty change is not out of range when a transition period becomes valid. }
/// <summary> /// Returns a solved block that builds on top of this one. This exists for unit tests. /// </summary> internal Block CreateNextBlock(Address toAddress, uint time) { var block = new Block(NetworkParameters) {TargetDifficulty = _targetDifficulty}; block.AddCoinbaseTransaction(EmptyBytes); // Add a transaction paying 50 coins to the "to" address. var transaction = new Transaction(NetworkParameters); transaction.AddOutput(new TransactionOutput(NetworkParameters, transaction, Utils.ToNanoCoins(50, 0), toAddress)); // The input does not really need to be a valid signature, as long as it has the right general form. var input = new TransactionInput(NetworkParameters, transaction, 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) _transactionCounter++; input.Outpoint.Hash = new Sha256Hash(counter); transaction.AddInput(input); block.AddTransaction(transaction); block.PreviousBlockHash = Hash; block.TimeSeconds = time; block.Solve(); block.VerifyHeader(); return block; }
// Some blocks from the test net. private static Block GetBlock2() { var b2 = new Block(_testNet); b2.MerkleRoot = new Sha256Hash(Hex.Decode("addc858a17e21e68350f968ccd384d6439b64aafa6c193c8b9dd66320470838b")); b2.Nonce = 2642058077; b2.TimeSeconds = 1296734343; b2.PreviousBlockHash = new Sha256Hash(Hex.Decode("000000033cc282bc1fa9dcae7a533263fd7fe66490f550d80076433340831604")); Assert.AreEqual("000000037b21cac5d30fc6fda2581cf7b2612908aed2abbcc429c45b0557a15f", b2.HashAsString); b2.VerifyHeader(); return b2; }
/// <exception cref="BitcoinSharp.Blockchain.Store.BlockStoreException"/> public static Block MakeSolvedTestBlock(NetworkParameters @params, Block prev) { var b = prev.CreateNextBlock(new EcKey().ToAddress(@params)); b.Solve(); return b; }