a4466abf6dc697e8ae4bad0a0abaac04304af89b
[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 using System.Diagnostics.Contracts;
32 using System.Text;
33
34 namespace Novacoin
35 {
36     [Table("BlockStorage")]
37     public class CBlockStoreItem : IBlockStorageItem
38     {
39         #region IBlockStorageItem
40         /// <summary>
41         /// Item ID in the database
42         /// </summary>
43         [PrimaryKey, AutoIncrement]
44         public long 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         [Column("nVersion")]
56         public uint nVersion { get; set; }
57
58         /// <summary>
59         /// Previous block hash.
60         /// </summary>
61         [Column("prevHash")]
62         public byte[] prevHash { get; set; }
63
64         /// <summary>
65         /// Merkle root hash.
66         /// </summary>
67         [Column("merkleRoot")]
68         public byte[] merkleRoot { get; set; }
69
70         /// <summary>
71         /// Block timestamp.
72         /// </summary>
73         [Column("nTime")]
74         public uint nTime { get; set; }
75
76         /// <summary>
77         /// Compressed difficulty representation.
78         /// </summary>
79         [Column("nBits")]
80         public uint nBits { get; set; }
81
82         /// <summary>
83         /// Nonce counter.
84         /// </summary>
85         [Column("nNonce")]
86         public uint nNonce { get; set; }
87
88         /// <summary>
89         /// Next block hash.
90         /// </summary>
91         [Column("nextHash")]
92         public byte[] nextHash { get; set; }
93
94         /// <summary>
95         /// Block type flags
96         /// </summary>
97         [Column("BlockTypeFlag")]
98         public BlockType BlockTypeFlag { get; set; }
99
100         /// <summary>
101         /// Stake modifier
102         /// </summary>
103         [Column("nStakeModifier")]
104         public long nStakeModifier { get; set; }
105
106         /// <summary>
107         /// Proof-of-Stake hash
108         /// </summary>
109         [Column("hashProofOfStake")]
110         public byte[] hashProofOfStake { get; set; }
111
112         /// <summary>
113         /// Stake generation outpoint.
114         /// </summary>
115         [Column("prevoutStake")]
116         public byte[] prevoutStake { get; set; }
117
118         /// <summary>
119         /// Stake generation time.
120         /// </summary>
121         [Column("nStakeTime")]
122         public uint nStakeTime { get; set; }
123
124         /// <summary>
125         /// Block height, encoded in VarInt format
126         /// </summary>
127         [Column("nHeight")]
128         public uint nHeight { get; set; }
129
130         /// <summary>
131         /// Chain trust score, serialized and trimmed uint256 representation.
132         /// </summary>
133         [Column("ChainTrust")]
134         public byte[] ChainTrust { get; set; }
135
136         /// <summary>
137         /// Block position in file, encoded in VarInt format
138         /// </summary>
139         [Column("BlockPos")]
140         public byte[] BlockPos { get; set; }
141
142         /// <summary>
143         /// Block size in bytes, encoded in VarInt format
144         /// </summary>
145         [Column("BlockSize")]
146         public byte[] BlockSize { get; set; }
147         #endregion
148
149         /// <summary>
150         /// Accessor and mutator for BlockPos value.
151         /// </summary>
152         [Ignore]
153         public long nBlockPos
154         {
155             get { return (long)VarInt.DecodeVarInt(BlockPos); }
156             set { BlockPos = VarInt.EncodeVarInt(value); }
157         }
158
159         /// <summary>
160         /// Accessor and mutator for BlockSize value.
161         /// </summary>
162         [Ignore]
163         public int nBlockSize
164         {
165             get { return (int)VarInt.DecodeVarInt(BlockSize); }
166             set { BlockSize = VarInt.EncodeVarInt(value); }
167         }
168
169         /// <summary>
170         /// Fill database item with data from given block header.
171         /// </summary>
172         /// <param name="header">Block header</param>
173         /// <returns>Header hash</returns>
174         public uint256 FillHeader(CBlockHeader header)
175         {
176             uint256 _hash = header.Hash;
177
178             Hash = _hash;
179
180             nVersion = header.nVersion;
181             prevHash = header.prevHash;
182             merkleRoot = header.merkleRoot;
183             nTime = header.nTime;
184             nBits = header.nBits;
185             nNonce = header.nNonce;
186
187             return _hash;
188         }
189
190         /// <summary>
191         /// Reconstruct block header from item data.
192         /// </summary>
193         public CBlockHeader BlockHeader
194         {
195             get
196             {
197                 CBlockHeader header = new CBlockHeader();
198
199                 header.nVersion = nVersion;
200                 header.prevHash = prevHash;
201                 header.merkleRoot = merkleRoot;
202                 header.nTime = nTime;
203                 header.nBits = nBits;
204                 header.nNonce = nNonce;
205
206                 return header;
207             }
208         }
209
210         /// <summary>
211         /// Read block from file.
212         /// </summary>
213         /// <param name="reader">Stream with read access.</param>
214         /// <param name="reader">CBlock reference.</param>
215         /// <returns>Result</returns>
216         public bool ReadFromFile(ref Stream reader, out CBlock block)
217         {
218             var buffer = new byte[nBlockSize];
219             block = null;
220
221             try
222             {
223                 reader.Seek(nBlockPos, SeekOrigin.Begin);
224
225                 if (nBlockSize != reader.Read(buffer, 0, nBlockSize))
226                 {
227                     return false;
228                 }
229
230                 block = new CBlock(buffer);
231
232                 return true;
233             }
234             catch (IOException)
235             {
236                 // I/O error
237                 return false;
238             }
239             catch (BlockException)
240             {
241                 // Constructor exception
242                 return false;
243             }
244         }
245
246         /// <summary>
247         /// Writes given block to file and prepares cursor object for insertion into the database.
248         /// </summary>
249         /// <param name="writer">Stream with write access.</param>
250         /// <param name="block">CBlock reference.</param>
251         /// <returns>Result</returns>
252         public bool WriteToFile(ref Stream writer, ref CBlock block)
253         {
254             try
255             {
256                 byte[] blockBytes = block;
257
258                 var magicBytes = BitConverter.GetBytes(CBlockStore.nMagicNumber);
259                 var blkLenBytes = BitConverter.GetBytes(blockBytes.Length);
260
261                 // Seek to the end and then append magic bytes there.
262                 writer.Seek(0, SeekOrigin.End);
263                 writer.Write(magicBytes, 0, magicBytes.Length);
264                 writer.Write(blkLenBytes, 0, blkLenBytes.Length);
265
266                 // Save block size and current position in the block cursor fields.
267                 nBlockPos = writer.Position;
268                 nBlockSize = blockBytes.Length;                
269
270                 // Write block and flush the stream.
271                 writer.Write(blockBytes, 0, blockBytes.Length);
272                 writer.Flush();
273
274                 return true;
275             }
276             catch (IOException)
277             {
278                 // I/O error
279                 return false;
280             }
281             catch (Exception)
282             {
283                 // Some serialization error
284                 return false;
285             }
286         }
287
288         /// <summary>
289         /// Previous block cursor
290         /// </summary>
291         [Ignore]
292         public CBlockStoreItem prev {
293             get { return CBlockStore.Instance.GetCursor(prevHash); }
294         }
295
296         /// <summary>
297         /// Next block cursor
298         /// </summary>
299         [Ignore]
300         public CBlockStoreItem next
301         {
302             get
303             {
304                 if (nextHash == null)
305                 {
306                     return null;
307                 }
308
309                 return CBlockStore.Instance.GetCursor(nextHash);
310             }
311             set
312             {
313                 CBlockStoreItem newCursor = this;
314                 newCursor.nextHash = value.Hash;
315
316                 CBlockStore.Instance.UpdateCursor(this, ref newCursor);
317             }
318         }
319
320         [Ignore]
321         bool IsInMainChain
322         {
323             get { return (next != null); }
324         }
325
326         /// <summary>
327         /// STake modifier generation flag
328         /// </summary>
329         [Ignore]
330         public bool GeneratedStakeModifier
331         {
332             get { return (BlockTypeFlag & BlockType.BLOCK_STAKE_MODIFIER) != 0; }
333         }
334
335         /// <summary>
336         /// Stake entropy bit
337         /// </summary>
338         [Ignore]
339         public uint StakeEntropyBit
340         {
341             get { return ((uint)(BlockTypeFlag & BlockType.BLOCK_STAKE_ENTROPY) >> 1); }
342         }
343
344         /// <summary>
345         /// Sets stake modifier and flag.
346         /// </summary>
347         /// <param name="nModifier">New stake modifier.</param>
348         /// <param name="fGeneratedStakeModifier">Set generation flag?</param>
349         public void SetStakeModifier(long nModifier, bool fGeneratedStakeModifier)
350         {
351             nStakeModifier = nModifier;
352             if (fGeneratedStakeModifier)
353                 BlockTypeFlag |= BlockType.BLOCK_STAKE_MODIFIER;
354         }
355
356         /// <summary>
357         /// Set entropy bit.
358         /// </summary>
359         /// <param name="nEntropyBit">Entropy bit value (0 or 1).</param>
360         /// <returns>False if value is our of range.</returns>
361         public bool SetStakeEntropyBit(byte nEntropyBit)
362         {
363             if (nEntropyBit > 1)
364                 return false;
365             BlockTypeFlag |= (nEntropyBit != 0 ? BlockType.BLOCK_STAKE_ENTROPY : 0);
366             return true;
367         }
368
369         /// <summary>
370         /// Set proof-of-stake flag.
371         /// </summary>
372         public void SetProofOfStake()
373         {
374             BlockTypeFlag |= BlockType.BLOCK_PROOF_OF_STAKE;
375         }
376
377         /// <summary>
378         /// Block has no proof-of-stake flag.
379         /// </summary>
380         [Ignore]
381         public bool IsProofOfWork
382         {
383             get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) == 0; }
384         }
385
386         /// <summary>
387         /// Block has proof-of-stake flag set.
388         /// </summary>
389         [Ignore]
390         public bool IsProofOfStake 
391         {
392             get { return (BlockTypeFlag & BlockType.BLOCK_PROOF_OF_STAKE) != 0; }
393         }
394
395         /// <summary>
396         /// Block trust score.
397         /// </summary>
398         [Ignore]
399         public uint256 nBlockTrust
400         {
401             get
402             {
403                 uint256 nTarget = 0;
404                 nTarget.Compact = nBits;
405
406                 /* Old protocol */
407                 if (nTime < NetUtils.nChainChecksSwitchTime)
408                 {
409                     return IsProofOfStake ? (new uint256(1) << 256) / (nTarget + 1) : 1;
410                 }
411
412                 /* New protocol */
413
414                 // Calculate work amount for block
415                 var nPoWTrust = NetUtils.nPoWBase / (nTarget + 1);
416
417                 // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low
418                 nPoWTrust = (IsProofOfStake || !nPoWTrust) ? 1 : nPoWTrust;
419
420                 // Return nPoWTrust for the first 12 blocks
421                 if (prev == null || prev.nHeight < 12)
422                     return nPoWTrust;
423
424                 CBlockStoreItem currentIndex = prev;
425
426                 if (IsProofOfStake)
427                 {
428                     var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
429
430                     // Return 1/3 of score if parent block is not the PoW block
431                     if (!prev.IsProofOfWork)
432                     {
433                         return nNewTrust / 3;
434                     }
435
436                     int nPoWCount = 0;
437
438                     // Check last 12 blocks type
439                     while (prev.nHeight - currentIndex.nHeight < 12)
440                     {
441                         if (currentIndex.IsProofOfWork)
442                         {
443                             nPoWCount++;
444                         }
445                         currentIndex = currentIndex.prev;
446                     }
447
448                     // Return 1/3 of score if less than 3 PoW blocks found
449                     if (nPoWCount < 3)
450                     {
451                         return nNewTrust / 3;
452                     }
453
454                     return nNewTrust;
455                 }
456                 else
457                 {
458                     var nLastBlockTrust = prev.nChainTrust - prev.prev.nChainTrust;
459
460                     // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks
461                     if (!prev.IsProofOfStake || !prev.prev.IsProofOfStake)
462                     {
463                         return nPoWTrust + (2 * nLastBlockTrust / 3);
464                     }
465
466                     int nPoSCount = 0;
467
468                     // Check last 12 blocks type
469                     while (prev.nHeight - currentIndex.nHeight < 12)
470                     {
471                         if (currentIndex.IsProofOfStake)
472                         {
473                             nPoSCount++;
474                         }
475                         currentIndex = currentIndex.prev;
476                     }
477
478                     // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found
479                     if (nPoSCount < 7)
480                     {
481                         return nPoWTrust + (2 * nLastBlockTrust / 3);
482                     }
483
484                     nTarget.Compact = prev.nBits;
485
486                     if (!nTarget)
487                     {
488                         return 0;
489                     }
490
491                     var nNewTrust = (new uint256(1) << 256) / (nTarget + 1);
492
493                     // Return nPoWTrust + full trust score for previous block nBits
494                     return nPoWTrust + nNewTrust;
495                 }
496             }
497         }
498
499         /// <summary>
500         /// Stake modifier checksum.
501         /// </summary>
502         public uint nStakeModifierChecksum;
503
504         /// <summary>
505         /// Chain trust score
506         /// </summary>
507         [Ignore]
508         public uint256 nChainTrust {
509             get { return Interop.AppendWithZeros(ChainTrust); }
510             set { ChainTrust = Interop.TrimArray(value); }
511         }
512     }
513
514     /// <summary>
515     /// Block type.
516     /// </summary>
517     public enum BlockType
518     {
519         BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
520         BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
521         BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
522     };
523
524     /// <summary>
525     /// Transaction type.
526     /// </summary>
527     public enum TxFlags : byte
528     {
529         TX_COINBASE,
530         TX_COINSTAKE,
531         TX_USER
532     }
533
534     /// <summary>
535     /// Output flags.
536     /// </summary>
537     public enum OutputFlags : byte
538     {
539         AVAILABLE, // Unspent output
540         SPENT      // Spent output
541     }
542
543     [Table("MerkleNodes")]
544     public class CMerkleNode : IMerkleNode
545     {
546         #region IMerkleNode
547         /// <summary>
548         /// Node identifier
549         /// </summary>
550         [PrimaryKey, AutoIncrement]
551         public long nMerkleNodeID { get; set; }
552
553         /// <summary>
554         /// Reference to parent block database item.
555         /// </summary>
556         [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")]
557         public long nParentBlockID { get; set; }
558
559         /// <summary>
560         /// Transaction type flag
561         /// </summary>
562         [Column("TransactionFlags")]
563         public TxFlags TransactionFlags { get; set; }
564
565         /// <summary>
566         /// Transaction hash
567         /// </summary>
568         [Column("TransactionHash")]
569         public byte[] TransactionHash { get; set; }
570
571         /// <summary>
572         /// Transaction offset from the beginning of block header, encoded in VarInt format.
573         /// </summary>
574         [Column("TxOffset")]
575         public byte[] TxOffset { get; set; }
576
577         /// <summary>
578         /// Transaction size, encoded in VarInt format.
579         /// </summary>
580         [Column("TxSize")]
581         public byte[] TxSize { get; set; }
582         #endregion
583
584         /// <summary>
585         /// Read transaction from file.
586         /// </summary>
587         /// <param name="reader">Stream with read access.</param>
588         /// <param name="tx">CTransaction reference.</param>
589         /// <returns>Result</returns>
590         public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx)
591         {
592             var buffer = new byte[CTransaction.nMaxTxSize];
593
594             tx = null;
595
596             try
597             {
598                 reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset
599
600                 if (nTxSize != reader.Read(buffer, 0, nTxSize))
601                 {
602                     return false;
603                 }
604
605                 tx = new CTransaction(buffer);
606
607                 return true;
608             }
609             catch (IOException)
610             {
611                 // I/O error
612                 return false;
613             }
614             catch (TransactionConstructorException)
615             {
616                 // Constructor error
617                 return false;
618             }
619         }
620
621         /// <summary>
622         /// Transaction offset accessor
623         /// </summary>
624         [Ignore]
625         public long nTxOffset
626         {
627             get { return (long) VarInt.DecodeVarInt(TxOffset); }
628         }
629
630         /// <summary>
631         /// Transaction size accessor
632         /// </summary>
633         [Ignore]
634         public int nTxSize
635         {
636             get { return (int)VarInt.DecodeVarInt(TxSize); }
637         }
638
639     }
640
641     [Table("Outputs")]
642     public class TxOutItem : ITxOutItem
643     {
644         /// <summary>
645         /// Reference to transaction item.
646         /// </summary>
647         [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")]
648         public long nMerkleNodeID { get; set; }
649
650         /// <summary>
651         /// Output flags
652         /// </summary>
653         public OutputFlags outputFlags { get; set; }
654
655         /// <summary>
656         /// Output number in VarInt format.
657         /// </summary>
658         public byte[] OutputNumber { get; set; }
659
660         /// <summary>
661         /// Output value in VarInt format.
662         /// </summary>
663         public byte[] OutputValue { get; set; }
664
665         /// <summary>
666         /// Second half of script which contains spending instructions.
667         /// </summary>
668         public byte[] scriptPubKey { get; set; }
669
670         /// <summary>
671         /// Getter for output number.
672         /// </summary>
673         public uint nOut
674         {
675             get { return (uint)VarInt.DecodeVarInt(OutputNumber); }
676         }
677
678         /// <summary>
679         /// Getter for output value.
680         /// </summary>
681         public ulong nValue
682         {
683             get { return VarInt.DecodeVarInt(OutputValue); }
684         }
685
686         /// <summary>
687         /// Getter ans setter for IsSpent flag.
688         /// </summary>
689         public bool IsSpent
690         {
691             get { return (outputFlags & OutputFlags.SPENT) != 0; }
692             set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; }
693         }
694     }
695
696     public class CBlockStore : IDisposable
697     {
698         public const uint nMagicNumber = 0xe5e9e8e4;
699
700         private bool disposed = false;
701         private object LockObj = new object();
702
703         /// <summary>
704         /// SQLite connection object.
705         /// </summary>
706         private SQLiteConnection dbConn;
707
708         /// <summary>
709         /// Current SQLite platform
710         /// </summary>
711         private ISQLitePlatform dbPlatform;
712
713         /// <summary>
714         /// Block file.
715         /// </summary>
716         private string strBlockFile;
717
718         /// <summary>
719         /// Index database file.
720         /// </summary>
721         private string strDbFile;
722
723         /// <summary>
724         /// Map of block tree nodes.
725         /// 
726         /// blockHash => CBlockStoreItem
727         /// </summary>
728         private ConcurrentDictionary<uint256, CBlockStoreItem> blockMap = new ConcurrentDictionary<uint256, CBlockStoreItem>();
729
730         /// <summary>
731         /// Orphaned blocks map.
732         /// </summary>
733         private ConcurrentDictionary<uint256, CBlock> orphanMap = new ConcurrentDictionary<uint256, CBlock>();
734         private ConcurrentDictionary<uint256, CBlock> orphanMapByPrev = new ConcurrentDictionary<uint256, CBlock>();
735
736         /// <summary>
737         /// Unconfirmed transactions.
738         /// 
739         /// TxID => Transaction
740         /// </summary>
741         private ConcurrentDictionary<uint256, CTransaction> mapUnconfirmedTx = new ConcurrentDictionary<uint256, CTransaction>();
742
743         /// <summary>
744         /// Map of the proof-of-stake hashes. This is necessary for stake duplication checks.
745         /// </summary>
746         private ConcurrentDictionary<uint256, uint256> mapProofOfStake = new ConcurrentDictionary<uint256, uint256>();
747
748
749         private ConcurrentDictionary<COutPoint, uint> mapStakeSeen = new ConcurrentDictionary<COutPoint, uint>();
750         private ConcurrentDictionary<COutPoint, uint> mapStakeSeenOrphan = new ConcurrentDictionary<COutPoint, uint>();
751
752         /// <summary>
753         /// Trust score for the longest chain.
754         /// </summary>
755         private uint256 nBestChainTrust = 0;
756
757         /// <summary>
758         /// Top block of the best chain.
759         /// </summary>
760         private uint256 nHashBestChain = 0;
761
762         /// <summary>
763         /// Cursor which is pointing us to the end of best chain.
764         /// </summary>
765         private CBlockStoreItem bestBlockCursor = null;
766
767         /// <summary>
768         /// Cursor which is always pointing us to genesis block.
769         /// </summary>
770         private CBlockStoreItem genesisBlockCursor = null;
771
772         /// <summary>
773         /// Current and the only instance of block storage manager. Should be a property with private setter though it's enough for the beginning.
774         /// </summary>
775         public static CBlockStore Instance = null;
776
777         /// <summary>
778         /// Block file stream with read/write access
779         /// </summary>
780         private Stream fStreamReadWrite;
781         private uint nBestHeight;
782         private uint nTimeBestReceived;
783         private int nTransactionsUpdated;
784
785         /// <summary>
786         /// Init the block storage manager.
787         /// </summary>
788         /// <param name="IndexDB">Path to index database</param>
789         /// <param name="BlockFile">Path to block file</param>
790         public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
791         {
792             strDbFile = IndexDB;
793             strBlockFile = BlockFile;
794
795             bool firstInit = !File.Exists(strDbFile);
796             dbPlatform = new SQLitePlatformGeneric();
797             dbConn = new SQLiteConnection(dbPlatform, strDbFile);
798
799             fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
800
801             Instance = this;
802
803             if (firstInit)
804             {
805                 lock (LockObj)
806                 {
807                     // Create tables
808                     dbConn.CreateTable<CBlockStoreItem>(CreateFlags.AutoIncPK);
809                     dbConn.CreateTable<CMerkleNode>(CreateFlags.AutoIncPK);
810                     dbConn.CreateTable<TxOutItem>(CreateFlags.ImplicitPK);
811
812                     var genesisBlock = new CBlock(
813                         Interop.HexToArray(
814                             "01000000" + // nVersion=1
815                             "0000000000000000000000000000000000000000000000000000000000000000" + // prevhash is zero
816                             "7b0502ad2f9f675528183f83d6385794fbcaa914e6d385c6cb1d866a3b3bb34c" + // merkle root
817                             "398e1151" + // nTime=1360105017
818                             "ffff0f1e" + // nBits=0x1e0fffff
819                             "d3091800" + // nNonce=1575379
820                             "01" +       // nTxCount=1
821                             "01000000" + // nVersion=1
822                             "398e1151" + // nTime=1360105017
823                             "01" +       // nInputs=1
824                             "0000000000000000000000000000000000000000000000000000000000000000" + // input txid is zero
825                             "ffffffff" + // n=uint.maxValue
826                             "4d" +       // scriptSigLen=77
827                             "04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936" + // scriptSig
828                             "ffffffff" + // nSequence=uint.maxValue
829                             "01" +       // nOutputs=1
830                             "0000000000000000" + // nValue=0
831                             "00" +       // scriptPubkeyLen=0
832                             "00000000" + // nLockTime=0
833                             "00"         // sigLen=0
834                     ));
835
836                     // Write block to file.
837                     var itemTemplate = new CBlockStoreItem()
838                     {
839                         nHeight = 0
840                     };
841
842                     itemTemplate.FillHeader(genesisBlock.header);
843
844                     if (!AddItemToIndex(ref itemTemplate, ref genesisBlock))
845                     {
846                         throw new Exception("Unable to write genesis block");
847                     }
848                 }
849             }
850             else
851             {
852                 var blockTreeItems = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] order by [ItemId] asc");
853
854                 // Init list of block items
855                 foreach (var item in blockTreeItems)
856                 {
857                     blockMap.TryAdd(item.Hash, item);
858
859                     if (item.IsProofOfStake)
860                     {
861                         // build mapStakeSeen
862                         mapStakeSeen.TryAdd(item.prevoutStake, item.nStakeTime);
863                     }
864                 }
865             }
866         }
867
868         public bool GetTxOutCursor(COutPoint outpoint, ref TxOutItem txOutCursor)
869         {
870             var queryResults = dbConn.Query<TxOutItem>("select o.* from [Outputs] o left join [MerkleNodes] m on (m.nMerkleNodeID = o.nMerkleNodeID) where m.[TransactionHash] = ?", (byte[])outpoint.hash);
871
872             if (queryResults.Count == 1)
873             {
874                 txOutCursor = queryResults[0];
875
876                 return true;
877             }
878
879             // Tx not found
880
881             return false;
882         }
883
884         interface InputsJoin : ITxOutItem
885         {
886             byte[] TransactionHash { get; set; }
887         }
888
889         public bool FetchInputs(ref CTransaction tx, ref Dictionary<COutPoint, CTxOut> queued, ref Dictionary<COutPoint, CTxOut> inputs, bool IsBlock, out bool Invalid)
890         {
891             Invalid = false;
892
893             if (tx.IsCoinBase)
894             {
895                 // Coinbase transactions have no inputs to fetch.
896                 return true; 
897             }
898
899             StringBuilder queryBuilder = new StringBuilder();
900             
901             queryBuilder.Append("select o.*, m.[TransactionHash] from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where ");
902
903             for (var i = 0; i < tx.vin.Length; i++)
904             {
905                 queryBuilder.AppendFormat(" {0} (m.[TransactionHash] = x'{1}' and o.[OutputNumber] = x'{2}')", 
906                     (i > 0 ? "or" : string.Empty), Interop.ToHex(tx.vin[i].prevout.hash), 
907                     Interop.ToHex(VarInt.EncodeVarInt(tx.vin[i].prevout.n)
908                 ));
909             }
910
911             var queryResults = dbConn.Query<InputsJoin>(queryBuilder.ToString());
912
913             foreach (var item in queryResults)
914             {
915                 if (item.IsSpent)
916                 {
917                     return false; // Already spent
918                 }
919
920                 var inputsKey =  new COutPoint(item.TransactionHash, item.nOut);
921
922                 // Add output data to dictionary
923                 inputs[inputsKey] = new CTxOut(item.nValue, item.scriptPubKey);
924             }
925
926             if (queryResults.Count < tx.vin.Length)
927             {
928                 if (IsBlock)
929                 {
930                     // It seems that some transactions are being spent in the same block.
931
932                     foreach (var txin in tx.vin)
933                     {
934                         var outPoint = txin.prevout;
935
936                         if (!queued.ContainsKey(outPoint))
937                         {
938                             return false; // No such transaction
939                         }
940
941                         // Add output data to dictionary
942                         inputs[outPoint] = queued[outPoint];
943
944                         // And remove it from queued data
945                         queued.Remove(outPoint);
946                     }
947                 }
948                 else
949                 {
950                     // Unconfirmed transaction
951
952                     foreach (var txin in tx.vin)
953                     {
954                         var outPoint = txin.prevout;
955                         CTransaction txPrev;
956
957                         if (!mapUnconfirmedTx.TryGetValue(outPoint.hash, out txPrev))
958                         {
959                             return false; // No such transaction
960                         }
961
962                         if (outPoint.n > txPrev.vout.Length)
963                         {
964                             Invalid = true;
965
966                             return false; // nOut is out of range
967                         }
968
969                         inputs[outPoint] = txPrev.vout[outPoint.n];
970                     }
971
972                     return false;
973                 }
974             }
975
976             return true;
977         }
978
979         private bool AddItemToIndex(ref CBlockStoreItem itemTemplate, ref CBlock block)
980         {
981             var writer = new BinaryWriter(fStreamReadWrite).BaseStream;
982             uint256 blockHash = itemTemplate.Hash;
983
984             if (blockMap.ContainsKey(blockHash))
985             {
986                 // Already have this block.
987                 return false;
988             }
989
990             // Compute chain trust score
991             itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
992
993             if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash)))
994             {
995                 return false; // SetStakeEntropyBit() failed
996             }
997
998             // Save proof-of-stake hash value
999             if (itemTemplate.IsProofOfStake)
1000             {
1001                 uint256 hashProofOfStake;
1002                 if (!GetProofOfStakeHash(blockHash, out hashProofOfStake))
1003                 {
1004                     return false;  // hashProofOfStake not found 
1005                 }
1006                 itemTemplate.hashProofOfStake = hashProofOfStake;
1007             }
1008
1009             // compute stake modifier
1010             long nStakeModifier = 0;
1011             bool fGeneratedStakeModifier = false;
1012             if (!StakeModifier.ComputeNextStakeModifier(itemTemplate, ref nStakeModifier, ref fGeneratedStakeModifier))
1013             {
1014                 return false;  // ComputeNextStakeModifier() failed
1015             }
1016
1017             itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
1018             itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
1019
1020             // TODO: verify stake modifier checkpoints
1021
1022             // Add to index
1023             if (block.IsProofOfStake)
1024             {
1025                 itemTemplate.SetProofOfStake();
1026
1027                 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
1028                 itemTemplate.nStakeTime = block.vtx[1].nTime;
1029             }
1030
1031             if (!itemTemplate.WriteToFile(ref writer, ref block))
1032             {
1033                 return false;
1034             }
1035
1036             if (dbConn.Insert(itemTemplate) == 0)
1037             {
1038                 return false; // Insert failed
1039             }
1040
1041             // Get last RowID.
1042             itemTemplate.ItemID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
1043             
1044             if (!blockMap.TryAdd(blockHash, itemTemplate))
1045             {
1046                 return false; // blockMap add failed
1047             }
1048
1049             if (itemTemplate.nChainTrust > nBestChainTrust)
1050             {
1051                 // New best chain
1052
1053                 if (!SetBestChain(ref itemTemplate))
1054                 {
1055                     return false; // SetBestChain failed.
1056                 }
1057             }
1058
1059             return true;
1060         }
1061
1062         private bool SetBestChain(ref CBlockStoreItem cursor)
1063         {
1064             uint256 hashBlock = cursor.Hash;
1065
1066             if (genesisBlockCursor == null && hashBlock == NetUtils.nHashGenesisBlock)
1067             {
1068                 genesisBlockCursor = cursor;
1069             }
1070             else if (nHashBestChain == (uint256)cursor.prevHash)
1071             {
1072                 if (!SetBestChainInner(cursor))
1073                 {
1074                     return false;
1075                 }
1076             }
1077             else
1078             {
1079                 // the first block in the new chain that will cause it to become the new best chain
1080                 var cursorIntermediate = cursor;
1081
1082                 // list of blocks that need to be connected afterwards
1083                 var secondary = new List<CBlockStoreItem>();
1084
1085                 // Reorganize is costly in terms of db load, as it works in a single db transaction.
1086                 // Try to limit how much needs to be done inside
1087                 while (cursorIntermediate.prev != null && cursorIntermediate.prev.nChainTrust > bestBlockCursor.nChainTrust)
1088                 {
1089                     secondary.Add(cursorIntermediate);
1090                     cursorIntermediate = cursorIntermediate.prev;
1091                 }
1092
1093                 // Switch to new best branch
1094                 if (!Reorganize(cursorIntermediate))
1095                 {
1096                     InvalidChainFound(cursor);
1097                     return false; // reorganize failed
1098                 }
1099
1100                 // Connect further blocks
1101                 foreach (var currentCursor in secondary)
1102                 {
1103                     CBlock block;
1104                     if (!currentCursor.ReadFromFile(ref fStreamReadWrite, out block))
1105                     {
1106                         // ReadFromDisk failed
1107                         break;
1108                     }
1109
1110                     // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
1111                     if (!SetBestChainInner(currentCursor))
1112                     {
1113                         break;
1114                     }
1115                 }
1116             }
1117
1118             nHashBestChain = cursor.Hash;
1119             bestBlockCursor = cursor;
1120             nBestHeight = cursor.nHeight;
1121             nBestChainTrust = cursor.nChainTrust;
1122             nTimeBestReceived = Interop.GetTime();
1123             nTransactionsUpdated++;
1124
1125             return true;
1126         }
1127
1128         private void InvalidChainFound(CBlockStoreItem cursor)
1129         {
1130             throw new NotImplementedException();
1131         }
1132
1133         private bool Reorganize(CBlockStoreItem cursorIntermediate)
1134         {
1135             // Find the fork
1136             var fork = bestBlockCursor;
1137             var longer = cursorIntermediate;
1138
1139             while (fork.ItemID != longer.ItemID)
1140             {
1141                 while (longer.nHeight > fork.nHeight)
1142                 {
1143                     if ((longer = longer.prev) == null)
1144                     {
1145                         return false; // longer.prev is null
1146                     }
1147                 }
1148
1149                 if (fork.ItemID == longer.ItemID)
1150                 {
1151                     break;
1152                 }
1153
1154                 if ((fork = fork.prev) == null)
1155                 {
1156                     return false; // fork.prev is null
1157                 }
1158             }
1159
1160             // List of what to disconnect
1161             var disconnect = new List<CBlockStoreItem>();
1162             for (var cursor = bestBlockCursor; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
1163             {
1164                 disconnect.Add(cursor);
1165             }
1166
1167             // List of what to connect
1168             var connect = new List<CBlockStoreItem>();
1169             for (var cursor = cursorIntermediate; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
1170             {
1171                 connect.Add(cursor);
1172             }
1173             connect.Reverse();
1174
1175             // Disconnect shorter branch
1176             var txResurrect = new List<CTransaction>();
1177             foreach (var blockCursor in disconnect)
1178             {
1179                 CBlock block;
1180                 if (!blockCursor.ReadFromFile(ref fStreamReadWrite, out block))
1181                 {
1182                     return false; // ReadFromFile for disconnect failed.
1183                 }
1184                 if (!DisconnectBlock(blockCursor, ref block))
1185                 {
1186                     return false; // DisconnectBlock failed.
1187                 }
1188
1189                 // Queue memory transactions to resurrect
1190                 foreach (var tx in block.vtx)
1191                 {
1192                     if (!tx.IsCoinBase && !tx.IsCoinStake)
1193                     {
1194                         txResurrect.Add(tx);
1195                     }
1196                 }
1197             }
1198
1199
1200             // Connect longer branch
1201             var txDelete = new List<CTransaction>();
1202             foreach (var cursor in connect)
1203             {
1204                 CBlock block;
1205                 if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
1206                 {
1207                     return false; // ReadFromDisk for connect failed
1208                 }
1209
1210                 if (!ConnectBlock(cursor, ref block))
1211                 {
1212                     // Invalid block
1213                     return false; // ConnectBlock failed
1214                 }
1215
1216                 // Queue memory transactions to delete
1217                 foreach (var tx in block.vtx)
1218                 {
1219                     txDelete.Add(tx);
1220                 }
1221             }
1222
1223             if (!WriteHashBestChain(cursorIntermediate.Hash))
1224             {
1225                 return false; // WriteHashBestChain failed
1226             }
1227
1228             // Make sure it's successfully written to disk 
1229             dbConn.Commit();
1230
1231             // Resurrect memory transactions that were in the disconnected branch
1232             foreach (var tx in txResurrect)
1233             {
1234                 mapUnconfirmedTx.TryAdd(tx.Hash, tx);
1235             }
1236
1237             // Delete redundant memory transactions that are in the connected branch
1238             foreach (var tx in txDelete)
1239             {
1240                 CTransaction dummy;
1241                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
1242             }
1243
1244             return true; // Done
1245         }
1246
1247         private bool DisconnectBlock(CBlockStoreItem blockCursor, ref CBlock block)
1248         {
1249             throw new NotImplementedException();
1250         }
1251
1252         private bool SetBestChainInner(CBlockStoreItem cursor)
1253         {
1254             uint256 hash = cursor.Hash;
1255             CBlock block;
1256             if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
1257             {
1258                 return false; // Unable to read block from file.
1259             }
1260
1261             // Adding to current best branch
1262             if (!ConnectBlock(cursor, ref block) || !WriteHashBestChain(hash))
1263             {
1264                 InvalidChainFound(cursor);
1265                 return false;
1266             }
1267
1268             // Add to current best branch
1269             cursor.prev.next = cursor;
1270
1271             dbConn.Commit();
1272
1273             // Delete redundant memory transactions
1274             foreach (var tx in block.vtx)
1275             {
1276                 CTransaction dummy;
1277                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
1278             }
1279
1280             return true;
1281         }
1282
1283         private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck=false)
1284         {
1285             // Check it again in case a previous version let a bad block in, but skip BlockSig checking
1286             if (!block.CheckBlock(!fJustCheck, !fJustCheck, false))
1287             {
1288                 return false; // Invalid block found.
1289             }
1290
1291             // TODO: the remaining stuff lol :D
1292
1293             throw new NotImplementedException();
1294         }
1295
1296         private bool WriteHashBestChain(uint256 hash)
1297         {
1298             throw new NotImplementedException();
1299         }
1300
1301         /// <summary>
1302         /// Try to find proof-of-stake hash in the map.
1303         /// </summary>
1304         /// <param name="blockHash">Block hash</param>
1305         /// <param name="hashProofOfStake">Proof-of-stake hash</param>
1306         /// <returns>Proof-of-Stake hash value</returns>
1307         private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake)
1308         {
1309             return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake);
1310         }
1311
1312         public bool AcceptBlock(ref CBlock block)
1313         {
1314             uint256 nHash = block.header.Hash;
1315
1316             if (blockMap.ContainsKey(nHash))
1317             {
1318                 // Already have this block.
1319                 return false;
1320             }
1321
1322             CBlockStoreItem prevBlockCursor = null;
1323             if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor))
1324             {
1325                 // Unable to get the cursor.
1326                 return false;
1327             }
1328
1329             var prevBlockHeader = prevBlockCursor.BlockHeader;
1330
1331             // TODO: proof-of-work/proof-of-stake verification
1332             uint nHeight = prevBlockCursor.nHeight + 1;
1333
1334             // Check timestamp against prev
1335             if (NetUtils.FutureDrift(block.header.nTime) < prevBlockHeader.nTime)
1336             {
1337                 // block's timestamp is too early
1338                 return false;
1339             }
1340
1341             // Check that all transactions are finalized
1342             foreach (var tx in block.vtx)
1343             {
1344                 if (!tx.IsFinal(nHeight, block.header.nTime))
1345                 {
1346                     return false;
1347                 }
1348             }
1349
1350             // TODO: Enforce rule that the coinbase starts with serialized block height
1351
1352             // Write block to file.
1353             var itemTemplate = new CBlockStoreItem()
1354             {
1355                 nHeight = nHeight,
1356             };
1357
1358             itemTemplate.FillHeader(block.header);
1359
1360             if (!AddItemToIndex(ref itemTemplate, ref block))
1361             {
1362                 return false;
1363             }
1364
1365             return true;
1366         }
1367
1368         public bool GetBlock(uint256 blockHash, ref CBlock block, ref long nBlockPos)
1369         {
1370             var reader = new BinaryReader(fStreamReadWrite).BaseStream;
1371
1372             var QueryBlock = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1373
1374             if (QueryBlock.Count == 1)
1375             {
1376                 nBlockPos = QueryBlock[0].nBlockPos;
1377                 return QueryBlock[0].ReadFromFile(ref reader, out block);
1378             }
1379
1380             // Block not found
1381
1382             return false;
1383         }
1384
1385
1386         /// <summary>
1387         /// Interface for join
1388         /// </summary>
1389         interface IBlockJoinMerkle : IBlockStorageItem, IMerkleNode
1390         {
1391         }
1392
1393         /// <summary>
1394         /// Get block and transaction by transaction hash.
1395         /// </summary>
1396         /// <param name="TxID">Transaction hash</param>
1397         /// <param name="block">Block reference</param>
1398         /// <param name="tx">Transaction reference</param>
1399         /// <param name="nBlockPos">Block position reference</param>
1400         /// <param name="nTxPos">Transaction position reference</param>
1401         /// <returns>Result of operation</returns>
1402         public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref CTransaction tx, ref long nBlockPos, ref long nTxPos)
1403         {
1404             var queryResult = dbConn.Query<IBlockJoinMerkle>("select *,  from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID);
1405
1406             if (queryResult.Count == 1)
1407             {
1408                 CBlockStoreItem blockCursor = (CBlockStoreItem) queryResult[0];
1409                 CMerkleNode txCursor = (CMerkleNode)queryResult[0];
1410
1411                 var reader = new BinaryReader(fStreamReadWrite).BaseStream;
1412
1413                 if (!txCursor.ReadFromFile(ref reader, blockCursor.nBlockPos, out tx))
1414                 {
1415                     return false; // Unable to read transaction
1416                 }
1417
1418                 return blockCursor.ReadFromFile(ref reader, out block);
1419             }
1420
1421             // Tx not found
1422
1423             return false;
1424         }
1425
1426         /// <summary>
1427         /// Get block cursor from map.
1428         /// </summary>
1429         /// <param name="blockHash">block hash</param>
1430         /// <returns>Cursor or null</returns>
1431         public CBlockStoreItem GetCursor(uint256 blockHash)
1432         {
1433             if (blockHash == 0)
1434             {
1435                 // Genesis block has zero prevHash and no parent.
1436                 return null;
1437             }
1438
1439             // First, check our block map.
1440             CBlockStoreItem item = null;
1441             if (blockMap.TryGetValue(blockHash, out item))
1442             {
1443                 return item;
1444             }
1445
1446             // Trying to get cursor from the database.
1447             var QueryBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1448
1449             if (QueryBlockCursor.Count == 1)
1450             {
1451                 blockMap.TryAdd(blockHash, QueryBlockCursor[0]);
1452
1453                 return QueryBlockCursor[0];
1454             }
1455
1456             // Nothing found.
1457             return null;
1458         }
1459
1460         /// <summary>
1461         /// Update cursor in memory and on disk.
1462         /// </summary>
1463         /// <param name="originalItem">Original cursor</param>
1464         /// <param name="newItem">New cursor</param>
1465         /// <returns></returns>
1466         public bool UpdateCursor(CBlockStoreItem originalItem, ref CBlockStoreItem newItem)
1467         {
1468             if (blockMap.TryUpdate(originalItem.Hash, newItem, originalItem))
1469             {
1470                 return dbConn.Update(newItem) != 0;
1471             }
1472
1473             return false;
1474         }
1475
1476         public bool ProcessBlock(ref CBlock block)
1477         {
1478             var blockHash = block.header.Hash;
1479
1480             if (blockMap.ContainsKey(blockHash))
1481             {
1482                 // We already have this block.
1483                 return false;
1484             }
1485
1486             if (orphanMap.ContainsKey(blockHash))
1487             {
1488                 // We already have block in the list of orphans.
1489                 return false;
1490             }
1491
1492             // TODO: Limited duplicity on stake and reserialization of block signature
1493
1494             if (!block.CheckBlock(true, true, true))
1495             {
1496                 // Preliminary checks failure.
1497                 return false;
1498             }
1499
1500             if (block.IsProofOfStake)
1501             {
1502                 if (!block.SignatureOK)
1503                 {
1504                     // Proof-of-Stake signature validation failure.
1505                     return false;
1506                 }
1507
1508                 // TODO: proof-of-stake validation
1509
1510                 uint256 hashProofOfStake = 0, targetProofOfStake = 0;
1511                 if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
1512                 {
1513                     return false; // do not error here as we expect this during initial block download
1514                 }
1515                 if (!mapProofOfStake.ContainsKey(blockHash)) 
1516                 {
1517                     // add to mapProofOfStake
1518                     mapProofOfStake.TryAdd(blockHash, hashProofOfStake);
1519                 }
1520
1521             }
1522
1523             // TODO: difficulty verification
1524
1525             // If don't already have its previous block, shunt it off to holding area until we get it
1526             if (!blockMap.ContainsKey(block.header.prevHash))
1527             {
1528                 if (block.IsProofOfStake)
1529                 {
1530                     // TODO: limit duplicity on stake
1531                 }
1532
1533                 var block2 = new CBlock(block);
1534                 orphanMap.TryAdd(blockHash, block2);
1535                 orphanMapByPrev.TryAdd(blockHash, block2);
1536
1537                 return true;
1538             }
1539
1540             // Store block to disk
1541             if (!AcceptBlock(ref block))
1542             {
1543                 // Accept failed
1544                 return false;
1545             }
1546
1547             // Recursively process any orphan blocks that depended on this one
1548             var orphansQueue = new List<uint256>();
1549             orphansQueue.Add(blockHash);
1550
1551             for (int i = 0; i < orphansQueue.Count; i++)
1552             {
1553                 var hashPrev = orphansQueue[i];
1554
1555                 foreach (var pair in orphanMap)
1556                 {
1557                     var orphanBlock = pair.Value;
1558
1559                     if (orphanBlock.header.prevHash == blockHash)
1560                     {
1561                         if (AcceptBlock(ref orphanBlock))
1562                         {
1563                             orphansQueue.Add(pair.Key);
1564                         }
1565
1566                         CBlock dummy1;
1567                         orphanMap.TryRemove(pair.Key, out dummy1);
1568                     }
1569                 }
1570
1571                 CBlock dummy2;
1572                 orphanMap.TryRemove(hashPrev, out dummy2);
1573             }
1574
1575             return true;
1576         }
1577
1578         public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
1579         {
1580             // TODO: Rewrite completely.
1581
1582             var nOffset = 0L;
1583
1584             var buffer = new byte[CBlock.nMaxBlockSize]; // Max block size is 1Mb
1585             var intBuffer = new byte[4];
1586
1587             var fStream2 = File.OpenRead(BlockFile);
1588             var readerForBlocks = new BinaryReader(fStream2).BaseStream;
1589
1590             readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
1591
1592             while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
1593             {
1594                 var nMagic = BitConverter.ToUInt32(buffer, 0);
1595                 if (nMagic != 0xe5e9e8e4)
1596                 {
1597                     throw new Exception("Incorrect magic number.");
1598                 }
1599
1600                 var nBytesRead = readerForBlocks.Read(buffer, 0, 4);
1601                 if (nBytesRead != 4)
1602                 {
1603                     throw new Exception("BLKSZ EOF");
1604                 }
1605
1606                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
1607
1608                 nOffset = readerForBlocks.Position;
1609
1610                 nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize);
1611
1612                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
1613                 {
1614                     throw new Exception("BLK EOF");
1615                 }
1616
1617                 var block = new CBlock(buffer);
1618                 var hash = block.header.Hash;
1619
1620                 if (blockMap.ContainsKey(hash))
1621                 {
1622                     continue;
1623                 }
1624
1625                 if (!ProcessBlock(ref block))
1626                 {
1627                     throw new Exception("Invalid block: " + block.header.Hash);
1628                 }
1629
1630                 int nCount = blockMap.Count;
1631                 Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
1632
1633                 /*
1634                 if (nCount % 100 == 0 && nCount != 0)
1635                 {
1636                     Console.WriteLine("Commit...");
1637                     dbConn.Commit();
1638                     dbConn.BeginTransaction();
1639                 }*/
1640             }
1641
1642             dbConn.Commit();
1643
1644             return true;
1645         }
1646
1647         ~CBlockStore()
1648         {
1649             Dispose(false);
1650         }
1651
1652         public void Dispose()
1653         {
1654             Dispose(true);
1655             GC.SuppressFinalize(this);
1656         }
1657
1658         protected virtual void Dispose(bool disposing)
1659         {
1660             if (!disposed)
1661             {
1662                 if (disposing)
1663                 {
1664                     // Free other state (managed objects).
1665
1666                     fStreamReadWrite.Dispose();
1667                 }
1668
1669                 if (dbConn != null)
1670                 {
1671                     dbConn.Close();
1672                     dbConn = null;
1673                 }
1674
1675                 disposed = true;
1676             }
1677         }
1678
1679     }
1680 }