コード例 #1
0
        public async Task PutAsync_Inserts_MultipleProvenBlockHeadersAsync()
        {
            string folder = CreateTestDir(this);

            PosBlock          posBlock = CreatePosBlockMock();
            ProvenBlockHeader header1  = CreateNewProvenBlockHeaderMock(posBlock);
            ProvenBlockHeader header2  = CreateNewProvenBlockHeaderMock(posBlock);

            var items = new List <ProvenBlockHeader> {
                header1, header2
            };

            // Put the items in the repository.
            using (IProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                await repo.PutAsync(items, new HashHeightPair(header2.GetHash(), items.Count - 1));
            }

            // Check the ProvenBlockHeader exists in the database.
            using (var engine = new DBreezeEngine(folder))
            {
                DBreeze.Transactions.Transaction txn = engine.GetTransaction();
                txn.SynchronizeTables(ProvenBlockHeaderTable);
                txn.ValuesLazyLoadingIsOn = false;

                var headersOut = txn.SelectDictionary <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable);

                headersOut.Keys.Count.Should().Be(2);
                headersOut.First().Value.GetHash().Should().Be(items[0].GetHash());
                headersOut.Last().Value.GetHash().Should().Be(items[1].GetHash());
            }
        }
コード例 #2
0
        /// <summary>
        /// Retrieves POS blocks information from the database.
        /// </summary>
        /// <param name="blocklist">List of partially initialized POS block information that is to be fully initialized with the values from the database.</param>
        public Task GetStakeAsync(IEnumerable <StakeItem> blocklist)
        {
            Task task = Task.Run(() =>
            {
                using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
                {
                    transaction.SynchronizeTables("Stake");
                    transaction.ValuesLazyLoadingIsOn = false;

                    foreach (StakeItem blockStake in blocklist)
                    {
                        this.logger.LogTrace("Loading POS block hash '{0}' from the database.", blockStake.BlockId);
                        Row <byte[], byte[]> stakeRow = transaction.Select <byte[], byte[]>("Stake", blockStake.BlockId.ToBytes(false));

                        if (stakeRow.Exists)
                        {
                            blockStake.BlockStake = this.dBreezeSerializer.Deserialize <BlockStake>(stakeRow.Value);
                            blockStake.InStore    = true;
                        }
                    }
                }
            });

            return(task);
        }
コード例 #3
0
        /// <inheritdoc />
        public FetchCoinsResponse FetchCoins(uint256[] txIds, CancellationToken cancellationToken = default(CancellationToken))
        {
            FetchCoinsResponse res = null;

            using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
            {
                transaction.SynchronizeTables("BlockHash", "Coins");
                transaction.ValuesLazyLoadingIsOn = false;

                using (new StopwatchDisposable(o => this.performanceCounter.AddQueryTime(o)))
                {
                    uint256 blockHash = this.GetTipHash(transaction);
                    var     result    = new UnspentOutputs[txIds.Length];
                    this.performanceCounter.AddQueriedEntities(txIds.Length);

                    int i = 0;
                    foreach (uint256 input in txIds)
                    {
                        Row <byte[], byte[]> row     = transaction.Select <byte[], byte[]>("Coins", input.ToBytes(false));
                        UnspentOutputs       outputs = row.Exists ? new UnspentOutputs(input, this.dBreezeSerializer.Deserialize <Coins>(row.Value)) : null;

                        this.logger.LogDebug("Outputs for '{0}' were {1}.", input, outputs == null ? "NOT loaded" : "loaded");

                        result[i++] = outputs;
                    }

                    res = new FetchCoinsResponse(result, blockHash);
                }
            }

            return(res);
        }
コード例 #4
0
        /// <inheritdoc />
        public Task PutAsync(List <ProvenBlockHeader> headers, HashHeightPair newTip)
        {
            Guard.NotNull(headers, nameof(headers));
            Guard.NotNull(newTip, nameof(newTip));

            Guard.Assert(newTip.Hash == headers.Last().GetHash());

            if ((this.provenBlockHeaderTip != null) && (newTip.Hash == this.provenBlockHeaderTip.GetHash()))
            {
                this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]");

                throw new ProvenBlockHeaderException("Invalid new tip hash, tip hash has not changed.");
            }

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("({0}.Count():{1})", nameof(headers), headers.Count());

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables(BlockHashHeightTable, ProvenBlockHeaderTable);

                    this.InsertHeaders(transaction, headers);

                    this.SetTip(transaction, newTip);

                    transaction.Commit();

                    this.TipHashHeight = newTip;
                }
            });

            return(task);
        }
コード例 #5
0
        /// <summary>
        /// Retrieves POS blocks information from the database.
        /// </summary>
        /// <param name="blocklist">List of partially initialized POS block information that is to be fully initialized with the values from the database.</param>
        public Task GetStakeAsync(IEnumerable <StakeItem> blocklist)
        {
            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("({0}.Count():{1})", nameof(blocklist), blocklist.Count());

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables("Stake");
                    transaction.ValuesLazyLoadingIsOn = false;

                    foreach (StakeItem blockStake in blocklist)
                    {
                        this.logger.LogTrace("Loading POS block hash '{0}' from the database.", blockStake.BlockId);
                        Row <byte[], BlockStake> stakeRow = transaction.Select <byte[], BlockStake>("Stake", blockStake.BlockId.ToBytes(false));

                        if (stakeRow.Exists)
                        {
                            blockStake.BlockStake = stakeRow.Value;
                            blockStake.InStore    = true;
                        }
                    }

                    this.logger.LogTrace("(-)");
                }
            });

            return(task);
        }
コード例 #6
0
        /// <inheritdoc />
        public Task PutAsync(SortedDictionary <int, ProvenBlockHeader> headers, HashHeightPair newTip)
        {
            Guard.NotNull(headers, nameof(headers));
            Guard.NotNull(newTip, nameof(newTip));

            Guard.Assert(newTip.Hash == headers.Values.Last().GetHash());

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("({0}.Count():{1})", nameof(headers), headers.Count());

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables(BlockHashHeightTable, ProvenBlockHeaderTable);

                    this.InsertHeaders(transaction, headers);

                    this.SetTip(transaction, newTip);

                    transaction.Commit();

                    this.TipHashHeight = newTip;
                }
            });

            return(task);
        }
コード例 #7
0
        /// <summary>
        /// Initializes the database tables used by the coinview.
        /// </summary>
        public Task InitializeAsync()
        {
            this.logger.LogTrace("()");

            Block genesis = this.network.GetGenesis();

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("()");

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash");

                    if (this.GetCurrentHash(transaction) == null)
                    {
                        this.SetBlockHash(transaction, genesis.GetHash());

                        // Genesis coin is unspendable so do not add the coins.
                        transaction.Commit();
                    }
                }

                this.logger.LogTrace("(-)");
            });

            this.logger.LogTrace("(-)");
            return(task);
        }
コード例 #8
0
        /// <summary>
        /// Compacts the block and transaction database by recreating the tables without the deleted references.
        /// </summary>
        private void CompactDataBase()
        {
            Task task = Task.Run(() =>
            {
                using (DBreeze.Transactions.Transaction dbreezeTransaction = this.blockRepository.DBreeze.GetTransaction())
                {
                    dbreezeTransaction.SynchronizeTables(BlockRepository.BlockTableName, BlockRepository.TransactionTableName);

                    var tempBlocks = dbreezeTransaction.SelectDictionary <byte[], byte[]>(BlockRepository.BlockTableName);

                    if (tempBlocks.Count != 0)
                    {
                        this.logger.LogInformation($"{tempBlocks.Count} blocks will be copied to the pruned table.");

                        dbreezeTransaction.RemoveAllKeys(BlockRepository.BlockTableName, true);
                        dbreezeTransaction.InsertDictionary(BlockRepository.BlockTableName, tempBlocks, false);

                        var tempTransactions = dbreezeTransaction.SelectDictionary <byte[], byte[]>(BlockRepository.TransactionTableName);
                        if (tempTransactions.Count != 0)
                        {
                            this.logger.LogInformation($"{tempTransactions.Count} transactions will be copied to the pruned table.");
                            dbreezeTransaction.RemoveAllKeys(BlockRepository.TransactionTableName, true);
                            dbreezeTransaction.InsertDictionary(BlockRepository.TransactionTableName, tempTransactions, false);
                        }

                        // Save the hash and height of where the node was pruned up to.
                        dbreezeTransaction.Insert(BlockRepository.CommonTableName, prunedTipKey, this.dBreezeSerializer.Serialize(this.PrunedTip));
                    }

                    dbreezeTransaction.Commit();
                }

                return(Task.CompletedTask);
            });
        }
コード例 #9
0
        public FetchCoinsResponse FetchCoins(OutPoint[] utxos)
        {
            FetchCoinsResponse res = new FetchCoinsResponse();

            using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
            {
                transaction.SynchronizeTables("BlockHash", "Coins");
                transaction.ValuesLazyLoadingIsOn = false;

                using (new StopwatchDisposable(o => this.performanceCounter.AddQueryTime(o)))
                {
                    this.performanceCounter.AddQueriedEntities(utxos.Length);

                    foreach (OutPoint outPoint in utxos)
                    {
                        Row <byte[], byte[]> row = transaction.Select <byte[], byte[]>("Coins", outPoint.ToBytes());
                        Coins outputs            = row.Exists ? this.dBreezeSerializer.Deserialize <Utilities.Coins>(row.Value) : null;

                        this.logger.LogTrace("Outputs for '{0}' were {1}.", outPoint, outputs == null ? "NOT loaded" : "loaded");

                        res.UnspentOutputs.Add(outPoint, new UnspentOutput(outPoint, outputs));
                    }
                }
            }

            return(res);
        }
コード例 #10
0
        public async Task PutAsync_WritesProvenBlockHeaderAndSavesBlockHashAsync()
        {
            string folder = CreateTestDir(this);

            ProvenBlockHeader provenBlockHeaderIn = CreateNewProvenBlockHeaderMock();

            var blockHashHieghtPair = new HashHeightPair(provenBlockHeaderIn.GetHash(), 0);
            var items = new List <ProvenBlockHeader> {
                provenBlockHeaderIn
            };

            using (IProvenBlockHeaderRepository repo = this.SetupRepository(this.Network, folder))
            {
                await repo.PutAsync(items, blockHashHieghtPair);
            }

            using (var engine = new DBreezeEngine(folder))
            {
                DBreeze.Transactions.Transaction txn = engine.GetTransaction();
                txn.SynchronizeTables(ProvenBlockHeaderTable);
                txn.ValuesLazyLoadingIsOn = false;

                var headerOut         = txn.Select <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable, blockHashHieghtPair.Height.ToBytes(false)).Value;
                var hashHeightPairOut = txn.Select <byte[], HashHeightPair>(BlockHashTable, new byte[0].ToBytes()).Value;

                headerOut.Should().NotBeNull();
                headerOut.GetHash().Should().Be(provenBlockHeaderIn.GetHash());

                hashHeightPairOut.Should().NotBeNull();
                hashHeightPairOut.Hash.Should().Be(provenBlockHeaderIn.GetHash());
            }
        }
コード例 #11
0
        /// <summary>
        /// Initializes the database tables used by the coinview.
        /// </summary>
        public Task InitializeAsync()
        {
            this.logger.LogTrace("()");

            Block genesis = this.network.GetGenesis();

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("()");
                uint256 currentHash;
                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash");

                    currentHash = this.GetCurrentHash(transaction);
                    if (currentHash == null)
                    {
                        this.SetBlockHash(transaction, genesis.GetHash(this.network.NetworkOptions));

                        // Genesis coin is unspendable so do not add the coins.
                        transaction.Commit();
                    }
                }

                ////////Neo **
                if (currentHash == null)
                {
                    var genesisChainedBlock = new ChainedBlock(genesis.Header, this.network.GenesisHash, 0);
                    var chained             = this.MakeNext(genesisChainedBlock, this.network);
                    int length             = genesis.Transactions.Count;
                    UnspentOutputs[] utxos = new UnspentOutputs[length];
                    for (int i = 0; i < length; i++)
                    {
                        utxos[i] = new UnspentOutputs(genesis.Transactions[i].GetHash(), new Coins(genesis.Transactions[i], 0));
                    }
                    //uint256 txId = genesis.Transactions[0].GetHash();
                    //uint256 txId2 = genesis.Transactions[1].GetHash();
                    //Coins coins = new Coins(genesis.Transactions[0], 0);
                    //Coins coins2 = new Coins(genesis.Transactions[1], 0);
                    //UnspentOutputs[] utxos = new UnspentOutputs[2];
                    //utxos[0] = new UnspentOutputs(txId, coins);
                    //utxos[1] = new UnspentOutputs(txId2, coins2);

                    this.SaveChangesAsync(utxos, null, genesisChainedBlock.HashBlock, chained.HashBlock).Wait();
                }
                //this.SaveChangesAsync(new UnspentOutputs[] { new UnspentOutputs(genesis.Transactions[0].GetHash(), new Coins(genesis.Transactions[0], 0)) }, null, genesisChainedBlock.HashBlock, chained.HashBlock).Wait();
                //this.SaveChangesAsync(new UnspentOutputs[] { new UnspentOutputs(genesis.Transactions[1].GetHash(), new Coins(genesis.Transactions[1], 0)) }, null, genesisChainedBlock.HashBlock, chained.HashBlock).Wait();
                //Assert.NotNull(ctx.PersistentCoinView.FetchCoinsAsync(new[] { genesis.Transactions[0].GetHash() }).Result.UnspentOutputs[0]);
                ///////////////////////////////



                this.logger.LogTrace("(-)");
            });

            this.logger.LogTrace("(-)");
            return(task);
        }
コード例 #12
0
 /// <summary>
 /// Persists unsaved POS blocks information to the database.
 /// </summary>
 /// <param name="stakeEntries">List of POS block information to be examined and persists if unsaved.</param>
 public void PutStake(IEnumerable <StakeItem> stakeEntries)
 {
     using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
     {
         transaction.SynchronizeTables("Stake");
         this.PutStakeInternal(transaction, stakeEntries);
         transaction.Commit();
     }
 }
コード例 #13
0
 public RewindData GetRewindData(int height)
 {
     using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
     {
         transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
         Row <int, byte[]> row = transaction.Select <int, byte[]>("Rewind", height);
         return(row.Exists ? this.dBreezeSerializer.Deserialize <RewindData>(row.Value) : null);
     }
 }
コード例 #14
0
        /// <summary>
        /// Persists unsaved POS blocks information to the database.
        /// </summary>
        /// <param name="stakeEntries">List of POS block information to be examined and persists if unsaved.</param>
        public Task PutStakeAsync(IEnumerable <StakeItem> stakeEntries)
        {
            Task task = Task.Run(() =>
            {
                using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
                {
                    transaction.SynchronizeTables("Stake");
                    this.PutStakeInternal(transaction, stakeEntries);
                    transaction.Commit();
                }
            });

            return(task);
        }
コード例 #15
0
        public Task <RewindData> GetRewindData(int height)
        {
            Task <RewindData> task = Task.Run(() =>
            {
                using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
                {
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
                    Row <int, RewindData> row = transaction.Select <int, RewindData>("Rewind", height);
                    return(row.Exists ? row.Value : null);
                }
            });

            return(task);
        }
コード例 #16
0
        /// <inheritdoc />
        public override Task <uint256> Rewind()
        {
            Task <uint256> task = Task.Run(() =>
            {
                this.logger.LogTrace("()");

                uint256 res = null;
                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
                    if (this.GetRewindIndex(transaction) == -1)
                    {
                        transaction.RemoveAllKeys("Coins", true);
                        this.SetBlockHash(transaction, this.network.GenesisHash);

                        res = this.network.GenesisHash;
                    }
                    else
                    {
                        transaction.ValuesLazyLoadingIsOn = false;

                        Row <int, RewindData> firstRow = transaction.SelectBackward <int, RewindData>("Rewind").FirstOrDefault();
                        transaction.RemoveKey("Rewind", firstRow.Key);
                        this.SetBlockHash(transaction, firstRow.Value.PreviousBlockHash);

                        foreach (uint256 txId in firstRow.Value.TransactionsToRemove)
                        {
                            this.logger.LogTrace("Outputs of transaction ID '{0}' will be removed.", txId);
                            transaction.RemoveKey("Coins", txId.ToBytes(false));
                        }

                        foreach (UnspentOutputs coin in firstRow.Value.OutputsToRestore)
                        {
                            this.logger.LogTrace("Outputs of transaction ID '{0}' will be restored.", coin.TransactionId);
                            transaction.Insert("Coins", coin.TransactionId.ToBytes(false), coin.ToCoins());
                        }

                        res = firstRow.Value.PreviousBlockHash;
                    }

                    transaction.Commit();
                }

                this.logger.LogTrace("(-):'{0}'", res);
                return(res);
            });

            return(task);
        }
コード例 #17
0
        public IndexRepository(Network network, string folder, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory, Dictionary <string, IndexExpression> requiredIndexes = null) :
            base(network, folder, dateTimeProvider, loggerFactory)
        {
            Guard.NotNull(network, nameof(network));
            Guard.NotEmpty(folder, nameof(folder));
            Guard.NotNull(dateTimeProvider, nameof(dateTimeProvider));
            Guard.NotNull(loggerFactory, nameof(loggerFactory));

            this.tableNames = new HashSet <string> {
                "Block", "Transaction", "Common"
            };
            this.Indexes         = new ConcurrentDictionary <string, Index>();
            this.requiredIndexes = requiredIndexes;

            using (DBreeze.Transactions.Transaction transaction = this.DBreeze.GetTransaction())
            {
                transaction.SynchronizeTables("Block", "Common");
                transaction.ValuesLazyLoadingIsOn = false;

                // Discover and add indexes to dictionary and tables to synchronize.
                foreach (Row <string, string> row in transaction.SelectForwardStartsWith <string, string>("Common", IndexTablePrefix))
                {
                    if (!row.Exists)
                    {
                        continue;
                    }

                    string name  = row.Key.Substring(IndexTablePrefix.Length);
                    Index  index = Index.Parse(this, row.Value, row.Key);
                    if (index.compiled != null)
                    {
                        this.Indexes.AddOrReplace(name, index);
                        if (!this.tableNames.Contains(row.Key))
                        {
                            this.tableNames.Add(row.Key);
                        }
                    }
                }

                // Remove any index tables that are not being used (not transactional).
                foreach (string indexTable in this.GetIndexTables())
                {
                    if (!transaction.Select <string, string>("Common", indexTable).Exists)
                    {
                        this.DeleteIndexTable(indexTable);
                    }
                }
            }
        }
コード例 #18
0
        public void Initialize()
        {
            Block genesis = this.network.GetGenesis();

            using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
            {
                transaction.ValuesLazyLoadingIsOn = false;
                transaction.SynchronizeTables("BlockHash");

                if (this.GetTipHash(transaction) == null)
                {
                    this.SetBlockHash(transaction, new HashHeightPair(genesis.GetHash(), 0));

                    // Genesis coin is unspendable so do not add the coins.
                    transaction.Commit();
                }
            }
        }
コード例 #19
0
        /// <summary>
        /// Persists unsaved POS blocks information to the database.
        /// </summary>
        /// <param name="stakeEntries">List of POS block information to be examined and persists if unsaved.</param>
        public Task PutStakeAsync(IEnumerable <StakeItem> stakeEntries)
        {
            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("({0}.Count():{1})", nameof(stakeEntries), stakeEntries.Count());

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables("Stake");
                    this.PutStakeInternal(transaction, stakeEntries);
                    transaction.Commit();
                }

                this.logger.LogTrace("(-)");
            });

            return(task);
        }
コード例 #20
0
        /// <inheritdoc />
        public HashHeightPair Rewind()
        {
            HashHeightPair res = null;

            using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
            {
                transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");

                transaction.ValuesLazyLoadingIsOn = false;

                HashHeightPair current = this.GetTipHash(transaction);

                Row <int, byte[]> row = transaction.Select <int, byte[]>("Rewind", current.Height);

                if (!row.Exists)
                {
                    throw new InvalidOperationException($"No rewind data found for block `{current}`");
                }

                transaction.RemoveKey("Rewind", row.Key);

                var rewindData = this.dBreezeSerializer.Deserialize <RewindData>(row.Value);

                this.SetBlockHash(transaction, rewindData.PreviousBlockHash);

                foreach (OutPoint outPoint in rewindData.OutputsToRemove)
                {
                    this.logger.LogTrace("Outputs of outpoint '{0}' will be removed.", outPoint);
                    transaction.RemoveKey("Coins", outPoint.ToBytes());
                }

                foreach (RewindDataOutput rewindDataOutput in rewindData.OutputsToRestore)
                {
                    this.logger.LogTrace("Outputs of outpoint '{0}' will be restored.", rewindDataOutput.OutPoint);
                    transaction.Insert("Coins", rewindDataOutput.OutPoint.ToBytes(), this.dBreezeSerializer.Serialize(rewindDataOutput.Coins));
                }

                res = rewindData.PreviousBlockHash;

                transaction.Commit();
            }

            return(res);
        }
コード例 #21
0
        /// <inheritdoc />
        public uint256 Rewind()
        {
            uint256 res = null;

            using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
            {
                transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
                if (this.GetRewindIndex(transaction) == 0)
                {
                    transaction.RemoveAllKeys("Coins", true);
                    this.SetBlockHash(transaction, this.network.GenesisHash);

                    res = this.network.GenesisHash;
                }
                else
                {
                    transaction.ValuesLazyLoadingIsOn = false;

                    Row <int, byte[]> firstRow = transaction.SelectBackward <int, byte[]>("Rewind").FirstOrDefault();
                    transaction.RemoveKey("Rewind", firstRow.Key);
                    var rewindData = this.dBreezeSerializer.Deserialize <RewindData>(firstRow.Value);
                    this.SetBlockHash(transaction, rewindData.PreviousBlockHash);

                    foreach (uint256 txId in rewindData.TransactionsToRemove)
                    {
                        this.logger.LogDebug("Outputs of transaction ID '{0}' will be removed.", txId);
                        transaction.RemoveKey("Coins", txId.ToBytes(false));
                    }

                    foreach (UnspentOutputs coin in rewindData.OutputsToRestore)
                    {
                        this.logger.LogDebug("Outputs of transaction ID '{0}' will be restored.", coin.TransactionId);
                        transaction.Insert("Coins", coin.TransactionId.ToBytes(false), this.dBreezeSerializer.Serialize(coin.ToCoins()));
                    }

                    res = rewindData.PreviousBlockHash;
                }

                transaction.Commit();
            }

            return(res);
        }
コード例 #22
0
        public override Task InitializeAsync()
        {
            Task task = base.InitializeAsync().ContinueWith((o) =>
            {
                using (DBreeze.Transactions.Transaction transaction = this.DBreeze.GetTransaction())
                {
                    transaction.SynchronizeTables("Block", "Common");

                    // Ensure this is set so that the base code calls us to index blocks.
                    SaveTxIndex(transaction, true);

                    // Clean-up any invalid indexes.
                    foreach (Row <string, string> row in transaction.SelectForwardStartsWith <string, string>("Common", IndexTablePrefix))
                    {
                        string name = row.Key.Substring(IndexTablePrefix.Length);
                        if (!this.Indexes.ContainsKey(name))
                        {
                            DropIndex(name, transaction);
                        }
                    }

                    // Create configured indexes that do not exist yet.
                    transaction.ValuesLazyLoadingIsOn = false;
                    if (this.requiredIndexes != null)
                    {
                        foreach (KeyValuePair <string, IndexExpression> kv in this.requiredIndexes)
                        {
                            if (!this.Indexes.ContainsKey(kv.Key))
                            {
                                IndexExpression index = kv.Value;
                                this.CreateIndex(transaction, kv.Key, index.Many, index.Builder, index.Dependencies);
                            }
                        }
                    }

                    // One commit per transaction.
                    transaction.Commit();
                }
            });

            return(task);
        }
コード例 #23
0
        /// <inheritdoc />
        public Task <FetchCoinsResponse> FetchCoinsAsync(uint256[] txIds, CancellationToken cancellationToken = default(CancellationToken))
        {
            Task <FetchCoinsResponse> task = Task.Run(() =>
            {
                this.logger.LogTrace("({0}.{1}:{2})", nameof(txIds), nameof(txIds.Length), txIds?.Length);

                FetchCoinsResponse res = null;
                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables("BlockHash", "Coins");
                    transaction.ValuesLazyLoadingIsOn = false;

                    using (new StopwatchDisposable(o => this.PerformanceCounter.AddQueryTime(o)))
                    {
                        uint256 blockHash = this.GetTipHash(transaction);
                        var result        = new UnspentOutputs[txIds.Length];
                        this.PerformanceCounter.AddQueriedEntities(txIds.Length);

                        int i = 0;
                        foreach (uint256 input in txIds)
                        {
                            Row <byte[], Coins> row = transaction.Select <byte[], Coins>("Coins", input.ToBytes(false));
                            UnspentOutputs outputs  = row.Exists ? new UnspentOutputs(input, row.Value) : null;

                            this.logger.LogTrace("Outputs for '{0}' were {1}.", input, outputs == null ? "NOT loaded" : "loaded");

                            result[i++] = outputs;
                        }

                        res = new FetchCoinsResponse(result, blockHash);
                    }
                }

                this.logger.LogTrace("(-):*.{0}='{1}',*.{2}.{3}={4}", nameof(res.BlockHash), res.BlockHash, nameof(res.UnspentOutputs), nameof(res.UnspentOutputs.Length), res.UnspentOutputs.Length);
                return(res);
            }, cancellationToken);

            return(task);
        }
コード例 #24
0
        /// <inheritdoc />
        public Task <ProvenBlockHeader> GetAsync(int blockHeight)
        {
            Task <ProvenBlockHeader> task = Task.Run(() =>
            {
                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables(ProvenBlockHeaderTable);

                    transaction.ValuesLazyLoadingIsOn = false;

                    Row <byte[], byte[]> row = transaction.Select <byte[], byte[]>(ProvenBlockHeaderTable, blockHeight.ToBytes());

                    if (row.Exists)
                    {
                        return(this.dBreezeSerializer.Deserialize <ProvenBlockHeader>(row.Value));
                    }

                    return(null);
                }
            });

            return(task);
        }
コード例 #25
0
        /// <inheritdoc />
        public Task <List <ProvenBlockHeader> > GetAsync(int fromBlockHeight, int toBlockHeight)
        {
            Task <List <ProvenBlockHeader> > task = Task.Run(() =>
            {
                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.SynchronizeTables(ProvenBlockHeaderTable);

                    transaction.ValuesLazyLoadingIsOn = false;

                    this.logger.LogTrace("Loading ProvenBlockHeaders from block height {0} to {1} from the database.",
                                         fromBlockHeight, toBlockHeight);

                    var headers = new List <ProvenBlockHeader>();

                    for (int i = fromBlockHeight; i <= toBlockHeight; i++)
                    {
                        Row <byte[], ProvenBlockHeader> row =
                            transaction.Select <byte[], ProvenBlockHeader>(ProvenBlockHeaderTable, i.ToBytes(false));

                        if (row.Exists)
                        {
                            headers.Add(row.Value);
                        }
                        else
                        {
                            this.logger.LogDebug("ProvenBlockHeader height {0} does not exist in the database.", i);
                            headers.Add(null);
                        }
                    }

                    return(headers);
                }
            });

            return(task);
        }
コード例 #26
0
        /// <inheritdoc />
        public Task SaveChangesAsync(IList <UnspentOutputs> unspentOutputs, IEnumerable <TxOut[]> originalOutputs, uint256 oldBlockHash, uint256 nextBlockHash, int height, List <RewindData> rewindDataList = null)
        {
            Task task = Task.Run(() =>
            {
                int insertedEntities = 0;

                using (DBreeze.Transactions.Transaction transaction = this.CreateTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");

                    // Speed can degrade when keys are in random order and, especially, if these keys have high entropy.
                    // This settings helps with speed, see dBreeze documentations about details.
                    // We should double check if this settings help in our scenario, or sorting keys and operations is enough.
                    // Refers to issue #2483. https://github.com/stratisproject/StratisBitcoinFullNode/issues/2483
                    transaction.Technical_SetTable_OverwriteIsNotAllowed("Coins");

                    using (new StopwatchDisposable(o => this.performanceCounter.AddInsertTime(o)))
                    {
                        uint256 current = this.GetTipHash(transaction);
                        if (current != oldBlockHash)
                        {
                            this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]");
                            throw new InvalidOperationException("Invalid oldBlockHash");
                        }

                        this.SetBlockHash(transaction, nextBlockHash);

                        // Here we'll add items to be inserted in a second pass.
                        List <UnspentOutputs> toInsert = new List <UnspentOutputs>();

                        foreach (var coin in unspentOutputs.OrderBy(utxo => utxo.TransactionId, new UInt256Comparer()))
                        {
                            if (coin.IsPrunable)
                            {
                                this.logger.LogTrace("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.TransactionId);
                                transaction.RemoveKey("Coins", coin.TransactionId.ToBytes(false));
                            }
                            else
                            {
                                // Add the item to another list that will be used in the second pass.
                                // This is for performance reasons: dBreeze is optimized to run the same kind of operations, sorted.
                                toInsert.Add(coin);
                            }
                        }

                        for (int i = 0; i < toInsert.Count; i++)
                        {
                            var coin = toInsert[i];
                            this.logger.LogTrace("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.TransactionId, i, toInsert.Count);

                            transaction.Insert("Coins", coin.TransactionId.ToBytes(false), coin.ToCoins());
                        }

                        if (rewindDataList != null)
                        {
                            int nextRewindIndex = this.GetRewindIndex(transaction) + 1;
                            foreach (RewindData rewindData in rewindDataList)
                            {
                                this.logger.LogTrace("Rewind state #{0} created.", nextRewindIndex);

                                transaction.Insert("Rewind", nextRewindIndex, rewindData);
                                nextRewindIndex++;
                            }
                        }

                        insertedEntities += unspentOutputs.Count;
                        transaction.Commit();
                    }
                }

                this.performanceCounter.AddInsertedEntities(insertedEntities);
            });

            return(task);
        }
コード例 #27
0
        /// <inheritdoc />
        public Task SaveChangesAsync(IEnumerable <UnspentOutputs> unspentOutputs, IEnumerable <TxOut[]> originalOutputs, uint256 oldBlockHash, uint256 nextBlockHash, List <RewindData> rewindDataList = null)
        {
            List <UnspentOutputs> all = unspentOutputs.ToList();

            this.logger.LogTrace("({0}.Count():{1},{2}:'{3}',{4}:'{5}')", nameof(unspentOutputs), all.Count, nameof(oldBlockHash), oldBlockHash, nameof(nextBlockHash), nextBlockHash);

            int insertedEntities = 0;

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("()");

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
                    //transaction.Technical_SetTable_OverwriteIsNotAllowed("Coins"); // Why it was there and what is it for? No one knows.

                    using (new StopwatchDisposable(o => this.PerformanceCounter.AddInsertTime(o)))
                    {
                        uint256 current = this.GetTipHash(transaction);
                        if (current != oldBlockHash)
                        {
                            this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]");
                            throw new InvalidOperationException("Invalid oldBlockHash");
                        }

                        this.SetBlockHash(transaction, nextBlockHash);

                        all.Sort(UnspentOutputsComparer.Instance);
                        foreach (UnspentOutputs coin in all)
                        {
                            this.logger.LogTrace("Outputs of transaction ID '{0}' are {1} and will be {2} to the database.", coin.TransactionId, coin.IsPrunable ? "PRUNABLE" : "NOT PRUNABLE", coin.IsPrunable ? "removed" : "inserted");

                            if (coin.IsPrunable)
                            {
                                transaction.RemoveKey("Coins", coin.TransactionId.ToBytes(false));
                            }
                            else
                            {
                                transaction.Insert("Coins", coin.TransactionId.ToBytes(false), coin.ToCoins());
                            }
                        }

                        if (rewindDataList != null)
                        {
                            int nextRewindIndex = this.GetRewindIndex(transaction) + 1;
                            foreach (RewindData rewindData in rewindDataList)
                            {
                                this.logger.LogTrace("Rewind state #{0} created.", nextRewindIndex);

                                transaction.Insert("Rewind", nextRewindIndex, rewindData);
                                nextRewindIndex++;
                            }
                        }

                        insertedEntities += all.Count;
                        transaction.Commit();
                    }
                }

                this.PerformanceCounter.AddInsertedEntities(insertedEntities);
                this.logger.LogTrace("(-)");
            });

            this.logger.LogTrace("(-)");
            return(task);
        }
コード例 #28
0
        /// <inheritdoc />
        public override Task SaveChangesAsync(IEnumerable <UnspentOutputs> unspentOutputs, IEnumerable <TxOut[]> originalOutputs, uint256 oldBlockHash, uint256 nextBlockHash)
        {
            this.logger.LogTrace("({0}.Count():{1},{2}.Count():{3},{4}:'{5}',{6}:'{7}')", nameof(unspentOutputs), unspentOutputs?.Count(), nameof(originalOutputs), originalOutputs?.Count(), nameof(oldBlockHash), oldBlockHash, nameof(nextBlockHash), nextBlockHash);

            RewindData rewindData       = originalOutputs != null ? new RewindData(oldBlockHash) : null;
            int        insertedEntities = 0;

            List <UnspentOutputs>         all = unspentOutputs.ToList();
            Dictionary <uint256, TxOut[]> unspentToOriginal = new Dictionary <uint256, TxOut[]>(all.Count);

            using (new StopwatchDisposable(o => this.PerformanceCounter.AddInsertTime(o)))
            {
                if (originalOutputs != null)
                {
                    IEnumerator <TxOut[]> originalEnumerator = originalOutputs.GetEnumerator();
                    foreach (UnspentOutputs output in all)
                    {
                        originalEnumerator.MoveNext();
                        unspentToOriginal.Add(output.TransactionId, originalEnumerator.Current);
                    }
                }
            }

            Task task = Task.Run(() =>
            {
                this.logger.LogTrace("()");

                using (DBreeze.Transactions.Transaction transaction = this.dbreeze.GetTransaction())
                {
                    transaction.ValuesLazyLoadingIsOn = false;
                    transaction.SynchronizeTables("BlockHash", "Coins", "Rewind");
                    transaction.Technical_SetTable_OverwriteIsNotAllowed("Coins");

                    using (new StopwatchDisposable(o => this.PerformanceCounter.AddInsertTime(o)))
                    {
                        uint256 current = this.GetCurrentHash(transaction);
                        if (current != oldBlockHash)
                        {
                            this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]");
                            throw new InvalidOperationException("Invalid oldBlockHash");
                        }

                        this.SetBlockHash(transaction, nextBlockHash);

                        all.Sort(UnspentOutputsComparer.Instance);
                        foreach (UnspentOutputs coin in all)
                        {
                            this.logger.LogTrace("Outputs of transaction ID '{0}' are {1} and will be {2} to the database.", coin.TransactionId, coin.IsPrunable ? "PRUNABLE" : "NOT PRUNABLE", coin.IsPrunable ? "removed" : "inserted");
                            if (coin.IsPrunable)
                            {
                                transaction.RemoveKey("Coins", coin.TransactionId.ToBytes(false));
                            }
                            else
                            {
                                transaction.Insert("Coins", coin.TransactionId.ToBytes(false), coin.ToCoins());
                            }

                            if (originalOutputs != null)
                            {
                                TxOut[] original = null;
                                unspentToOriginal.TryGetValue(coin.TransactionId, out original);
                                if (original == null)
                                {
                                    // This one haven't existed before, if we rewind, delete it.
                                    rewindData.TransactionsToRemove.Add(coin.TransactionId);
                                }
                                else
                                {
                                    // We'll need to restore the original outputs.
                                    UnspentOutputs clone = coin.Clone();
                                    clone._Outputs       = original.ToArray();
                                    rewindData.OutputsToRestore.Add(clone);
                                }
                            }
                        }

                        if (rewindData != null)
                        {
                            int nextRewindIndex = this.GetRewindIndex(transaction) + 1;
                            this.logger.LogTrace("Rewind state #{0} created.", nextRewindIndex);
                            transaction.Insert("Rewind", nextRewindIndex, rewindData);
                        }

                        insertedEntities += all.Count;
                        transaction.Commit();
                    }
                }

                this.PerformanceCounter.AddInsertedEntities(insertedEntities);
                this.logger.LogTrace("(-)");
            });

            this.logger.LogTrace("(-)");
            return(task);
        }