Exemple #1
0
        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);
                    }
                }
            }
        }
Exemple #5
0
        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);
                    }
                }
            }
        }