public void TestInvalidMerkleRoot() { // prepare mocks var coreStorage = new Mock <ICoreStorage>(); var storageManager = new Mock <IStorageManager>(); var chainStateCursor = new Mock <IDeferredChainStateCursor>(); storageManager.Setup(x => x.OpenChainStateCursor()).Returns( new DisposeHandle <IChainStateCursor>(_ => { }, chainStateCursor.Object)); storageManager.Setup(x => x.OpenDeferredChainStateCursor(It.IsAny <IChainState>())).Returns( new DisposeHandle <IDeferredChainStateCursor>(_ => { }, chainStateCursor.Object)); chainStateCursor.Setup(x => x.CursorCount).Returns(1); chainStateCursor.Setup(x => x.DataFlowBlocks).Returns(new IDataflowBlock[0]); // prepare a test block var testBlocks = new TestBlocks(); var rules = testBlocks.Rules; var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; // create an invalid version of the header where the merkle root is incorrect var invalidChainedHeader = ChainedHeader.CreateFromPrev(rules.ChainParams.GenesisChainedHeader, block.Header.With(MerkleRoot: UInt256.Zero), DateTimeOffset.Now); // mock genesis block & chain tip var genesisHeader = rules.ChainParams.GenesisChainedHeader; chainStateCursor.Setup(x => x.ChainTip).Returns(genesisHeader); chainStateCursor.Setup(x => x.TryGetHeader(genesisHeader.Hash, out genesisHeader)).Returns(true); // mock invalid block chainStateCursor.Setup(x => x.TryGetHeader(chainedHeader.Hash, out invalidChainedHeader)).Returns(true); // init chain state builder var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object); Assert.AreEqual(rules.ChainParams.GenesisBlock.Hash, chainStateBuilder.Chain.LastBlock.Hash); // attempt to add block with invalid merkle root ValidationException actualEx; AssertMethods.AssertAggregateThrows <ValidationException>(() => chainStateBuilder.AddBlockAsync(invalidChainedHeader, Enumerable.Empty <BlockTx>()).Wait(), out actualEx); // verify error Assert.AreEqual($"Failing block {invalidChainedHeader.Hash} at height 1: Merkle root is invalid", actualEx.Message); }
public void TestInvalidMerkleRoot() { // prepare mocks var coreStorage = new Mock<ICoreStorage>(); var storageManager = new Mock<IStorageManager>(); var chainStateCursor = new Mock<IDeferredChainStateCursor>(); storageManager.Setup(x => x.OpenChainStateCursor()).Returns( new DisposeHandle<IChainStateCursor>(_ => { }, chainStateCursor.Object)); storageManager.Setup(x => x.OpenDeferredChainStateCursor(It.IsAny<IChainState>())).Returns( new DisposeHandle<IDeferredChainStateCursor>(_ => { }, chainStateCursor.Object)); chainStateCursor.Setup(x => x.CursorCount).Returns(1); chainStateCursor.Setup(x => x.DataFlowBlocks).Returns(new IDataflowBlock[0]); // prepare a test block var testBlocks = new TestBlocks(); var rules = testBlocks.Rules; var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; // create an invalid version of the header where the merkle root is incorrect var invalidChainedHeader = ChainedHeader.CreateFromPrev(rules.ChainParams.GenesisChainedHeader, block.Header.With(MerkleRoot: UInt256.Zero), DateTimeOffset.Now); // mock genesis block & chain tip var genesisHeader = rules.ChainParams.GenesisChainedHeader; chainStateCursor.Setup(x => x.ChainTip).Returns(genesisHeader); chainStateCursor.Setup(x => x.TryGetHeader(genesisHeader.Hash, out genesisHeader)).Returns(true); // mock invalid block chainStateCursor.Setup(x => x.TryGetHeader(chainedHeader.Hash, out invalidChainedHeader)).Returns(true); // init chain state builder var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object); Assert.AreEqual(rules.ChainParams.GenesisBlock.Hash, chainStateBuilder.Chain.LastBlock.Hash); // attempt to add block with invalid merkle root ValidationException actualEx; AssertMethods.AssertAggregateThrows<ValidationException>(() => chainStateBuilder.AddBlockAsync(invalidChainedHeader, Enumerable.Empty<BlockTx>()).Wait(), out actualEx); // verify error Assert.AreEqual($"Failing block {invalidChainedHeader.Hash} at height 1: Merkle root is invalid", actualEx.Message); }
public void TestReplayForward() { var coreStorage = new Mock<ICoreStorage>(); var chainState = new Mock<IChainState>(); chainState.Setup(x => x.CursorCount).Returns(4); var testBlocks = new TestBlocks(); var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; chainState.Setup(x => x.Chain).Returns(() => testBlocks.Chain); // mock block txes read var blockTxes = block.Transactions.Select((tx, txIndex) => new BlockTx(txIndex, tx)).GetEnumerator(); coreStorage.Setup(x => x.TryReadBlockTransactions(chainedHeader.Hash, true, out blockTxes)).Returns(true); // mock unspent tx lookup for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // create a fake unspent tx, with enough outputs for this input var unspentTx = new UnspentTx(input.PreviousTxOutputKey.TxHash, blockIndex: 1, txIndex: txIndex * inputIndex, outputStates: new OutputStates(input.PreviousTxOutputKey.TxOutputIndex.ToIntChecked() + 1, OutputState.Unspent)); chainState.Setup(x => x.TryGetUnspentTx(unspentTx.TxHash, out unspentTx)).Returns(true); } } { var loadingTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage.Object, chainState.Object, chainedHeader); var txIndex = -1; foreach (var loadingTx in loadingTxes.ToEnumerable()) { txIndex++; // verify loading tx matches original block tx Assert.AreEqual(block.Transactions[txIndex].Hash, loadingTx.Transaction.Hash); // if coinbase, verify no lookup keys for coinbase inputs if (txIndex == 0) { Assert.AreEqual(0, loadingTx.PrevOutputTxKeys.Length); } else { // verify there is a lookup key for each input Assert.AreEqual(block.Transactions[txIndex].Inputs.Length, loadingTx.PrevOutputTxKeys.Length); // verify each lookup key matches the mocked data for (var inputIndex = 0; inputIndex < loadingTx.Transaction.Inputs.Length; inputIndex++) { var prevOutputTxKey = loadingTx.PrevOutputTxKeys[inputIndex]; Assert.AreEqual(prevOutputTxKey.BlockHash, block.Hash); Assert.AreEqual(prevOutputTxKey.TxIndex, txIndex * inputIndex); } } } // verify correct number of transactions were replayed Assert.AreEqual(txIndex + 1, block.Transactions.Length); } }
public void TestReplayForward() { var coreStorage = new Mock<ICoreStorage>(); var chainState = new Mock<IChainState>(); chainState.Setup(x => x.CursorCount).Returns(4); var testBlocks = new TestBlocks(); var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; chainState.Setup(x => x.Chain).Returns(() => testBlocks.Chain); // mock block txes read var blockTxes = block.Transactions.Select((tx, txIndex) => (BlockTx)BlockTx.Create(txIndex, tx)).GetEnumerator(); coreStorage.Setup(x => x.TryReadBlockTransactions(chainedHeader.Hash, out blockTxes)).Returns(true); // mock unspent tx lookup var expectedValue = 50UL * (ulong)100.MILLION(); for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // create a fake unspent tx, with enough outputs for this input var unspentTx = new UnspentTx(input.PrevTxOutputKey.TxHash, blockIndex: 1, txIndex: txIndex * inputIndex, txVersion: 0, isCoinbase: false, outputStates: tx.IsCoinbase ? OutputStates.Empty : new OutputStates(input.PrevTxOutputKey.TxOutputIndex.ToIntChecked() + 1, OutputState.Unspent)); var txOutput = new TxOutput(tx.Outputs[0].Value, tx.Outputs[0].ScriptPublicKey); chainState.Setup(x => x.TryGetUnspentTx(unspentTx.TxHash, out unspentTx)).Returns(true); chainState.Setup(x => x.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)).Returns(true); } } var validatableTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage.Object, chainState.Object, chainedHeader).ToEnumerable().ToList(); // verify correct number of transactions were replayed Assert.AreEqual(validatableTxes.Count, block.Transactions.Length); expectedValue = 50UL * (ulong)100.MILLION(); foreach (var validatableTx in validatableTxes) { // verify validatable tx matches original block tx Assert.AreEqual(block.Transactions[validatableTx.Index].Hash, validatableTx.Transaction.Hash); // if coinbase, verify no tx outputs for coinbase inputs if (validatableTx.IsCoinbase) { Assert.AreEqual(0, validatableTx.PrevTxOutputs.Length); } else { // verify there is a tx output for each input Assert.AreEqual(block.Transactions[validatableTx.Index].Inputs.Length, validatableTx.PrevTxOutputs.Length); // verify each tx output matches the mocked data for (var inputIndex = 0; inputIndex < validatableTx.Transaction.Inputs.Length; inputIndex++) { var prevTxOutput = validatableTx.PrevTxOutputs[inputIndex]; expectedValue -= 1; Assert.AreEqual(expectedValue, prevTxOutput.Value); CollectionAssert.AreEqual(block.Transactions[0].Outputs[0].ScriptPublicKey, prevTxOutput.ScriptPublicKey); } } } }
public void TestReplayForward() { var coreStorage = new Mock <ICoreStorage>(); var chainState = new Mock <IChainState>(); chainState.Setup(x => x.CursorCount).Returns(4); var testBlocks = new TestBlocks(); var block = testBlocks.MineAndAddBlock(txCount: 10); var chainedHeader = testBlocks.Chain.LastBlock; chainState.Setup(x => x.Chain).Returns(() => testBlocks.Chain); // mock block txes read var blockTxes = block.Transactions.Select((tx, txIndex) => (BlockTx)BlockTx.Create(txIndex, tx)).GetEnumerator(); coreStorage.Setup(x => x.TryReadBlockTransactions(chainedHeader.Hash, out blockTxes)).Returns(true); // mock unspent tx lookup var expectedValue = 50UL * (ulong)100.MILLION(); for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++) { var tx = block.Transactions[txIndex]; for (var inputIndex = 0; inputIndex < tx.Inputs.Length; inputIndex++) { var input = tx.Inputs[inputIndex]; // create a fake unspent tx, with enough outputs for this input var unspentTx = new UnspentTx(input.PrevTxOutputKey.TxHash, blockIndex: 1, txIndex: txIndex * inputIndex, txVersion: 0, isCoinbase: false, outputStates: tx.IsCoinbase ? OutputStates.Empty : new OutputStates(input.PrevTxOutputKey.TxOutputIndex.ToIntChecked() + 1, OutputState.Unspent)); var txOutput = new TxOutput(tx.Outputs[0].Value, tx.Outputs[0].ScriptPublicKey); chainState.Setup(x => x.TryGetUnspentTx(unspentTx.TxHash, out unspentTx)).Returns(true); chainState.Setup(x => x.TryGetUnspentTxOutput(input.PrevTxOutputKey, out txOutput)).Returns(true); } } var validatableTxes = UtxoReplayer.ReplayCalculateUtxo(coreStorage.Object, chainState.Object, chainedHeader).ToEnumerable().ToList(); // verify correct number of transactions were replayed Assert.AreEqual(validatableTxes.Count, block.Transactions.Length); expectedValue = 50UL * (ulong)100.MILLION(); foreach (var validatableTx in validatableTxes) { // verify validatable tx matches original block tx Assert.AreEqual(block.Transactions[validatableTx.Index].Hash, validatableTx.Transaction.Hash); // if coinbase, verify no tx outputs for coinbase inputs if (validatableTx.IsCoinbase) { Assert.AreEqual(0, validatableTx.PrevTxOutputs.Length); } else { // verify there is a tx output for each input Assert.AreEqual(block.Transactions[validatableTx.Index].Inputs.Length, validatableTx.PrevTxOutputs.Length); // verify each tx output matches the mocked data for (var inputIndex = 0; inputIndex < validatableTx.Transaction.Inputs.Length; inputIndex++) { var prevTxOutput = validatableTx.PrevTxOutputs[inputIndex]; expectedValue -= 1; Assert.AreEqual(expectedValue, prevTxOutput.Value); CollectionAssert.AreEqual(block.Transactions[0].Outputs[0].ScriptPublicKey, prevTxOutput.ScriptPublicKey); } } } }