Remove interfaces, split database objects into new file.
[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.Collections.Concurrent;
23
24 using SQLite.Net;
25 using SQLite.Net.Interop;
26 using SQLite.Net.Platform.Generic;
27 using System.Collections.Generic;
28 using System.Text;
29
30 namespace Novacoin
31 {
32     public class CBlockStore : IDisposable
33     {
34         public const uint nMagicNumber = 0xe5e9e8e4;
35
36         private bool disposed = false;
37         private object LockObj = new object();
38
39         /// <summary>
40         /// SQLite connection object.
41         /// </summary>
42         private SQLiteConnection dbConn;
43
44         /// <summary>
45         /// Current SQLite platform
46         /// </summary>
47         private ISQLitePlatform dbPlatform;
48
49         /// <summary>
50         /// Block file.
51         /// </summary>
52         private string strBlockFile;
53
54         /// <summary>
55         /// Index database file.
56         /// </summary>
57         private string strDbFile;
58
59         /// <summary>
60         /// Map of block tree nodes.
61         /// 
62         /// blockHash => CBlockStoreItem
63         /// </summary>
64         private ConcurrentDictionary<uint256, CBlockStoreItem> blockMap = new ConcurrentDictionary<uint256, CBlockStoreItem>();
65
66         /// <summary>
67         /// Orphaned blocks map.
68         /// </summary>
69         private ConcurrentDictionary<uint256, CBlock> orphanMap = new ConcurrentDictionary<uint256, CBlock>();
70         private ConcurrentDictionary<uint256, CBlock> orphanMapByPrev = new ConcurrentDictionary<uint256, CBlock>();
71
72         /// <summary>
73         /// Unconfirmed transactions.
74         /// 
75         /// TxID => Transaction
76         /// </summary>
77         private ConcurrentDictionary<uint256, CTransaction> mapUnconfirmedTx = new ConcurrentDictionary<uint256, CTransaction>();
78
79         /// <summary>
80         /// Map of the proof-of-stake hashes. This is necessary for stake duplication checks.
81         /// </summary>
82         private ConcurrentDictionary<uint256, uint256> mapProofOfStake = new ConcurrentDictionary<uint256, uint256>();
83
84
85         private ConcurrentDictionary<COutPoint, uint> mapStakeSeen = new ConcurrentDictionary<COutPoint, uint>();
86         private ConcurrentDictionary<COutPoint, uint> mapStakeSeenOrphan = new ConcurrentDictionary<COutPoint, uint>();
87
88
89         /// <summary>
90         /// Copy of chain state object.
91         /// </summary>
92         private ChainState ChainParams;
93
94         /// <summary>
95         /// Cursor which is pointing us to the end of best chain.
96         /// </summary>
97         private CBlockStoreItem bestBlockCursor = null;
98
99         /// <summary>
100         /// Cursor which is always pointing us to genesis block.
101         /// </summary>
102         private CBlockStoreItem genesisBlockCursor = null;
103
104         /// <summary>
105         /// Current and the only instance of block storage manager. Should be a property with private setter though it's enough for the beginning.
106         /// </summary>
107         public static CBlockStore Instance = null;
108
109         /// <summary>
110         /// Block file stream with read/write access
111         /// </summary>
112         private Stream fStreamReadWrite;
113         private uint nTimeBestReceived;
114         private int nTransactionsUpdated;
115
116         /// <summary>
117         /// Init the block storage manager.
118         /// </summary>
119         /// <param name="IndexDB">Path to index database</param>
120         /// <param name="BlockFile">Path to block file</param>
121         public CBlockStore(string IndexDB = "blockstore.dat", string BlockFile = "blk0001.dat")
122         {
123             strDbFile = IndexDB;
124             strBlockFile = BlockFile;
125
126             bool firstInit = !File.Exists(strDbFile);
127             dbPlatform = new SQLitePlatformGeneric();
128             dbConn = new SQLiteConnection(dbPlatform, strDbFile);
129
130             fStreamReadWrite = File.Open(strBlockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
131
132             Instance = this;
133
134             if (firstInit)
135             {
136                 lock (LockObj)
137                 {
138                     // Create tables
139                     dbConn.CreateTable<CBlockStoreItem>(CreateFlags.AutoIncPK);
140                     dbConn.CreateTable<CMerkleNode>(CreateFlags.AutoIncPK);
141                     dbConn.CreateTable<TxOutItem>(CreateFlags.ImplicitPK);
142                     dbConn.CreateTable<ChainState>(CreateFlags.AutoIncPK);
143
144                     ChainParams = new ChainState()
145                     {
146                         nBestChainTrust = 0,
147                         nBestHeight = 0,
148                         nHashBestChain = 0
149                     };
150
151                     dbConn.Insert(ChainParams);
152
153                     var genesisBlock = new CBlock(
154                         Interop.HexToArray(
155                             "01000000" + // nVersion=1
156                             "0000000000000000000000000000000000000000000000000000000000000000" + // prevhash is zero
157                             "7b0502ad2f9f675528183f83d6385794fbcaa914e6d385c6cb1d866a3b3bb34c" + // merkle root
158                             "398e1151" + // nTime=1360105017
159                             "ffff0f1e" + // nBits=0x1e0fffff
160                             "d3091800" + // nNonce=1575379
161                             "01" +       // nTxCount=1
162                             "01000000" + // nVersion=1
163                             "398e1151" + // nTime=1360105017
164                             "01" +       // nInputs=1
165                             "0000000000000000000000000000000000000000000000000000000000000000" + // input txid is zero
166                             "ffffffff" + // n=uint.maxValue
167                             "4d" +       // scriptSigLen=77
168                             "04ffff001d020f274468747470733a2f2f626974636f696e74616c6b2e6f72672f696e6465782e7068703f746f7069633d3133343137392e6d736731353032313936236d736731353032313936" + // scriptSig
169                             "ffffffff" + // nSequence=uint.maxValue
170                             "01" +       // nOutputs=1
171                             "0000000000000000" + // nValue=0
172                             "00" +       // scriptPubkeyLen=0
173                             "00000000" + // nLockTime=0
174                             "00"         // sigLen=0
175                     ));
176
177                     // Write block to file.
178                     var itemTemplate = new CBlockStoreItem()
179                     {
180                         nHeight = 0
181                     };
182
183                     itemTemplate.FillHeader(genesisBlock.header);
184
185                     if (!AddItemToIndex(ref itemTemplate, ref genesisBlock))
186                     {
187                         throw new Exception("Unable to write genesis block");
188                     }
189                 }
190             }
191             else
192             {
193                 var blockTreeItems = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] order by [ItemId] asc");
194
195                 // Init list of block items
196                 foreach (var item in blockTreeItems)
197                 {
198                     blockMap.TryAdd(item.Hash, item);
199
200                     if (item.IsProofOfStake)
201                     {
202                         // build mapStakeSeen
203                         mapStakeSeen.TryAdd(item.prevoutStake, item.nStakeTime);
204                     }
205                 }
206
207                 // Load data about the top node.
208                 ChainParams = dbConn.Table<ChainState>().First();
209             }
210         }
211
212         public bool GetTxOutCursor(COutPoint outpoint, ref TxOutItem txOutCursor)
213         {
214             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);
215
216             if (queryResults.Count == 1)
217             {
218                 txOutCursor = queryResults[0];
219
220                 return true;
221             }
222
223             // Tx not found
224
225             return false;
226         }
227
228
229         public bool FetchInputs(CTransaction tx, ref Dictionary<COutPoint, TxOutItem> queued, ref Dictionary<COutPoint, TxOutItem> inputs, bool IsBlock, out bool Invalid)
230         {
231             Invalid = false;
232
233             if (tx.IsCoinBase)
234             {
235                 // Coinbase transactions have no inputs to fetch.
236                 return true; 
237             }
238
239             StringBuilder queryBuilder = new StringBuilder();
240             
241             queryBuilder.Append("select o.*, m.[TransactionHash] from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where ");
242
243             for (var i = 0; i < tx.vin.Length; i++)
244             {
245                 queryBuilder.AppendFormat(" {0} (m.[TransactionHash] = x'{1}' and o.[OutputNumber] = x'{2}')", 
246                     (i > 0 ? "or" : string.Empty), Interop.ToHex(tx.vin[i].prevout.hash), 
247                     Interop.ToHex(VarInt.EncodeVarInt(tx.vin[i].prevout.n)
248                 ));
249             }
250
251             var queryResults = dbConn.Query<InputsJoin>(queryBuilder.ToString());
252
253             foreach (var item in queryResults)
254             {
255                 if (item.IsSpent)
256                 {
257                     return false; // Already spent
258                 }
259
260                 var inputsKey =  new COutPoint(item.TransactionHash, item.nOut);
261
262                 item.IsSpent = true;
263
264                 // Add output data to dictionary
265                 inputs.Add(inputsKey, (TxOutItem) item);
266             }
267
268             if (queryResults.Count < tx.vin.Length)
269             {
270                 if (IsBlock)
271                 {
272                     // It seems that some transactions are being spent in the same block.
273
274                     foreach (var txin in tx.vin)
275                     {
276                         var outPoint = txin.prevout;
277
278                         if (!queued.ContainsKey(outPoint))
279                         {
280                             return false; // No such transaction
281                         }
282
283                         // Add output data to dictionary
284                         inputs.Add(outPoint, queued[outPoint]);
285
286                         // Mark output as spent
287                         queued[outPoint].IsSpent = true;                        
288                     }
289                 }
290                 else
291                 {
292                     // Unconfirmed transaction
293
294                     foreach (var txin in tx.vin)
295                     {
296                         var outPoint = txin.prevout;
297                         CTransaction txPrev;
298
299                         if (!mapUnconfirmedTx.TryGetValue(outPoint.hash, out txPrev))
300                         {
301                             return false; // No such transaction
302                         }
303
304                         if (outPoint.n > txPrev.vout.Length)
305                         {
306                             Invalid = true;
307
308                             return false; // nOut is out of range
309                         }
310                         
311                         // TODO: return inputs from map 
312                         throw new NotImplementedException();
313
314                     }
315
316                     return false;
317                 }
318             }
319
320             return true;
321         }
322
323         private bool AddItemToIndex(ref CBlockStoreItem itemTemplate, ref CBlock block)
324         {
325             var writer = new BinaryWriter(fStreamReadWrite).BaseStream;
326             uint256 blockHash = itemTemplate.Hash;
327
328             if (blockMap.ContainsKey(blockHash))
329             {
330                 // Already have this block.
331                 return false;
332             }
333
334             // Compute chain trust score
335             itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
336
337             if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash)))
338             {
339                 return false; // SetStakeEntropyBit() failed
340             }
341
342             // Save proof-of-stake hash value
343             if (itemTemplate.IsProofOfStake)
344             {
345                 uint256 hashProofOfStake;
346                 if (!GetProofOfStakeHash(blockHash, out hashProofOfStake))
347                 {
348                     return false;  // hashProofOfStake not found 
349                 }
350                 itemTemplate.hashProofOfStake = hashProofOfStake;
351             }
352
353             // compute stake modifier
354             long nStakeModifier = 0;
355             bool fGeneratedStakeModifier = false;
356             if (!StakeModifier.ComputeNextStakeModifier(itemTemplate, ref nStakeModifier, ref fGeneratedStakeModifier))
357             {
358                 return false;  // ComputeNextStakeModifier() failed
359             }
360
361             itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
362             itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
363
364             // TODO: verify stake modifier checkpoints
365
366             // Add to index
367             if (block.IsProofOfStake)
368             {
369                 itemTemplate.SetProofOfStake();
370
371                 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
372                 itemTemplate.nStakeTime = block.vtx[1].nTime;
373             }
374
375             if (!itemTemplate.WriteToFile(ref writer, ref block))
376             {
377                 return false;
378             }
379
380             if (dbConn.Insert(itemTemplate) == 0)
381             {
382                 return false; // Insert failed
383             }
384
385             // Get last RowID.
386             itemTemplate.ItemID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
387             
388             if (!blockMap.TryAdd(blockHash, itemTemplate))
389             {
390                 return false; // blockMap add failed
391             }
392
393             if (itemTemplate.nChainTrust > ChainParams.nBestChainTrust)
394             {
395                 // New best chain
396
397                 if (!SetBestChain(ref itemTemplate))
398                 {
399                     return false; // SetBestChain failed.
400                 }
401             }
402
403             return true;
404         }
405
406         private bool SetBestChain(ref CBlockStoreItem cursor)
407         {
408             uint256 hashBlock = cursor.Hash;
409
410             if (genesisBlockCursor == null && hashBlock == NetInfo.nHashGenesisBlock)
411             {
412                 genesisBlockCursor = cursor;
413             }
414             else if (ChainParams.nHashBestChain == (uint256)cursor.prevHash)
415             {
416                 if (!SetBestChainInner(cursor))
417                 {
418                     return false;
419                 }
420             }
421             else
422             {
423                 // the first block in the new chain that will cause it to become the new best chain
424                 var cursorIntermediate = cursor;
425
426                 // list of blocks that need to be connected afterwards
427                 var secondary = new List<CBlockStoreItem>();
428
429                 // Reorganize is costly in terms of db load, as it works in a single db transaction.
430                 // Try to limit how much needs to be done inside
431                 while (cursorIntermediate.prev != null && cursorIntermediate.prev.nChainTrust > bestBlockCursor.nChainTrust)
432                 {
433                     secondary.Add(cursorIntermediate);
434                     cursorIntermediate = cursorIntermediate.prev;
435                 }
436
437                 // Switch to new best branch
438                 if (!Reorganize(cursorIntermediate))
439                 {
440                     InvalidChainFound(cursor);
441                     return false; // reorganize failed
442                 }
443
444                 // Connect further blocks
445                 foreach (var currentCursor in secondary)
446                 {
447                     CBlock block;
448                     if (!currentCursor.ReadFromFile(ref fStreamReadWrite, out block))
449                     {
450                         // ReadFromDisk failed
451                         break;
452                     }
453
454                     // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
455                     if (!SetBestChainInner(currentCursor))
456                     {
457                         break;
458                     }
459                 }
460             }
461
462             bestBlockCursor = cursor;
463             nTimeBestReceived = Interop.GetTime();
464             nTransactionsUpdated++;
465
466             return true;
467         }
468
469         private void InvalidChainFound(CBlockStoreItem cursor)
470         {
471             throw new NotImplementedException();
472         }
473
474         private bool Reorganize(CBlockStoreItem cursorIntermediate)
475         {
476             // Find the fork
477             var fork = bestBlockCursor;
478             var longer = cursorIntermediate;
479
480             while (fork.ItemID != longer.ItemID)
481             {
482                 while (longer.nHeight > fork.nHeight)
483                 {
484                     if ((longer = longer.prev) == null)
485                     {
486                         return false; // longer.prev is null
487                     }
488                 }
489
490                 if (fork.ItemID == longer.ItemID)
491                 {
492                     break;
493                 }
494
495                 if ((fork = fork.prev) == null)
496                 {
497                     return false; // fork.prev is null
498                 }
499             }
500
501             // List of what to disconnect
502             var disconnect = new List<CBlockStoreItem>();
503             for (var cursor = bestBlockCursor; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
504             {
505                 disconnect.Add(cursor);
506             }
507
508             // List of what to connect
509             var connect = new List<CBlockStoreItem>();
510             for (var cursor = cursorIntermediate; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
511             {
512                 connect.Add(cursor);
513             }
514             connect.Reverse();
515
516             // Disconnect shorter branch
517             var txResurrect = new List<CTransaction>();
518             foreach (var blockCursor in disconnect)
519             {
520                 CBlock block;
521                 if (!blockCursor.ReadFromFile(ref fStreamReadWrite, out block))
522                 {
523                     return false; // ReadFromFile for disconnect failed.
524                 }
525                 if (!DisconnectBlock(blockCursor, ref block))
526                 {
527                     return false; // DisconnectBlock failed.
528                 }
529
530                 // Queue memory transactions to resurrect
531                 foreach (var tx in block.vtx)
532                 {
533                     if (!tx.IsCoinBase && !tx.IsCoinStake)
534                     {
535                         txResurrect.Add(tx);
536                     }
537                 }
538             }
539
540
541             // Connect longer branch
542             var txDelete = new List<CTransaction>();
543             foreach (var cursor in connect)
544             {
545                 CBlock block;
546                 if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
547                 {
548                     return false; // ReadFromDisk for connect failed
549                 }
550
551                 if (!ConnectBlock(cursor, ref block))
552                 {
553                     // Invalid block
554                     return false; // ConnectBlock failed
555                 }
556
557                 // Queue memory transactions to delete
558                 foreach (var tx in block.vtx)
559                 {
560                     txDelete.Add(tx);
561                 }
562             }
563
564             if (!UpdateTopChain(cursorIntermediate))
565             {
566                 return false; // UpdateTopChain failed
567             }
568
569             // Make sure it's successfully written to disk 
570             dbConn.Commit();
571
572             // Resurrect memory transactions that were in the disconnected branch
573             foreach (var tx in txResurrect)
574             {
575                 mapUnconfirmedTx.TryAdd(tx.Hash, tx);
576             }
577
578             // Delete redundant memory transactions that are in the connected branch
579             foreach (var tx in txDelete)
580             {
581                 CTransaction dummy;
582                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
583             }
584
585             return true; // Done
586         }
587
588         private bool DisconnectBlock(CBlockStoreItem blockCursor, ref CBlock block)
589         {
590             throw new NotImplementedException();
591         }
592
593         private bool SetBestChainInner(CBlockStoreItem cursor)
594         {
595             uint256 hash = cursor.Hash;
596             CBlock block;
597             if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
598             {
599                 return false; // Unable to read block from file.
600             }
601
602             // Adding to current best branch
603             if (!ConnectBlock(cursor, ref block) || !UpdateTopChain(cursor))
604             {
605                 InvalidChainFound(cursor);
606                 return false;
607             }
608
609             // Add to current best branch
610             cursor.prev.next = cursor;
611
612             dbConn.Commit();
613
614             // Delete redundant memory transactions
615             foreach (var tx in block.vtx)
616             {
617                 CTransaction dummy;
618                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
619             }
620
621             return true;
622         }
623
624         private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck=false)
625         {
626             // Check it again in case a previous version let a bad block in, but skip BlockSig checking
627             if (!block.CheckBlock(!fJustCheck, !fJustCheck, false))
628             {
629                 return false; // Invalid block found.
630             }
631
632             bool fScriptChecks = cursor.nHeight >= Checkpoints.TotalBlocksEstimate;
633             var scriptFlags = scriptflag.SCRIPT_VERIFY_NOCACHE | scriptflag.SCRIPT_VERIFY_P2SH;
634
635             ulong nFees = 0;
636             ulong nValueIn = 0;
637             ulong nValueOut = 0;
638             uint nSigOps = 0;
639
640             var queuedMerkleNodes = new Dictionary<uint256, CMerkleNode>();
641             var queued = new Dictionary<COutPoint, TxOutItem>();
642
643             for (var nTx = 0; nTx < block.vtx.Length; nTx++)
644             {
645                 var tx = block.vtx[nTx];
646                 var hashTx = tx.Hash;
647                 var nTxPos = cursor.nBlockPos + block.GetTxOffset(nTx);
648
649                 Dictionary<COutPoint, TxOutItem> txouts;
650                 if (GetOutputs(hashTx, out txouts))
651                 {
652                     // Do not allow blocks that contain transactions which 'overwrite' older transactions,
653                     // unless those are already completely spent.
654                     return false;
655                 }
656
657                 nSigOps += tx.LegacySigOpCount;
658                 if (nSigOps > CBlock.nMaxSigOps)
659                 {
660                     return false; // too many sigops
661                 }
662
663                 var inputs = new Dictionary<COutPoint, TxOutItem>();
664
665                 if (tx.IsCoinBase)
666                 {
667                     nValueOut += tx.nValueOut;
668                 }
669                 else
670                 {
671                     bool Invalid;
672                     if (!FetchInputs(tx, ref queued, ref inputs, true, out Invalid))
673                     {
674                         return false; // Unable to fetch some inputs.
675                     }
676
677                     // Add in sigops done by pay-to-script-hash inputs;
678                     // this is to prevent a "rogue miner" from creating
679                     // an incredibly-expensive-to-validate block.
680                     nSigOps += tx.GetP2SHSigOpCount(inputs);
681                     if (nSigOps > CBlock.nMaxSigOps)
682                     {
683                         return false; // too many sigops
684                     }
685
686                     ulong nTxValueIn = tx.GetValueIn(inputs);
687                     ulong nTxValueOut = tx.nValueOut;
688
689                     nValueIn += nTxValueIn;
690                     nValueOut += nTxValueOut;
691
692                     if (!tx.IsCoinStake)
693                     {
694                         nFees += nTxValueIn - nTxValueOut;
695                     }
696
697                     if (!ConnectInputs(tx, inputs, queued, cursor, fScriptChecks, scriptFlags))
698                     {
699                         return false;
700                     }
701                 }
702
703                 for (var i = 0u; i < tx.vout.Length; i++)
704                 {
705                     var mNode = new CMerkleNode(cursor.ItemID, nTxPos, tx);
706                     queuedMerkleNodes.Add(hashTx, mNode);
707
708                     var outKey = new COutPoint(hashTx, i);
709                     var outData = new TxOutItem();
710
711                     outData.nValue = tx.vout[i].nValue;
712                     outData.scriptPubKey = tx.vout[i].scriptPubKey;
713                     outData.nOut = i;
714
715
716                     outData.IsSpent = false;
717
718                     queued.Add(outKey, outData);
719                 }
720             }
721
722             if (!block.IsProofOfStake)
723             {
724                 ulong nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees);
725
726                 // Check coinbase reward
727                 if (block.vtx[0].nValueOut > nBlockReward)
728                 {
729                     return false; // coinbase reward exceeded
730                 }
731             }
732
733             cursor.nMint = (long) (nValueOut - nValueIn + nFees);
734             cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + (long)nValueOut - (long)nValueIn;
735
736             if (!UpdateDBCursor(ref cursor))
737             {
738                 return false; // Unable to commit changes
739             }
740
741             if (fJustCheck)
742             {
743                 return true;
744             }
745
746             // Write queued transaction changes
747             var actualMerkleNodes = new Dictionary<uint256, CMerkleNode>();
748             var queuedOutpointItems = new List<TxOutItem>();
749             foreach(KeyValuePair<COutPoint, TxOutItem> outPair in queued)
750             {
751                 uint256 txID = outPair.Key.hash;
752                 CMerkleNode merkleNode;
753
754                 if (actualMerkleNodes.ContainsKey(txID))
755                 {
756                     merkleNode = actualMerkleNodes[txID];
757                 }
758                 else
759                 {
760                     merkleNode = queuedMerkleNodes[txID];
761                     if (!SaveMerkleNode(ref merkleNode))
762                     {
763                         // Unable to save merkle tree cursor.
764                         return false;
765                     }
766                     actualMerkleNodes.Add(txID, merkleNode);
767                 }
768
769                 var outItem = outPair.Value;
770                 outItem.nMerkleNodeID = merkleNode.nMerkleNodeID;
771
772                 queuedOutpointItems.Add(outItem);
773             }
774
775             if (!SaveOutpoints(ref queuedOutpointItems))
776             {
777                 return false; // Unable to save outpoints
778             }
779
780             return true;
781         }
782
783         /// <summary>
784         /// Insert set of outpoints
785         /// </summary>
786         /// <param name="queuedOutpointItems">List of TxOutItem objects.</param>
787         /// <returns>Result</returns>
788         private bool SaveOutpoints(ref List<TxOutItem> queuedOutpointItems)
789         {
790             return dbConn.InsertAll(queuedOutpointItems, false) != 0;
791         }
792
793         /// <summary>
794         /// Insert merkle node into db and set actual record id value.
795         /// </summary>
796         /// <param name="merkleNode">Merkle node object reference.</param>
797         /// <returns>Result</returns>
798         private bool SaveMerkleNode(ref CMerkleNode merkleNode)
799         {
800             if (dbConn.Insert(merkleNode) == 0)
801             {
802                 return false;
803             }
804
805             merkleNode.nMerkleNodeID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
806
807             return true;
808         }
809
810         private bool ConnectInputs(CTransaction tx, Dictionary<COutPoint, TxOutItem> inputs, Dictionary<COutPoint, TxOutItem> queued, CBlockStoreItem cursor, bool fScriptChecks, scriptflag scriptFlags)
811         {
812             throw new NotImplementedException();
813         }
814
815         /// <summary>
816         /// Set new top node or current best chain.
817         /// </summary>
818         /// <param name="cursor"></param>
819         /// <returns></returns>
820         private bool UpdateTopChain(CBlockStoreItem cursor)
821         {
822             ChainParams.HashBestChain = cursor.Hash;
823             ChainParams.nBestChainTrust = cursor.nChainTrust;
824             ChainParams.nBestHeight = cursor.nHeight;
825
826             return dbConn.Update(ChainParams) != 0;
827         }
828
829         /// <summary>
830         /// Try to find proof-of-stake hash in the map.
831         /// </summary>
832         /// <param name="blockHash">Block hash</param>
833         /// <param name="hashProofOfStake">Proof-of-stake hash</param>
834         /// <returns>Proof-of-Stake hash value</returns>
835         private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake)
836         {
837             return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake);
838         }
839
840         public bool AcceptBlock(ref CBlock block)
841         {
842             uint256 nHash = block.header.Hash;
843
844             if (blockMap.ContainsKey(nHash))
845             {
846                 // Already have this block.
847                 return false;
848             }
849
850             CBlockStoreItem prevBlockCursor = null;
851             if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor))
852             {
853                 // Unable to get the cursor.
854                 return false;
855             }
856
857             var prevBlockHeader = prevBlockCursor.BlockHeader;
858
859             // TODO: proof-of-work/proof-of-stake verification
860             uint nHeight = prevBlockCursor.nHeight + 1;
861
862             // Check timestamp against prev
863             if (NetInfo.FutureDrift(block.header.nTime) < prevBlockHeader.nTime)
864             {
865                 // block's timestamp is too early
866                 return false;
867             }
868
869             // Check that all transactions are finalized
870             foreach (var tx in block.vtx)
871             {
872                 if (!tx.IsFinal(nHeight, block.header.nTime))
873                 {
874                     return false;
875                 }
876             }
877
878             // TODO: Enforce rule that the coinbase starts with serialized block height
879
880             // Write block to file.
881             var itemTemplate = new CBlockStoreItem()
882             {
883                 nHeight = nHeight,
884             };
885
886             itemTemplate.FillHeader(block.header);
887
888             if (!AddItemToIndex(ref itemTemplate, ref block))
889             {
890                 return false;
891             }
892
893             return true;
894         }
895
896         /// <summary>
897         /// GEt block by hash.
898         /// </summary>
899         /// <param name="blockHash">Block hash</param>
900         /// <param name="block">Block object reference</param>
901         /// <param name="nBlockPos">Block position reference</param>
902         /// <returns>Result</returns>
903         public bool GetBlock(uint256 blockHash, ref CBlock block, ref long nBlockPos)
904         {
905             CBlockStoreItem cursor;
906
907             if (!blockMap.TryGetValue(blockHash, out cursor))
908             {
909                 return false; // Unable to fetch block cursor
910             }
911
912             nBlockPos = cursor.nBlockPos;
913
914             return cursor.ReadFromFile(ref fStreamReadWrite, out block);
915         }
916
917         /// <summary>
918         /// Get block and transaction by transaction hash.
919         /// </summary>
920         /// <param name="TxID">Transaction hash</param>
921         /// <param name="block">Block reference</param>
922         /// <param name="nBlockPos">Block position reference</param>
923         /// <returns>Result of operation</returns>
924         public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref long nBlockPos)
925         {
926             var queryResult = dbConn.Query<CBlockStoreItem>("select b.* from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID);
927
928             if (queryResult.Count == 1)
929             {
930                 CBlockStoreItem blockCursor = queryResult[0];
931
932                 return blockCursor.ReadFromFile(ref fStreamReadWrite, out block);
933             }
934
935             // Tx not found
936
937             return false;
938         }
939
940         public bool GetOutputs(uint256 transactionHash, out Dictionary<COutPoint, TxOutItem> txouts, bool fUnspentOnly=true)
941         {
942             txouts = null;
943
944             var queryParams = new object[] { (byte[])transactionHash, fUnspentOnly ? OutputFlags.AVAILABLE : (OutputFlags.AVAILABLE | OutputFlags.SPENT) };
945             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);
946
947             if (queryResult.Count != 0)
948             {
949                 txouts = new Dictionary<COutPoint, TxOutItem>();
950
951                 foreach (var o in queryResult)
952                 {
953                     var outpointKey = new COutPoint(transactionHash, o.nOut);
954                     var outpointData = o;
955
956                     txouts.Add(outpointKey, outpointData);
957                 }
958
959                 // There are some unspent inputs.
960                 return true;
961             }
962
963             // This transaction has been spent completely.
964             return false;
965         }
966
967         public bool WriteNodes(ref CMerkleNode[] merkleNodes)
968         {
969             
970
971             return true;
972         }
973
974         /// <summary>
975         /// Get block cursor from map.
976         /// </summary>
977         /// <param name="blockHash">block hash</param>
978         /// <returns>Cursor or null</returns>
979         public CBlockStoreItem GetMapCursor(uint256 blockHash)
980         {
981             if (blockHash == 0)
982             {
983                 // Genesis block has zero prevHash and no parent.
984                 return null;
985             }
986
987             CBlockStoreItem cursor = null;
988             blockMap.TryGetValue(blockHash, out cursor);
989
990             return cursor;
991         }
992
993         /// <summary>
994         /// Load cursor from database.
995         /// </summary>
996         /// <param name="blockHash">Block hash</param>
997         /// <returns>Block cursor object</returns>
998         public CBlockStoreItem GetDBCursor(uint256 blockHash)
999         {
1000             // Trying to get cursor from the database.
1001             var QueryBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1002
1003             if (QueryBlockCursor.Count == 1)
1004             {
1005                 return QueryBlockCursor[0];
1006             }
1007
1008             // Nothing found.
1009             return null;
1010         }
1011
1012         /// <summary>
1013         /// Update cursor in memory and on disk.
1014         /// </summary>
1015         /// <param name="cursor">Block cursor</param>
1016         /// <returns>Result</returns>
1017         public bool UpdateMapCursor(CBlockStoreItem cursor)
1018         {
1019             var original = blockMap[cursor.Hash];
1020             return blockMap.TryUpdate(cursor.Hash, cursor, original);
1021         }
1022
1023         /// <summary>
1024         /// Update cursor record in database.
1025         /// </summary>
1026         /// <param name="cursor">Block cursor object</param>
1027         /// <returns>Result</returns>
1028         public bool UpdateDBCursor(ref CBlockStoreItem cursor)
1029         {
1030             return dbConn.Update(cursor) != 0;
1031         }
1032
1033         public bool ProcessBlock(ref CBlock block)
1034         {
1035             var blockHash = block.header.Hash;
1036
1037             if (blockMap.ContainsKey(blockHash))
1038             {
1039                 // We already have this block.
1040                 return false;
1041             }
1042
1043             if (orphanMap.ContainsKey(blockHash))
1044             {
1045                 // We already have block in the list of orphans.
1046                 return false;
1047             }
1048
1049             // TODO: Limited duplicity on stake and reserialization of block signature
1050
1051             if (!block.CheckBlock(true, true, true))
1052             {
1053                 // Preliminary checks failure.
1054                 return false;
1055             }
1056
1057             if (block.IsProofOfStake)
1058             {
1059                 if (!block.SignatureOK)
1060                 {
1061                     // Proof-of-Stake signature validation failure.
1062                     return false;
1063                 }
1064
1065                 // TODO: proof-of-stake validation
1066
1067                 uint256 hashProofOfStake = 0, targetProofOfStake = 0;
1068                 if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
1069                 {
1070                     return false; // do not error here as we expect this during initial block download
1071                 }
1072                 if (!mapProofOfStake.ContainsKey(blockHash)) 
1073                 {
1074                     // add to mapProofOfStake
1075                     mapProofOfStake.TryAdd(blockHash, hashProofOfStake);
1076                 }
1077
1078             }
1079
1080             // TODO: difficulty verification
1081
1082             // If don't already have its previous block, shunt it off to holding area until we get it
1083             if (!blockMap.ContainsKey(block.header.prevHash))
1084             {
1085                 if (block.IsProofOfStake)
1086                 {
1087                     // TODO: limit duplicity on stake
1088                 }
1089
1090                 var block2 = new CBlock(block);
1091                 orphanMap.TryAdd(blockHash, block2);
1092                 orphanMapByPrev.TryAdd(blockHash, block2);
1093
1094                 return true;
1095             }
1096
1097             // Store block to disk
1098             if (!AcceptBlock(ref block))
1099             {
1100                 // Accept failed
1101                 return false;
1102             }
1103
1104             // Recursively process any orphan blocks that depended on this one
1105             var orphansQueue = new List<uint256>();
1106             orphansQueue.Add(blockHash);
1107
1108             for (int i = 0; i < orphansQueue.Count; i++)
1109             {
1110                 var hashPrev = orphansQueue[i];
1111
1112                 foreach (var pair in orphanMap)
1113                 {
1114                     var orphanBlock = pair.Value;
1115
1116                     if (orphanBlock.header.prevHash == blockHash)
1117                     {
1118                         if (AcceptBlock(ref orphanBlock))
1119                         {
1120                             orphansQueue.Add(pair.Key);
1121                         }
1122
1123                         CBlock dummy1;
1124                         orphanMap.TryRemove(pair.Key, out dummy1);
1125                     }
1126                 }
1127
1128                 CBlock dummy2;
1129                 orphanMap.TryRemove(hashPrev, out dummy2);
1130             }
1131
1132             return true;
1133         }
1134
1135         public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
1136         {
1137             // TODO: Rewrite completely.
1138
1139             var nOffset = 0L;
1140
1141             var buffer = new byte[CBlock.nMaxBlockSize]; // Max block size is 1Mb
1142             var intBuffer = new byte[4];
1143
1144             var fStream2 = File.OpenRead(BlockFile);
1145             var readerForBlocks = new BinaryReader(fStream2).BaseStream;
1146
1147             readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
1148
1149             while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
1150             {
1151                 var nMagic = BitConverter.ToUInt32(buffer, 0);
1152                 if (nMagic != 0xe5e9e8e4)
1153                 {
1154                     throw new Exception("Incorrect magic number.");
1155                 }
1156
1157                 var nBytesRead = readerForBlocks.Read(buffer, 0, 4);
1158                 if (nBytesRead != 4)
1159                 {
1160                     throw new Exception("BLKSZ EOF");
1161                 }
1162
1163                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
1164
1165                 nOffset = readerForBlocks.Position;
1166
1167                 nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize);
1168
1169                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
1170                 {
1171                     throw new Exception("BLK EOF");
1172                 }
1173
1174                 var block = new CBlock(buffer);
1175                 var hash = block.header.Hash;
1176
1177                 if (blockMap.ContainsKey(hash))
1178                 {
1179                     continue;
1180                 }
1181
1182                 if (!ProcessBlock(ref block))
1183                 {
1184                     throw new Exception("Invalid block: " + block.header.Hash);
1185                 }
1186
1187                 int nCount = blockMap.Count;
1188                 Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
1189
1190                 /*
1191                 if (nCount % 100 == 0 && nCount != 0)
1192                 {
1193                     Console.WriteLine("Commit...");
1194                     dbConn.Commit();
1195                     dbConn.BeginTransaction();
1196                 }*/
1197             }
1198
1199             dbConn.Commit();
1200
1201             return true;
1202         }
1203
1204         ~CBlockStore()
1205         {
1206             Dispose(false);
1207         }
1208
1209         public void Dispose()
1210         {
1211             Dispose(true);
1212             GC.SuppressFinalize(this);
1213         }
1214
1215         protected virtual void Dispose(bool disposing)
1216         {
1217             if (!disposed)
1218             {
1219                 if (disposing)
1220                 {
1221                     // Free other state (managed objects).
1222
1223                     fStreamReadWrite.Dispose();
1224                 }
1225
1226                 if (dbConn != null)
1227                 {
1228                     dbConn.Close();
1229                     dbConn = null;
1230                 }
1231
1232                 disposed = true;
1233             }
1234         }
1235
1236     }
1237 }