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