diff --git a/src/Blockcore.sln b/src/Blockcore.sln index cf759ce0b..837fd9c39 100644 --- a/src/Blockcore.sln +++ b/src/Blockcore.sln @@ -74,6 +74,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Global", "Global", "{0C6FDC Directory.Build.props = Directory.Build.props None.ruleset = None.ruleset stylecop.json = stylecop.json + test.runsettings = test.runsettings EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.Bitcoin", "Networks\Bitcoin\Blockcore.Networks.Bitcoin\Blockcore.Networks.Bitcoin.csproj", "{7EE5025D-FA5E-484B-84C0-6B9C08D01CDD}" diff --git a/src/Blockcore/Base/BaseFeature.cs b/src/Blockcore/Base/BaseFeature.cs index 6fe917550..604b40414 100644 --- a/src/Blockcore/Base/BaseFeature.cs +++ b/src/Blockcore/Base/BaseFeature.cs @@ -397,7 +397,8 @@ public static IFullNodeBuilder UseBaseFeature(this IFullNodeBuilder fullNodeBuil services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + // services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Blockcore/Blockcore.csproj b/src/Blockcore/Blockcore.csproj index 6f301cadc..445b7cf5f 100644 --- a/src/Blockcore/Blockcore.csproj +++ b/src/Blockcore/Blockcore.csproj @@ -49,6 +49,8 @@ + + diff --git a/src/Blockcore/Consensus/Chain/RocksdbChainStore.cs b/src/Blockcore/Consensus/Chain/RocksdbChainStore.cs new file mode 100644 index 000000000..e778ab0b1 --- /dev/null +++ b/src/Blockcore/Consensus/Chain/RocksdbChainStore.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using Blockcore.Configuration; +using Blockcore.Utilities; +using RocksDbSharp; + +namespace NBitcoin +{ + public class RocksdbChainStore : IChainStore, IDisposable + { + private readonly Network network; + + internal static readonly byte ChainTableName = 1; + internal static readonly byte HeaderTableName = 2; + + /// + /// Headers that are close to the tip + /// + private readonly MemoryCountCache nearTipHeaders; + + /// + /// Headers that are close to the tip + /// + private readonly MemoryCountCache recentHeaders; + + private readonly RocksDb rocksdb; + + private object locker; + + public RocksdbChainStore(Network network, DataFolder dataFolder, ChainIndexer chainIndexer) + { + this.network = network; + this.ChainIndexer = chainIndexer; + // this.headers = new Dictionary(); + this.nearTipHeaders = new MemoryCountCache(601); + this.recentHeaders = new MemoryCountCache(100); + this.locker = new object(); + + // Open a connection to a new DB and create if not found + var options = new DbOptions().SetCreateIfMissing(true); + this.rocksdb = RocksDb.Open(options, dataFolder.ChainPath); + } + + public ChainIndexer ChainIndexer { get; } + + public BlockHeader GetHeader(ChainedHeader chainedHeader, uint256 hash) + { + if (this.nearTipHeaders.TryGetValue(hash, out BlockHeader blockHeader)) + { + return blockHeader; + } + + if (this.recentHeaders.TryGetValue(hash, out blockHeader)) + { + return blockHeader; + } + + ReadOnlySpan bytes = hash.ToReadOnlySpan(); + + lock (this.locker) + { + bytes = this.rocksdb.Get(DBH.Key(HeaderTableName, bytes)); + } + + if (bytes == null) + { + throw new ApplicationException("Header must exist if requested"); + } + + blockHeader = this.network.Consensus.ConsensusFactory.CreateBlockHeader(); + blockHeader.FromBytes(bytes.ToArray(), this.network.Consensus.ConsensusFactory); + + // If the header is 500 blocks behind tip or 100 blocks ahead cache it. + if ((chainedHeader.Height > this.ChainIndexer.Height - 500) && (chainedHeader.Height <= this.ChainIndexer.Height + 100)) + { + this.nearTipHeaders.AddOrUpdate(hash, blockHeader); + } + else + { + this.recentHeaders.AddOrUpdate(hash, blockHeader); + } + + return blockHeader; + } + + public bool PutHeader(BlockHeader blockHeader) + { + ConsensusFactory consensusFactory = this.network.Consensus.ConsensusFactory; + + lock (this.locker) + { + this.rocksdb.Put(DBH.Key(HeaderTableName, blockHeader.GetHash().ToReadOnlySpan()), blockHeader.ToBytes(consensusFactory)); + } + + return true; + } + + public ChainData GetChainData(int height) + { + byte[] bytes = null; + + lock (this.locker) + { + bytes = this.rocksdb.Get(DBH.Key(ChainTableName, BitConverter.GetBytes(height))); + } + + if (bytes == null) + { + return null; + } + + var data = new ChainData(); + data.FromBytes(bytes, this.network.Consensus.ConsensusFactory); + + return data; + } + + public void PutChainData(IEnumerable items) + { + using (var batch = new WriteBatch()) + { + foreach (var item in items) + { + batch.Put(DBH.Key(ChainTableName, BitConverter.GetBytes(item.Height)), item.Data.ToBytes(this.network.Consensus.ConsensusFactory)); + } + + lock (this.locker) + { + this.rocksdb.Write(batch); + } + } + } + + public void Dispose() + { + this.rocksdb?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Blockcore/Utilities/KeyValueRepository.cs b/src/Blockcore/Utilities/KeyValueRepository.cs index 8ad6ec77c..177a35f01 100644 --- a/src/Blockcore/Utilities/KeyValueRepository.cs +++ b/src/Blockcore/Utilities/KeyValueRepository.cs @@ -4,6 +4,7 @@ using Blockcore.Configuration; using Blockcore.Utilities.JsonConverters; using LevelDB; +using RocksDbSharp; namespace Blockcore.Utilities { @@ -32,7 +33,7 @@ public interface IKeyValueRepository : IDisposable public class KeyValueRepository : IKeyValueRepository { /// Access to database. - private readonly DB leveldb; + private readonly RocksDb rocksdb; private readonly DataStoreSerializer dataStoreSerializer; @@ -46,8 +47,8 @@ public KeyValueRepository(string folder, DataStoreSerializer dataStoreSerializer this.dataStoreSerializer = dataStoreSerializer; // Open a connection to a new DB and create if not found - var options = new Options { CreateIfMissing = true }; - this.leveldb = new DB(options, folder); + var options = new DbOptions().SetCreateIfMissing(true); + this.rocksdb = RocksDb.Open(options, folder); } /// @@ -55,7 +56,7 @@ public void SaveBytes(string key, byte[] bytes) { byte[] keyBytes = Encoding.ASCII.GetBytes(key); - this.leveldb.Put(keyBytes, bytes); + this.rocksdb.Put(keyBytes, bytes); } /// @@ -78,7 +79,7 @@ public byte[] LoadBytes(string key) { byte[] keyBytes = Encoding.ASCII.GetBytes(key); - byte[] row = this.leveldb.Get(keyBytes); + byte[] row = this.rocksdb.Get(keyBytes); if (row == null) return null; @@ -116,7 +117,7 @@ public T LoadValueJson(string key) /// public void Dispose() { - this.leveldb.Dispose(); + this.rocksdb.Dispose(); } } } \ No newline at end of file diff --git a/src/Blockcore/Utilities/LeveldbHelper.cs b/src/Blockcore/Utilities/LeveldbHelper.cs index 9ea12486b..56f39a07b 100644 --- a/src/Blockcore/Utilities/LeveldbHelper.cs +++ b/src/Blockcore/Utilities/LeveldbHelper.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using LevelDB; +using RocksDbSharp; namespace Blockcore.Utilities { @@ -35,5 +36,19 @@ public static Dictionary SelectDictionary(this DB db, byte table return dict; } + + public static Dictionary SelectDictionary(this RocksDb db, byte table) + { + var dict = new Dictionary(); + + var enumerator = db.NewIterator(); + for (enumerator.SeekToFirst(); enumerator.Valid(); enumerator.Next()) + { + if (enumerator.Key()[0] == table) + dict.Add(enumerator.Key().AsSpan().Slice(1).ToArray(), enumerator.Value()); + } + + return dict; + } } } \ No newline at end of file diff --git a/src/Features/Blockcore.Features.BlockStore/BlockRepository.cs b/src/Features/Blockcore.Features.BlockStore/BlockRepository.cs index df883194d..73c46aab3 100644 --- a/src/Features/Blockcore.Features.BlockStore/BlockRepository.cs +++ b/src/Features/Blockcore.Features.BlockStore/BlockRepository.cs @@ -7,7 +7,7 @@ using Blockcore.Interfaces; using Blockcore.Utilities; using DBreeze.Utils; -using LevelDB; +using RocksDbSharp; using Microsoft.Extensions.Logging; using NBitcoin; using System; @@ -20,7 +20,7 @@ namespace Blockcore.Features.BlockStore public interface IBlockRepository : IBlockStore { /// The dbreeze database engine. - DB Leveldb { get; } + RocksDb RocksDb { get; } /// Really ugly temporary hack. object Locker { get; } @@ -83,7 +83,7 @@ public class BlockRepository : IBlockRepository internal static readonly byte CommonTableName = 2; internal static readonly byte TransactionTableName = 3; - private readonly DB leveldb; + private readonly RocksDb rocksdb; public object Locker { get; } @@ -101,7 +101,7 @@ public class BlockRepository : IBlockRepository /// public bool TxIndex { get; private set; } - public DB Leveldb => this.leveldb; + public RocksDb RocksDb => this.rocksdb; private readonly DataStoreSerializer dataStoreSerializer; private readonly IReadOnlyDictionary genesisTransactions; @@ -118,8 +118,8 @@ public BlockRepository(Network network, string folder, ILoggerFactory loggerFact Guard.NotEmpty(folder, nameof(folder)); Directory.CreateDirectory(folder); - var options = new Options { CreateIfMissing = true }; - this.leveldb = new DB(options, folder); + var options = new DbOptions().SetCreateIfMissing(true); + this.rocksdb = RocksDb.Open(options, folder); this.Locker = new object(); this.logger = loggerFactory.CreateLogger(this.GetType().FullName); @@ -166,7 +166,7 @@ public Transaction GetTransactionById(uint256 trxid) Transaction res = null; lock (this.Locker) { - byte[] transactionRow = this.leveldb.Get(DBH.Key(TransactionTableName, trxid.ToBytes())); + byte[] transactionRow = this.rocksdb.Get(DBH.Key(TransactionTableName, trxid.ToBytes())); if (transactionRow == null) { @@ -174,7 +174,7 @@ public Transaction GetTransactionById(uint256 trxid) return null; } - byte[] blockRow = this.leveldb.Get(DBH.Key(BlockTableName, transactionRow)); + byte[] blockRow = this.rocksdb.Get(DBH.Key(BlockTableName, transactionRow)); if (blockRow != null) { @@ -219,14 +219,14 @@ public Transaction GetTransactionById(uint256 trxid) continue; } - byte[] transactionRow = this.leveldb.Get(DBH.Key(TransactionTableName, trxids[i].ToBytes())); + byte[] transactionRow = this.rocksdb.Get(DBH.Key(TransactionTableName, trxids[i].ToBytes())); if (transactionRow == null) { this.logger.LogTrace("(-)[NO_TX_ROW]:null"); return null; } - byte[] blockRow = this.leveldb.Get(DBH.Key(BlockTableName, transactionRow)); + byte[] blockRow = this.rocksdb.Get(DBH.Key(BlockTableName, transactionRow)); if (blockRow != null) { @@ -263,7 +263,7 @@ public uint256 GetBlockIdByTransactionId(uint256 trxid) uint256 res = null; lock (this.Locker) { - byte[] transactionRow = this.leveldb.Get(DBH.Key(TransactionTableName, trxid.ToBytes())); + byte[] transactionRow = this.rocksdb.Get(DBH.Key(TransactionTableName, trxid.ToBytes())); if (transactionRow != null) res = new uint256(transactionRow); } @@ -305,7 +305,7 @@ protected virtual void OnInsertBlocks(List blocks) } } - this.leveldb.Write(batch); + this.rocksdb.Write(batch); } if (this.TxIndex) @@ -323,7 +323,7 @@ protected virtual void OnInsertTransactions(List<(Transaction, Block)> transacti foreach ((Transaction transaction, Block block) in transactions) batch.Put(DBH.Key(TransactionTableName, transaction.GetHash().ToBytes()), block.GetHash().ToBytes()); - this.leveldb.Write(batch); + this.rocksdb.Write(batch); } } @@ -333,7 +333,7 @@ public IEnumerable EnumeratehBatch(List headers) { foreach (ChainedHeader chainedHeader in headers) { - byte[] blockRow = this.leveldb.Get(DBH.Key(BlockTableName, chainedHeader.HashBlock.ToBytes())); + byte[] blockRow = this.rocksdb.Get(DBH.Key(BlockTableName, chainedHeader.HashBlock.ToBytes())); Block block = blockRow != null ? this.dataStoreSerializer.Deserialize(blockRow) : null; yield return block; } @@ -364,12 +364,12 @@ public void ReIndex() this.logger.LogInformation(warningMessage.ToString()); using (var batch = new WriteBatch()) { - var enumerator = this.leveldb.GetEnumerator(); - while (enumerator.MoveNext()) + var enumerator = this.rocksdb.NewIterator(); + for (enumerator.SeekToFirst(); enumerator.Valid(); enumerator.Next()) { - if (enumerator.Current.Key[0] == BlockTableName) + if (enumerator.Key()[0] == BlockTableName) { - var block = this.dataStoreSerializer.Deserialize(enumerator.Current.Value); + var block = this.dataStoreSerializer.Deserialize(enumerator.Value()); foreach (Transaction transaction in block.Transactions) { batch.Put(DBH.Key(TransactionTableName, transaction.GetHash().ToBytes()), block.GetHash().ToBytes()); @@ -383,19 +383,19 @@ public void ReIndex() } } - this.leveldb.Write(batch); + this.rocksdb.Write(batch); } this.logger.LogInformation("Reindex completed successfully."); } else { - var enumerator = this.leveldb.GetEnumerator(); - while (enumerator.MoveNext()) + var enumerator = this.rocksdb.NewIterator(); + for (enumerator.SeekToFirst(); enumerator.Valid(); enumerator.Next()) { // Clear tx from database. - if (enumerator.Current.Key[0] == TransactionTableName) - this.leveldb.Delete(enumerator.Current.Key); + if (enumerator.Key()[0] == TransactionTableName) + this.rocksdb.Remove(enumerator.Key()); } } } @@ -421,7 +421,7 @@ public void PutBlocks(HashHeightPair newTip, List blocks) private bool? LoadTxIndex() { bool? res = null; - byte[] row = this.leveldb.Get(DBH.Key(CommonTableName, TxIndexKey)); + byte[] row = this.rocksdb.Get(DBH.Key(CommonTableName, TxIndexKey)); if (row != null) { this.TxIndex = BitConverter.ToBoolean(row); @@ -434,7 +434,7 @@ public void PutBlocks(HashHeightPair newTip, List blocks) private void SaveTxIndex(bool txIndex) { this.TxIndex = txIndex; - this.leveldb.Put(DBH.Key(CommonTableName, TxIndexKey), BitConverter.GetBytes(txIndex)); + this.rocksdb.Put(DBH.Key(CommonTableName, TxIndexKey), BitConverter.GetBytes(txIndex)); } /// @@ -450,7 +450,7 @@ private HashHeightPair LoadTipHashAndHeight() { if (this.TipHashAndHeight == null) { - byte[] row = this.leveldb.Get(DBH.Key(CommonTableName, RepositoryTipKey)); + byte[] row = this.rocksdb.Get(DBH.Key(CommonTableName, RepositoryTipKey)); if (row != null) this.TipHashAndHeight = this.dataStoreSerializer.Deserialize(row); } @@ -461,7 +461,7 @@ private HashHeightPair LoadTipHashAndHeight() private void SaveTipHashAndHeight(HashHeightPair newTip) { this.TipHashAndHeight = newTip; - this.leveldb.Put(DBH.Key(CommonTableName, RepositoryTipKey), this.dataStoreSerializer.Serialize(newTip)); + this.rocksdb.Put(DBH.Key(CommonTableName, RepositoryTipKey), this.dataStoreSerializer.Serialize(newTip)); } /// @@ -506,7 +506,7 @@ public bool Exist(uint256 hash) { // Lazy loading is on so we don't fetch the whole value, just the row. byte[] key = hash.ToBytes(); - byte[] blockRow = this.leveldb.Get(DBH.Key(BlockTableName, key)); + byte[] blockRow = this.rocksdb.Get(DBH.Key(BlockTableName, key)); if (blockRow != null) res = true; } @@ -517,7 +517,7 @@ public bool Exist(uint256 hash) protected virtual void OnDeleteTransactions(List<(Transaction, Block)> transactions) { foreach ((Transaction transaction, Block block) in transactions) - this.leveldb.Delete(DBH.Key(TransactionTableName, transaction.GetHash().ToBytes())); + this.rocksdb.Remove(DBH.Key(TransactionTableName, transaction.GetHash().ToBytes())); } protected virtual void OnDeleteBlocks(List blocks) @@ -534,7 +534,7 @@ protected virtual void OnDeleteBlocks(List blocks) } foreach (Block block in blocks) - this.leveldb.Delete(DBH.Key(BlockTableName, block.GetHash().ToBytes())); + this.rocksdb.Remove(DBH.Key(BlockTableName, block.GetHash().ToBytes())); } public List GetBlocksFromHashes(List hashes) @@ -556,7 +556,7 @@ public List GetBlocksFromHashes(List hashes) continue; } - byte[] blockRow = this.leveldb.Get(DBH.Key(BlockTableName, key.Item2)); + byte[] blockRow = this.rocksdb.Get(DBH.Key(BlockTableName, key.Item2)); if (blockRow != null) { results[key.Item1] = this.dataStoreSerializer.Deserialize(blockRow); @@ -605,7 +605,7 @@ public void DeleteBlocks(List hashes) /// public void Dispose() { - this.leveldb.Dispose(); + this.rocksdb.Dispose(); } } } \ No newline at end of file diff --git a/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj b/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj index b0a155aa6..6cc8dad51 100644 --- a/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj +++ b/src/Features/Blockcore.Features.BlockStore/Blockcore.Features.BlockStore.csproj @@ -15,6 +15,8 @@ + + diff --git a/src/Features/Blockcore.Features.BlockStore/Pruning/PrunedBlockRepository.cs b/src/Features/Blockcore.Features.BlockStore/Pruning/PrunedBlockRepository.cs index e280e362c..0ae5eaeb2 100644 --- a/src/Features/Blockcore.Features.BlockStore/Pruning/PrunedBlockRepository.cs +++ b/src/Features/Blockcore.Features.BlockStore/Pruning/PrunedBlockRepository.cs @@ -34,7 +34,7 @@ public PrunedBlockRepository(IBlockRepository blockRepository, DataStoreSerializ /// public void Initialize() { - this.LoadPrunedTip(this.blockRepository.Leveldb); + this.LoadPrunedTip(this.blockRepository.RocksDb); } /// @@ -48,20 +48,20 @@ public void PrepareDatabase() lock (this.blockRepository.Locker) { - this.blockRepository.Leveldb.Put(DBH.Key(BlockRepository.CommonTableName, prunedTipKey), this.dataStoreSerializer.Serialize(this.PrunedTip)); + this.blockRepository.RocksDb.Put(DBH.Key(BlockRepository.CommonTableName, prunedTipKey), this.dataStoreSerializer.Serialize(this.PrunedTip)); } } return; } - private void LoadPrunedTip(DB leveldb) + private void LoadPrunedTip(RocksDbSharp.RocksDb rocksdb) { if (this.PrunedTip == null) { lock (this.blockRepository.Locker) { - byte[] row = leveldb.Get(DBH.Key(BlockRepository.CommonTableName, prunedTipKey)); + byte[] row = rocksdb.Get(DBH.Key(BlockRepository.CommonTableName, prunedTipKey)); if (row != null) { this.PrunedTip = this.dataStoreSerializer.Deserialize(row); @@ -77,7 +77,7 @@ public void UpdatePrunedTip(ChainedHeader tip) lock (this.blockRepository.Locker) { - this.blockRepository.Leveldb.Put(DBH.Key(BlockRepository.CommonTableName, prunedTipKey), this.dataStoreSerializer.Serialize(this.PrunedTip)); + this.blockRepository.RocksDb.Put(DBH.Key(BlockRepository.CommonTableName, prunedTipKey), this.dataStoreSerializer.Serialize(this.PrunedTip)); } } } diff --git a/src/Features/Blockcore.Features.Consensus/Blockcore.Features.Consensus.csproj b/src/Features/Blockcore.Features.Consensus/Blockcore.Features.Consensus.csproj index af1705021..e4a5331bb 100644 --- a/src/Features/Blockcore.Features.Consensus/Blockcore.Features.Consensus.csproj +++ b/src/Features/Blockcore.Features.Consensus/Blockcore.Features.Consensus.csproj @@ -19,6 +19,8 @@ + + diff --git a/src/Features/Blockcore.Features.Consensus/CoinViews/Coindb/RocksdbCoindb .cs b/src/Features/Blockcore.Features.Consensus/CoinViews/Coindb/RocksdbCoindb .cs new file mode 100644 index 000000000..e9d5e1cf5 --- /dev/null +++ b/src/Features/Blockcore.Features.Consensus/CoinViews/Coindb/RocksdbCoindb .cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Blockcore.Configuration; +using Blockcore.Utilities; +using Microsoft.Extensions.Logging; +using NBitcoin; +using RocksDbSharp; + +namespace Blockcore.Features.Consensus.CoinViews.Coindb +{ + /// + /// Persistent implementation of coinview using dBreeze database. + /// + public class RocksdbCoindb : ICoindb, IStakdb, IDisposable + { + /// Database key under which the block hash of the coin view's current tip is stored. + private static readonly byte[] blockHashKey = new byte[0]; + + private static readonly byte coinsTable = 1; + private static readonly byte blockTable = 2; + private static readonly byte rewindTable = 3; + private static readonly byte stakeTable = 4; + + /// Instance logger. + private readonly ILogger logger; + + /// Specification of the network the node runs on - regtest/testnet/mainnet. + private readonly Network network; + + /// Hash of the block which is currently the tip of the coinview. + private HashHeightPair blockHash; + + /// Performance counter to measure performance of the database insert and query operations. + private readonly BackendPerformanceCounter performanceCounter; + + private BackendPerformanceSnapshot latestPerformanceSnapShot; + + /// Access to rocksdb database. + private readonly RocksDb rocksdb; + + private DataStoreSerializer dataStoreSerializer; + + public RocksdbCoindb(Network network, DataFolder dataFolder, IDateTimeProvider dateTimeProvider, + ILoggerFactory loggerFactory, INodeStats nodeStats, DataStoreSerializer dataStoreSerializer) + : this(network, dataFolder.CoindbPath, dateTimeProvider, loggerFactory, nodeStats, dataStoreSerializer) + { + } + + public RocksdbCoindb(Network network, string folder, IDateTimeProvider dateTimeProvider, + ILoggerFactory loggerFactory, INodeStats nodeStats, DataStoreSerializer dataStoreSerializer) + { + Guard.NotNull(network, nameof(network)); + Guard.NotEmpty(folder, nameof(folder)); + + this.dataStoreSerializer = dataStoreSerializer; + + this.logger = loggerFactory.CreateLogger(this.GetType().FullName); + + // Open a connection to a new DB and create if not found + var options = new DbOptions().SetCreateIfMissing(true); + this.rocksdb = RocksDb.Open(options, folder); + + this.network = network; + this.performanceCounter = new BackendPerformanceCounter(dateTimeProvider); + + nodeStats.RegisterStats(this.AddBenchStats, StatsType.Benchmark, this.GetType().Name, 400); + } + + public void Initialize() + { + Block genesis = this.network.GetGenesis(); + + if (this.GetTipHash() == null) + { + this.SetBlockHash(new HashHeightPair(genesis.GetHash(), 0)); + } + } + + private void SetBlockHash(HashHeightPair nextBlockHash) + { + this.blockHash = nextBlockHash; + this.rocksdb.Put(new byte[] { blockTable }.Concat(blockHashKey).ToArray(), nextBlockHash.ToBytes()); + } + + public HashHeightPair GetTipHash() + { + if (this.blockHash == null) + { + var row = this.rocksdb.Get(new byte[] { blockTable }.Concat(blockHashKey).ToArray()); + if (row != null) + { + this.blockHash = new HashHeightPair(); + this.blockHash.FromBytes(row); + } + } + + return this.blockHash; + } + + public FetchCoinsResponse FetchCoins(OutPoint[] utxos) + { + FetchCoinsResponse res = new FetchCoinsResponse(); + + using (new StopwatchDisposable(o => this.performanceCounter.AddQueryTime(o))) + { + this.performanceCounter.AddQueriedEntities(utxos.Length); + + foreach (OutPoint outPoint in utxos) + { + byte[] row = this.rocksdb.Get(new byte[] { coinsTable }.Concat(outPoint.ToBytes()).ToArray()); + Coins outputs = row != null ? this.dataStoreSerializer.Deserialize(row) : null; + + this.logger.LogDebug("Outputs for '{0}' were {1}.", outPoint, outputs == null ? "NOT loaded" : "loaded"); + + res.UnspentOutputs.Add(outPoint, new UnspentOutput(outPoint, outputs)); + } + } + + return res; + } + + public void SaveChanges(IList unspentOutputs, HashHeightPair oldBlockHash, HashHeightPair nextBlockHash, List rewindDataList = null) + { + int insertedEntities = 0; + + using (var batch = new WriteBatch()) + { + using (new StopwatchDisposable(o => this.performanceCounter.AddInsertTime(o))) + { + HashHeightPair current = this.GetTipHash(); + if (current != oldBlockHash) + { + this.logger.LogTrace("(-)[BLOCKHASH_MISMATCH]"); + throw new InvalidOperationException("Invalid oldBlockHash"); + } + + // Here we'll add items to be inserted in a second pass. + List toInsert = new List(); + + foreach (var coin in unspentOutputs.OrderBy(utxo => utxo.OutPoint, new OutPointComparer())) + { + if (coin.Coins == null) + { + this.logger.LogDebug("Outputs of transaction ID '{0}' are prunable and will be removed from the database.", coin.OutPoint); + batch.Delete(new byte[] { coinsTable }.Concat(coin.OutPoint.ToBytes()).ToArray()); + } + 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.LogDebug("Outputs of transaction ID '{0}' are NOT PRUNABLE and will be inserted into the database. {1}/{2}.", coin.OutPoint, i, toInsert.Count); + + batch.Put(new byte[] { coinsTable }.Concat(coin.OutPoint.ToBytes()).ToArray(), this.dataStoreSerializer.Serialize(coin.Coins)); + } + + if (rewindDataList != null) + { + foreach (RewindData rewindData in rewindDataList) + { + var nextRewindIndex = rewindData.PreviousBlockHash.Height + 1; + + this.logger.LogDebug("Rewind state #{0} created.", nextRewindIndex); + + batch.Put(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(nextRewindIndex)).ToArray(), this.dataStoreSerializer.Serialize(rewindData)); + } + } + + insertedEntities += unspentOutputs.Count; + this.rocksdb.Write(batch); + + this.SetBlockHash(nextBlockHash); + } + } + + this.performanceCounter.AddInsertedEntities(insertedEntities); + } + + /// + public HashHeightPair Rewind() + { + HashHeightPair res = null; + using (var batch = new WriteBatch()) + { + HashHeightPair current = this.GetTipHash(); + + byte[] row = this.rocksdb.Get(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(current.Height)).ToArray()); + + if (row == null) + { + throw new InvalidOperationException($"No rewind data found for block `{current}`"); + } + + batch.Delete(BitConverter.GetBytes(current.Height)); + + var rewindData = this.dataStoreSerializer.Deserialize(row); + + foreach (OutPoint outPoint in rewindData.OutputsToRemove) + { + this.logger.LogDebug("Outputs of outpoint '{0}' will be removed.", outPoint); + batch.Delete(new byte[] { coinsTable }.Concat(outPoint.ToBytes()).ToArray()); + } + + foreach (RewindDataOutput rewindDataOutput in rewindData.OutputsToRestore) + { + this.logger.LogDebug("Outputs of outpoint '{0}' will be restored.", rewindDataOutput.OutPoint); + batch.Put(new byte[] { coinsTable }.Concat(rewindDataOutput.OutPoint.ToBytes()).ToArray(), this.dataStoreSerializer.Serialize(rewindDataOutput.Coins)); + } + + res = rewindData.PreviousBlockHash; + + this.rocksdb.Write(batch); + + this.SetBlockHash(rewindData.PreviousBlockHash); + } + + return res; + } + + public RewindData GetRewindData(int height) + { + byte[] row = this.rocksdb.Get(new byte[] { rewindTable }.Concat(BitConverter.GetBytes(height)).ToArray()); + return row != null ? this.dataStoreSerializer.Deserialize(row) : null; + } + + /// + /// Persists unsaved POS blocks information to the database. + /// + /// List of POS block information to be examined and persists if unsaved. + public void PutStake(IEnumerable stakeEntries) + { + using (var batch = new WriteBatch()) + { + foreach (StakeItem stakeEntry in stakeEntries) + { + if (!stakeEntry.InStore) + { + batch.Put(new byte[] { stakeTable }.Concat(stakeEntry.BlockId.ToBytes(false)).ToArray(), this.dataStoreSerializer.Serialize(stakeEntry.BlockStake)); + stakeEntry.InStore = true; + } + } + + this.rocksdb.Write(batch); + } + } + + /// + /// Retrieves POS blocks information from the database. + /// + /// List of partially initialized POS block information that is to be fully initialized with the values from the database. + public void GetStake(IEnumerable blocklist) + { + foreach (StakeItem blockStake in blocklist) + { + this.logger.LogDebug("Loading POS block hash '{0}' from the database.", blockStake.BlockId); + byte[] stakeRow = this.rocksdb.Get(new byte[] { stakeTable }.Concat(blockStake.BlockId.ToBytes(false)).ToArray()); + + if (stakeRow != null) + { + blockStake.BlockStake = this.dataStoreSerializer.Deserialize(stakeRow); + blockStake.InStore = true; + } + } + } + + private void AddBenchStats(StringBuilder log) + { + log.AppendLine("======Leveldb Bench======"); + + BackendPerformanceSnapshot snapShot = this.performanceCounter.Snapshot(); + + if (this.latestPerformanceSnapShot == null) + log.AppendLine(snapShot.ToString()); + else + log.AppendLine((snapShot - this.latestPerformanceSnapShot).ToString()); + + this.latestPerformanceSnapShot = snapShot; + } + + /// + public void Dispose() + { + this.rocksdb.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Features/Blockcore.Features.Consensus/FullNodeBuilderConsensusExtension.cs b/src/Features/Blockcore.Features.Consensus/FullNodeBuilderConsensusExtension.cs index 4530002c7..e01f5f007 100644 --- a/src/Features/Blockcore.Features.Consensus/FullNodeBuilderConsensusExtension.cs +++ b/src/Features/Blockcore.Features.Consensus/FullNodeBuilderConsensusExtension.cs @@ -18,7 +18,7 @@ namespace Blockcore.Features.Consensus /// public static class FullNodeBuilderConsensusExtension { - public static IFullNodeBuilder UsePowConsensus(this IFullNodeBuilder fullNodeBuilder, DbType coindbType = DbType.Leveldb) + public static IFullNodeBuilder UsePowConsensus(this IFullNodeBuilder fullNodeBuilder, DbType coindbType = DbType.Rocksdb) { LoggingConfiguration.RegisterFeatureNamespace("powconsensus"); @@ -42,7 +42,7 @@ public static IFullNodeBuilder UsePowConsensus(this IFullNodeBuilder fullNodeBui return fullNodeBuilder; } - public static IFullNodeBuilder UsePosConsensus(this IFullNodeBuilder fullNodeBuilder, DbType coindbType = DbType.Leveldb) + public static IFullNodeBuilder UsePosConsensus(this IFullNodeBuilder fullNodeBuilder, DbType coindbType = DbType.Rocksdb) { LoggingConfiguration.RegisterFeatureNamespace("posconsensus"); @@ -81,6 +81,9 @@ private static void AddCoindbImplementation(IServiceCollection services, DbType if (coindbType == DbType.Faster) services.AddSingleton(); + + if (coindbType == DbType.Rocksdb) + services.AddSingleton(); } } @@ -88,6 +91,7 @@ public enum DbType { Leveldb, Dbreeze, - Faster + Faster, + Rocksdb } } \ No newline at end of file diff --git a/src/Features/Blockcore.Features.Consensus/ProvenBlockHeaders/ProvenBlockHeaderRepository.cs b/src/Features/Blockcore.Features.Consensus/ProvenBlockHeaders/ProvenBlockHeaderRepository.cs index b0d9135f2..bba582ab8 100644 --- a/src/Features/Blockcore.Features.Consensus/ProvenBlockHeaders/ProvenBlockHeaderRepository.cs +++ b/src/Features/Blockcore.Features.Consensus/ProvenBlockHeaders/ProvenBlockHeaderRepository.cs @@ -7,9 +7,9 @@ using Blockcore.Interfaces; using Blockcore.Utilities; using DBreeze.Utils; -using LevelDB; using Microsoft.Extensions.Logging; using NBitcoin; +using RocksDbSharp; namespace Blockcore.Features.Consensus.ProvenBlockHeaders { @@ -26,7 +26,7 @@ public class ProvenBlockHeaderRepository : IProvenBlockHeaderRepository /// /// Access to database. /// - private readonly DB leveldb; + private readonly RocksDb rocksdb; private object locker; @@ -85,8 +85,9 @@ public ProvenBlockHeaderRepository(Network network, string folder, ILoggerFactor Directory.CreateDirectory(folder); // Open a connection to a new DB and create if not found - var options = new Options { CreateIfMissing = true }; - this.leveldb = new DB(options, folder); + var options = new DbOptions().SetCreateIfMissing(true); + this.rocksdb = RocksDb.Open(options, folder); + this.locker = new object(); this.network = network; @@ -115,20 +116,20 @@ public Task InitializeAsync() /// public Task GetAsync(int blockHeight) { - Task task = Task.Run(() => + var task = Task.Run((Func)(() => { byte[] row = null; lock (this.locker) { - row = this.leveldb.Get(DBH.Key(provenBlockHeaderTable, BitConverter.GetBytes(blockHeight))); + row = this.rocksdb.Get(DBH.Key(provenBlockHeaderTable, BitConverter.GetBytes(blockHeight))); } if (row != null) return this.dataStoreSerializer.Deserialize(row); return null; - }); + })); return task; } @@ -166,7 +167,7 @@ private void SetTip(HashHeightPair newTip) lock (this.locker) { - this.leveldb.Put(DBH.Key(blockHashHeightTable, blockHashHeightKey), this.dataStoreSerializer.Serialize(newTip)); + this.rocksdb.Put(DBH.Key(blockHashHeightTable, blockHashHeightKey), this.dataStoreSerializer.Serialize(newTip)); } } @@ -183,7 +184,7 @@ private void InsertHeaders(SortedDictionary headers) lock (this.locker) { - this.leveldb.Write(batch); + this.rocksdb.Write(batch); } } @@ -202,7 +203,7 @@ private HashHeightPair GetTipHash() byte[] row = null; lock (this.locker) { - row = this.leveldb.Get(DBH.Key(blockHashHeightTable, blockHashHeightKey)); + row = this.rocksdb.Get(DBH.Key(blockHashHeightTable, blockHashHeightKey)); } if (row != null) @@ -214,7 +215,7 @@ private HashHeightPair GetTipHash() /// public void Dispose() { - this.leveldb?.Dispose(); + this.rocksdb?.Dispose(); } } } \ No newline at end of file diff --git a/src/Tests/Blockcore.Features.BlockStore.Tests/BlockRepositoryTests.cs b/src/Tests/Blockcore.Features.BlockStore.Tests/BlockRepositoryTests.cs index 4ef6438d5..c33da9616 100644 --- a/src/Tests/Blockcore.Features.BlockStore.Tests/BlockRepositoryTests.cs +++ b/src/Tests/Blockcore.Features.BlockStore.Tests/BlockRepositoryTests.cs @@ -3,10 +3,8 @@ using System.Linq; using Blockcore.Tests.Common.Logging; using Blockcore.Utilities; -using DBreeze; -using DBreeze.DataTypes; -using LevelDB; using NBitcoin; +using RocksDbSharp; using Xunit; namespace Blockcore.Features.BlockStore.Tests @@ -21,7 +19,7 @@ public void InitializesGenesisBlockAndTxIndexOnFirstLoad() { } - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { byte[] blockRow = engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[0])); bool txIndexRow = BitConverter.ToBoolean(engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[1]))); @@ -36,7 +34,7 @@ public void DoesNotOverwriteExistingBlockAndTxIndexOnFirstLoad() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(new uint256(56), 1))); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[1]), BitConverter.GetBytes(true)); @@ -46,7 +44,7 @@ public void DoesNotOverwriteExistingBlockAndTxIndexOnFirstLoad() { } - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { byte[] blockRow = engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[0])); bool txIndexRow = BitConverter.ToBoolean(engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[1]))); @@ -61,7 +59,7 @@ public void GetTrxAsyncWithoutTransactionIndexReturnsNewTransaction() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(uint256.Zero, 1))); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[1]), BitConverter.GetBytes(false)); @@ -78,7 +76,7 @@ public void GetTrxAsyncWithoutTransactionInIndexReturnsNull() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { var blockId = new uint256(8920); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(uint256.Zero, 1))); @@ -98,7 +96,7 @@ public void GetTrxAsyncWithTransactionReturnsExistingTransaction() Transaction trans = this.Network.CreateTransaction(); trans.Version = 125; - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { Block block = this.Network.CreateBlock(); block.Header.GetHash(); @@ -121,7 +119,7 @@ public void GetTrxBlockIdAsyncWithoutTxIndexReturnsDefaultId() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(uint256.Zero, 1))); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[1]), BitConverter.GetBytes(false)); @@ -138,7 +136,7 @@ public void GetTrxBlockIdAsyncWithoutExistingTransactionReturnsNull() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(uint256.Zero, 1))); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[1]), BitConverter.GetBytes(true)); @@ -155,7 +153,7 @@ public void GetTrxBlockIdAsyncWithTransactionReturnsBlockId() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.TransactionTableName, new uint256(26).ToBytes()), new uint256(42).ToBytes()); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(uint256.Zero, 1))); @@ -193,7 +191,7 @@ public void PutAsyncWritesBlocksAndTransactionsToDbAndSavesNextBlockHash() block2.Transactions.Add(transaction); blocks.Add(block2); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[0]), this.DataStoreSerializer.Serialize(new HashHeightPair(uint256.Zero, 1))); engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[1]), BitConverter.GetBytes(true)); @@ -204,7 +202,7 @@ public void PutAsyncWritesBlocksAndTransactionsToDbAndSavesNextBlockHash() repository.PutBlocks(new HashHeightPair(nextBlockHash, 100), blocks); } - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { byte[] blockHashKeyRow = engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[0])); @@ -233,7 +231,7 @@ public void PutAsyncWritesBlocksAndTransactionsToDbAndSavesNextBlockHash() public void SetTxIndexUpdatesTxIndex() { string dir = CreateTestDir(this); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.CommonTableName, new byte[1]), BitConverter.GetBytes(true)); } @@ -243,7 +241,7 @@ public void SetTxIndexUpdatesTxIndex() repository.SetTxIndex(false); } - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { bool txIndexRow = BitConverter.ToBoolean(engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[1]))); Assert.False(txIndexRow); @@ -256,7 +254,7 @@ public void GetAsyncWithExistingBlockReturnsBlock() string dir = CreateTestDir(this); Block block = this.Network.Consensus.ConsensusFactory.CreateBlock(); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.BlockTableName, block.GetHash().ToBytes()), block.ToBytes()); } @@ -280,7 +278,7 @@ public void GetAsyncWithExistingBlocksReturnsBlocks() blocks[i].Header.HashPrevBlock = blocks[i - 1].Header.GetHash(); } - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { for (int i = 0; i < blocks.Length; i++) engine.Put(DBH.Key(BlockRepository.BlockTableName, blocks[i].GetHash().ToBytes()), blocks[i].ToBytes()); @@ -313,7 +311,7 @@ public void ExistAsyncWithExistingBlockReturnsTrue() string dir = CreateTestDir(this); Block block = this.Network.Consensus.ConsensusFactory.CreateBlock(); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.BlockTableName, block.GetHash().ToBytes()), block.ToBytes()); } @@ -342,7 +340,7 @@ public void DeleteAsyncRemovesBlocksAndTransactions() Block block = this.Network.CreateBlock(); block.Transactions.Add(this.Network.CreateTransaction()); - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.BlockTableName, block.GetHash().ToBytes()), block.ToBytes()); engine.Put(DBH.Key(BlockRepository.TransactionTableName, block.Transactions[0].GetHash().ToBytes()), block.GetHash().ToBytes()); @@ -356,7 +354,7 @@ public void DeleteAsyncRemovesBlocksAndTransactions() repository.Delete(tip, new List { block.GetHash() }); } - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { byte[] blockHashKeyRow = engine.Get(DBH.Key(BlockRepository.CommonTableName, new byte[0])); Dictionary blockDict = engine.SelectDictionary(BlockRepository.BlockTableName); @@ -377,7 +375,7 @@ public void ReIndexAsync_TxIndex_OffToOn() block.Transactions.Add(transaction); // Set up database to mimic that created when TxIndex was off. No transactions stored. - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.BlockTableName, block.GetHash().ToBytes()), block.ToBytes()); } @@ -390,7 +388,7 @@ public void ReIndexAsync_TxIndex_OffToOn() } // Check that after indexing database, the transaction inside the block is now indexed. - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { Dictionary blockDict = engine.SelectDictionary(BlockRepository.BlockTableName); Dictionary transDict = engine.SelectDictionary(BlockRepository.TransactionTableName); @@ -416,7 +414,7 @@ public void ReIndexAsync_TxIndex_OnToOff() block.Transactions.Add(transaction); // Set up database to mimic that created when TxIndex was on. Transaction from block is stored. - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { engine.Put(DBH.Key(BlockRepository.BlockTableName, block.GetHash().ToBytes()), block.ToBytes()); engine.Put(DBH.Key(BlockRepository.TransactionTableName, transaction.GetHash().ToBytes()), block.GetHash().ToBytes()); @@ -430,7 +428,7 @@ public void ReIndexAsync_TxIndex_OnToOff() } // Check that after indexing database, the transaction is no longer stored. - using (var engine = new DB(new Options() { CreateIfMissing = true }, dir)) + using (var engine = RocksDb.Open(new DbOptions().SetCreateIfMissing(true), dir)) { Dictionary blockDict = engine.SelectDictionary(BlockRepository.BlockTableName); Dictionary transDict = engine.SelectDictionary(BlockRepository.TransactionTableName); diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Listener.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Listener.zip index e10f9b92b..6a55ce1f7 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Listener.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Listener.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Miner.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Miner.zip index f00a2b78b..91ca88deb 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Miner.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100Miner.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100NoWallet.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100NoWallet.zip index 7dfc032bc..c58eeed76 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100NoWallet.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest100NoWallet.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Listener.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Listener.zip index 1068d4b53..d4f36899b 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Listener.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Listener.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Miner.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Miner.zip index 989c92179..982ea19f3 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Miner.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10Miner.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10NoWallet.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10NoWallet.zip index 411e7aa9c..2f13ebd83 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10NoWallet.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest10NoWallet.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Listener.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Listener.zip index 216696c14..762d8a2a8 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Listener.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Listener.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Miner.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Miner.zip index a1df6338e..7e242f1f1 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Miner.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150Miner.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150NoWallet.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150NoWallet.zip index 6a4825203..1bce5fd68 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150NoWallet.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/RegTest150NoWallet.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Listener.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Listener.zip index a52420261..ca90ebfc0 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Listener.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Listener.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Miner.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Miner.zip index 8edd5ff29..130cd28ea 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Miner.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100Miner.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100NoWallet.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100NoWallet.zip index e064f63da..80fd4d8b0 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100NoWallet.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest100NoWallet.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Listener.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Listener.zip index a863c02eb..aa43ba258 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Listener.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Listener.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Miner.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Miner.zip index 9cca6b2cc..e914f40e0 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Miner.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10Miner.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10NoWallet.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10NoWallet.zip index 8237cc028..1dcda89d0 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10NoWallet.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest10NoWallet.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Listener.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Listener.zip index b817fb1a7..831708c17 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Listener.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Listener.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Miner.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Miner.zip index 7c5a97906..58929ed52 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Miner.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150Miner.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150NoWallet.zip b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150NoWallet.zip index 0a86aaedb..a2a50af94 100644 Binary files a/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150NoWallet.zip and b/src/Tests/Blockcore.IntegrationTests.Common/ReadyData/StratisRegTest150NoWallet.zip differ diff --git a/src/Tests/Blockcore.IntegrationTests/Blockcore.IntegrationTests.csproj b/src/Tests/Blockcore.IntegrationTests/Blockcore.IntegrationTests.csproj index db6acda53..45a4e752d 100644 --- a/src/Tests/Blockcore.IntegrationTests/Blockcore.IntegrationTests.csproj +++ b/src/Tests/Blockcore.IntegrationTests/Blockcore.IntegrationTests.csproj @@ -27,6 +27,8 @@ + + diff --git a/src/test.runsettings b/src/test.runsettings new file mode 100644 index 000000000..5a190869d --- /dev/null +++ b/src/test.runsettings @@ -0,0 +1,13 @@ + + + + + 1 + + .\TestResults + + + x64 + + \ No newline at end of file