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