Checkpoints valudation, PoW reward calculation and continue working on block index.
[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         {
294             get { return CBlockStore.Instance.GetMapCursor(prevHash); }
295         }
296
297         /// <summary>
298         /// Next block cursor
299         /// </summary>
300         [Ignore]
301         public CBlockStoreItem next
302         {
303             get
304             {
305                 if (nextHash == null)
306                 {
307                     return null;
308                 }
309
310                 return CBlockStore.Instance.GetMapCursor(nextHash);
311             }
312             set
313             {
314                 nextHash = value.Hash;
315
316                 CBlockStore.Instance.UpdateMapCursor(this);
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 < NetInfo.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 = NetInfo.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         public long nMint { get; internal set; }
514         public long nMoneySupply { get; internal set; }
515     }
516
517     /// <summary>
518     /// Block type.
519     /// </summary>
520     public enum BlockType
521     {
522         BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
523         BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
524         BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
525     };
526
527     /// <summary>
528     /// Transaction type.
529     /// </summary>
530     public enum TxFlags : byte
531     {
532         TX_COINBASE,
533         TX_COINSTAKE,
534         TX_USER
535     }
536
537     /// <summary>
538     /// Output flags.
539     /// </summary>
540     public enum OutputFlags : byte
541     {
542         AVAILABLE, // Unspent output
543         SPENT      // Spent output
544     }
545
546     [Table("MerkleNodes")]
547     public class CMerkleNode : IMerkleNode
548     {
549         #region IMerkleNode
550         /// <summary>
551         /// Node identifier
552         /// </summary>
553         [PrimaryKey, AutoIncrement]
554         public long nMerkleNodeID { get; set; }
555
556         /// <summary>
557         /// Reference to parent block database item.
558         /// </summary>
559         [ForeignKey(typeof(CBlockStoreItem), Name = "ItemId")]
560         public long nParentBlockID { get; set; }
561
562         /// <summary>
563         /// Transaction type flag
564         /// </summary>
565         [Column("TransactionFlags")]
566         public TxFlags TransactionFlags { get; set; }
567
568         /// <summary>
569         /// Transaction hash
570         /// </summary>
571         [Column("TransactionHash")]
572         public byte[] TransactionHash { get; set; }
573
574         /// <summary>
575         /// Transaction offset from the beginning of block header, encoded in VarInt format.
576         /// </summary>
577         [Column("TxOffset")]
578         public byte[] TxOffset { get; set; }
579
580         /// <summary>
581         /// Transaction size, encoded in VarInt format.
582         /// </summary>
583         [Column("TxSize")]
584         public byte[] TxSize { get; set; }
585         #endregion
586
587         /// <summary>
588         /// Read transaction from file.
589         /// </summary>
590         /// <param name="reader">Stream with read access.</param>
591         /// <param name="tx">CTransaction reference.</param>
592         /// <returns>Result</returns>
593         public bool ReadFromFile(ref Stream reader, long nBlockPos, out CTransaction tx)
594         {
595             var buffer = new byte[CTransaction.nMaxTxSize];
596
597             tx = null;
598
599             try
600             {
601                 reader.Seek(nBlockPos + nTxOffset, SeekOrigin.Begin); // Seek to transaction offset
602
603                 if (nTxSize != reader.Read(buffer, 0, nTxSize))
604                 {
605                     return false;
606                 }
607
608                 tx = new CTransaction(buffer);
609
610                 return true;
611             }
612             catch (IOException)
613             {
614                 // I/O error
615                 return false;
616             }
617             catch (TransactionConstructorException)
618             {
619                 // Constructor error
620                 return false;
621             }
622         }
623
624         /// <summary>
625         /// Transaction offset accessor
626         /// </summary>
627         [Ignore]
628         public long nTxOffset
629         {
630             get { return (long) VarInt.DecodeVarInt(TxOffset); }
631             private set { TxOffset = VarInt.EncodeVarInt(value); }
632         }
633
634         /// <summary>
635         /// Transaction size accessor
636         /// </summary>
637         [Ignore]
638         public int nTxSize
639         {
640             get { return (int)VarInt.DecodeVarInt(TxSize); }
641             private set { TxSize = VarInt.EncodeVarInt(value); }
642         }
643
644         public CMerkleNode(CTransaction tx)
645         {
646             nTxOffset = -1;
647             nParentBlockID = -1;
648
649             nTxSize = tx.Size;
650             TransactionHash = tx.Hash;
651
652             if (tx.IsCoinBase)
653             {
654                 TransactionFlags |= TxFlags.TX_COINBASE;
655             }
656             else if (tx.IsCoinStake)
657             {
658                 TransactionFlags |= TxFlags.TX_COINSTAKE;
659             }
660             else
661             {
662                 TransactionFlags |= TxFlags.TX_USER;
663             }
664         }
665
666         public CMerkleNode(long nBlockId, long nOffset, CTransaction tx)
667         {
668             nParentBlockID = nBlockId;
669
670             nTxOffset = nOffset;
671             nTxSize = tx.Size;
672             TransactionHash = tx.Hash;
673
674             if (tx.IsCoinBase)
675             {
676                 TransactionFlags |= TxFlags.TX_COINBASE;
677             }
678             else if (tx.IsCoinStake)
679             {
680                 TransactionFlags |= TxFlags.TX_COINSTAKE;
681             }
682             else
683             {
684                 TransactionFlags |= TxFlags.TX_USER;
685             }
686         }
687
688     }
689
690     [Table("Outputs")]
691     public class TxOutItem : ITxOutItem
692     {
693         /// <summary>
694         /// Reference to transaction item.
695         /// </summary>
696         [ForeignKey(typeof(CMerkleNode), Name = "nMerkleNodeID")]
697         public long nMerkleNodeID { get; set; }
698
699         /// <summary>
700         /// Output flags
701         /// </summary>
702         public OutputFlags outputFlags { get; set; }
703
704         /// <summary>
705         /// Output number in VarInt format.
706         /// </summary>
707         public byte[] OutputNumber { get; set; }
708
709         /// <summary>
710         /// Output value in VarInt format.
711         /// </summary>
712         public byte[] OutputValue { get; set; }
713
714         /// <summary>
715         /// Second half of script which contains spending instructions.
716         /// </summary>
717         public byte[] scriptPubKey { get; set; }
718
719         /// <summary>
720         /// Getter for output number.
721         /// </summary>
722         [Ignore]
723         public uint nOut
724         {
725             get { return (uint)VarInt.DecodeVarInt(OutputNumber); }
726             private set { OutputNumber = VarInt.EncodeVarInt(value); }
727         }
728
729         /// <summary>
730         /// Getter for output value.
731         /// </summary>
732         [Ignore]
733         public ulong nValue
734         {
735             get { return VarInt.DecodeVarInt(OutputValue); }
736             private set { OutputValue = VarInt.EncodeVarInt(value); }
737         }
738
739         /// <summary>
740         /// Getter ans setter for IsSpent flag.
741         /// </summary>
742         [Ignore]
743         public bool IsSpent
744         {
745             get { return (outputFlags & OutputFlags.SPENT) != 0; }
746             set { outputFlags |= value ? OutputFlags.SPENT : OutputFlags.AVAILABLE; }
747         }
748
749         public TxOutItem(CTxOut o, uint nOut)
750         {
751             nValue = o.nValue;
752             scriptPubKey = o.scriptPubKey;
753             this.nOut = nOut;
754         }
755     }
756
757     public class CBlockStore : IDisposable
758     {
759         public const uint nMagicNumber = 0xe5e9e8e4;
760
761         private bool disposed = false;
762         private object LockObj = new object();
763
764         /// <summary>
765         /// SQLite connection object.
766         /// </summary>
767         private SQLiteConnection dbConn;
768
769         /// <summary>
770         /// Current SQLite platform
771         /// </summary>
772         private ISQLitePlatform dbPlatform;
773
774         /// <summary>
775         /// Block file.
776         /// </summary>
777         private string strBlockFile;
778
779         /// <summary>
780         /// Index database file.
781         /// </summary>
782         private string strDbFile;
783
784         /// <summary>
785         /// Map of block tree nodes.
786         /// 
787         /// blockHash => CBlockStoreItem
788         /// </summary>
789         private ConcurrentDictionary<uint256, CBlockStoreItem> blockMap = new ConcurrentDictionary<uint256, CBlockStoreItem>();
790
791         /// <summary>
792         /// Orphaned blocks map.
793         /// </summary>
794         private ConcurrentDictionary<uint256, CBlock> orphanMap = new ConcurrentDictionary<uint256, CBlock>();
795         private ConcurrentDictionary<uint256, CBlock> orphanMapByPrev = new ConcurrentDictionary<uint256, CBlock>();
796
797         /// <summary>
798         /// Unconfirmed transactions.
799         /// 
800         /// TxID => Transaction
801         /// </summary>
802         private ConcurrentDictionary<uint256, CTransaction> mapUnconfirmedTx = new ConcurrentDictionary<uint256, CTransaction>();
803
804         /// <summary>
805         /// Map of the proof-of-stake hashes. This is necessary for stake duplication checks.
806         /// </summary>
807         private ConcurrentDictionary<uint256, uint256> mapProofOfStake = new ConcurrentDictionary<uint256, uint256>();
808
809
810         private ConcurrentDictionary<COutPoint, uint> mapStakeSeen = new ConcurrentDictionary<COutPoint, uint>();
811         private ConcurrentDictionary<COutPoint, uint> mapStakeSeenOrphan = new ConcurrentDictionary<COutPoint, uint>();
812
813         /// <summary>
814         /// Trust score for the longest chain.
815         /// </summary>
816         private uint256 nBestChainTrust = 0;
817
818         /// <summary>
819         /// Top block of the best chain.
820         /// </summary>
821         private uint256 nHashBestChain = 0;
822
823         /// <summary>
824         /// Cursor which is pointing us to the end of best chain.
825         /// </summary>
826         private CBlockStoreItem bestBlockCursor = null;
827
828         /// <summary>
829         /// Cursor which is always pointing us to genesis block.
830         /// </summary>
831         private CBlockStoreItem genesisBlockCursor = null;
832
833         /// <summary>
834         /// Current and the only instance of block storage manager. Should be a property with private setter though it's enough for the beginning.
835         /// </summary>
836         public static CBlockStore Instance = null;
837
838         /// <summary>
839         /// Block file stream with read/write access
840         /// </summary>
841         private Stream fStreamReadWrite;
842         private uint nBestHeight;
843         private uint nTimeBestReceived;
844         private int nTransactionsUpdated;
845
846         /// <summary>
847         /// Init the block storage manager.
848         /// </summary>
849         /// <param name="IndexDB">Path to index database</param>
850         /// <param name="BlockFile">Path to block file</param>
851         public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
852         {
853             strDbFile = IndexDB;
854             strBlockFile = BlockFile;
855
856             bool firstInit = !File.Exists(strDbFile);
857             dbPlatform = new SQLitePlatformGeneric();
858             dbConn = new SQLiteConnection(dbPlatform, strDbFile);
859
860             fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
861
862             Instance = this;
863
864             if (firstInit)
865             {
866                 lock (LockObj)
867                 {
868                     // Create tables
869                     dbConn.CreateTable<CBlockStoreItem>(CreateFlags.AutoIncPK);
870                     dbConn.CreateTable<CMerkleNode>(CreateFlags.AutoIncPK);
871                     dbConn.CreateTable<TxOutItem>(CreateFlags.ImplicitPK);
872
873                     var genesisBlock = new CBlock(
874                         Interop.HexToArray(
875                             "01000000" + // nVersion=1
876                             "0000000000000000000000000000000000000000000000000000000000000000" + // prevhash is zero
877                             "7b0502ad2f9f675528183f83d6385794fbcaa914e6d385c6cb1d866a3b3bb34c" + // merkle root
878                             "398e1151" + // nTime=1360105017
879                             "ffff0f1e" + // nBits=0x1e0fffff
880                             "d3091800" + // nNonce=1575379
881                             "01" +       // nTxCount=1
882                             "01000000" + // nVersion=1
883                             "398e1151" + // nTime=1360105017
884                             "01" +       // nInputs=1
885                             "0000000000000000000000000000000000000000000000000000000000000000" + // input txid is zero
886                             "ffffffff" + // n=uint.maxValue
887                             "4d" +       // scriptSigLen=77
888                             "04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936" + // scriptSig
889                             "ffffffff" + // nSequence=uint.maxValue
890                             "01" +       // nOutputs=1
891                             "0000000000000000" + // nValue=0
892                             "00" +       // scriptPubkeyLen=0
893                             "00000000" + // nLockTime=0
894                             "00"         // sigLen=0
895                     ));
896
897                     // Write block to file.
898                     var itemTemplate = new CBlockStoreItem()
899                     {
900                         nHeight = 0
901                     };
902
903                     itemTemplate.FillHeader(genesisBlock.header);
904
905                     if (!AddItemToIndex(ref itemTemplate, ref genesisBlock))
906                     {
907                         throw new Exception("Unable to write genesis block");
908                     }
909                 }
910             }
911             else
912             {
913                 var blockTreeItems = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] order by [ItemId] asc");
914
915                 // Init list of block items
916                 foreach (var item in blockTreeItems)
917                 {
918                     blockMap.TryAdd(item.Hash, item);
919
920                     if (item.IsProofOfStake)
921                     {
922                         // build mapStakeSeen
923                         mapStakeSeen.TryAdd(item.prevoutStake, item.nStakeTime);
924                     }
925                 }
926             }
927         }
928
929         public bool GetTxOutCursor(COutPoint outpoint, ref TxOutItem txOutCursor)
930         {
931             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);
932
933             if (queryResults.Count == 1)
934             {
935                 txOutCursor = queryResults[0];
936
937                 return true;
938             }
939
940             // Tx not found
941
942             return false;
943         }
944
945         interface InputsJoin : ITxOutItem
946         {
947             byte[] TransactionHash { get; set; }
948         }
949
950         public bool FetchInputs(CTransaction tx, ref Dictionary<COutPoint, TxOutItem> queued, ref Dictionary<COutPoint, TxOutItem> inputs, bool IsBlock, out bool Invalid)
951         {
952             Invalid = false;
953
954             if (tx.IsCoinBase)
955             {
956                 // Coinbase transactions have no inputs to fetch.
957                 return true; 
958             }
959
960             StringBuilder queryBuilder = new StringBuilder();
961             
962             queryBuilder.Append("select o.*, m.[TransactionHash] from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where ");
963
964             for (var i = 0; i < tx.vin.Length; i++)
965             {
966                 queryBuilder.AppendFormat(" {0} (m.[TransactionHash] = x'{1}' and o.[OutputNumber] = x'{2}')", 
967                     (i > 0 ? "or" : string.Empty), Interop.ToHex(tx.vin[i].prevout.hash), 
968                     Interop.ToHex(VarInt.EncodeVarInt(tx.vin[i].prevout.n)
969                 ));
970             }
971
972             var queryResults = dbConn.Query<InputsJoin>(queryBuilder.ToString());
973
974             foreach (var item in queryResults)
975             {
976                 if (item.IsSpent)
977                 {
978                     return false; // Already spent
979                 }
980
981                 var inputsKey =  new COutPoint(item.TransactionHash, item.nOut);
982
983                 item.IsSpent = true;
984
985                 // Add output data to dictionary
986                 inputs.Add(inputsKey, (TxOutItem) item);
987             }
988
989             if (queryResults.Count < tx.vin.Length)
990             {
991                 if (IsBlock)
992                 {
993                     // It seems that some transactions are being spent in the same block.
994
995                     foreach (var txin in tx.vin)
996                     {
997                         var outPoint = txin.prevout;
998
999                         if (!queued.ContainsKey(outPoint))
1000                         {
1001                             return false; // No such transaction
1002                         }
1003
1004                         // Add output data to dictionary
1005                         inputs.Add(outPoint, queued[outPoint]);
1006
1007                         // Mark output as spent
1008                         queued[outPoint].IsSpent = true;                        
1009                     }
1010                 }
1011                 else
1012                 {
1013                     // Unconfirmed transaction
1014
1015                     foreach (var txin in tx.vin)
1016                     {
1017                         var outPoint = txin.prevout;
1018                         CTransaction txPrev;
1019
1020                         if (!mapUnconfirmedTx.TryGetValue(outPoint.hash, out txPrev))
1021                         {
1022                             return false; // No such transaction
1023                         }
1024
1025                         if (outPoint.n > txPrev.vout.Length)
1026                         {
1027                             Invalid = true;
1028
1029                             return false; // nOut is out of range
1030                         }
1031                         
1032                         // TODO: return inputs from map 
1033                         throw new NotImplementedException();
1034
1035                     }
1036
1037                     return false;
1038                 }
1039             }
1040
1041             return true;
1042         }
1043
1044         private bool AddItemToIndex(ref CBlockStoreItem itemTemplate, ref CBlock block)
1045         {
1046             var writer = new BinaryWriter(fStreamReadWrite).BaseStream;
1047             uint256 blockHash = itemTemplate.Hash;
1048
1049             if (blockMap.ContainsKey(blockHash))
1050             {
1051                 // Already have this block.
1052                 return false;
1053             }
1054
1055             // Compute chain trust score
1056             itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
1057
1058             if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash)))
1059             {
1060                 return false; // SetStakeEntropyBit() failed
1061             }
1062
1063             // Save proof-of-stake hash value
1064             if (itemTemplate.IsProofOfStake)
1065             {
1066                 uint256 hashProofOfStake;
1067                 if (!GetProofOfStakeHash(blockHash, out hashProofOfStake))
1068                 {
1069                     return false;  // hashProofOfStake not found 
1070                 }
1071                 itemTemplate.hashProofOfStake = hashProofOfStake;
1072             }
1073
1074             // compute stake modifier
1075             long nStakeModifier = 0;
1076             bool fGeneratedStakeModifier = false;
1077             if (!StakeModifier.ComputeNextStakeModifier(itemTemplate, ref nStakeModifier, ref fGeneratedStakeModifier))
1078             {
1079                 return false;  // ComputeNextStakeModifier() failed
1080             }
1081
1082             itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
1083             itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
1084
1085             // TODO: verify stake modifier checkpoints
1086
1087             // Add to index
1088             if (block.IsProofOfStake)
1089             {
1090                 itemTemplate.SetProofOfStake();
1091
1092                 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
1093                 itemTemplate.nStakeTime = block.vtx[1].nTime;
1094             }
1095
1096             if (!itemTemplate.WriteToFile(ref writer, ref block))
1097             {
1098                 return false;
1099             }
1100
1101             if (dbConn.Insert(itemTemplate) == 0)
1102             {
1103                 return false; // Insert failed
1104             }
1105
1106             // Get last RowID.
1107             itemTemplate.ItemID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
1108             
1109             if (!blockMap.TryAdd(blockHash, itemTemplate))
1110             {
1111                 return false; // blockMap add failed
1112             }
1113
1114             if (itemTemplate.nChainTrust > nBestChainTrust)
1115             {
1116                 // New best chain
1117
1118                 if (!SetBestChain(ref itemTemplate))
1119                 {
1120                     return false; // SetBestChain failed.
1121                 }
1122             }
1123
1124             return true;
1125         }
1126
1127         private bool SetBestChain(ref CBlockStoreItem cursor)
1128         {
1129             uint256 hashBlock = cursor.Hash;
1130
1131             if (genesisBlockCursor == null && hashBlock == NetInfo.nHashGenesisBlock)
1132             {
1133                 genesisBlockCursor = cursor;
1134             }
1135             else if (nHashBestChain == (uint256)cursor.prevHash)
1136             {
1137                 if (!SetBestChainInner(cursor))
1138                 {
1139                     return false;
1140                 }
1141             }
1142             else
1143             {
1144                 // the first block in the new chain that will cause it to become the new best chain
1145                 var cursorIntermediate = cursor;
1146
1147                 // list of blocks that need to be connected afterwards
1148                 var secondary = new List<CBlockStoreItem>();
1149
1150                 // Reorganize is costly in terms of db load, as it works in a single db transaction.
1151                 // Try to limit how much needs to be done inside
1152                 while (cursorIntermediate.prev != null && cursorIntermediate.prev.nChainTrust > bestBlockCursor.nChainTrust)
1153                 {
1154                     secondary.Add(cursorIntermediate);
1155                     cursorIntermediate = cursorIntermediate.prev;
1156                 }
1157
1158                 // Switch to new best branch
1159                 if (!Reorganize(cursorIntermediate))
1160                 {
1161                     InvalidChainFound(cursor);
1162                     return false; // reorganize failed
1163                 }
1164
1165                 // Connect further blocks
1166                 foreach (var currentCursor in secondary)
1167                 {
1168                     CBlock block;
1169                     if (!currentCursor.ReadFromFile(ref fStreamReadWrite, out block))
1170                     {
1171                         // ReadFromDisk failed
1172                         break;
1173                     }
1174
1175                     // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
1176                     if (!SetBestChainInner(currentCursor))
1177                     {
1178                         break;
1179                     }
1180                 }
1181             }
1182
1183             nHashBestChain = cursor.Hash;
1184             bestBlockCursor = cursor;
1185             nBestHeight = cursor.nHeight;
1186             nBestChainTrust = cursor.nChainTrust;
1187             nTimeBestReceived = Interop.GetTime();
1188             nTransactionsUpdated++;
1189
1190             return true;
1191         }
1192
1193         private void InvalidChainFound(CBlockStoreItem cursor)
1194         {
1195             throw new NotImplementedException();
1196         }
1197
1198         private bool Reorganize(CBlockStoreItem cursorIntermediate)
1199         {
1200             // Find the fork
1201             var fork = bestBlockCursor;
1202             var longer = cursorIntermediate;
1203
1204             while (fork.ItemID != longer.ItemID)
1205             {
1206                 while (longer.nHeight > fork.nHeight)
1207                 {
1208                     if ((longer = longer.prev) == null)
1209                     {
1210                         return false; // longer.prev is null
1211                     }
1212                 }
1213
1214                 if (fork.ItemID == longer.ItemID)
1215                 {
1216                     break;
1217                 }
1218
1219                 if ((fork = fork.prev) == null)
1220                 {
1221                     return false; // fork.prev is null
1222                 }
1223             }
1224
1225             // List of what to disconnect
1226             var disconnect = new List<CBlockStoreItem>();
1227             for (var cursor = bestBlockCursor; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
1228             {
1229                 disconnect.Add(cursor);
1230             }
1231
1232             // List of what to connect
1233             var connect = new List<CBlockStoreItem>();
1234             for (var cursor = cursorIntermediate; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
1235             {
1236                 connect.Add(cursor);
1237             }
1238             connect.Reverse();
1239
1240             // Disconnect shorter branch
1241             var txResurrect = new List<CTransaction>();
1242             foreach (var blockCursor in disconnect)
1243             {
1244                 CBlock block;
1245                 if (!blockCursor.ReadFromFile(ref fStreamReadWrite, out block))
1246                 {
1247                     return false; // ReadFromFile for disconnect failed.
1248                 }
1249                 if (!DisconnectBlock(blockCursor, ref block))
1250                 {
1251                     return false; // DisconnectBlock failed.
1252                 }
1253
1254                 // Queue memory transactions to resurrect
1255                 foreach (var tx in block.vtx)
1256                 {
1257                     if (!tx.IsCoinBase && !tx.IsCoinStake)
1258                     {
1259                         txResurrect.Add(tx);
1260                     }
1261                 }
1262             }
1263
1264
1265             // Connect longer branch
1266             var txDelete = new List<CTransaction>();
1267             foreach (var cursor in connect)
1268             {
1269                 CBlock block;
1270                 if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
1271                 {
1272                     return false; // ReadFromDisk for connect failed
1273                 }
1274
1275                 if (!ConnectBlock(cursor, ref block))
1276                 {
1277                     // Invalid block
1278                     return false; // ConnectBlock failed
1279                 }
1280
1281                 // Queue memory transactions to delete
1282                 foreach (var tx in block.vtx)
1283                 {
1284                     txDelete.Add(tx);
1285                 }
1286             }
1287
1288             if (!WriteHashBestChain(cursorIntermediate.Hash))
1289             {
1290                 return false; // WriteHashBestChain failed
1291             }
1292
1293             // Make sure it's successfully written to disk 
1294             dbConn.Commit();
1295
1296             // Resurrect memory transactions that were in the disconnected branch
1297             foreach (var tx in txResurrect)
1298             {
1299                 mapUnconfirmedTx.TryAdd(tx.Hash, tx);
1300             }
1301
1302             // Delete redundant memory transactions that are in the connected branch
1303             foreach (var tx in txDelete)
1304             {
1305                 CTransaction dummy;
1306                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
1307             }
1308
1309             return true; // Done
1310         }
1311
1312         private bool DisconnectBlock(CBlockStoreItem blockCursor, ref CBlock block)
1313         {
1314             throw new NotImplementedException();
1315         }
1316
1317         private bool SetBestChainInner(CBlockStoreItem cursor)
1318         {
1319             uint256 hash = cursor.Hash;
1320             CBlock block;
1321             if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
1322             {
1323                 return false; // Unable to read block from file.
1324             }
1325
1326             // Adding to current best branch
1327             if (!ConnectBlock(cursor, ref block) || !WriteHashBestChain(hash))
1328             {
1329                 InvalidChainFound(cursor);
1330                 return false;
1331             }
1332
1333             // Add to current best branch
1334             cursor.prev.next = cursor;
1335
1336             dbConn.Commit();
1337
1338             // Delete redundant memory transactions
1339             foreach (var tx in block.vtx)
1340             {
1341                 CTransaction dummy;
1342                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
1343             }
1344
1345             return true;
1346         }
1347
1348         private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck=false)
1349         {
1350             // Check it again in case a previous version let a bad block in, but skip BlockSig checking
1351             if (!block.CheckBlock(!fJustCheck, !fJustCheck, false))
1352             {
1353                 return false; // Invalid block found.
1354             }
1355
1356             bool fScriptChecks = cursor.nHeight >= Checkpoints.TotalBlocksEstimate;
1357             var scriptFlags = scriptflag.SCRIPT_VERIFY_NOCACHE | scriptflag.SCRIPT_VERIFY_P2SH;
1358
1359             ulong nFees = 0;
1360             ulong nValueIn = 0;
1361             ulong nValueOut = 0;
1362             uint nSigOps = 0;
1363
1364             var queuedMerkleNodes = new Dictionary<uint256, CMerkleNode>();
1365             var queued = new Dictionary<COutPoint, TxOutItem>();
1366
1367             for (var nTx = 0; nTx < block.vtx.Length; nTx++)
1368             {
1369                 var tx = block.vtx[nTx];
1370                 var hashTx = tx.Hash;
1371                 var nTxPos = cursor.nBlockPos + block.GetTxOffset(nTx);
1372
1373                 Dictionary<COutPoint, TxOutItem> txouts;
1374                 if (GetOutputs(hashTx, out txouts))
1375                 {
1376                     // Do not allow blocks that contain transactions which 'overwrite' older transactions,
1377                     // unless those are already completely spent.
1378                     return false;
1379                 }
1380
1381                 nSigOps += tx.LegacySigOpCount;
1382                 if (nSigOps > CBlock.nMaxSigOps)
1383                 {
1384                     return false; // too many sigops
1385                 }
1386
1387                 var inputs = new Dictionary<COutPoint, TxOutItem>();
1388
1389                 if (tx.IsCoinBase)
1390                 {
1391                     nValueOut += tx.nValueOut;
1392                 }
1393                 else
1394                 {
1395                     bool Invalid;
1396                     if (!FetchInputs(tx, ref queued, ref inputs, true, out Invalid))
1397                     {
1398                         return false; // Unable to fetch some inputs.
1399                     }
1400
1401                     // Add in sigops done by pay-to-script-hash inputs;
1402                     // this is to prevent a "rogue miner" from creating
1403                     // an incredibly-expensive-to-validate block.
1404                     nSigOps += tx.GetP2SHSigOpCount(inputs);
1405                     if (nSigOps > CBlock.nMaxSigOps)
1406                     {
1407                         return false; // too many sigops
1408                     }
1409
1410                     ulong nTxValueIn = tx.GetValueIn(inputs);
1411                     ulong nTxValueOut = tx.nValueOut;
1412
1413                     nValueIn += nTxValueIn;
1414                     nValueOut += nTxValueOut;
1415
1416                     if (!tx.IsCoinStake)
1417                     {
1418                         nFees += nTxValueIn - nTxValueOut;
1419                     }
1420
1421                     if (!ConnectInputs(tx, inputs, queued, cursor, fScriptChecks, scriptFlags))
1422                     {
1423                         return false;
1424                     }
1425                 }
1426
1427                 for (var i = 0u; i < tx.vout.Length; i++)
1428                 {
1429                     var mNode = new CMerkleNode(cursor.ItemID, nTxPos, tx);
1430                     queuedMerkleNodes.Add(hashTx, mNode);
1431
1432                     var outKey = new COutPoint(hashTx, i);
1433                     var outData = new TxOutItem(tx.vout[i], i);
1434
1435                     outData.IsSpent = false;
1436
1437                     queued.Add(outKey, outData);
1438                 }
1439             }
1440
1441             if (!block.IsProofOfStake)
1442             {
1443                 ulong nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees);
1444
1445                 // Check coinbase reward
1446                 if (block.vtx[0].nValueOut > nBlockReward)
1447                 {
1448                     return false; // coinbase reward exceeded
1449                 }
1450             }
1451
1452             cursor.nMint = (long) (nValueOut - nValueIn + nFees);
1453             cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + (long)nValueOut - (long)nValueIn;
1454
1455             if (!UpdateDBCursor(ref cursor))
1456             {
1457                 return false; // Unable to commit changes
1458             }
1459
1460             if (fJustCheck)
1461             {
1462                 return true;
1463             }
1464
1465             // Write queued transaction changes
1466             var actualMerkleNodes = new Dictionary<uint256, CMerkleNode>();
1467             var queuedOutpointItems = new List<TxOutItem>();
1468             foreach(KeyValuePair<COutPoint, TxOutItem> outPair in queued)
1469             {
1470                 uint256 txID = outPair.Key.hash;
1471                 CMerkleNode merkleNode;
1472
1473                 if (actualMerkleNodes.ContainsKey(txID))
1474                 {
1475                     merkleNode = actualMerkleNodes[txID];
1476                 }
1477                 else
1478                 {
1479                     merkleNode = queuedMerkleNodes[txID];
1480                     if (!SaveMerkleNode(ref merkleNode))
1481                     {
1482                         // Unable to save merkle tree cursor.
1483                         return false;
1484                     }
1485                     actualMerkleNodes.Add(txID, merkleNode);
1486                 }
1487
1488                 var outItem = outPair.Value;
1489                 outItem.nMerkleNodeID = merkleNode.nMerkleNodeID;
1490
1491                 queuedOutpointItems.Add(outItem);
1492             }
1493
1494             if (!SaveOutpoints(ref queuedOutpointItems))
1495             {
1496                 return false; // Unable to save outpoints
1497             }
1498
1499             // TODO: the remaining stuff lol :D
1500
1501             throw new NotImplementedException();
1502         }
1503
1504         /// <summary>
1505         /// Insert set of outpoints
1506         /// </summary>
1507         /// <param name="queuedOutpointItems">List of TxOutItem objects.</param>
1508         /// <returns>Result</returns>
1509         private bool SaveOutpoints(ref List<TxOutItem> queuedOutpointItems)
1510         {
1511             return dbConn.InsertAll(queuedOutpointItems, false) != 0;
1512         }
1513
1514         /// <summary>
1515         /// Insert merkle node into db and set actual record id value.
1516         /// </summary>
1517         /// <param name="merkleNode">Merkle node object reference.</param>
1518         /// <returns>Result</returns>
1519         private bool SaveMerkleNode(ref CMerkleNode merkleNode)
1520         {
1521             if (dbConn.Insert(merkleNode) == 0)
1522             {
1523                 return false;
1524             }
1525
1526             merkleNode.nMerkleNodeID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
1527
1528             return true;
1529         }
1530
1531         private bool ConnectInputs(CTransaction tx, Dictionary<COutPoint, TxOutItem> inputs, Dictionary<COutPoint, TxOutItem> queued, CBlockStoreItem cursor, bool fScriptChecks, scriptflag scriptFlags)
1532         {
1533             throw new NotImplementedException();
1534         }
1535
1536         private bool WriteHashBestChain(uint256 hash)
1537         {
1538             throw new NotImplementedException();
1539         }
1540
1541         /// <summary>
1542         /// Try to find proof-of-stake hash in the map.
1543         /// </summary>
1544         /// <param name="blockHash">Block hash</param>
1545         /// <param name="hashProofOfStake">Proof-of-stake hash</param>
1546         /// <returns>Proof-of-Stake hash value</returns>
1547         private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake)
1548         {
1549             return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake);
1550         }
1551
1552         public bool AcceptBlock(ref CBlock block)
1553         {
1554             uint256 nHash = block.header.Hash;
1555
1556             if (blockMap.ContainsKey(nHash))
1557             {
1558                 // Already have this block.
1559                 return false;
1560             }
1561
1562             CBlockStoreItem prevBlockCursor = null;
1563             if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor))
1564             {
1565                 // Unable to get the cursor.
1566                 return false;
1567             }
1568
1569             var prevBlockHeader = prevBlockCursor.BlockHeader;
1570
1571             // TODO: proof-of-work/proof-of-stake verification
1572             uint nHeight = prevBlockCursor.nHeight + 1;
1573
1574             // Check timestamp against prev
1575             if (NetInfo.FutureDrift(block.header.nTime) < prevBlockHeader.nTime)
1576             {
1577                 // block's timestamp is too early
1578                 return false;
1579             }
1580
1581             // Check that all transactions are finalized
1582             foreach (var tx in block.vtx)
1583             {
1584                 if (!tx.IsFinal(nHeight, block.header.nTime))
1585                 {
1586                     return false;
1587                 }
1588             }
1589
1590             // TODO: Enforce rule that the coinbase starts with serialized block height
1591
1592             // Write block to file.
1593             var itemTemplate = new CBlockStoreItem()
1594             {
1595                 nHeight = nHeight,
1596             };
1597
1598             itemTemplate.FillHeader(block.header);
1599
1600             if (!AddItemToIndex(ref itemTemplate, ref block))
1601             {
1602                 return false;
1603             }
1604
1605             return true;
1606         }
1607
1608         /// <summary>
1609         /// GEt block by hash.
1610         /// </summary>
1611         /// <param name="blockHash">Block hash</param>
1612         /// <param name="block">Block object reference</param>
1613         /// <param name="nBlockPos">Block position reference</param>
1614         /// <returns>Result</returns>
1615         public bool GetBlock(uint256 blockHash, ref CBlock block, ref long nBlockPos)
1616         {
1617             CBlockStoreItem cursor;
1618
1619             if (!blockMap.TryGetValue(blockHash, out cursor))
1620             {
1621                 return false; // Unable to fetch block cursor
1622             }
1623
1624             nBlockPos = cursor.nBlockPos;
1625
1626             return cursor.ReadFromFile(ref fStreamReadWrite, out block);
1627         }
1628
1629
1630         /// <summary>
1631         /// Interface for join
1632         /// </summary>
1633         interface IBlockJoinMerkle : IBlockStorageItem, IMerkleNode
1634         {
1635         }
1636
1637         /// <summary>
1638         /// Get block and transaction by transaction hash.
1639         /// </summary>
1640         /// <param name="TxID">Transaction hash</param>
1641         /// <param name="block">Block reference</param>
1642         /// <param name="tx">Transaction reference</param>
1643         /// <param name="nBlockPos">Block position reference</param>
1644         /// <param name="nTxPos">Transaction position reference</param>
1645         /// <returns>Result of operation</returns>
1646         public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref CTransaction tx, ref long nBlockPos, ref long nTxPos)
1647         {
1648             var queryResult = dbConn.Query<IBlockJoinMerkle>("select *  from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID);
1649
1650             if (queryResult.Count == 1)
1651             {
1652                 CBlockStoreItem blockCursor = (CBlockStoreItem) queryResult[0];
1653                 CMerkleNode txCursor = (CMerkleNode)queryResult[0];
1654
1655                 var reader = new BinaryReader(fStreamReadWrite).BaseStream;
1656
1657                 if (!txCursor.ReadFromFile(ref reader, blockCursor.nBlockPos, out tx))
1658                 {
1659                     return false; // Unable to read transaction
1660                 }
1661
1662                 return blockCursor.ReadFromFile(ref reader, out block);
1663             }
1664
1665             // Tx not found
1666
1667             return false;
1668         }
1669
1670         public bool GetOutputs(uint256 transactionHash, out Dictionary<COutPoint, TxOutItem> txouts, bool fUnspentOnly=true)
1671         {
1672             txouts = null;
1673
1674             var queryParams = new object[] { (byte[])transactionHash, fUnspentOnly ? OutputFlags.AVAILABLE : (OutputFlags.AVAILABLE | OutputFlags.SPENT) };
1675             var queryResult = dbConn.Query<TxOutItem>("select o.* from [Outputs] o left join [MerkleNodes] m on m.[nMerkleNodeID] = o.[nMerkleNodeID] where m.[TransactionHash] = ? and outputFlags = ?", queryParams);
1676
1677             if (queryResult.Count != 0)
1678             {
1679                 txouts = new Dictionary<COutPoint, TxOutItem>();
1680
1681                 foreach (var o in queryResult)
1682                 {
1683                     var outpointKey = new COutPoint(transactionHash, o.nOut);
1684                     var outpointData = o;
1685
1686                     txouts.Add(outpointKey, outpointData);
1687                 }
1688
1689                 // There are some unspent inputs.
1690                 return true;
1691             }
1692
1693             // This transaction has been spent completely.
1694             return false;
1695         }
1696
1697         public bool WriteNodes(ref CMerkleNode[] merkleNodes)
1698         {
1699             
1700
1701             return true;
1702         }
1703
1704         /// <summary>
1705         /// Get block cursor from map.
1706         /// </summary>
1707         /// <param name="blockHash">block hash</param>
1708         /// <returns>Cursor or null</returns>
1709         public CBlockStoreItem GetMapCursor(uint256 blockHash)
1710         {
1711             if (blockHash == 0)
1712             {
1713                 // Genesis block has zero prevHash and no parent.
1714                 return null;
1715             }
1716
1717             CBlockStoreItem cursor = null;
1718             blockMap.TryGetValue(blockHash, out cursor);
1719
1720             return cursor;
1721         }
1722
1723         /// <summary>
1724         /// Load cursor from database.
1725         /// </summary>
1726         /// <param name="blockHash">Block hash</param>
1727         /// <returns>Block cursor object</returns>
1728         public CBlockStoreItem GetDBCursor(uint256 blockHash)
1729         {
1730             // Trying to get cursor from the database.
1731             var QueryBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1732
1733             if (QueryBlockCursor.Count == 1)
1734             {
1735                 return QueryBlockCursor[0];
1736             }
1737
1738             // Nothing found.
1739             return null;
1740         }
1741
1742         /// <summary>
1743         /// Update cursor in memory and on disk.
1744         /// </summary>
1745         /// <param name="cursor">Block cursor</param>
1746         /// <returns>Result</returns>
1747         public bool UpdateMapCursor(CBlockStoreItem cursor)
1748         {
1749             var original = blockMap[cursor.Hash];
1750             return blockMap.TryUpdate(cursor.Hash, cursor, original);
1751         }
1752
1753         /// <summary>
1754         /// Update cursor record in database.
1755         /// </summary>
1756         /// <param name="cursor">Block cursor object</param>
1757         /// <returns>Result</returns>
1758         public bool UpdateDBCursor(ref CBlockStoreItem cursor)
1759         {
1760             return dbConn.Update(cursor) != 0;
1761         }
1762
1763         public bool ProcessBlock(ref CBlock block)
1764         {
1765             var blockHash = block.header.Hash;
1766
1767             if (blockMap.ContainsKey(blockHash))
1768             {
1769                 // We already have this block.
1770                 return false;
1771             }
1772
1773             if (orphanMap.ContainsKey(blockHash))
1774             {
1775                 // We already have block in the list of orphans.
1776                 return false;
1777             }
1778
1779             // TODO: Limited duplicity on stake and reserialization of block signature
1780
1781             if (!block.CheckBlock(true, true, true))
1782             {
1783                 // Preliminary checks failure.
1784                 return false;
1785             }
1786
1787             if (block.IsProofOfStake)
1788             {
1789                 if (!block.SignatureOK)
1790                 {
1791                     // Proof-of-Stake signature validation failure.
1792                     return false;
1793                 }
1794
1795                 // TODO: proof-of-stake validation
1796
1797                 uint256 hashProofOfStake = 0, targetProofOfStake = 0;
1798                 if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
1799                 {
1800                     return false; // do not error here as we expect this during initial block download
1801                 }
1802                 if (!mapProofOfStake.ContainsKey(blockHash)) 
1803                 {
1804                     // add to mapProofOfStake
1805                     mapProofOfStake.TryAdd(blockHash, hashProofOfStake);
1806                 }
1807
1808             }
1809
1810             // TODO: difficulty verification
1811
1812             // If don't already have its previous block, shunt it off to holding area until we get it
1813             if (!blockMap.ContainsKey(block.header.prevHash))
1814             {
1815                 if (block.IsProofOfStake)
1816                 {
1817                     // TODO: limit duplicity on stake
1818                 }
1819
1820                 var block2 = new CBlock(block);
1821                 orphanMap.TryAdd(blockHash, block2);
1822                 orphanMapByPrev.TryAdd(blockHash, block2);
1823
1824                 return true;
1825             }
1826
1827             // Store block to disk
1828             if (!AcceptBlock(ref block))
1829             {
1830                 // Accept failed
1831                 return false;
1832             }
1833
1834             // Recursively process any orphan blocks that depended on this one
1835             var orphansQueue = new List<uint256>();
1836             orphansQueue.Add(blockHash);
1837
1838             for (int i = 0; i < orphansQueue.Count; i++)
1839             {
1840                 var hashPrev = orphansQueue[i];
1841
1842                 foreach (var pair in orphanMap)
1843                 {
1844                     var orphanBlock = pair.Value;
1845
1846                     if (orphanBlock.header.prevHash == blockHash)
1847                     {
1848                         if (AcceptBlock(ref orphanBlock))
1849                         {
1850                             orphansQueue.Add(pair.Key);
1851                         }
1852
1853                         CBlock dummy1;
1854                         orphanMap.TryRemove(pair.Key, out dummy1);
1855                     }
1856                 }
1857
1858                 CBlock dummy2;
1859                 orphanMap.TryRemove(hashPrev, out dummy2);
1860             }
1861
1862             return true;
1863         }
1864
1865         public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
1866         {
1867             // TODO: Rewrite completely.
1868
1869             var nOffset = 0L;
1870
1871             var buffer = new byte[CBlock.nMaxBlockSize]; // Max block size is 1Mb
1872             var intBuffer = new byte[4];
1873
1874             var fStream2 = File.OpenRead(BlockFile);
1875             var readerForBlocks = new BinaryReader(fStream2).BaseStream;
1876
1877             readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
1878
1879             while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
1880             {
1881                 var nMagic = BitConverter.ToUInt32(buffer, 0);
1882                 if (nMagic != 0xe5e9e8e4)
1883                 {
1884                     throw new Exception("Incorrect magic number.");
1885                 }
1886
1887                 var nBytesRead = readerForBlocks.Read(buffer, 0, 4);
1888                 if (nBytesRead != 4)
1889                 {
1890                     throw new Exception("BLKSZ EOF");
1891                 }
1892
1893                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
1894
1895                 nOffset = readerForBlocks.Position;
1896
1897                 nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize);
1898
1899                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
1900                 {
1901                     throw new Exception("BLK EOF");
1902                 }
1903
1904                 var block = new CBlock(buffer);
1905                 var hash = block.header.Hash;
1906
1907                 if (blockMap.ContainsKey(hash))
1908                 {
1909                     continue;
1910                 }
1911
1912                 if (!ProcessBlock(ref block))
1913                 {
1914                     throw new Exception("Invalid block: " + block.header.Hash);
1915                 }
1916
1917                 int nCount = blockMap.Count;
1918                 Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
1919
1920                 /*
1921                 if (nCount % 100 == 0 && nCount != 0)
1922                 {
1923                     Console.WriteLine("Commit...");
1924                     dbConn.Commit();
1925                     dbConn.BeginTransaction();
1926                 }*/
1927             }
1928
1929             dbConn.Commit();
1930
1931             return true;
1932         }
1933
1934         ~CBlockStore()
1935         {
1936             Dispose(false);
1937         }
1938
1939         public void Dispose()
1940         {
1941             Dispose(true);
1942             GC.SuppressFinalize(this);
1943         }
1944
1945         protected virtual void Dispose(bool disposing)
1946         {
1947             if (!disposed)
1948             {
1949                 if (disposing)
1950                 {
1951                     // Free other state (managed objects).
1952
1953                     fStreamReadWrite.Dispose();
1954                 }
1955
1956                 if (dbConn != null)
1957                 {
1958                     dbConn.Close();
1959                     dbConn = null;
1960                 }
1961
1962                 disposed = true;
1963             }
1964         }
1965
1966     }
1967 }