Chain trust score computation.
[NovacoinLibrary.git] / Novacoin / CBlockStore.cs
1 \feffusing System;
2 using System.IO;
3 using System.Linq;
4 using System.Collections.Concurrent;
5
6 using SQLite.Net;
7 using SQLite.Net.Attributes;
8 using SQLite.Net.Interop;
9 using SQLite.Net.Platform.Generic;
10 using SQLiteNetExtensions.Attributes;
11 using System.Collections.Generic;
12
13 namespace Novacoin
14 {
15     /// <summary>
16     /// Block headers table
17     /// </summary>
18     [Table("BlockStorage")]
19     public class CBlockStoreItem
20     {
21         /// <summary>
22         /// Item ID in the database
23         /// </summary>
24         [PrimaryKey, AutoIncrement]
25         public int ItemID { get; set; }
26
27         /// <summary>
28         /// PBKDF2+Salsa20 of block hash
29         /// </summary>
30         [Unique]
31         public byte[] Hash { get; set; }
32
33         /// <summary>
34         /// Version of block schema
35         /// </summary>
36         public uint nVersion { get; set; }
37
38         /// <summary>
39         /// Previous block hash.
40         /// </summary>
41         public byte[] prevHash { get; set; }
42
43         /// <summary>
44         /// Merkle root hash.
45         /// </summary>
46         public byte[] merkleRoot { get; set; }
47
48         /// <summary>
49         /// Block timestamp.
50         /// </summary>
51         public uint nTime { get; set; }
52
53         /// <summary>
54         /// Compressed difficulty representation.
55         /// </summary>
56         public uint nBits { get; set; }
57
58         /// <summary>
59         /// Nonce counter.
60         /// </summary>
61         public uint nNonce { get; set; }
62
63         /// <summary>
64         /// Next block hash.
65         /// </summary>
66         public byte[] nextHash { get; set; }
67
68         /// <summary>
69         /// Block type flags
70         /// </summary>
71         public BlockType BlockTypeFlag { get; set; }
72
73         /// <summary>
74         /// Stake modifier
75         /// </summary>
76         public long nStakeModifier { get; set; }
77
78         /// <summary>
79         /// Chain trust score
80         /// </summary>
81         public byte[] ChainTrust { get; set; }
82
83         /// <summary>
84         /// Proof-of-Stake hash
85         /// </summary>
86         public byte[] hashProofOfStake { get; set; }
87
88         /// <summary>
89         /// Block height
90         /// </summary>
91         public uint nHeight { get; set; }
92
93         /// <summary>
94         /// Block position in file
95         /// </summary>
96         public long nBlockPos { get; set; }
97
98         /// <summary>
99         /// Block size in bytes
100         /// </summary>
101         public int nBlockSize { get; set; }
102
103         /// <summary>
104         /// Fill database item with data from given block header.
105         /// </summary>
106         /// <param name="header">Block header</param>
107         /// <returns>Header hash</returns>
108         public uint256 FillHeader(CBlockHeader header)
109         {
110             uint256 _hash;
111             Hash = _hash = header.Hash;
112
113             nVersion = header.nVersion;
114             prevHash = header.prevHash;
115             merkleRoot = header.merkleRoot;
116             nTime = header.nTime;
117             nBits = header.nBits;
118             nNonce = header.nNonce;
119
120             return _hash;
121         }
122
123         /// <summary>
124         /// Reconstruct block header from item data.
125         /// </summary>
126         public CBlockHeader BlockHeader
127         {
128             get
129             {
130                 CBlockHeader header = new CBlockHeader();
131
132                 header.nVersion = nVersion;
133                 header.prevHash = prevHash;
134                 header.merkleRoot = merkleRoot;
135                 header.nTime = nTime;
136                 header.nBits = nBits;
137                 header.nNonce = nNonce;
138
139                 return header;
140             }
141         }
142
143         /// <summary>
144         /// Read block from file.
145         /// </summary>
146         /// <param name="reader">Stream with read access.</param>
147         /// <param name="reader">CBlock reference.</param>
148         /// <returns>Result</returns>
149         public bool ReadFromFile(ref Stream reader, out CBlock block)
150         {
151             var buffer = new byte[nBlockSize];
152             block = null;
153
154             try
155             {
156                 reader.Seek(nBlockPos, SeekOrigin.Begin);
157
158                 if (nBlockSize != reader.Read(buffer, 0, nBlockSize))
159                 {
160                     return false;
161                 }
162
163                 block = new CBlock(buffer);
164
165                 return true;
166             }
167             catch (IOException)
168             {
169                 // I/O error
170                 return false;
171             }
172             catch (BlockException)
173             {
174                 // Constructor exception
175                 return false;
176             }
177         }
178
179         /// <summary>
180         /// Writes given block to file and prepares cursor object for insertion into the database.
181         /// </summary>
182         /// <param name="writer">Stream with write access.</param>
183         /// <param name="block">CBlock reference.</param>
184         /// <returns>Result</returns>
185         public bool WriteToFile(ref Stream writer, ref CBlock block)
186         {
187             try
188             {
189                 byte[] blockBytes = block;
190
191                 var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber);
192                 var blkLenBytes = BitConverter.GetBytes(blockBytes.Length);
193
194                 // Seek to the end and then append magic bytes there.
195                 writer.Seek(0, SeekOrigin.End);
196                 writer.Write(magicBytes, 0, magicBytes.Length);
197                 writer.Write(blkLenBytes, 0, blkLenBytes.Length);
198
199                 // Save block size and current position in the block cursor fields.
200                 nBlockPos = writer.Position;
201                 nBlockSize = blockBytes.Length;                
202
203                 // Write block and flush the stream.
204                 writer.Write(blockBytes, 0, blockBytes.Length);
205                 writer.Flush();
206
207                 return true;
208             }
209             catch (IOException)
210             {
211                 // I/O error
212                 return false;
213             }
214             catch (Exception)
215             {
216                 // Some serialization error
217                 return false;
218             }
219         }
220
221         /// <summary>
222         /// Previous block cursor
223         /// </summary>
224         [Ignore]
225         public CBlockStoreItem prev {
226             get { return CBlockStore.Instance.GetCursor(prevHash); }
227         }
228
229         /// <summary>
230         /// Next block cursor
231         /// </summary>
232         [Ignore]
233         public CBlockStoreItem next
234         {
235             get { return CBlockStore.Instance.GetCursor(nextHash); }
236         }
237
238         [Ignore]
239         bool IsInMainChain
240         {
241             get { return (next != null); }
242         }
243
244         /// <summary>
245         /// STake modifier generation flag
246         /// </summary>
247         [Ignore]
248         public bool GeneratedStakeModifier
249         {
250             get { return (BlockTypeFlag & BlockType.BLOCK_STAKE_MODIFIER) != 0; }
251         }
252
253         /// <summary>
254         /// Stake entropy bit
255         /// </summary>
256         [Ignore]
257         public uint StakeEntropyBit
258         {
259             get { return ((uint)(BlockTypeFlag & BlockType.BLOCK_STAKE_ENTROPY) >> 1); }
260         }
261
262         /// <summary>
263         /// Sets stake modifier and flag.
264         /// </summary>
265         /// <param name="nModifier">New stake modifier.</param>
266         /// <param name="fGeneratedStakeModifier">Set generation flag?</param>
267         public void SetStakeModifier(long nModifier, bool fGeneratedStakeModifier)
268         {
269             nStakeModifier = nModifier;
270             if (fGeneratedStakeModifier)
271                 BlockTypeFlag |= BlockType.BLOCK_STAKE_MODIFIER;
272         }
273
274         /// <summary>
275         /// Set entropy bit.
276         /// </summary>
277         /// <param name="nEntropyBit">Entropy bit value (0 or 1).</param>
278         /// <returns>False if value is our of range.</returns>
279         public bool SetStakeEntropyBit(byte nEntropyBit)
280         {
281             if (nEntropyBit > 1)
282                 return false;
283             BlockTypeFlag |= (nEntropyBit != 0 ? BlockType.BLOCK_STAKE_ENTROPY : 0);
284             return true;
285         }
286
287         /// <summary>
288         /// Set proof-of-stake flag.
289         /// </summary>
290         public void SetProofOfStake()
291         {
292             BlockTypeFlag |= BlockType.BLOCK_PROOF_OF_STAKE;
293         }
294
295         /// <summary>
296         /// Block has no proof-of-stake flag.
297         /// </summary>
298         [Ignore]
299         public bool IsProofOfWork
300         {
301             get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) == 0; }
302         }
303
304         /// <summary>
305         /// Block has proof-of-stake flag set.
306         /// </summary>
307         [Ignore]
308         public bool IsProofOfStake 
309         {
310             get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) != 0; }
311         }
312
313         /// <summary>
314         /// Chain trust score.
315         /// </summary>
316         [Ignore]
317         public uint256 nChainTrust {
318             get
319             {
320                 if (ChainTrust.Length != 32)
321                 {
322                     byte[] tmp = ChainTrust;
323                     Array.Resize(ref tmp, 32);
324                     ChainTrust = tmp;
325                 }
326
327                 return ChainTrust;
328             }
329             set { ChainTrust = Interop.TrimArray(value); }
330         }
331
332         /// <summary>
333         /// Block trust score.
334         /// </summary>
335         [Ignore]
336         public uint256 nBlockTrust
337         {
338             get
339             {
340                 uint256 nTarget = 0;
341                 nTarget.Compact = nBits;
342
343                 /* Old protocol */
344                 if (nTime < NetUtils.nChainChecksSwitchTime)
345                 {
346                     return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1;
347                 }
348
349                 /* New protocol */
350
351                 // Calculate work amount for block
352                 var nPoWTrust = NetUtils.nPoWBase / (nTarget + 1);
353
354                 // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
355                 nPoWTrust = (IsProofOfStake || !nPoWTrust) ? 1 : nPoWTrust;
356
357                 // Return nPoWTrust for the first 12 blocks
358                 if (prev == null || prev.nHeight < 12)
359                     return nPoWTrust;
360
361                 CBlockStoreItem currentIndex = prev;
362
363                 if (IsProofOfStake)
364                 {
365                     var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
366
367                     // Return 1/3 of score if parent block is not the PoW block
368                     if (!prev.IsProofOfWork)
369                     {
370                         return nNewTrust / 3;
371                     }
372
373                     int nPoWCount = 0;
374
375                     // Check last 12 blocks type
376                     while (prev.nHeight - currentIndex.nHeight < 12)
377                     {
378                         if (currentIndex.IsProofOfWork)
379                         {
380                             nPoWCount++;
381                         }
382                         currentIndex = currentIndex.prev;
383                     }
384
385                     // Return 1/3 of score if less than 3 PoW blocks found
386                     if (nPoWCount < 3)
387                     {
388                         return nNewTrust / 3;
389                     }
390
391                     return nNewTrust;
392                 }
393                 else
394                 {
395                     var nLastBlockTrust = prev.nChainTrust - prev.prev.nChainTrust;
396
397                     // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
398                     if (!prev.IsProofOfStake || !prev.prev.IsProofOfStake)
399                     {
400                         return nPoWTrust + (2 * nLastBlockTrust / 3);
401                     }
402
403                     int nPoSCount = 0;
404
405                     // Check last 12 blocks type
406                     while (prev.nHeight - currentIndex.nHeight < 12)
407                     {
408                         if (currentIndex.IsProofOfStake)
409                         {
410                             nPoSCount++;
411                         }
412                         currentIndex = currentIndex.prev;
413                     }
414
415                     // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
416                     if (nPoSCount < 7)
417                     {
418                         return nPoWTrust + (2 * nLastBlockTrust / 3);
419                     }
420
421                     nTarget.Compact = prev.nBits;
422
423                     if (!nTarget)
424                     {
425                         return 0;
426                     }
427
428                     var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
429
430                     // Return nPoWTrust + full trust score for previous block nBits
431                     return nPoWTrust + nNewTrust;
432                 }
433             }
434         }
435
436     }
437
438     /// <summary>
439     /// Block type.
440     /// </summary>
441     public enum BlockType
442     {
443         BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
444         BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
445         BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
446     };
447
448     /// <summary>
449     /// Transaction type.
450     /// </summary>
451     public enum TxType
452     {
453         TX_COINBASE,
454         TX_COINSTAKE,
455         TX_USER
456     }
457
458     [Table("TransactionStorage")]
459     public class CTransactionStoreItem
460     {
461         /// <summary>
462         /// Transaction hash
463         /// </summary>
464         [PrimaryKey]
465         public byte[] TransactionHash { get; set; }
466
467         /// <summary>
468         /// Block hash
469         /// </summary>
470         [ForeignKey(typeof(CBlockStoreItem), Name = "Hash")]
471         public byte[] BlockHash { get; set; }
472
473         /// <summary>
474         /// Transaction type flag
475         /// </summary>
476         public TxType txType { get; set; }
477
478         /// <summary>
479         /// Tx position in file
480         /// </summary>
481         public long nTxPos { get; set; }
482
483         /// <summary>
484         /// Transaction size
485         /// </summary>
486         public int nTxSize { get; set; }
487
488         /// <summary>
489         /// Read transaction from file.
490         /// </summary>
491         /// <param name="reader">Stream with read access.</param>
492         /// <param name="tx">CTransaction reference.</param>
493         /// <returns>Result</returns>
494         public bool ReadFromFile(ref Stream reader, out CTransaction tx)
495         {
496             var buffer = new byte[CTransaction.nMaxTxSize];
497             tx = null;
498
499             try
500             {
501                 reader.Seek(nTxPos, SeekOrigin.Begin); // Seek to transaction offset
502
503                 if (nTxSize != reader.Read(buffer, 0, nTxSize))
504                 {
505                     return false;
506                 }
507
508                 tx = new CTransaction(buffer);
509
510                 return true;
511             }
512             catch (IOException)
513             {
514                 // I/O error
515                 return false;
516             }
517             catch (TransactionConstructorException)
518             {
519                 // Constructor error
520                 return false;
521             }
522         }
523     }
524
525     public class CBlockStore : IDisposable
526     {
527         public const uint nMagicNumber = 0xe5e9e8e4;
528
529         private bool disposed = false;
530         private object LockObj = new object();
531
532         /// <summary>
533         /// SQLite connection object.
534         /// </summary>
535         private SQLiteConnection dbConn;
536
537         /// <summary>
538         /// Block file.
539         /// </summary>
540         private string strBlockFile;
541
542         /// <summary>
543         /// Index database file.
544         /// </summary>
545         private string strDbFile;
546
547         /// <summary>
548         /// Map of block tree nodes.
549         /// </summary>
550         private ConcurrentDictionary<uint256, CBlockStoreItem> blockMap = new ConcurrentDictionary<uint256, CBlockStoreItem>();
551
552         /// <summary>
553         /// Orphaned blocks map.
554         /// </summary>
555         private ConcurrentDictionary<uint256, CBlock> orphanMap = new ConcurrentDictionary<uint256, CBlock>();
556         private ConcurrentDictionary<uint256, CBlock> orphanMapByPrev = new ConcurrentDictionary<uint256, CBlock>();
557
558         /// <summary>
559         /// Map of unspent items.
560         /// </summary>
561         private ConcurrentDictionary<uint256, CTransactionStoreItem> txMap = new ConcurrentDictionary<uint256, CTransactionStoreItem>();
562
563         public static CBlockStore Instance;
564
565         /// <summary>
566         /// Block file stream with read access
567         /// </summary>
568         private Stream fStreamReadWrite;
569
570         /// <summary>
571         /// Init the block storage manager.
572         /// </summary>
573         /// <param name="IndexDB">Path to index database</param>
574         /// <param name="BlockFile">Path to block file</param>
575         public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
576         {
577             strDbFile = IndexDB;
578             strBlockFile = BlockFile;
579
580             bool firstInit = !File.Exists(strDbFile);
581             dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), strDbFile);
582
583             fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
584
585             Instance = this;
586
587             if (firstInit)
588             {
589                 lock (LockObj)
590                 {
591                     // Create tables
592                     dbConn.CreateTable<CBlockStoreItem>(CreateFlags.AutoIncPK);
593                     dbConn.CreateTable<CTransactionStoreItem>(CreateFlags.ImplicitPK);
594
595                     var genesisBlock = new CBlock(
596                         Interop.HexToArray(
597                             "01000000" + // nVersion=1
598                             "0000000000000000000000000000000000000000000000000000000000000000" + // prevhash is zero
599                             "7b0502ad2f9f675528183f83d6385794fbcaa914e6d385c6cb1d866a3b3bb34c" + // merkle root
600                             "398e1151" + // nTime=1360105017
601                             "ffff0f1e" + // nBits=0x1e0fffff
602                             "d3091800" + // nNonce=1575379
603                             "01" +       // nTxCount=1
604                             "01000000" + // nVersion=1
605                             "398e1151" + // nTime=1360105017
606                             "01" +       // nInputs=1
607                             "0000000000000000000000000000000000000000000000000000000000000000" + // input txid is zero
608                             "ffffffff" + // n=uint.maxValue
609                             "4d" +       // scriptSigLen=77
610                             "04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936" + // scriptSig
611                             "ffffffff" + // nSequence=uint.maxValue
612                             "01" +       // nOutputs=1
613                             "0000000000000000" + // nValue=0
614                             "00" +       // scriptPubkeyLen=0
615                             "00000000" + // nLockTime=0
616                             "00"         // sigLen=0
617                     ));
618
619                     // Write block to file.
620                     var itemTemplate = new CBlockStoreItem()
621                     {
622                         nHeight = 0
623                     };
624
625                     itemTemplate.FillHeader(genesisBlock.header);
626
627                     if (!AddItemToIndex(ref itemTemplate, ref genesisBlock))
628                     {
629                         throw new Exception("Unable to write genesis block");
630                     }
631                 }
632             }
633             else
634             {
635                 var blockTreeItems = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] order by [ItemId] asc");
636
637                 // Init list of block items
638                 foreach (var item in blockTreeItems)
639                 {
640                     blockMap.TryAdd(item.Hash, item);
641                 }
642             }
643         }
644
645         public bool GetTransaction(uint256 TxID, ref CTransaction tx)
646         {
647             var reader = new BinaryReader(fStreamReadWrite).BaseStream;
648             var QueryTx = dbConn.Query<CTransactionStoreItem>("select * from [TransactionStorage] where [TransactionHash] = ?", (byte[])TxID);
649
650             if (QueryTx.Count == 1)
651             {
652                 return QueryTx[0].ReadFromFile(ref reader, out tx);
653             }
654
655             // Tx not found
656
657             return false;
658         }
659
660         private bool AddItemToIndex(ref CBlockStoreItem itemTemplate, ref CBlock block)
661         {
662             var writer = new BinaryWriter(fStreamReadWrite).BaseStream;
663             uint256 blockHash = itemTemplate.Hash;
664
665             if (blockMap.ContainsKey(blockHash))
666             {
667                 // Already have this block.
668                 return false;
669             }
670
671             // Compute chain trust score
672             itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
673
674             if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash)))
675             {
676                 return false; // SetStakeEntropyBit() failed
677             }
678
679             // TODO: set stake entropy bit, record proof-of-stake hash value
680
681             // TODO: compute stake modifier
682
683             // Add to index
684             if (block.IsProofOfStake)
685             {
686                 itemTemplate.SetProofOfStake();
687             }
688
689             if (!itemTemplate.WriteToFile(ref writer, ref block))
690             {
691                 return false;
692             }
693
694             dbConn.Insert(itemTemplate);
695
696             // We have no SetBestChain and ConnectBlock/Disconnect block yet, so adding these transactions manually.
697             for (int i = 0; i < block.vtx.Length; i++)
698             {
699                 // Handle trasactions
700
701                 if (!block.vtx[i].VerifyScripts())
702                 {
703                     return false;
704                 }
705
706                 var nTxOffset = itemTemplate.nBlockPos + block.GetTxOffset(i);
707                 TxType txnType = TxType.TX_USER;
708
709                 if (block.vtx[i].IsCoinBase)
710                 {
711                     txnType = TxType.TX_COINBASE;
712                 }
713                 else if (block.vtx[i].IsCoinStake)
714                 {
715                     txnType = TxType.TX_COINSTAKE;
716                 }
717
718                 var NewTxItem = new CTransactionStoreItem()
719                 {
720                     TransactionHash = block.vtx[i].Hash,
721                     BlockHash = blockHash,
722                     nTxPos = nTxOffset,
723                     nTxSize = block.vtx[i].Size,
724                     txType = txnType
725                 };
726
727                 dbConn.Insert(NewTxItem);
728             }
729
730             return blockMap.TryAdd(blockHash, itemTemplate);
731         }
732
733         public bool AcceptBlock(ref CBlock block)
734         {
735             uint256 nHash = block.header.Hash;
736
737             if (blockMap.ContainsKey(nHash))
738             {
739                 // Already have this block.
740                 return false;
741             }
742
743             CBlockStoreItem prevBlockCursor = null;
744             if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor))
745             {
746                 // Unable to get the cursor.
747                 return false;
748             }
749
750             var prevBlockHeader = prevBlockCursor.BlockHeader;
751
752             // TODO: proof-of-work/proof-of-stake verification
753             uint nHeight = prevBlockCursor.nHeight + 1;
754
755             // Check timestamp against prev
756             if (NetUtils.FutureDrift(block.header.nTime) < prevBlockHeader.nTime)
757             {
758                 // block's timestamp is too early
759                 return false;
760             }
761
762             // Check that all transactions are finalized
763             foreach (var tx in block.vtx)
764             {
765                 if (!tx.IsFinal(nHeight, block.header.nTime))
766                 {
767                     return false;
768                 }
769             }
770
771             // TODO: Enforce rule that the coinbase starts with serialized block height
772
773             // Write block to file.
774             var itemTemplate = new CBlockStoreItem()
775             {
776                 nHeight = nHeight,
777             };
778
779             itemTemplate.FillHeader(block.header);
780
781             if (!AddItemToIndex(ref itemTemplate, ref block))
782             {
783                 return false;
784             }
785
786             return true;
787         }
788
789         public bool GetBlock(uint256 blockHash, ref CBlock block)
790         {
791             var reader = new BinaryReader(fStreamReadWrite).BaseStream;
792
793             var QueryBlock = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
794
795             if (QueryBlock.Count == 1)
796             {
797                 return QueryBlock[0].ReadFromFile(ref reader, out block);
798             }
799
800             // Block not found
801
802             return false;
803         }
804
805         /// <summary>
806         /// Get block cursor from map.
807         /// </summary>
808         /// <param name="blockHash">block hash</param>
809         /// <returns>Cursor or null</returns>
810         public CBlockStoreItem GetCursor(uint256 blockHash)
811         {
812             if (blockHash == 0)
813             {
814                 // Genesis block has zero prevHash and no parent.
815                 return null;
816             }
817
818             // First, check our block map.
819             CBlockStoreItem item = null;
820             if (blockMap.TryGetValue(blockHash, out item))
821             {
822                 return item;
823             }
824
825             // Trying to get cursor from the database.
826             var QueryBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
827
828             if (QueryBlockCursor.Count == 1)
829             {
830                 return QueryBlockCursor[0];
831             }
832
833             // Nothing found.
834             return null;
835         }
836
837         public bool ProcessBlock(ref CBlock block)
838         {
839             var blockHash = block.header.Hash;
840
841             if (blockMap.ContainsKey(blockHash))
842             {
843                 // We already have this block.
844                 return false;
845             }
846
847             if (orphanMap.ContainsKey(blockHash))
848             {
849                 // We already have block in the list of orphans.
850                 return false;
851             }
852
853             // TODO: Limited duplicity on stake and reserialization of block signature
854
855             if (!block.CheckBlock(true, true, true))
856             {
857                 // Preliminary checks failure.
858                 return false;
859             }
860
861             if (block.IsProofOfStake)
862             {
863                 if (!block.SignatureOK || !block.vtx[1].VerifyScripts())
864                 {
865                     // Proof-of-Stake signature validation failure.
866                     return false;
867                 }
868
869                 // TODO: proof-of-stake validation
870             }
871
872             // TODO: difficulty verification
873
874             // If don't already have its previous block, shunt it off to holding area until we get it
875             if (!blockMap.ContainsKey(block.header.prevHash))
876             {
877                 if (block.IsProofOfStake)
878                 {
879                     // TODO: limit duplicity on stake
880                 }
881
882                 var block2 = new CBlock(block);
883                 orphanMap.TryAdd(blockHash, block2);
884                 orphanMapByPrev.TryAdd(blockHash, block2);
885
886                 return true;
887             }
888
889             // Store block to disk
890             if (!AcceptBlock(ref block))
891             {
892                 // Accept failed
893                 return false;
894             }
895
896             // Recursively process any orphan blocks that depended on this one
897             var orphansQueue = new List<uint256>();
898             orphansQueue.Add(blockHash);
899
900             for (int i = 0; i < orphansQueue.Count; i++)
901             {
902                 var hashPrev = orphansQueue[i];
903
904                 foreach (var pair in orphanMap)
905                 {
906                     var orphanBlock = pair.Value;
907
908                     if (orphanBlock.header.prevHash == blockHash)
909                     {
910                         if (AcceptBlock(ref orphanBlock))
911                         {
912                             orphansQueue.Add(pair.Key);
913                         }
914
915                         CBlock dummy1;
916                         orphanMap.TryRemove(pair.Key, out dummy1);
917                     }
918                 }
919
920                 CBlock dummy2;
921                 orphanMap.TryRemove(hashPrev, out dummy2);
922             }
923
924             return true;
925         }
926
927         public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
928         {
929             // TODO: Rewrite completely.
930
931             var nOffset = 0L;
932
933             var buffer = new byte[CBlock.nMaxBlockSize]; // Max block size is 1Mb
934             var intBuffer = new byte[4];
935
936             var fStream2 = File.OpenRead(BlockFile);
937             var readerForBlocks = new BinaryReader(fStream2).BaseStream;
938
939             readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
940
941             dbConn.BeginTransaction();
942
943             while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
944             {
945                 var nMagic = BitConverter.ToUInt32(buffer, 0);
946                 if (nMagic != 0xe5e9e8e4)
947                 {
948                     throw new Exception("Incorrect magic number.");
949                 }
950
951                 var nBytesRead = readerForBlocks.Read(buffer, 0, 4);
952                 if (nBytesRead != 4)
953                 {
954                     throw new Exception("BLKSZ EOF");
955                 }
956
957                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
958
959                 nOffset = readerForBlocks.Position;
960
961                 nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize);
962
963                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
964                 {
965                     throw new Exception("BLK EOF");
966                 }
967
968                 var block = new CBlock(buffer);
969                 var hash = block.header.Hash;
970
971                 if (blockMap.ContainsKey(hash))
972                 {
973                     continue;
974                 }
975
976                 if (!ProcessBlock(ref block))
977                 {
978                     throw new Exception("Invalid block: " + block.header.Hash);
979                 }
980
981                 int nCount = blockMap.Count;
982                 Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
983
984                 if (nCount % 100 == 0 && nCount != 0)
985                 {
986                     Console.WriteLine("Commit...");
987                     dbConn.Commit();
988                     dbConn.BeginTransaction();
989                 }
990             }
991
992             dbConn.Commit();
993
994             return true;
995         }
996
997         ~CBlockStore()
998         {
999             Dispose(false);
1000         }
1001
1002         public void Dispose()
1003         {
1004             Dispose(true);
1005             GC.SuppressFinalize(this);
1006         }
1007
1008         protected virtual void Dispose(bool disposing)
1009         {
1010             if (!disposed)
1011             {
1012                 if (disposing)
1013                 {
1014                     // Free other state (managed objects).
1015
1016                     fStreamReadWrite.Dispose();
1017                 }
1018
1019                 if (dbConn != null)
1020                 {
1021                     dbConn.Close();
1022                     dbConn = null;
1023                 }
1024
1025                 disposed = true;
1026             }
1027         }
1028
1029     }
1030 }