7df688799af8296d7b80a46e12ba9583ea775528
[NovacoinLibrary.git] / Novacoin / CBlockStore.cs
1 \feffusing System;
2 using System.IO;
3 using System.Linq;
4 using System.Collections.Generic;
5
6 using SQLite.Net;
7 using SQLite.Net.Attributes;
8 using SQLite.Net.Interop;
9 using SQLite.Net.Platform.Generic;
10
11 namespace Novacoin
12 {
13     public enum BlockType
14     {
15         PROOF_OF_WORK,
16         PROOF_OF_WORK_MODIFIER,
17         PROOF_OF_STAKE,
18         PROOF_OF_STAKE_MODIFIER
19     };
20
21     [Table("BlockStorage")]
22     class CBlockStoreItem
23     {
24         /// <summary>
25         /// Item ID in the database
26         /// </summary>
27         [PrimaryKey, AutoIncrement]
28         public int ItemID { get; set; }
29
30         /// <summary>
31         /// PBKDF2+Salsa20 of block hash
32         /// </summary>
33         [Unique]
34         public byte[] Hash { get; set; }
35
36         /// <summary>
37         /// Next block hash
38         /// </summary>
39         public byte[] NextHash { get; set; }
40
41         /// <summary>
42         /// Serialized representation of block header
43         /// </summary>
44         public byte[] BlockHeader { get; set; }
45
46         /// <summary>
47         /// Block type flags
48         /// </summary>
49         public BlockType BlockTypeFlag { get; set; }
50
51         /// <summary>
52         /// Block position in file
53         /// </summary>
54         public long nBlockPos { get; set; }
55
56         /// <summary>
57         /// Block size in bytes
58         /// </summary>
59         public int nBlockSize { get; set; }
60     }
61
62     /// <summary>
63     /// Block chain node
64     /// </summary>
65     public class CChainNode
66     {
67         /// <summary>
68         /// Block number
69         /// </summary>
70         public int nDepth;
71
72         /// <summary>
73         /// Block header
74         /// </summary>
75         public CBlockHeader blockHeader;
76
77         /// <summary>
78         /// Block type flag
79         /// </summary>
80         public BlockType blockType;
81
82         /// <summary>
83         /// Next block hash
84         /// </summary>
85         public ScryptHash256 hashNextBlock;
86     }
87
88     public class CBlockStore : IDisposable
89     {
90         private bool disposed = false;
91         private object LockObj = new object();
92         private SQLiteConnection dbConn = null;
93
94         private Dictionary<ScryptHash256, CChainNode> blockIndex = new Dictionary<ScryptHash256, CChainNode>();
95         
96         /// <summary>
97         /// Init the block storage manager.
98         /// </summary>
99         /// <param name="IndexDB">Path to index database</param>
100         /// <param name="BlockFile">Path to block file</param>
101         public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
102         {
103             bool firstInit = !File.Exists(IndexDB);
104             dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), IndexDB);
105
106             if (firstInit)
107             {
108                 lock (LockObj)
109                 {
110                     dbConn.CreateTable<CBlockStoreItem>(CreateFlags.AutoIncPK);
111                 }
112             }
113         }
114
115         public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
116         {
117             // TODO: Rewrite completely.
118
119             var QueryGet = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] order by [ItemId] desc limit 1");
120
121             var nOffset = 0L;
122
123             if (QueryGet.Count() == 1)
124             {
125                 var res = QueryGet.First();
126                 nOffset = res.nBlockPos + res.nBlockSize;
127             }
128
129             var fileReader = new BinaryReader(File.OpenRead(BlockFile));
130             var fileStream = fileReader.BaseStream;
131
132             var buffer = new byte[1000000]; // Max block size is 1Mb
133             var intBuffer = new byte[4];
134
135             fileStream.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
136
137             dbConn.BeginTransaction();
138
139             while (fileStream.Read(buffer, 0, 4) == 4) // Read magic number
140             {
141                 var nMagic = BitConverter.ToUInt32(buffer, 0);
142                 if (nMagic != 0xe5e9e8e4)
143                 {
144                     Console.WriteLine("Incorrect magic number.");
145                     break;
146                 }
147
148                 var nBytesRead = fileStream.Read(buffer, 0, 4);
149                 if (nBytesRead != 4)
150                 {
151                     Console.WriteLine("BLKSZ EOF");
152                     break;
153                 }
154
155                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
156
157                 nOffset = fileStream.Position;
158
159                 nBytesRead = fileStream.Read(buffer, 0, nBlockSize);
160
161                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
162                 {
163                     Console.WriteLine("BLK EOF");
164                     break;
165                 }
166
167                 var block = new CBlock(buffer);
168
169                 if (nOffset % 1000 == 0)  // Commit on each 1000th block
170                 {
171                     Console.WriteLine("Offset={0}, Hash: {1}", nOffset, block.header.Hash.ToString());
172                     dbConn.Commit();
173                     dbConn.BeginTransaction();
174                 }
175
176                 var result = dbConn.Insert(new CBlockStoreItem()
177                 {
178                     Hash = block.header.Hash,
179                     BlockHeader = block.header,
180                     BlockTypeFlag = block.IsProofOfStake ? BlockType.PROOF_OF_STAKE : BlockType.PROOF_OF_WORK,
181                     nBlockPos = nOffset,
182                     nBlockSize = nBlockSize
183                 });
184             }
185
186             dbConn.Commit();
187             
188             fileReader.Dispose();
189
190             return true;
191         }
192
193         ~CBlockStore()
194         {
195             Dispose(false);
196         }
197
198         public void Dispose()
199         {
200             Dispose(true);
201             GC.SuppressFinalize(this);
202         }
203
204         protected virtual void Dispose(bool disposing)
205         {
206             if (!disposed)
207             {
208                 if (disposing)
209                 {
210                     // Free other state (managed objects).
211                 }
212
213                 if (dbConn != null)
214                 {
215                     dbConn.Close();
216                     dbConn = null;
217                 }
218
219                 disposed = true;
220             }
221         }
222
223     }
224 }