public void TestSmoke()
        {
            string testFolder = TestUtils.PrepareTestFolder(GetType(), nameof(TestSmoke), "*.db");

            using (SQLiteBlockchainStorage storage = SQLiteBlockchainStorage.Open(testFolder))
            {
                Assert.Null(storage.FindBlockByHash(GenesisBlock.Hash));
                Assert.Null(storage.FindFirst(new BlockSelector
                {
                    IsInBestHeaderChain = true,
                    IsInBestBlockChain = true,
                    Order = BlockSelector.SortOrder.Height,
                    Direction = BlockSelector.SortDirection.Desc
                }));
                Assert.That(storage.Find(new BlockSelector
                {
                    Heights = new int[] {0, 1},
                    IsInBestHeaderChain = true,
                    Order = BlockSelector.SortOrder.Height,
                    Direction = BlockSelector.SortDirection.Asc
                }, 100).Select(b => b.Height).ToList(), Is.EqualTo(new int[] {}));

                StoredBlockBuilder blockBuilder = new StoredBlockBuilder(GenesisBlock.GetHeader());
                blockBuilder.Height = 0;
                blockBuilder.IsInBestHeaderChain = true;
                blockBuilder.IsInBestBlockChain = true;

                storage.AddBlock(blockBuilder.Build());
            }

            using (SQLiteBlockchainStorage storage = SQLiteBlockchainStorage.Open(testFolder))
            {
                StoredBlock block = storage.FindBlockByHash(GenesisBlock.Hash);

                Assert.NotNull(block);
                Assert.That(block.Hash, Is.EqualTo(GenesisBlock.Hash));

                block = storage.FindFirst(new BlockSelector
                {
                    IsInBestHeaderChain = true,
                    IsInBestBlockChain = true,
                    Order = BlockSelector.SortOrder.Height,
                    Direction = BlockSelector.SortDirection.Desc
                });

                Assert.NotNull(block);
                Assert.That(block.Hash, Is.EqualTo(GenesisBlock.Hash));

                Assert.That(storage.Find(new BlockSelector
                {
                    Heights = new int[] {0, 1},
                    IsInBestHeaderChain = true,
                    Order = BlockSelector.SortOrder.Height,
                    Direction = BlockSelector.SortDirection.Asc
                }, 100).Select(b => b.Height).ToList(), Is.EqualTo(new int[] {0}));
            }
        }
        public void TestIsTimestampValid()
        {
            List<StoredBlock> blocks = new List<StoredBlock>();

            StoredBlock prevBlock = null;

            for (int i = 0; i < 11; i++)
            {
                BlockHeader header = new BlockHeader
                    (
                    genesisBlockHeader.Version,
                    prevBlock == null ? new byte[32] : prevBlock.Hash,
                    new byte[32],
                    (uint) i,
                    0x21100000,
                    0);
                StoredBlock storedBlock = new StoredBlockBuilder(header).Build();
                blocks.Add(storedBlock);
                prevBlock = storedBlock;
            }

            Subchain subchain = new Subchain(blocks);

            {
                BlockHeader header = new BlockHeader
                    (
                    genesisBlockHeader.Version,
                    new byte[32],
                    new byte[32],
                    5,
                    0x21100000,
                    0);

                StoredBlock storedBlock = new StoredBlockBuilder(header).Build();

                Assert.False(BlockHeaderValidator.IsTimeStampValid(storedBlock, subchain));
            }

            {
                BlockHeader header = new BlockHeader
                    (
                    genesisBlockHeader.Version,
                    new byte[32],
                    new byte[32],
                    6,
                    0x21100000,
                    0);

                StoredBlock storedBlock = new StoredBlockBuilder(header).Build();

                Assert.True(BlockHeaderValidator.IsTimeStampValid(storedBlock, subchain));
            }
        }
        private StoredBlock SetIsInBestHeaderChain(StoredBlock block, bool val)
        {
            StoredBlockBuilder builder = new StoredBlockBuilder(block);
            builder.IsInBestHeaderChain = val;
            StoredBlock newBlock = builder.Build();

            currentState = currentState.Update(block, newBlock);

            return newBlock;
        }
        private void AddGenesisBlock()
        {
            //todo: use network specific genesis block
            StoredBlockBuilder blockBuilder = new StoredBlockBuilder(GenesisBlock.GetHeader());

            double blockWork = DifficultyUtils.DifficultyTargetToWork(DifficultyUtils.NBitsToTarget(blockBuilder.Header.NBits));

            blockBuilder.Height = 0;
            blockBuilder.TotalWork = blockWork;
            blockBuilder.IsInBestBlockChain = true;
            blockBuilder.IsInBestHeaderChain = true;

            StoredBlock genesisBlock = blockBuilder.Build();

            //todo: review transaction usage in this class
            storage.AddBlock(genesisBlock);
            //todo: use network specific genesis block
            storage.AddBlockContent(genesisBlock.Hash, GenesisBlock.Raw);
        }
        public void TestTransactionsWithCache()
        {
            string testFolder = TestUtils.PrepareTestFolder(GetType(), nameof(TestTransactionsWithCache), "*.db");

            using (SQLiteBlockchainStorage storage = SQLiteBlockchainStorage.Open(testFolder))
            {
                CachingBlockchainStorage cache = new CachingBlockchainStorage(storage);

                StoredBlockBuilder blockBuilder = new StoredBlockBuilder(GenesisBlock.GetHeader());
                blockBuilder.IsInBestHeaderChain = true;
                blockBuilder.IsInBestBlockChain = false;
                StoredBlock block = blockBuilder.Build();

                Assert.Throws<InvalidOperationException>(() => cache.AddBlock(block));

                using (new TransactionScope(TransactionScopeOption.Required))
                {
                    cache.AddBlock(block);
                    Assert.That(cache.FindBlockByHash(block.Hash), Is.Not.Null);
                    Assert.That(cache.FindSubchain(block.Hash, 10), Is.Not.Null);
                }

                Assert.That(cache.FindBlockByHash(block.Hash), Is.Null);
                Assert.That(cache.FindSubchain(block.Hash, 10), Is.Null);

                using (var tx = new TransactionScope(TransactionScopeOption.Required))
                {
                    cache.AddBlock(block);
                    tx.Complete();
                }

                Assert.That(cache.FindBlockByHash(block.Hash), Is.Not.Null);
                Assert.That(cache.FindSubchain(block.Hash, 10), Is.Not.Null);

                blockBuilder = new StoredBlockBuilder(block);
                blockBuilder.IsInBestBlockChain = true;
                block = blockBuilder.Build();

                Assert.Throws<InvalidOperationException>(() => cache.UpdateBlock(block));

                using (new TransactionScope(TransactionScopeOption.Required))
                {
                    cache.UpdateBlock(block);
                    Assert.True(cache.FindBlockByHash(block.Hash).IsInBestBlockChain);
                    Assert.True(cache.FindSubchain(block.Hash, 10).GetBlockByOffset(0).IsInBestBlockChain);
                }

                Assert.False(cache.FindBlockByHash(block.Hash).IsInBestBlockChain);
                Assert.False(cache.FindSubchain(block.Hash, 10).GetBlockByOffset(0).IsInBestBlockChain);

                using (var tx = new TransactionScope(TransactionScopeOption.Required))
                {
                    cache.UpdateBlock(block);
                    tx.Complete();
                }

                Assert.True(cache.FindBlockByHash(block.Hash).IsInBestBlockChain);
                Assert.True(cache.FindSubchain(block.Hash, 10).GetBlockByOffset(0).IsInBestBlockChain);

                Assert.Throws<InvalidOperationException>(() => cache.AddBlockContent(block.Hash, GenesisBlock.Raw));

                using (new TransactionScope(TransactionScopeOption.Required))
                {
                    cache.AddBlockContent(block.Hash, GenesisBlock.Raw);
                    Assert.True(cache.FindBlockByHash(block.Hash).HasContent);
                    Assert.True(cache.FindSubchain(block.Hash, 10).GetBlockByOffset(0).HasContent);
                }

                Assert.False(cache.FindBlockByHash(block.Hash).HasContent);
                Assert.False(cache.FindSubchain(block.Hash, 10).GetBlockByOffset(0).HasContent);

                using (var tx = new TransactionScope(TransactionScopeOption.Required))
                {
                    cache.AddBlockContent(block.Hash, GenesisBlock.Raw);
                    tx.Complete();
                }

                Assert.True(cache.FindBlockByHash(block.Hash).HasContent);
                Assert.True(cache.FindSubchain(block.Hash, 10).GetBlockByOffset(0).HasContent);
            }
        }
        /// <summary>
        /// Returns a copy of this block with the <see cref="Height"/> and <see cref="TotalWork"/> properties based on the given parent block.
        /// </summary>
        /// <param name="parentBlock">The parent block.</param>
        public StoredBlock AppendAfter(StoredBlock parentBlock)
        {
            double blockWork = DifficultyUtils.DifficultyTargetToWork(DifficultyUtils.NBitsToTarget(Header.NBits));

            StoredBlockBuilder builder = new StoredBlockBuilder(this);
            builder.Height = parentBlock.Height + 1;
            builder.TotalWork = parentBlock.TotalWork + blockWork;

            return builder.Build();
        }
 /// <summary>
 /// Returns a copy of this block with the <see cref="HasContent"/> flag set. 
 /// </summary>
 /// <returns></returns>
 public StoredBlock AddContent()
 {
     StoredBlockBuilder builder = new StoredBlockBuilder(this);
     builder.HasContent = true;
     return builder.Build();
 }