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()); } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }); }
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); }
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()); } }
/// <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); }
/// <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(); } }
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); } }
/// <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); }
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); }
/// <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); }
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); } } } }
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(); } } }
/// <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); }
/// <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); }
/// <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); }
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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }