// IBlockStorage.ContainsChainedHeader
        private void TestContainsChainedHeader(ITestStorageProvider provider)
        {
            using (var storageManager = provider.OpenStorageManager())
            {
                var blockStorage = storageManager.BlockStorage;

                // create a chained header
                var fakeHeaders = new FakeHeaders();
                var chainedHeader = fakeHeaders.GenesisChained();

                // header should not be present
                Assert.IsFalse(blockStorage.ContainsChainedHeader(chainedHeader.Hash));

                // add the header
                blockStorage.TryAddChainedHeader(chainedHeader);

                // header should be present
                Assert.IsTrue(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); ;

                // remove the header
                blockStorage.TryRemoveChainedHeader(chainedHeader.Hash);

                // header should not be present
                Assert.IsFalse(blockStorage.ContainsChainedHeader(chainedHeader.Hash)); ;
            }
        }
        public void TestInitWithChain()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var rules = Mock.Of<ICoreRules>();
            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.ChainTip).Returns(header1);
            chainStateCursor.Setup(x => x.TryGetHeader(header0.Hash, out header0)).Returns(true);
            chainStateCursor.Setup(x => x.TryGetHeader(header1.Hash, out header1)).Returns(true);

            var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object);

            CollectionAssert.AreEqual(new[] { header0, header1 }, chainStateBuilder.Chain.Blocks);
        }
Beispiel #3
0
        public void TestCreateFromInvalidEnumerable()
        {
            // create a chain
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            // create builder from out of order enumerable
            AssertMethods.AssertThrows<InvalidOperationException>(() => new ChainBuilder(new[] { header1, header0 }));
        }
Beispiel #4
0
        public void TestCreateFromEnumerable()
        {
            // create a chain
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            // create builder from enumerable
            var chainBuilder = new ChainBuilder(new[] { header0, header1 });

            // verify
            CollectionAssert.AreEqual(new[] { header0, header1 }, chainBuilder.Blocks);
            CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chainBuilder.BlocksByHash);
        }
        public void TestChainTipOutOfSync()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();

            var rules = Mock.Of<ICoreRules>();
            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.TryGetHeader(header0.Hash, out header0)).Returns(true);
            chainStateCursor.Setup(x => x.TryGetHeader(header1.Hash, out header1)).Returns(true);
            chainStateCursor.Setup(x => x.TryGetHeader(header2.Hash, out header2)).Returns(true);

            // return header 1 as the chain tip
            chainStateCursor.Setup(x => x.ChainTip).Returns(header1);

            // init chain state builder seeing header 1
            var chainStateBuilder = new ChainStateBuilder(rules, coreStorage.Object, storageManager.Object);
            Assert.AreEqual(header1.Hash, chainStateBuilder.Chain.LastBlock.Hash);

            // alter the chain tip outside of the chain state builder
            chainStateCursor.Setup(x => x.ChainTip).Returns(header2);

            // attempt to add block when out of sync
            ChainStateOutOfSyncException actualEx;
            AssertMethods.AssertAggregateThrows<ChainStateOutOfSyncException>(() =>
                chainStateBuilder.AddBlockAsync(header2, Enumerable.Empty<BlockTx>()).Wait(),
                out actualEx);

            Assert.AreEqual(header1.Hash, actualEx.ExpectedChainTip.Hash);
            Assert.AreEqual(header2.Hash, actualEx.ActualChainTip.Hash);

            // attempt to rollback block when out of sync
            AssertMethods.AssertThrows<ChainStateOutOfSyncException>(() =>
                chainStateBuilder.RollbackBlock(header2, Enumerable.Empty<BlockTx>()),
                out actualEx);

            Assert.AreEqual(header1.Hash, actualEx.ExpectedChainTip.Hash);
            Assert.AreEqual(header2.Hash, actualEx.ActualChainTip.Hash);
        }
        public void TestDoubleSpend()
        {
            // prepare block
            var fakeHeaders = new FakeHeaders();
            var chainedHeader0 = fakeHeaders.GenesisChained();
            var chainedHeader1 = fakeHeaders.NextChained();
            var chainedHeader2 = fakeHeaders.NextChained();
            var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder();
            var emptyCoinbaseTx0 = new BlockTx(0, new Transaction(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0));
            var emptyCoinbaseTx1 = new BlockTx(0, new Transaction(1, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0));

            // initialize memory utxo builder storage
            var memoryStorage = new MemoryStorageManager();
            var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item;
            memoryChainStateCursor.BeginTransaction();

            // initialize utxo builder
            var utxoBuilder = new UtxoBuilder();

            // prepare an unspent transaction
            var txHash = new UInt256(100);
            var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 1, OutputState.Unspent);

            // add the unspent transaction
            memoryChainStateCursor.TryAddUnspentTx(unspentTx);

            // create an input to spend the unspent transaction
            var input = new TxInput(new TxOutputKey(txHash, txOutputIndex: 0), ImmutableArray.Create<byte>(), 0);
            var tx = new BlockTx(1, new Transaction(0, ImmutableArray.Create(input), ImmutableArray.Create<TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader1);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            UnspentTx actualUnspentTx;
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.IsFullySpent);

            // attempt to spend the input again, validation exception should be thrown
            chain.AddBlock(chainedHeader2);
            AssertMethods.AssertAggregateThrows<ValidationException>(() =>
                utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx }.ToBufferBlock()).ToEnumerable().ToList());
        }
Beispiel #7
0
        public void TestLastBlock()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // verify last block with 0 blocks
            Assert.IsNull(chainBuilder.LastBlock);

            // verify last block with 1 block
            chainBuilder.AddBlock(header0);
            Assert.AreEqual(header0, chainBuilder.LastBlock);

            // verify last block with 2 blocks
            chainBuilder.AddBlock(header1);
            Assert.AreEqual(header1, chainBuilder.LastBlock);
        }
Beispiel #8
0
        public void TestGenesisBlock()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // verify genesis with 0 blocks
            var chainEmpty = chainBuilder.ToImmutable();
            Assert.IsNull(chainEmpty.GenesisBlock);

            // verify genesis with 1 block
            chainBuilder.AddBlock(header0);
            var chain0 = chainBuilder.ToImmutable();
            Assert.AreEqual(header0, chain0.GenesisBlock);

            // verify genesis with 2 blocks
            chainBuilder.AddBlock(header1);
            var chain1 = chainBuilder.ToImmutable();
            Assert.AreEqual(header0, chain1.GenesisBlock);
        }
Beispiel #9
0
        public void TestHeight()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // verify height with 0 blocks
            var chainEmpty = chainBuilder.ToImmutable();
            Assert.AreEqual(-1, chainEmpty.Height);

            // verify height with 1 block
            chainBuilder.AddBlock(header0);
            var chain0 = chainBuilder.ToImmutable();
            Assert.AreEqual(0, chain0.Height);

            // verify height with 2 blocks
            chainBuilder.AddBlock(header1);
            var chain1 = chainBuilder.ToImmutable();
            Assert.AreEqual(1, chain1.Height);
        }
Beispiel #10
0
        public void TestRemoveBlock()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder(new[] { header0, header1 });

            // remove header 1 and verify
            chainBuilder.RemoveBlock(header1);
            CollectionAssert.AreEqual(new[] { header0 }, chainBuilder.Blocks);
            CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 } }, chainBuilder.BlocksByHash);

            // remove header 0 and verify
            chainBuilder.RemoveBlock(header0);
            Assert.AreEqual(0, chainBuilder.Blocks.Count);
            Assert.AreEqual(0, chainBuilder.BlocksByHash.Count);
        }
Beispiel #11
0
        public void TestRemoveBlockInvalid()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder(new[] { header0, header1, header2 });

            // removing header 1 first should fail
            AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.RemoveBlock(header1));

            // remove header 2
            chainBuilder.RemoveBlock(header2);

            // removing header 0 with header 1 present should fail
            AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.AddBlock(header0));
        }
Beispiel #12
0
        public void TestBlocksByHash()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // verify blocks dictionary with 0 blocks
            Assert.AreEqual(0, chainBuilder.BlocksByHash.Count);

            // verify blocks dictionary with 1 block
            chainBuilder.AddBlock(header0);
            CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 } }, chainBuilder.BlocksByHash);

            // verify blocks dictionary with 2 blocks
            chainBuilder.AddBlock(header1);
            CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chainBuilder.BlocksByHash);
        }
Beispiel #13
0
        public void TestAddBlockInvalid()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // adding header 1 first should fail
            AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.AddBlock(header1));

            // add header 0
            chainBuilder.AddBlock(header0);

            // adding header 2 without header 1 should fail
            AssertMethods.AssertThrows<InvalidOperationException>(() => chainBuilder.AddBlock(header2));
        }
        private void TestChainTip(ITestStorageProvider provider)
        {
            var fakeHeaders = new FakeHeaders();
            var chainedHeader0 = fakeHeaders.GenesisChained();
            var chainedHeader1 = fakeHeaders.NextChained();

            using (var storageManager = provider.OpenStorageManager())
            using (var handle = storageManager.OpenChainStateCursor())
            {
                var chainStateCursor = handle.Item;

                // begin transaction
                chainStateCursor.BeginTransaction();

                // verify initial tip
                Assert.IsNull(chainStateCursor.ChainTip);

                // set tip
                chainStateCursor.ChainTip = chainedHeader0;

                // verify tip
                Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip);

                // set tip
                chainStateCursor.ChainTip = chainedHeader1;

                // verify tip
                Assert.AreEqual(chainedHeader1, chainStateCursor.ChainTip);
            }
        }
Beispiel #15
0
        public void TestNavigateTowardsEmpty()
        {
            // create chain
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();

            var chainEmpty = new ChainBuilder().ToImmutable();
            var chain = new ChainBuilder(new[] { header0, header1, header2 }).ToImmutable();

            // verify chaining to empty does nothing
            Assert.AreEqual(0, chainEmpty.NavigateTowards(chainEmpty).Count());
            Assert.AreEqual(0, chain.NavigateTowards(chainEmpty).Count());

            // verify path from empty chain to chain
            CollectionAssert.AreEqual(
                new[]
                {
                    Tuple.Create(+1, header0),
                    Tuple.Create(+1, header1),
                    Tuple.Create(+1, header2)
                }
                , chainEmpty.NavigateTowards(chain).ToList());
        }
        private void TestTransactionIsolation(ITestStorageProvider provider)
        {
            var fakeHeaders = new FakeHeaders();
            var chainedHeader0 = fakeHeaders.GenesisChained();

            // open two chain state cursors
            using (var storageManager = provider.OpenStorageManager())
            using (var txBeganEvent = new ManualResetEventSlim())
            using (var headerAddedEvent = new ManualResetEventSlim())
            using (var doCommitEvent = new ManualResetEventSlim())
            using (var committedEvent = new ManualResetEventSlim())
            {
                var thread1 = new Thread(() =>
                {
                    using (var handle1 = storageManager.OpenChainStateCursor())
                    {
                        var chainStateCursor1 = handle1.Item;

                        // open transactions on cusor 1
                        chainStateCursor1.BeginTransaction();

                        // add a header on cursor 1
                        chainStateCursor1.ChainTip = chainedHeader0;

                        // alert header has been added
                        headerAddedEvent.Set();

                        doCommitEvent.Wait();

                        // commit transaction on cursor 1
                        chainStateCursor1.CommitTransaction();

                        // alert transaction has been committed
                        committedEvent.Set();
                    }
                });
                thread1.Start();

                // wait for header to be added on cursor 1
                headerAddedEvent.Wait();

                using (var handle2 = storageManager.OpenChainStateCursor())
                {
                    var chainStateCursor2 = handle2.Item;

                    // open transactions on cusor 2
                    chainStateCursor2.BeginTransaction(readOnly: true);

                    // verify empty chain
                    Assert.IsNull(chainStateCursor2.ChainTip);

                    doCommitEvent.Set();
                    committedEvent.Wait();

                    // verify empty chain
                    Assert.IsNull(chainStateCursor2.ChainTip);

                    chainStateCursor2.CommitTransaction();
                    chainStateCursor2.BeginTransaction();

                    // verify cursor 2 now sees the new header
                    Assert.AreEqual(chainedHeader0, chainStateCursor2.ChainTip);

                    chainStateCursor2.RollbackTransaction();
                }

                thread1.Join();
            }
        }
Beispiel #17
0
        public void TestSimpleChain()
        {
            // prepare test kernel
            var kernel = new StandardKernel(new ConsoleLoggingModule(), new MemoryStorageModule());

            kernel.Bind <CoreStorage>().ToSelf().InSingletonScope();
            var coreStorage = kernel.Get <CoreStorage>();

            // initialize data
            var fakeHeaders = new FakeHeaders();
            var header0     = fakeHeaders.Genesis();
            var header1     = fakeHeaders.Next();
            var header2     = fakeHeaders.Next();

            // store genesis block
            var chainedHeader0 = ChainedHeader.CreateForGenesisBlock(header0);

            coreStorage.AddGenesisBlock(chainedHeader0);

            // mock rules
            var mockRules = new Mock <IBlockchainRules>();

            mockRules.Setup(rules => rules.GenesisChainedHeader).Returns(chainedHeader0);
            kernel.Bind <IBlockchainRules>().ToConstant(mockRules.Object);

            // initialize the target chain worker
            using (var targetChainWorker = kernel.Get <TargetChainWorker>(new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue))))
            {
                // verify initial state
                Assert.AreEqual(null, targetChainWorker.TargetChain);

                // monitor event firing
                var targetChainChangedEvent   = new AutoResetEvent(false);
                var onTargetChainChangedCount = 0;
                targetChainWorker.OnTargetChainChanged += () => { onTargetChainChangedCount++; targetChainChangedEvent.Set(); };

                // start worker and wait for initial chain
                targetChainWorker.Start();
                targetChainChangedEvent.WaitOne();

                // verify chained to block 0
                Assert.AreEqual(chainedHeader0, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(1, onTargetChainChangedCount);

                // add block 1
                ChainedHeader chainedHeader1;
                coreStorage.TryChainHeader(header1, out chainedHeader1);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 1
                Assert.AreEqual(chainedHeader1, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(2, onTargetChainChangedCount);

                // add block 2
                ChainedHeader chainedHeader2;
                coreStorage.TryChainHeader(header2, out chainedHeader2);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 2
                Assert.AreEqual(chainedHeader2, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(3, onTargetChainChangedCount);

                // verify no other work was done
                Assert.IsFalse(targetChainChangedEvent.WaitOne(50));
            }
        }
Beispiel #18
0
        public void TestReadChainWithGap()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();

            var chainedHeaders = new Dictionary<UInt256, ChainedHeader>();
            chainedHeaders.Add(header0.Hash, header0);
            chainedHeaders.Add(header2.Hash, header2);

            Chain chain;
            Assert.IsFalse(Chain.TryReadChain(header2.Hash, out chain,
                headerHash =>
                {
                    ChainedHeader chainedHeader;
                    chainedHeaders.TryGetValue(headerHash, out chainedHeader);
                    return chainedHeader;
                }));
        }
        private void TestContainsHeader(ITestStorageProvider provider)
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            using (var storageManager = provider.OpenStorageManager())
            using (var handle = storageManager.OpenChainStateCursor())
            {
                var chainStateCursor = handle.Item;

                // begin transaction
                chainStateCursor.BeginTransaction();

                // verify presence
                Assert.IsFalse(chainStateCursor.ContainsHeader(header0.Hash));
                Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash));

                // add header 0
                chainStateCursor.TryAddHeader(header0);

                // verify presence
                Assert.IsTrue(chainStateCursor.ContainsHeader(header0.Hash));
                Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash));

                // add header 1
                chainStateCursor.TryAddHeader(header1);

                // verify presence
                Assert.IsTrue(chainStateCursor.ContainsHeader(header0.Hash));
                Assert.IsTrue(chainStateCursor.ContainsHeader(header1.Hash));

                // remove header 1
                chainStateCursor.TryRemoveHeader(header1.Hash);

                // verify presence
                Assert.IsTrue(chainStateCursor.ContainsHeader(header0.Hash));
                Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash));

                // remove header 0
                chainStateCursor.TryRemoveHeader(header0.Hash);

                // verify presence
                Assert.IsFalse(chainStateCursor.ContainsHeader(header0.Hash));
                Assert.IsFalse(chainStateCursor.ContainsHeader(header1.Hash));
            }
        }
Beispiel #20
0
        public void TestReadChain()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();

            var chainedHeaders = new Dictionary<UInt256, ChainedHeader>();
            chainedHeaders.Add(header0.Hash, header0);
            chainedHeaders.Add(header1.Hash, header1);
            chainedHeaders.Add(header2.Hash, header2);

            Chain chain;
            Assert.IsTrue(Chain.TryReadChain(header2.Hash, out chain,
                headerHash =>
                {
                    ChainedHeader chainedHeader;
                    chainedHeaders.TryGetValue(headerHash, out chainedHeader);
                    return chainedHeader;
                }));

            CollectionAssert.AreEqual(fakeHeaders.ChainedHeaders, chain.Blocks);
        }
Beispiel #21
0
        public void TestCreateForGenesisBlock()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();

            var chain = Chain.CreateForGenesisBlock(header0);

            CollectionAssert.AreEqual(new[] { header0 }, chain.Blocks);
        }
Beispiel #22
0
        public void TestNavigateTowardsInvalidChains()
        {
            // create distinct chains
            var fakeHeadersA = new FakeHeaders();
            var header0A = fakeHeadersA.GenesisChained();
            var header1A = fakeHeadersA.NextChained();

            var fakeHeadersB = new FakeHeaders();
            var header0B = fakeHeadersB.GenesisChained();
            var header1B = fakeHeadersB.NextChained();

            var chainEmpty = new ChainBuilder().ToImmutable();
            var chainA = new ChainBuilder(new[] { header0A, header1A }).ToImmutable();
            var chainB = new ChainBuilder(new[] { header0B, header1B, }).ToImmutable();

            // unrelated chains should error
            AssertMethods.AssertThrows<InvalidOperationException>(() => chainA.NavigateTowards(chainB).ToList());
            AssertMethods.AssertThrows<InvalidOperationException>(() => chainB.NavigateTowards(chainA).ToList());
        }
Beispiel #23
0
        public void TestToImmutable()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // verify to builder with 0 blocks
            var chainEmpty = chainBuilder.ToImmutable();
            Assert.AreEqual(0, chainEmpty.Blocks.Count);
            Assert.AreEqual(0, chainEmpty.BlocksByHash.Count);

            // verify to builder with 1 block
            chainBuilder.AddBlock(header0);
            var chain0 = chainBuilder.ToImmutable();
            CollectionAssert.AreEqual(new[] { header0 }, chain0.Blocks);
            CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 } }, chain0.BlocksByHash);

            // verify to builder with 2 blocks
            chainBuilder.AddBlock(header1);
            var chain1 = chainBuilder.ToImmutable();
            CollectionAssert.AreEqual(new[] { header0, header1 }, chain1.Blocks);
            CollectionAssert.AreEquivalent(new Dictionary<UInt256, ChainedHeader> { { header0.Hash, header0 }, { header1.Hash, header1 } }, chain1.BlocksByHash);
        }
Beispiel #24
0
        public void TestTargetChainReorganize()
        {
            // prepare test kernel
            var kernel = new StandardKernel(new ConsoleLoggingModule(), new MemoryStorageModule());

            kernel.Bind <CoreStorage>().ToSelf().InSingletonScope();
            var coreStorage = kernel.Get <CoreStorage>();

            // initialize data
            var fakeHeaders = new FakeHeaders();
            var header0     = fakeHeaders.Genesis();
            var header1     = fakeHeaders.Next();
            var header2     = fakeHeaders.Next();

            var fakeHeadersA = new FakeHeaders(fakeHeaders);
            var header3A     = fakeHeadersA.Next();
            var header4A     = fakeHeadersA.Next();
            var header5A     = fakeHeadersA.Next();

            var fakeHeadersB = new FakeHeaders(fakeHeaders);
            var header3B     = fakeHeadersB.Next();
            var header4B     = fakeHeadersB.Next(DataCalculator.ToCompact(UnitTestParams.Target2));

            // store genesis block
            var chainedHeader0 = ChainedHeader.CreateForGenesisBlock(header0);

            coreStorage.AddGenesisBlock(chainedHeader0);

            // mock chain params
            var mockChainParams = new Mock <IChainParams>();

            mockChainParams.Setup(rules => rules.GenesisChainedHeader).Returns(chainedHeader0);
            kernel.Bind <IChainParams>().ToConstant(mockChainParams.Object);

            // initialize the target chain worker
            using (var targetChainWorker = kernel.Get <TargetChainWorker>(new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue))))
            {
                // monitor event firing
                var targetChainChangedEvent   = new AutoResetEvent(false);
                var onTargetChainChangedCount = 0;
                targetChainWorker.OnTargetChainChanged += () => { onTargetChainChangedCount++; targetChainChangedEvent.Set(); };

                // start worker and wait for initial chain
                targetChainWorker.Start();
                targetChainChangedEvent.WaitOne();

                // verify chained to block 0
                Assert.AreEqual(chainedHeader0, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(1, onTargetChainChangedCount);

                // add block 1
                ChainedHeader chainedHeader1;
                coreStorage.TryChainHeader(header1, out chainedHeader1);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 1
                Assert.AreEqual(chainedHeader1, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(2, onTargetChainChangedCount);

                // add block 2
                ChainedHeader chainedHeader2;
                coreStorage.TryChainHeader(header2, out chainedHeader2);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 2
                Assert.AreEqual(chainedHeader2, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(3, onTargetChainChangedCount);

                // add block 3A
                ChainedHeader chainedHeader3A;
                coreStorage.TryChainHeader(header3A, out chainedHeader3A);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 3A
                Assert.AreEqual(chainedHeader3A, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(4, onTargetChainChangedCount);

                // add block 4A
                ChainedHeader chainedHeader4A;
                coreStorage.TryChainHeader(header4A, out chainedHeader4A);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 4A
                Assert.AreEqual(chainedHeader4A, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A, chainedHeader4A }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(5, onTargetChainChangedCount);

                // add block 5A
                ChainedHeader chainedHeader5A;
                coreStorage.TryChainHeader(header5A, out chainedHeader5A);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 5A
                Assert.AreEqual(chainedHeader5A, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A, chainedHeader4A, chainedHeader5A }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(6, onTargetChainChangedCount);

                // add block 3B
                ChainedHeader chainedHeader3B;
                coreStorage.TryChainHeader(header3B, out chainedHeader3B);

                // wait for worker, it should not fire
                Assert.IsFalse(targetChainChangedEvent.WaitOne(50));

                // verify no chaining done
                Assert.AreEqual(chainedHeader5A, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3A, chainedHeader4A, chainedHeader5A }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(6, onTargetChainChangedCount);

                // add block 4B
                ChainedHeader chainedHeader4B;
                coreStorage.TryChainHeader(header4B, out chainedHeader4B);

                // wait for worker
                targetChainChangedEvent.WaitOne();

                // verify chained to block 4B
                Assert.AreEqual(chainedHeader4B, targetChainWorker.TargetChain.LastBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2, chainedHeader3B, chainedHeader4B }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(7, onTargetChainChangedCount);

                // verify no other work was done
                Assert.IsFalse(targetChainChangedEvent.WaitOne(50));
            }
        }
Beispiel #25
0
        public void TestSimpleChain()
        {
            // prepare test kernel
            var kernel = new StandardKernel(new ConsoleLoggingModule(), new MemoryStorageModule(), new CoreCacheModule());

            // initialize data
            var fakeHeaders    = new FakeHeaders();
            var chainedHeader0 = new ChainedHeader(fakeHeaders.Genesis(), height: 0, totalWork: 0);
            var chainedHeader1 = new ChainedHeader(fakeHeaders.Next(), height: 1, totalWork: 1);
            var chainedHeader2 = new ChainedHeader(fakeHeaders.Next(), height: 2, totalWork: 2);

            // mock rules
            var mockRules = new Mock <IBlockchainRules>();

            mockRules.Setup(rules => rules.GenesisChainedHeader).Returns(chainedHeader0);
            kernel.Bind <IBlockchainRules>().ToConstant(mockRules.Object);

            // store genesis block
            var chainedHeaderCache = kernel.Get <ChainedHeaderCache>();

            chainedHeaderCache[chainedHeader0.Hash] = chainedHeader0;

            // initialize the target chain worker
            using (var targetChainWorker = kernel.Get <TargetChainWorker>(new ConstructorArgument("workerConfig", new WorkerConfig(initialNotify: false, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue))))
            {
                // verify initial state
                Assert.AreEqual(null, targetChainWorker.TargetBlock);
                Assert.AreEqual(null, targetChainWorker.TargetChain);

                // monitor event firing
                var workNotifyEvent           = new AutoResetEvent(false);
                var workStoppedEvent          = new AutoResetEvent(false);
                var onTargetChainChangedCount = 0;

                targetChainWorker.OnNotifyWork         += () => workNotifyEvent.Set();
                targetChainWorker.OnWorkStopped        += () => workStoppedEvent.Set();
                targetChainWorker.OnTargetChainChanged += () => onTargetChainChangedCount++;

                // start worker and wait for initial chain
                targetChainWorker.Start();
                workNotifyEvent.WaitOne();
                workStoppedEvent.WaitOne();

                // verify chained to block 0
                Assert.AreEqual(chainedHeader0, targetChainWorker.TargetBlock);
                AssertBlockListEquals(new[] { chainedHeader0 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(1, onTargetChainChangedCount);

                // add block 1
                chainedHeaderCache[chainedHeader1.Hash] = chainedHeader1;

                // wait for worker (chained block addition)
                workNotifyEvent.WaitOne();
                workStoppedEvent.WaitOne();
                // wait for worker (target block changed)
                workNotifyEvent.WaitOneOrFail(1000);
                workStoppedEvent.WaitOneOrFail(1000);

                // verify chained to block 1
                Assert.AreEqual(chainedHeader1, targetChainWorker.TargetBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(2, onTargetChainChangedCount);

                // add block 2
                chainedHeaderCache[chainedHeader2.Hash] = chainedHeader2;

                // wait for worker (chained block addition)
                workNotifyEvent.WaitOne();
                workStoppedEvent.WaitOne();
                // wait for worker (target block changed)
                workNotifyEvent.WaitOneOrFail(1000);
                workStoppedEvent.WaitOneOrFail(1000);

                // verify chained to block 2
                Assert.AreEqual(chainedHeader2, targetChainWorker.TargetBlock);
                AssertBlockListEquals(new[] { chainedHeader0, chainedHeader1, chainedHeader2 }, targetChainWorker.TargetChain.Blocks);
                Assert.AreEqual(3, onTargetChainChangedCount);

                // verify no other work was done
                Assert.IsFalse(workNotifyEvent.WaitOne(0));
                Assert.IsFalse(workStoppedEvent.WaitOne(0));
            }
        }
Beispiel #26
0
        public void TestTotalWork()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var totalWork0 = DataCalculator.CalculateWork(header0);
            var totalWork1 = totalWork0 + DataCalculator.CalculateWork(header1);

            var chainBuilder = new ChainBuilder();

            // verify total work with 0 blocks
            Assert.AreEqual(0, chainBuilder.TotalWork);

            // verify total work with 1 block
            chainBuilder.AddBlock(header0);
            Assert.AreEqual(totalWork0, chainBuilder.TotalWork);

            // verify total work with 2 blocks
            chainBuilder.AddBlock(header1);
            Assert.AreEqual(totalWork1, chainBuilder.TotalWork);
        }
Beispiel #27
0
        public void TestSimpleSpend()
        {
            // prepare block
            var fakeHeaders      = new FakeHeaders();
            var chainedHeader0   = fakeHeaders.GenesisChained();
            var chainedHeader1   = fakeHeaders.NextChained();
            var chainedHeader2   = fakeHeaders.NextChained();
            var chainedHeader3   = fakeHeaders.NextChained();
            var chain            = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder();
            var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));
            var emptyCoinbaseTx1 = BlockTx.Create(1, Transaction.Create(1, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));
            var emptyCoinbaseTx2 = BlockTx.Create(2, Transaction.Create(2, ImmutableArray.Create <TxInput>(), ImmutableArray.Create <TxOutput>(), 0));

            // initialize memory utxo builder storage
            var memoryStorage          = new MemoryStorageManager();
            var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item;

            memoryChainStateCursor.BeginTransaction();

            // initialize utxo builder
            var utxoBuilder = new UtxoBuilder();

            // prepare an unspent transaction
            var txHash       = new UInt256(100);
            var unspentTx    = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 3, OutputState.Unspent);
            var txOutput1Key = new TxOutputKey(txHash, 0);
            var txOutput1    = new TxOutput(0, ImmutableArray <byte> .Empty);
            var txOutput2Key = new TxOutputKey(txHash, 1);
            var txOutput2    = new TxOutput(1, ImmutableArray <byte> .Empty);
            var txOutput3Key = new TxOutputKey(txHash, 2);
            var txOutput3    = new TxOutput(2, ImmutableArray <byte> .Empty);

            // prepare unspent output
            var unspentTransactions = ImmutableDictionary.Create <UInt256, UnspentTx>().Add(txHash, unspentTx);

            // add the unspent transaction
            memoryChainStateCursor.TryAddUnspentTx(unspentTx);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput2Key, txOutput2);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput3Key, txOutput3);

            // create an input to spend the unspent transaction's first output
            var input0 = new TxInput(txHash, 0, ImmutableArray.Create <byte>(), 0);
            var tx0    = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input0), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader1);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx0 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            UnspentTx actualUnspentTx; TxOutput actualTxOutput;

            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3);
            Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Unspent);
            Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent);

            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput));
            Assert.AreEqual(txOutput1, actualTxOutput);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput2Key, out actualTxOutput));
            Assert.AreEqual(txOutput2, actualTxOutput);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput3Key, out actualTxOutput));
            Assert.AreEqual(txOutput3, actualTxOutput);

            // create an input to spend the unspent transaction's second output
            var input1 = new TxInput(txHash, 1, ImmutableArray.Create <byte>(), 0);
            var tx1    = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input1), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader2);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx1 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3);
            Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent);

            // create an input to spend the unspent transaction's third output
            var input2 = new TxInput(txHash, 2, ImmutableArray.Create <byte>(), 0);
            var tx2    = BlockTx.Create(2, Transaction.Create(0, ImmutableArray.Create(input2), ImmutableArray.Create <TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader3);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx2, tx2 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.IsFullySpent);
        }
Beispiel #28
0
        public void TestBlocks()
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            var chainBuilder = new ChainBuilder();

            // verify block list with 0 blocks
            Assert.AreEqual(0, chainBuilder.Blocks.Count);

            // verify block list with 1 block
            chainBuilder.AddBlock(header0);
            CollectionAssert.AreEqual(new[] { header0 }, chainBuilder.Blocks);

            // verify block list with 2 blocks
            chainBuilder.AddBlock(header1);
            CollectionAssert.AreEqual(new[] { header0, header1 }, chainBuilder.Blocks);
        }
        private void TestCommitTransaction(ITestStorageProvider provider)
        {
            var fakeHeaders = new FakeHeaders();
            var chainedHeader0 = fakeHeaders.GenesisChained();
            var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Spent));
            var spentTxes = BlockSpentTxes.CreateRange(new[] { unspentTx.ToSpentTx() });

            using (var storageManager = provider.OpenStorageManager())
            using (var handle = storageManager.OpenChainStateCursor())
            {
                var chainStateCursor = handle.Item;

                // begin transaction
                chainStateCursor.BeginTransaction();

                // add data
                chainStateCursor.ChainTip = chainedHeader0;
                chainStateCursor.TryAddUnspentTx(unspentTx);
                chainStateCursor.UnspentTxCount++;
                chainStateCursor.TryAddBlockSpentTxes(0, spentTxes);

                // verify data
                Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip);
                Assert.AreEqual(1, chainStateCursor.UnspentTxCount);

                UnspentTx actualUnspentTx;
                Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx));
                Assert.AreEqual(unspentTx, actualUnspentTx);

                BlockSpentTxes actualSpentTxes;
                Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes));
                CollectionAssert.AreEqual(spentTxes.ToList(), actualSpentTxes.ToList());

                // commit transaction
                chainStateCursor.CommitTransaction();

                chainStateCursor.BeginTransaction();

                // verify data
                Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip);
                Assert.AreEqual(1, chainStateCursor.UnspentTxCount);

                Assert.IsTrue(chainStateCursor.TryGetUnspentTx(unspentTx.TxHash, out actualUnspentTx));
                Assert.AreEqual(unspentTx, actualUnspentTx);

                Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes));
                CollectionAssert.AreEqual(spentTxes.ToList(), actualSpentTxes.ToList());

                chainStateCursor.RollbackTransaction();
            }
        }
Beispiel #30
0
        public void TestNavigateTowardsFunc()
        {
            // create chains
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();
            var header2 = fakeHeaders.NextChained();
            var header3 = fakeHeaders.NextChained();

            var chain0 = new ChainBuilder(new[] { header0, }).ToImmutable();
            var chain1 = new ChainBuilder(new[] { header0, header1, }).ToImmutable();
            var chain2 = new ChainBuilder(new[] { header0, header1, header2 }).ToImmutable();
            var chain3 = new ChainBuilder(new[] { header0, header1, header2, header3 }).ToImmutable();

            // the list of target chains to use, stays 1 ahead and then catches up with chain 3
            var targetStack = new Stack<Chain>(new[] { chain1, chain2, chain3, chain3 }.Reverse());

            // verify navigating towards an updating chain
            CollectionAssert.AreEqual(
                new[]
                {
                    Tuple.Create(+1, header1),
                    Tuple.Create(+1, header2),
                    Tuple.Create(+1, header3)
                }
                , chain0.NavigateTowards(() => targetStack.Pop()).ToList());

            // verify all targets used
            Assert.AreEqual(0, targetStack.Count);
        }
        private void TestRollbackTransaction(ITestStorageProvider provider)
        {
            var fakeHeaders = new FakeHeaders();
            var chainedHeader0 = fakeHeaders.GenesisChained();
            var chainedHeader1 = fakeHeaders.NextChained();
            var chainedHeader2 = fakeHeaders.NextChained();

            var unspentTx = new UnspentTx(txHash: UInt256.Zero, blockIndex: 0, txIndex: 0, outputStates: new OutputStates(1, OutputState.Spent));
            var spentTxes = BlockSpentTxes.CreateRange(new[] { unspentTx.ToSpentTx() });

            using (var storageManager = provider.OpenStorageManager())
            using (var handle = storageManager.OpenChainStateCursor())
            {
                var chainStateCursor = handle.Item;

                // begin transaction
                chainStateCursor.BeginTransaction();

                // add header 0
                chainStateCursor.ChainTip = chainedHeader0;

                // verify chain
                Assert.AreEqual(chainedHeader0, chainStateCursor.ChainTip);

                // add unspent tx
                chainStateCursor.TryAddUnspentTx(unspentTx);
                chainStateCursor.UnspentTxCount++;

                // verify unspent tx
                Assert.IsTrue(chainStateCursor.ContainsUnspentTx(unspentTx.TxHash));
                Assert.AreEqual(1, chainStateCursor.UnspentTxCount);

                // add spent txes
                chainStateCursor.TryAddBlockSpentTxes(0, spentTxes);

                // verify spent txes
                BlockSpentTxes actualSpentTxes;
                Assert.IsTrue(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes));
                CollectionAssert.AreEqual(spentTxes.ToList(), actualSpentTxes.ToList());

                // rollback transaction
                chainStateCursor.RollbackTransaction();

                chainStateCursor.BeginTransaction();

                // verify chain
                Assert.IsNull(chainStateCursor.ChainTip);

                // verify unspent tx
                Assert.IsFalse(chainStateCursor.ContainsUnspentTx(unspentTx.TxHash));
                Assert.AreEqual(0, chainStateCursor.UnspentTxCount);

                // verify spent txes
                Assert.IsFalse(chainStateCursor.TryGetBlockSpentTxes(0, out actualSpentTxes));

                chainStateCursor.RollbackTransaction();
            }
        }
Beispiel #32
0
        public void TestSimpleSpend()
        {
            // prepare block
            var fakeHeaders = new FakeHeaders();
            var chainedHeader0 = fakeHeaders.GenesisChained();
            var chainedHeader1 = fakeHeaders.NextChained();
            var chainedHeader2 = fakeHeaders.NextChained();
            var chainedHeader3 = fakeHeaders.NextChained();
            var chain = Chain.CreateForGenesisBlock(chainedHeader0).ToBuilder();
            var emptyCoinbaseTx0 = BlockTx.Create(0, Transaction.Create(0, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0));
            var emptyCoinbaseTx1 = BlockTx.Create(1, Transaction.Create(1, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0));
            var emptyCoinbaseTx2 = BlockTx.Create(2, Transaction.Create(2, ImmutableArray.Create<TxInput>(), ImmutableArray.Create<TxOutput>(), 0));

            // initialize memory utxo builder storage
            var memoryStorage = new MemoryStorageManager();
            var memoryChainStateCursor = memoryStorage.OpenChainStateCursor().Item;
            memoryChainStateCursor.BeginTransaction();

            // initialize utxo builder
            var utxoBuilder = new UtxoBuilder();

            // prepare an unspent transaction
            var txHash = new UInt256(100);
            var unspentTx = new UnspentTx(txHash, chainedHeader1.Height, 0, 0, false, 3, OutputState.Unspent);
            var txOutput1Key = new TxOutputKey(txHash, 0);
            var txOutput1 = new TxOutput(0, ImmutableArray<byte>.Empty);
            var txOutput2Key = new TxOutputKey(txHash, 1);
            var txOutput2 = new TxOutput(1, ImmutableArray<byte>.Empty);
            var txOutput3Key = new TxOutputKey(txHash, 2);
            var txOutput3 = new TxOutput(2, ImmutableArray<byte>.Empty);

            // prepare unspent output
            var unspentTransactions = ImmutableDictionary.Create<UInt256, UnspentTx>().Add(txHash, unspentTx);

            // add the unspent transaction
            memoryChainStateCursor.TryAddUnspentTx(unspentTx);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput1Key, txOutput1);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput2Key, txOutput2);
            memoryChainStateCursor.TryAddUnspentTxOutput(txOutput3Key, txOutput3);

            // create an input to spend the unspent transaction's first output
            var input0 = new TxInput(txHash, 0, ImmutableArray.Create<byte>(), 0);
            var tx0 = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input0), ImmutableArray.Create<TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader1);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx0, tx0 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            UnspentTx actualUnspentTx; TxOutput actualTxOutput;
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3);
            Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Unspent);
            Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent);

            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput1Key, out actualTxOutput));
            Assert.AreEqual(txOutput1, actualTxOutput);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput2Key, out actualTxOutput));
            Assert.AreEqual(txOutput2, actualTxOutput);
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTxOutput(txOutput3Key, out actualTxOutput));
            Assert.AreEqual(txOutput3, actualTxOutput);

            // create an input to spend the unspent transaction's second output
            var input1 = new TxInput(txHash, 1, ImmutableArray.Create<byte>(), 0);
            var tx1 = BlockTx.Create(1, Transaction.Create(0, ImmutableArray.Create(input1), ImmutableArray.Create<TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader2);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx1, tx1 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.OutputStates.Length == 3);
            Assert.IsTrue(actualUnspentTx.OutputStates[0] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[1] == OutputState.Spent);
            Assert.IsTrue(actualUnspentTx.OutputStates[2] == OutputState.Unspent);

            // create an input to spend the unspent transaction's third output
            var input2 = new TxInput(txHash, 2, ImmutableArray.Create<byte>(), 0);
            var tx2 = BlockTx.Create(2, Transaction.Create(0, ImmutableArray.Create(input2), ImmutableArray.Create<TxOutput>(), 0));

            // spend the input
            chain.AddBlock(chainedHeader3);
            utxoBuilder.CalculateUtxo(memoryChainStateCursor, chain.ToImmutable(), new[] { emptyCoinbaseTx2, tx2 }.ToBufferBlock()).ToEnumerable().ToList();

            // verify utxo storage
            Assert.IsTrue(memoryChainStateCursor.TryGetUnspentTx(txHash, out actualUnspentTx));
            Assert.IsTrue(actualUnspentTx.IsFullySpent);
        }
        private void TestTryAddGetRemoveHeader(ITestStorageProvider provider)
        {
            var fakeHeaders = new FakeHeaders();
            var header0 = fakeHeaders.GenesisChained();
            var header1 = fakeHeaders.NextChained();

            using (var storageManager = provider.OpenStorageManager())
            using (var handle = storageManager.OpenChainStateCursor())
            {
                var chainStateCursor = handle.Item;

                // begin transaction
                chainStateCursor.BeginTransaction();

                // verify initial empty state
                ChainedHeader actualHeader0, actualHeader1;
                Assert.IsFalse(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0));
                Assert.IsFalse(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader1));

                // add header 0
                Assert.IsTrue(chainStateCursor.TryAddHeader(header0));

                // verify unspent txes
                Assert.IsTrue(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0));
                Assert.AreEqual(header0, actualHeader0);
                Assert.IsFalse(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1));

                // add header 1
                Assert.IsTrue(chainStateCursor.TryAddHeader(header1));

                // verify unspent txes
                Assert.IsTrue(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0));
                Assert.AreEqual(header0, actualHeader0);
                Assert.IsTrue(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1));
                Assert.AreEqual(header1, actualHeader1);

                // remove header 1
                Assert.IsTrue(chainStateCursor.TryRemoveHeader(header1.Hash));

                // verify unspent txes
                Assert.IsTrue(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0));
                Assert.AreEqual(header0, actualHeader0);
                Assert.IsFalse(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1));

                // remove header 0
                Assert.IsTrue(chainStateCursor.TryRemoveHeader(header0.Hash));

                // verify unspent txes
                Assert.IsFalse(chainStateCursor.TryGetHeader(header0.Hash, out actualHeader0));
                Assert.IsFalse(chainStateCursor.TryGetHeader(header1.Hash, out actualHeader1));
            }
        }
Beispiel #34
0
        public void TestPruneAllData()
        {
            var logger = LogManager.CreateNullLogger();

            // create genesis block
            var genesisblock = CreateFakeBlock(1);
            var genesisHeader = new ChainedHeader(genesisblock.Header, height: 0, totalWork: genesisblock.Header.CalculateWork(), dateSeen: DateTimeOffset.Now);

            // create a block
            var txCount = 100;
            var block = CreateFakeBlock(txCount, genesisblock.Hash);
            var chainedHeader = ChainedHeader.CreateFromPrev(genesisHeader, block.Header, dateSeen: DateTimeOffset.Now);

            // create a long chain based off the block, to account for pruning buffer
            var fakeHeaders = new FakeHeaders(new[] { genesisHeader, chainedHeader });
            var chain = new ChainBuilder(Enumerable.Concat(new[] { genesisHeader, chainedHeader }, Enumerable.Range(0, 2000).Select(x => fakeHeaders.NextChained()))).ToImmutable();

            // mock core daemon to return the chain
            var coreDaemon = new Mock<ICoreDaemon>();
            coreDaemon.Setup(x => x.CurrentChain).Returns(chain);

            // create memory storage with the block
            var storageManager = new MemoryStorageManager();
            storageManager.BlockTxesStorage.TryAddBlockTransactions(block.Hash, block.BlockTxes);

            // initialize the pruning worker
            var workerConfig = new WorkerConfig(initialNotify: false, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue);
            using (var pruningWorker = new PruningWorker(workerConfig, coreDaemon.Object, storageManager, null))
            // get a chain state cursor
            using (var handle = storageManager.OpenChainStateCursor())
            {
                var chainStateCursor = handle.Item;

                // set the pruning worker to prune all data
                pruningWorker.Mode = PruningMode.TxIndex | PruningMode.BlockSpentIndex | PruningMode.BlockTxesPreserveMerkle;

                // wire event to wait for work
                var workFinishedEvent = new AutoResetEvent(false);
                pruningWorker.OnWorkFinished += () => workFinishedEvent.Set();

                // wire event to track exceptions
                Exception workException = null;
                pruningWorker.OnWorkError += e => workException = e;

                // start the worker
                pruningWorker.Start();

                // pick a random pruning order
                var random = new Random();
                var pruneOrderSource = Enumerable.Range(0, txCount).ToList();
                var pruneOrder = new List<int>(txCount);
                while (pruneOrderSource.Count > 0)
                {
                    var randomIndex = random.Next(pruneOrderSource.Count);

                    pruneOrder.Add(pruneOrderSource[randomIndex]);
                    pruneOrderSource.RemoveAt(randomIndex);
                }

                // add an unspent tx for each transaction to storage
                var unspentTxes = new UnspentTx[block.Transactions.Length];
                chainStateCursor.BeginTransaction();
                for (var txIndex = 0; txIndex < block.Transactions.Length; txIndex++)
                {
                    var tx = block.Transactions[txIndex];
                    var unspentTx = new UnspentTx(tx.Hash, blockIndex: 1, txIndex: txIndex, txVersion: tx.Version, isCoinbase: txIndex == 0, outputStates: new OutputStates(1, OutputState.Spent));
                    unspentTxes[txIndex] = unspentTx;
                    chainStateCursor.TryAddUnspentTx(unspentTx);
                }
                chainStateCursor.CommitTransaction();

                // create a memory pruning cursor to verify expected pruning results
                var pruneCursor = new MemoryMerkleTreePruningCursor<BlockTxNode>(block.BlockTxes.Select(x => (BlockTxNode)x));

                // prune each transaction in random order
                var pruneHeight = 0;
                foreach (var pruneTxIndex in pruneOrder)
                {
                    // create a spent tx to prune the transaction
                    var pruneTx = block.Transactions[pruneTxIndex];
                    var spentTxes = BlockSpentTxes.CreateRange(new[] { unspentTxes[pruneTxIndex].ToSpentTx() });

                    // store the spent txes for the current pruning block
                    pruneHeight++;
                    chainStateCursor.BeginTransaction();
                    Assert.IsTrue(chainStateCursor.TryAddBlockSpentTxes(pruneHeight, spentTxes));
                    pruningWorker.PrunableHeight = pruneHeight;

                    // verify unspent tx is present before pruning
                    Assert.IsTrue(chainStateCursor.ContainsUnspentTx(pruneTx.Hash));
                    chainStateCursor.CommitTransaction();

                    // notify the pruning worker and wait
                    pruningWorker.NotifyWork();
                    workFinishedEvent.WaitOne();

                    // verify unspent tx is removed after pruning
                    chainStateCursor.BeginTransaction();
                    Assert.IsFalse(chainStateCursor.ContainsUnspentTx(pruneTx.Hash));

                    // verify unspent tx outputs are removed after pruning
                    for (var outputIndex = 0; outputIndex < pruneTx.Outputs.Length; outputIndex++)
                        Assert.IsFalse(chainStateCursor.ContainsUnspentTxOutput(new TxOutputKey(pruneTx.Hash, (uint)outputIndex)));

                    // verify the spent txes were removed
                    Assert.IsFalse(chainStateCursor.ContainsBlockSpentTxes(pruneHeight));
                    chainStateCursor.RollbackTransaction();

                    // prune to determine expected results
                    MerkleTree.PruneNode(pruneCursor, pruneTxIndex);
                    var expectedPrunedTxes = pruneCursor.ReadNodes().ToList();

                    // retrieve the actual transaction after pruning
                    IEnumerator<BlockTxNode> actualPrunedTxNodes;
                    Assert.IsTrue(storageManager.BlockTxesStorage.TryReadBlockTxNodes(block.Hash, out actualPrunedTxNodes));

                    // verify the actual pruned transactions match the expected results
                    CollectionAssert.AreEqual(expectedPrunedTxes, actualPrunedTxNodes.UsingAsEnumerable().ToList());
                }

                // verify all unspent txes were removed
                chainStateCursor.BeginTransaction();
                Assert.AreEqual(0, chainStateCursor.ReadUnspentTransactions().Count());
                chainStateCursor.CommitTransaction();

                // verify final block with all transactions pruned
                IEnumerator<BlockTxNode> finalPrunedTxNodes;
                Assert.IsTrue(storageManager.BlockTxesStorage.TryReadBlockTxNodes(block.Hash, out finalPrunedTxNodes));
                var finalPrunedTxesList = finalPrunedTxNodes.UsingAsEnumerable().ToList();
                Assert.AreEqual(1, finalPrunedTxesList.Count);
                Assert.AreEqual(block.Header.MerkleRoot, finalPrunedTxesList.Single().Hash);

                // verify no work exceptions occurred
                Assert.IsNull(workException);
            }
        }