Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Blockcore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
3 changes: 2 additions & 1 deletion src/Blockcore/Base/BaseFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ public static IFullNodeBuilder UseBaseFeature(this IFullNodeBuilder fullNodeBuil
services.AddSingleton<IInvalidBlockHashStore, InvalidBlockHashStore>();
services.AddSingleton<IChainState, ChainState>();
services.AddSingleton<IChainRepository, ChainRepository>();
services.AddSingleton<IChainStore, LeveldbChainStore>();
// services.AddSingleton<IChainStore, LeveldbChainStore>();
services.AddSingleton<IChainStore, RocksdbChainStore>();
services.AddSingleton<IFinalizedBlockInfoRepository, FinalizedBlockInfoRepository>();
services.AddSingleton<ITimeSyncBehaviorState, TimeSyncBehaviorState>();
services.AddSingleton<NodeDeployments>();
Expand Down
2 changes: 2 additions & 0 deletions src/Blockcore/Blockcore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" />
<PackageReference Include="LevelDB.Standard" Version="2.1.6.1" />
<PackageReference Include="NStratis.HashLib" Version="1.0.0.1" />
<PackageReference Include="RocksDbNative" Version="6.2.2" />
<PackageReference Include="RocksDbSharp" Version="6.2.2" />
</ItemGroup>

<ItemGroup>
Expand Down
139 changes: 139 additions & 0 deletions src/Blockcore/Consensus/Chain/RocksdbChainStore.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Headers that are close to the tip
/// </summary>
private readonly MemoryCountCache<uint256, BlockHeader> nearTipHeaders;

/// <summary>
/// Headers that are close to the tip
/// </summary>
private readonly MemoryCountCache<uint256, BlockHeader> 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<uint256, BlockHeader>();
this.nearTipHeaders = new MemoryCountCache<uint256, BlockHeader>(601);
this.recentHeaders = new MemoryCountCache<uint256, BlockHeader>(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<byte> 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<ChainDataItem> 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();
}
}
}
13 changes: 7 additions & 6 deletions src/Blockcore/Utilities/KeyValueRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Blockcore.Configuration;
using Blockcore.Utilities.JsonConverters;
using LevelDB;
using RocksDbSharp;

namespace Blockcore.Utilities
{
Expand Down Expand Up @@ -32,7 +33,7 @@ public interface IKeyValueRepository : IDisposable
public class KeyValueRepository : IKeyValueRepository
{
/// <summary>Access to database.</summary>
private readonly DB leveldb;
private readonly RocksDb rocksdb;

private readonly DataStoreSerializer dataStoreSerializer;

Expand All @@ -46,16 +47,16 @@ 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);
}

/// <inheritdoc />
public void SaveBytes(string key, byte[] bytes)
{
byte[] keyBytes = Encoding.ASCII.GetBytes(key);

this.leveldb.Put(keyBytes, bytes);
this.rocksdb.Put(keyBytes, bytes);
}

/// <inheritdoc />
Expand All @@ -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;
Expand Down Expand Up @@ -116,7 +117,7 @@ public T LoadValueJson<T>(string key)
/// <inheritdoc />
public void Dispose()
{
this.leveldb.Dispose();
this.rocksdb.Dispose();
}
}
}
15 changes: 15 additions & 0 deletions src/Blockcore/Utilities/LeveldbHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using LevelDB;
using RocksDbSharp;

namespace Blockcore.Utilities
{
Expand Down Expand Up @@ -35,5 +36,19 @@ public static Dictionary<byte[], byte[]> SelectDictionary(this DB db, byte table

return dict;
}

public static Dictionary<byte[], byte[]> SelectDictionary(this RocksDb db, byte table)
{
var dict = new Dictionary<byte[], byte[]>();

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;
}
}
}
Loading