Skip to content

Commit 7e90c31

Browse files
authored
Use rocksdb for utxo and block store and chain headers (#226)
* Use rocksdb for utxo and block store and chain headers * KeyValueRepository to use rocks * Rocksdb code all tests pass
1 parent 1b6ab3d commit 7e90c31

34 files changed

Lines changed: 558 additions & 83 deletions

src/Blockcore.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Global", "Global", "{0C6FDC
7474
Directory.Build.props = Directory.Build.props
7575
None.ruleset = None.ruleset
7676
stylecop.json = stylecop.json
77+
test.runsettings = test.runsettings
7778
EndProjectSection
7879
EndProject
7980
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Networks.Bitcoin", "Networks\Bitcoin\Blockcore.Networks.Bitcoin\Blockcore.Networks.Bitcoin.csproj", "{7EE5025D-FA5E-484B-84C0-6B9C08D01CDD}"

src/Blockcore/Base/BaseFeature.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,8 @@ public static IFullNodeBuilder UseBaseFeature(this IFullNodeBuilder fullNodeBuil
397397
services.AddSingleton<IInvalidBlockHashStore, InvalidBlockHashStore>();
398398
services.AddSingleton<IChainState, ChainState>();
399399
services.AddSingleton<IChainRepository, ChainRepository>();
400-
services.AddSingleton<IChainStore, LeveldbChainStore>();
400+
// services.AddSingleton<IChainStore, LeveldbChainStore>();
401+
services.AddSingleton<IChainStore, RocksdbChainStore>();
401402
services.AddSingleton<IFinalizedBlockInfoRepository, FinalizedBlockInfoRepository>();
402403
services.AddSingleton<ITimeSyncBehaviorState, TimeSyncBehaviorState>();
403404
services.AddSingleton<NodeDeployments>();

src/Blockcore/Blockcore.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.4" />
5050
<PackageReference Include="LevelDB.Standard" Version="2.1.6.1" />
5151
<PackageReference Include="NStratis.HashLib" Version="1.0.0.1" />
52+
<PackageReference Include="RocksDbNative" Version="6.2.2" />
53+
<PackageReference Include="RocksDbSharp" Version="6.2.2" />
5254
</ItemGroup>
5355

5456
<ItemGroup>
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Blockcore.Configuration;
4+
using Blockcore.Utilities;
5+
using RocksDbSharp;
6+
7+
namespace NBitcoin
8+
{
9+
public class RocksdbChainStore : IChainStore, IDisposable
10+
{
11+
private readonly Network network;
12+
13+
internal static readonly byte ChainTableName = 1;
14+
internal static readonly byte HeaderTableName = 2;
15+
16+
/// <summary>
17+
/// Headers that are close to the tip
18+
/// </summary>
19+
private readonly MemoryCountCache<uint256, BlockHeader> nearTipHeaders;
20+
21+
/// <summary>
22+
/// Headers that are close to the tip
23+
/// </summary>
24+
private readonly MemoryCountCache<uint256, BlockHeader> recentHeaders;
25+
26+
private readonly RocksDb rocksdb;
27+
28+
private object locker;
29+
30+
public RocksdbChainStore(Network network, DataFolder dataFolder, ChainIndexer chainIndexer)
31+
{
32+
this.network = network;
33+
this.ChainIndexer = chainIndexer;
34+
// this.headers = new Dictionary<uint256, BlockHeader>();
35+
this.nearTipHeaders = new MemoryCountCache<uint256, BlockHeader>(601);
36+
this.recentHeaders = new MemoryCountCache<uint256, BlockHeader>(100);
37+
this.locker = new object();
38+
39+
// Open a connection to a new DB and create if not found
40+
var options = new DbOptions().SetCreateIfMissing(true);
41+
this.rocksdb = RocksDb.Open(options, dataFolder.ChainPath);
42+
}
43+
44+
public ChainIndexer ChainIndexer { get; }
45+
46+
public BlockHeader GetHeader(ChainedHeader chainedHeader, uint256 hash)
47+
{
48+
if (this.nearTipHeaders.TryGetValue(hash, out BlockHeader blockHeader))
49+
{
50+
return blockHeader;
51+
}
52+
53+
if (this.recentHeaders.TryGetValue(hash, out blockHeader))
54+
{
55+
return blockHeader;
56+
}
57+
58+
ReadOnlySpan<byte> bytes = hash.ToReadOnlySpan();
59+
60+
lock (this.locker)
61+
{
62+
bytes = this.rocksdb.Get(DBH.Key(HeaderTableName, bytes));
63+
}
64+
65+
if (bytes == null)
66+
{
67+
throw new ApplicationException("Header must exist if requested");
68+
}
69+
70+
blockHeader = this.network.Consensus.ConsensusFactory.CreateBlockHeader();
71+
blockHeader.FromBytes(bytes.ToArray(), this.network.Consensus.ConsensusFactory);
72+
73+
// If the header is 500 blocks behind tip or 100 blocks ahead cache it.
74+
if ((chainedHeader.Height > this.ChainIndexer.Height - 500) && (chainedHeader.Height <= this.ChainIndexer.Height + 100))
75+
{
76+
this.nearTipHeaders.AddOrUpdate(hash, blockHeader);
77+
}
78+
else
79+
{
80+
this.recentHeaders.AddOrUpdate(hash, blockHeader);
81+
}
82+
83+
return blockHeader;
84+
}
85+
86+
public bool PutHeader(BlockHeader blockHeader)
87+
{
88+
ConsensusFactory consensusFactory = this.network.Consensus.ConsensusFactory;
89+
90+
lock (this.locker)
91+
{
92+
this.rocksdb.Put(DBH.Key(HeaderTableName, blockHeader.GetHash().ToReadOnlySpan()), blockHeader.ToBytes(consensusFactory));
93+
}
94+
95+
return true;
96+
}
97+
98+
public ChainData GetChainData(int height)
99+
{
100+
byte[] bytes = null;
101+
102+
lock (this.locker)
103+
{
104+
bytes = this.rocksdb.Get(DBH.Key(ChainTableName, BitConverter.GetBytes(height)));
105+
}
106+
107+
if (bytes == null)
108+
{
109+
return null;
110+
}
111+
112+
var data = new ChainData();
113+
data.FromBytes(bytes, this.network.Consensus.ConsensusFactory);
114+
115+
return data;
116+
}
117+
118+
public void PutChainData(IEnumerable<ChainDataItem> items)
119+
{
120+
using (var batch = new WriteBatch())
121+
{
122+
foreach (var item in items)
123+
{
124+
batch.Put(DBH.Key(ChainTableName, BitConverter.GetBytes(item.Height)), item.Data.ToBytes(this.network.Consensus.ConsensusFactory));
125+
}
126+
127+
lock (this.locker)
128+
{
129+
this.rocksdb.Write(batch);
130+
}
131+
}
132+
}
133+
134+
public void Dispose()
135+
{
136+
this.rocksdb?.Dispose();
137+
}
138+
}
139+
}

src/Blockcore/Utilities/KeyValueRepository.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Blockcore.Configuration;
55
using Blockcore.Utilities.JsonConverters;
66
using LevelDB;
7+
using RocksDbSharp;
78

89
namespace Blockcore.Utilities
910
{
@@ -32,7 +33,7 @@ public interface IKeyValueRepository : IDisposable
3233
public class KeyValueRepository : IKeyValueRepository
3334
{
3435
/// <summary>Access to database.</summary>
35-
private readonly DB leveldb;
36+
private readonly RocksDb rocksdb;
3637

3738
private readonly DataStoreSerializer dataStoreSerializer;
3839

@@ -46,16 +47,16 @@ public KeyValueRepository(string folder, DataStoreSerializer dataStoreSerializer
4647
this.dataStoreSerializer = dataStoreSerializer;
4748

4849
// Open a connection to a new DB and create if not found
49-
var options = new Options { CreateIfMissing = true };
50-
this.leveldb = new DB(options, folder);
50+
var options = new DbOptions().SetCreateIfMissing(true);
51+
this.rocksdb = RocksDb.Open(options, folder);
5152
}
5253

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

58-
this.leveldb.Put(keyBytes, bytes);
59+
this.rocksdb.Put(keyBytes, bytes);
5960
}
6061

6162
/// <inheritdoc />
@@ -78,7 +79,7 @@ public byte[] LoadBytes(string key)
7879
{
7980
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
8081

81-
byte[] row = this.leveldb.Get(keyBytes);
82+
byte[] row = this.rocksdb.Get(keyBytes);
8283

8384
if (row == null)
8485
return null;
@@ -116,7 +117,7 @@ public T LoadValueJson<T>(string key)
116117
/// <inheritdoc />
117118
public void Dispose()
118119
{
119-
this.leveldb.Dispose();
120+
this.rocksdb.Dispose();
120121
}
121122
}
122123
}

src/Blockcore/Utilities/LeveldbHelper.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using LevelDB;
4+
using RocksDbSharp;
45

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

3637
return dict;
3738
}
39+
40+
public static Dictionary<byte[], byte[]> SelectDictionary(this RocksDb db, byte table)
41+
{
42+
var dict = new Dictionary<byte[], byte[]>();
43+
44+
var enumerator = db.NewIterator();
45+
for (enumerator.SeekToFirst(); enumerator.Valid(); enumerator.Next())
46+
{
47+
if (enumerator.Key()[0] == table)
48+
dict.Add(enumerator.Key().AsSpan().Slice(1).ToArray(), enumerator.Value());
49+
}
50+
51+
return dict;
52+
}
3853
}
3954
}

0 commit comments

Comments
 (0)