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+ }
0 commit comments