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