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