public void TestSimpleSpend() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(daemon.GenesisBlock); var block2 = daemon.MineAndAddEmptyBlock(block1); // check daemon.WaitForUpdate(); AssertMethods.AssertDaemonAtBlock(2, block2.Hash, daemon.CoreDaemon); // attempt to spend block 2's coinbase in block 3 var spendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 50 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block3Unmined = daemon.CreateEmptyBlock(block2) .WithAddedTransactions(spendTx); var block3 = daemon.MineAndAddBlock(block3Unmined); // check daemon.WaitForUpdate(); AssertMethods.AssertDaemonAtBlock(3, block3.Hash, daemon.CoreDaemon); // add a simple block var block4 = daemon.MineAndAddEmptyBlock(block3); // check daemon.WaitForUpdate(); AssertMethods.AssertDaemonAtBlock(4, block4.Hash, daemon.CoreDaemon); } }
public void TestInsufficientBalance() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(daemon.GenesisBlock); var block2 = daemon.MineAndAddEmptyBlock(block1); // check daemon.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(2, block2.Hash, daemon.BlockchainDaemon); // attempt to spend block 2's coinbase in block 3, using twice its value var spendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 100 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block3BadUnmined = daemon.CreateEmptyBlock(block2) .WithAddedTransactions(spendTx); var block3Bad = daemon.MineAndAddBlock(block3BadUnmined); // check that bad block wasn't added daemon.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(2, block2.Hash, daemon.BlockchainDaemon); // add a simple block var block3Good = daemon.MineAndAddEmptyBlock(block2); // check daemon.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(3, block3Good.Hash, daemon.BlockchainDaemon); } }
public void TestShorterChainWins() { using (var daemon = new TestDaemon()) { // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(); var block2 = daemon.MineAndAddEmptyBlock(); var block3a = daemon.MineAndAddEmptyBlock(); var block4a = daemon.MineAndAddEmptyBlock(); var block5a = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(5, block5a.Hash); // create a split with 3b, but do more work than current height 5 chain var testBlocksFork = daemon.TestBlocks.Fork(3); daemon.ChainParams.HighestTarget = UnitTestParams.Target2; var block3b = daemon.AddBlock(testBlocksFork.MineAndAddEmptyBlock(UnitTestParams.Target2)); // check that blockchain reorganized to shorter chain daemon.WaitForUpdate(); daemon.AssertAtBlock(3, block3b.Hash); } }
public void TestDoubleSpend() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // create a new keypair to double spend to var toKeyPairBad = daemon.TxManager.CreateKeyPair(); var toPrivateKeyBad = toKeyPair.Item1; var toPublicKeyBad = toKeyPair.Item2; // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(); var block2 = daemon.MineAndAddEmptyBlock(); // add some blocks so coinbase is mature to spend Block lastBlock = null; for (var i = 0; i < 100; i++) { lastBlock = daemon.MineAndAddEmptyBlock(); } // check daemon.WaitForUpdate(); daemon.AssertAtBlock(102, lastBlock.Hash); // spend block 2's coinbase in block 3 var spendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 50 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block3Unmined = daemon.CreateEmptyBlock(lastBlock.Hash) .CreateWithAddedTransactions(spendTx); var block3 = daemon.MineAndAddBlock(block3Unmined); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(103, block3.Hash); // attempt to spend block 2's coinbase again in block 4 var doubleSpendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 50 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKeyBad); var block4BadUmined = daemon.CreateEmptyBlock(block3.Hash) .CreateWithAddedTransactions(doubleSpendTx); var block4Bad = daemon.MineAndAddBlock(block4BadUmined); // check that bad block wasn't added daemon.WaitForUpdate(); daemon.AssertAtBlock(103, block3.Hash); // add a simple block daemon.TestBlocks.Rollback(1); var block4Good = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(104, block4Good.Hash); } }
public void TestDoubleSpend() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // create a new keypair to double spend to var toKeyPairBad = daemon.TxManager.CreateKeyPair(); var toPrivateKeyBad = toKeyPair.Item1; var toPublicKeyBad = toKeyPair.Item2; // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(); var block2 = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(2, block2.Hash); // spend block 2's coinbase in block 3 var spendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 50 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block3Unmined = daemon.CreateEmptyBlock(block2.Hash) .WithAddedTransactions(spendTx); var block3 = daemon.MineAndAddBlock(block3Unmined); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(3, block3.Hash); // attempt to spend block 2's coinbase again in block 4 var doubleSpendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 50 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKeyBad); var block4BadUmined = daemon.CreateEmptyBlock(block3.Hash) .WithAddedTransactions(doubleSpendTx); var block4Bad = daemon.MineAndAddBlock(block4BadUmined); // check that bad block wasn't added daemon.WaitForUpdate(); daemon.AssertAtBlock(3, block3.Hash); // add a simple block daemon.TestBlocks.Rollback(1); var block4Good = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(4, block4Good.Hash); } }
public void TestAddSingleBlock() { using (var daemon = new TestDaemon()) { var block1 = daemon.MineAndAddEmptyBlock(daemon.GenesisBlock); daemon.WaitForUpdate(); AssertMethods.AssertDaemonAtBlock(1, block1.Hash, daemon.CoreDaemon); } }
public void TestAddSingleBlock() { using (var daemon = new TestDaemon()) { var block1 = daemon.MineAndAddEmptyBlock(); daemon.WaitForUpdate(); daemon.AssertAtBlock(1, block1.Hash); } }
public void TestAddSingleBlock() { using (var daemon = new TestDaemon()) { var block1 = daemon.MineAndAddEmptyBlock(); daemon.WaitForUpdate(); daemon.AssertAtBlock(1, block1.Hash); } }
public void TestSimpleSpend() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(); var block2 = daemon.MineAndAddEmptyBlock(); // add some blocks so coinbase is mature to spend Block lastBlock = null; for (var i = 0; i < 100; i++) lastBlock = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(102, lastBlock.Hash); // attempt to spend block 2's coinbase in block 3 var spendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 50 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block3Unmined = daemon.CreateEmptyBlock(lastBlock.Hash) .CreateWithAddedTransactions(spendTx); var block3 = daemon.MineAndAddBlock(block3Unmined); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(103, block3.Hash); // add a simple block var block4 = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(104, block4.Hash); } }
public void TestLongBlockchain() { using (var daemon = new TestDaemon()) { var count = 1.THOUSAND(); var height = 0; var block = daemon.GenesisBlock; for (var i = 0; i < count; i++) { height++; block = daemon.MineAndAddEmptyBlock(); } daemon.WaitForUpdate(); daemon.AssertAtBlock(height, block.Hash); } }
public void TestLongBlockchain() { using (var daemon = new TestDaemon()) { var count = 1.THOUSAND(); var height = 0; var block = daemon.GenesisBlock; for (var i = 0; i < count; i++) { height++; block = daemon.MineAndAddEmptyBlock(); } daemon.WaitForUpdate(); daemon.AssertAtBlock(height, block.Hash); } }
public void TestLongBlockchain() { if (Debugger.IsAttached) { Assert.Inconclusive(); } using (var daemon = new TestDaemon()) { var count = 1.THOUSAND(); var height = 0; var block = daemon.GenesisBlock; for (var i = 0; i < count; i++) { Debug.WriteLine("TestLongBlockchain mining block {0:#,##0}".Format2(i)); height++; block = daemon.MineAndAddEmptyBlock(block); } daemon.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(height, block.Hash, daemon.BlockchainDaemon); } }
public void TestShorterChainWins() { using (var daemon = new TestDaemon()) { // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(daemon.GenesisBlock); var block2 = daemon.MineAndAddEmptyBlock(block1); var block3a = daemon.MineAndAddEmptyBlock(block2); var block4a = daemon.MineAndAddEmptyBlock(block3a); var block5a = daemon.MineAndAddEmptyBlock(block4a); // check daemon.WaitForUpdate(); AssertMethods.AssertDaemonAtBlock(5, block5a.Hash, daemon.CoreDaemon); // create a split with 3b, but do more work than current height 5 chain daemon.Rules.SetHighestTarget(UnitTestRules.Target2); var block3b = daemon.MineAndAddEmptyBlock(block2, UnitTestRules.Target2); // check that blockchain reorganized to shorter chain daemon.WaitForUpdate(); AssertMethods.AssertDaemonAtBlock(3, block3b.Hash, daemon.CoreDaemon); } }
public void TestSimpleBlockchainSplit() { using (var daemon1 = new TestDaemon()) { // add some simple blocks var block1 = daemon1.MineAndAddEmptyBlock(); var block2 = daemon1.MineAndAddEmptyBlock(); var block3a = daemon1.MineAndAddEmptyBlock(); daemon1.WaitForUpdate(); // create a fork test block chain, starting at block2 var testBlocksFork = daemon1.TestBlocks.Fork(1); // wait for daemon daemon1.WaitForUpdate(); daemon1.AssertAtBlock(3, block3a.Hash); // introduce a tie split var block3b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that 3a is still current as it was first daemon1.WaitForUpdate(); daemon1.AssertAtBlock(3, block3a.Hash); // continue with currently winning branch var block4a = daemon1.MineAndAddEmptyBlock(); // wait for daemon daemon1.WaitForUpdate(); daemon1.AssertAtBlock(4, block4a.Hash); // continue with tied branch var block4b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that 4a is still current as it was first daemon1.WaitForUpdate(); daemon1.AssertAtBlock(4, block4a.Hash); // resolve tie split, with other chain winning var block5b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that blockchain reorged to the winning chain daemon1.WaitForUpdate(); daemon1.AssertAtBlock(5, block5b.Hash); // continue on winning fork var block6b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that blockchain continued on the winning chain daemon1.WaitForUpdate(); daemon1.AssertAtBlock(6, block6b.Hash); // create a second blockchain, reusing the genesis from the first using (var daemon2 = new TestDaemon(daemon1.GenesisBlock)) { // add only the winning blocks to the second blockchain daemon2.AddBlock(block1); daemon2.AddBlock(block2); daemon2.AddBlock(block3b); daemon2.AddBlock(block4b); daemon2.AddBlock(block5b); daemon2.AddBlock(block6b); // check second blockchain daemon2.WaitForUpdate(); daemon2.AssertAtBlock(6, block6b.Hash); // verify that re-organized blockchain matches winning-only blockchain using (var expectedChainState = daemon2.CoreDaemon.GetChainState()) using (var actualChainState = daemon1.CoreDaemon.GetChainState()) { var expectedUtxo = expectedChainState.ReadUnspentTransactions().ToList(); var actualUtxo = actualChainState.ReadUnspentTransactions().ToList(); CollectionAssert.AreEqual(expectedUtxo, actualUtxo); } } } }
public void TestSimpleBlockchainSplit() { using (var daemon1 = new TestDaemon()) { // add some simple blocks var block1 = daemon1.MineAndAddEmptyBlock(); var block2 = daemon1.MineAndAddEmptyBlock(); var block3a = daemon1.MineAndAddEmptyBlock(); daemon1.WaitForUpdate(); // create a fork test block chain, starting at block2 var testBlocksFork = daemon1.TestBlocks.Fork(1); // wait for daemon daemon1.WaitForUpdate(); daemon1.AssertAtBlock(3, block3a.Hash); // introduce a tie split var block3b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that 3a is still current as it was first daemon1.WaitForUpdate(); daemon1.AssertAtBlock(3, block3a.Hash); // continue with currently winning branch var block4a = daemon1.MineAndAddEmptyBlock(); // wait for daemon daemon1.WaitForUpdate(); daemon1.AssertAtBlock(4, block4a.Hash); // continue with tied branch var block4b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that 4a is still current as it was first daemon1.WaitForUpdate(); daemon1.AssertAtBlock(4, block4a.Hash); // resolve tie split, with other chain winning var block5b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that blockchain reorged to the winning chain daemon1.WaitForUpdate(); daemon1.AssertAtBlock(5, block5b.Hash); // continue on winning fork var block6b = daemon1.AddBlock(testBlocksFork.MineAndAddEmptyBlock()); // check that blockchain continued on the winning chain daemon1.WaitForUpdate(); daemon1.AssertAtBlock(6, block6b.Hash); // create a second blockchain, reusing the genesis from the first using (var daemon2 = new TestDaemon(daemon1.GenesisBlock)) { // add only the winning blocks to the second blockchain daemon2.AddBlock(block1); daemon2.AddBlock(block2); daemon2.AddBlock(block3b); daemon2.AddBlock(block4b); daemon2.AddBlock(block5b); daemon2.AddBlock(block6b); // check second blockchain daemon2.WaitForUpdate(); daemon2.AssertAtBlock(6, block6b.Hash); // verify that re-organized blockchain matches winning-only blockchain using (var expectedChainState = daemon2.CoreDaemon.GetChainState()) using (var actualChainState = daemon1.CoreDaemon.GetChainState()) { var expectedUtxo = expectedChainState.ReadUnspentTransactions().ToList(); var actualUtxo = actualChainState.ReadUnspentTransactions().ToList(); CollectionAssert.AreEqual(expectedUtxo, actualUtxo); } } } }
public void TestReplayBlockRollback() { var logger = LogManager.CreateNullLogger(); using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // add block 1 var block0 = daemon.GenesisBlock; var block1 = daemon.MineAndAddEmptyBlock(); // add block 2, spending from block 1 var spendTx1 = daemon.TxManager.CreateSpendTransaction(block1.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, block1.Transactions[0].OutputValue(), daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block2Unmined = daemon.CreateEmptyBlock(block1.Hash) .WithAddedTransactions(spendTx1); var block2 = daemon.MineAndAddBlock(block2Unmined); // add block 3, spending from block 2 var spendTx2 = daemon.TxManager.CreateSpendTransaction(block2.Transactions[1], 0, (byte)ScriptHashType.SIGHASH_ALL, block2.Transactions[1].OutputValue(), toPrivateKey, toPublicKey, toPublicKey); var block3Unmined = daemon.CreateEmptyBlock(block2.Hash) .WithAddedTransactions(spendTx2); var block3 = daemon.MineAndAddBlock(block3Unmined); // replay all blocks up to block 3 daemon.WaitForUpdate(); using (var chainState = daemon.CoreDaemon.GetChainState()) { Assert.AreEqual(3, chainState.Chain.Height); var replayTransactions = new List<LoadedTx>(); foreach (var blockHash in chainState.Chain.Blocks.Select(x => x.Hash)) { replayTransactions.AddRange(BlockReplayer.ReplayBlock(daemon.CoreStorage, chainState, blockHash, replayForward: true) .ToEnumerable()); } // verify all transactions were replayed Assert.AreEqual(6, replayTransactions.Count); Assert.AreEqual(block0.Transactions[0].Hash, replayTransactions[0].Transaction.Hash); Assert.AreEqual(block1.Transactions[0].Hash, replayTransactions[1].Transaction.Hash); Assert.AreEqual(block2.Transactions[0].Hash, replayTransactions[2].Transaction.Hash); Assert.AreEqual(block2.Transactions[1].Hash, replayTransactions[3].Transaction.Hash); Assert.AreEqual(block3.Transactions[0].Hash, replayTransactions[4].Transaction.Hash); Assert.AreEqual(block3.Transactions[1].Hash, replayTransactions[5].Transaction.Hash); } // mark blocks 2-3 invalid, they will be rolled back daemon.CoreStorage.MarkBlockInvalid(block3.Hash); daemon.CoreStorage.MarkBlockInvalid(block2.Hash); daemon.WaitForUpdate(); // replay rollback of block 3 using (var chainState = daemon.CoreDaemon.GetChainState()) { Assert.AreEqual(1, chainState.Chain.Height); var replayTransactions = new List<LoadedTx>( BlockReplayer.ReplayBlock(daemon.CoreStorage, chainState, block3.Hash, replayForward: false) .ToEnumerable()); // verify transactions were replayed Assert.AreEqual(2, replayTransactions.Count); Assert.AreEqual(block3.Transactions[1].Hash, replayTransactions[0].Transaction.Hash); Assert.AreEqual(block3.Transactions[0].Hash, replayTransactions[1].Transaction.Hash); // verify correct previous output was replayed (block 3 tx 1 spent block 2 tx 1) Assert.AreEqual(1, replayTransactions[0].InputTxes.Length); CollectionAssert.AreEqual(block2.Transactions[1].Outputs[0].ScriptPublicKey, replayTransactions[0].GetInputPrevTxOutput(0).ScriptPublicKey); // verify correct previous output was replayed (block 3 tx 0 spends nothing, coinbase) Assert.AreEqual(0, replayTransactions[1].InputTxes.Length); } // replay rollback of block 2 using (var chainState = daemon.CoreDaemon.GetChainState()) { Assert.AreEqual(1, chainState.Chain.Height); var replayTransactions = new List<LoadedTx>( BlockReplayer.ReplayBlock(daemon.CoreStorage, chainState, block2.Hash, replayForward: false) .ToEnumerable()); // verify transactions were replayed Assert.AreEqual(2, replayTransactions.Count); Assert.AreEqual(block2.Transactions[1].Hash, replayTransactions[0].Transaction.Hash); Assert.AreEqual(block2.Transactions[0].Hash, replayTransactions[1].Transaction.Hash); // verify correct previous output was replayed (block 2 tx 1 spent block 1 tx 0) Assert.AreEqual(1, replayTransactions[0].InputTxes.Length); CollectionAssert.AreEqual(block1.Transactions[0].Outputs[0].ScriptPublicKey, replayTransactions[0].GetInputPrevTxOutput(0).ScriptPublicKey); // verify correct previous output was replayed (block 2 tx 0 spends nothing, coinbase) Assert.AreEqual(0, replayTransactions[1].InputTxes.Length); } } }
public void TestInsufficientBalance() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(); var block2 = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(2, block2.Hash); // attempt to spend block 2's coinbase in block 3, using twice its value var spendTx = daemon.TxManager.CreateSpendTransaction(block2.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, 100 * SATOSHI_PER_BTC, daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block3BadUnmined = daemon.CreateEmptyBlock(block2.Hash) .CreateWithAddedTransactions(spendTx); var block3Bad = daemon.MineAndAddBlock(block3BadUnmined); // check that bad block wasn't added daemon.WaitForUpdate(); daemon.AssertAtBlock(2, block2.Hash); // add a simple block daemon.TestBlocks.Rollback(1); var block3Good = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(3, block3Good.Hash); } }
public void TestSimpleBlockchainSplit() { using (var daemon1 = new TestDaemon()) { // add some simple blocks var block1 = daemon1.MineAndAddEmptyBlock(daemon1.GenesisBlock); var block2 = daemon1.MineAndAddEmptyBlock(block1); // introduce a tie split var block3a = daemon1.MineAndAddEmptyBlock(block2); var block3b = daemon1.MineAndAddEmptyBlock(block2); // check that 3a is current as it was first daemon1.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(3, block3a.Hash, daemon1.BlockchainDaemon); // continue split var block4a = daemon1.MineAndAddEmptyBlock(block3a); var block4b = daemon1.MineAndAddEmptyBlock(block3b); // check that 4a is current as it was first daemon1.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(4, block4a.Hash, daemon1.BlockchainDaemon); // resolve tie split, with other chain winning var block5b = daemon1.MineAndAddEmptyBlock(block4b); // check that blockchain reorged to the winning chain daemon1.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(5, block5b.Hash, daemon1.BlockchainDaemon); // continue on winning fork var block6b = daemon1.MineAndAddEmptyBlock(block5b); // check that blockchain continued on the winning chain daemon1.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(6, block6b.Hash, daemon1.BlockchainDaemon); // create a second blockchain, reusing the genesis from the first using (var daemon2 = new TestDaemon(daemon1.GenesisBlock)) { // add only the winning blocks to the second blockchain daemon2.AddBlock(block1); daemon2.AddBlock(block2); daemon2.AddBlock(block3b); daemon2.AddBlock(block4b); daemon2.AddBlock(block5b); daemon2.AddBlock(block6b); // check second blockchain daemon2.WaitForDaemon(); AssertMethods.AssertDaemonAtBlock(6, block6b.Hash, daemon2.BlockchainDaemon); // verify that re-organized blockchain matches winning-only blockchain using (var expectedChainSate = daemon2.BlockchainDaemon.GetChainState()) using (var actualChainSate = daemon1.BlockchainDaemon.GetChainState()) { var expectedUtxo = expectedChainSate.Utxo; var expectedUnspentTransactions = ImmutableDictionary.CreateRange <UInt256, UnspentTx>(expectedUtxo.GetUnspentTransactions()); var expectedUnspentOutputs = ImmutableDictionary.CreateRange <TxOutputKey, TxOutput>(expectedUtxo.GetUnspentOutputs()); var actualUtxo = actualChainSate.Utxo; var actualUnspentTransactions = ImmutableDictionary.CreateRange <UInt256, UnspentTx>(actualUtxo.GetUnspentTransactions()); var actualUnspentOutputs = ImmutableDictionary.CreateRange <TxOutputKey, TxOutput>(actualUtxo.GetUnspentOutputs()); CollectionAssert.AreEquivalent(expectedUnspentTransactions, actualUnspentTransactions); CollectionAssert.AreEquivalent(expectedUnspentOutputs, actualUnspentOutputs); } } } }
public void TestShorterChainWins() { using (var daemon = new TestDaemon()) { // add some simple blocks var block1 = daemon.MineAndAddEmptyBlock(); var block2 = daemon.MineAndAddEmptyBlock(); var block3a = daemon.MineAndAddEmptyBlock(); var block4a = daemon.MineAndAddEmptyBlock(); var block5a = daemon.MineAndAddEmptyBlock(); // check daemon.WaitForUpdate(); daemon.AssertAtBlock(5, block5a.Hash); // create a split with 3b, but do more work than current height 5 chain var testBlocksFork = daemon.TestBlocks.Fork(3); daemon.ChainParams.HighestTarget = UnitTestParams.Target2; var block3b = daemon.AddBlock(testBlocksFork.MineAndAddEmptyBlock(UnitTestParams.Target2)); // check that blockchain reorganized to shorter chain daemon.WaitForUpdate(); daemon.AssertAtBlock(3, block3b.Hash); } }
public void TestReplayBlockRollback() { using (var daemon = new TestDaemon()) { // create a new keypair to spend to var toKeyPair = daemon.TxManager.CreateKeyPair(); var toPrivateKey = toKeyPair.Item1; var toPublicKey = toKeyPair.Item2; // add block 1 var block0 = daemon.GenesisBlock; var block1 = daemon.MineAndAddEmptyBlock(); // add some blocks so coinbase is mature to spend Block lastBlock = null; for (var i = 0; i < 100; i++) { lastBlock = daemon.MineAndAddEmptyBlock(); } // add block 2, spending from block 1 var spendTx1 = daemon.TxManager.CreateSpendTransaction(block1.Transactions[0], 0, (byte)ScriptHashType.SIGHASH_ALL, block1.Transactions[0].OutputValue(), daemon.CoinbasePrivateKey, daemon.CoinbasePublicKey, toPublicKey); var block2Unmined = daemon.CreateEmptyBlock(lastBlock.Hash) .CreateWithAddedTransactions(spendTx1); var block2 = daemon.MineAndAddBlock(block2Unmined); // add some blocks so coinbase is mature to spend for (var i = 0; i < 100; i++) { lastBlock = daemon.MineAndAddEmptyBlock(); } // add block 3, spending from block 2 var spendTx2 = daemon.TxManager.CreateSpendTransaction(block2.Transactions[1], 0, (byte)ScriptHashType.SIGHASH_ALL, block2.Transactions[1].OutputValue(), toPrivateKey, toPublicKey, toPublicKey); var block3Unmined = daemon.CreateEmptyBlock(lastBlock.Hash) .CreateWithAddedTransactions(spendTx2); var block3 = daemon.MineAndAddBlock(block3Unmined); // replay all blocks up to block 3 daemon.WaitForUpdate(); using (var chainState = daemon.CoreDaemon.GetChainState()) { Assert.AreEqual(203, chainState.Chain.Height); var replayTransactions = new List <ValidatableTx>(); foreach (var blockHash in chainState.Chain.Blocks.Select(x => x.Hash)) { replayTransactions.AddRange(BlockReplayer.ReplayBlock(daemon.CoreStorage, chainState, blockHash, replayForward: true) .ToEnumerable()); } // verify all transactions were replayed Assert.AreEqual(206, replayTransactions.Count); Assert.AreEqual(block0.Transactions[0].Hash, replayTransactions[0].Transaction.Hash); Assert.AreEqual(block1.Transactions[0].Hash, replayTransactions[1].Transaction.Hash); Assert.AreEqual(block2.Transactions[0].Hash, replayTransactions[102].Transaction.Hash); Assert.AreEqual(block2.Transactions[1].Hash, replayTransactions[103].Transaction.Hash); Assert.AreEqual(block3.Transactions[0].Hash, replayTransactions[204].Transaction.Hash); Assert.AreEqual(block3.Transactions[1].Hash, replayTransactions[205].Transaction.Hash); } // mark block 2 invalid, it will be rolled back daemon.CoreStorage.MarkBlockInvalid(block2.Hash, daemon.CoreDaemon.TargetChain); daemon.WaitForUpdate(); // replay rollback of block 3 using (var chainState = daemon.CoreDaemon.GetChainState()) { Assert.AreEqual(101, chainState.Chain.Height); var replayTransactions = new List <ValidatableTx>( BlockReplayer.ReplayBlock(daemon.CoreStorage, chainState, block3.Hash, replayForward: false) .ToEnumerable()); // verify transactions were replayed Assert.AreEqual(2, replayTransactions.Count); Assert.AreEqual(block3.Transactions[1].Hash, replayTransactions[0].Transaction.Hash); Assert.AreEqual(block3.Transactions[0].Hash, replayTransactions[1].Transaction.Hash); // verify correct previous output was replayed (block 3 tx 1 spent block 2 tx 1) Assert.AreEqual(1, replayTransactions[0].PrevTxOutputs.Length); CollectionAssert.AreEqual(block2.Transactions[1].Outputs[0].ScriptPublicKey, replayTransactions[0].PrevTxOutputs[0].ScriptPublicKey); // verify correct previous output was replayed (block 3 tx 0 spends nothing, coinbase) Assert.AreEqual(0, replayTransactions[1].PrevTxOutputs.Length); } // replay rollback of block 2 using (var chainState = daemon.CoreDaemon.GetChainState()) { Assert.AreEqual(101, chainState.Chain.Height); var replayTransactions = new List <ValidatableTx>( BlockReplayer.ReplayBlock(daemon.CoreStorage, chainState, block2.Hash, replayForward: false) .ToEnumerable()); // verify transactions were replayed Assert.AreEqual(2, replayTransactions.Count); Assert.AreEqual(block2.Transactions[1].Hash, replayTransactions[0].Transaction.Hash); Assert.AreEqual(block2.Transactions[0].Hash, replayTransactions[1].Transaction.Hash); // verify correct previous output was replayed (block 2 tx 1 spent block 1 tx 0) Assert.AreEqual(1, replayTransactions[0].PrevTxOutputs.Length); CollectionAssert.AreEqual(block1.Transactions[0].Outputs[0].ScriptPublicKey, replayTransactions[0].PrevTxOutputs[0].ScriptPublicKey); // verify correct previous output was replayed (block 2 tx 0 spends nothing, coinbase) Assert.AreEqual(0, replayTransactions[1].PrevTxOutputs.Length); } } }