fee329809a81156ac23bba0f6f7ebd68beef9450
[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             uint256 blockHash = itemTemplate.Hash;
326
327             if (blockMap.ContainsKey(blockHash))
328             {
329                 // Already have this block.
330                 return false;
331             }
332
333             // Begin transaction
334             dbConn.BeginTransaction();
335
336             // Compute chain trust score
337             itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
338
339             if (!itemTemplate.SetStakeEntropyBit(Entropy.GetStakeEntropyBit(itemTemplate.nHeight, blockHash)))
340             {
341                 return false; // SetStakeEntropyBit() failed
342             }
343
344             // Save proof-of-stake hash value
345             if (itemTemplate.IsProofOfStake)
346             {
347                 uint256 hashProofOfStake;
348                 if (!GetProofOfStakeHash(blockHash, out hashProofOfStake))
349                 {
350                     return false;  // hashProofOfStake not found 
351                 }
352                 itemTemplate.hashProofOfStake = hashProofOfStake;
353             }
354
355             // compute stake modifier
356             long nStakeModifier = 0;
357             bool fGeneratedStakeModifier = false;
358             if (!StakeModifier.ComputeNextStakeModifier(itemTemplate, ref nStakeModifier, ref fGeneratedStakeModifier))
359             {
360                 return false;  // ComputeNextStakeModifier() failed
361             }
362
363             itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
364             itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
365
366             // TODO: verify stake modifier checkpoints
367
368             // Add to index
369             if (block.IsProofOfStake)
370             {
371                 itemTemplate.SetProofOfStake();
372
373                 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
374                 itemTemplate.nStakeTime = block.vtx[1].nTime;
375             }
376
377             if (!itemTemplate.WriteToFile(ref fStreamReadWrite, ref block))
378             {
379                 return false;
380             }
381
382             if (dbConn.Insert(itemTemplate) == 0)
383             {
384                 return false; // Insert failed
385             }
386
387             // Get last RowID.
388             itemTemplate.ItemID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
389             
390             if (!blockMap.TryAdd(blockHash, itemTemplate))
391             {
392                 return false; // blockMap add failed
393             }
394
395             if (itemTemplate.nChainTrust > ChainParams.nBestChainTrust)
396             {
397                 // New best chain
398
399                 if (!SetBestChain(ref itemTemplate))
400                 {
401                     return false; // SetBestChain failed.
402                 }
403             }
404
405             // Commit transaction
406             dbConn.Commit();
407
408             return true;
409         }
410
411         private bool SetBestChain(ref CBlockStoreItem cursor)
412         {
413             uint256 hashBlock = cursor.Hash;
414
415             if (genesisBlockCursor == null && hashBlock == NetInfo.nHashGenesisBlock)
416             {
417                 genesisBlockCursor = cursor;
418             }
419             else if (ChainParams.nHashBestChain == (uint256)cursor.prevHash)
420             {
421                 if (!SetBestChainInner(cursor))
422                 {
423                     return false;
424                 }
425             }
426             else
427             {
428                 // the first block in the new chain that will cause it to become the new best chain
429                 var cursorIntermediate = cursor;
430
431                 // list of blocks that need to be connected afterwards
432                 var secondary = new List<CBlockStoreItem>();
433
434                 // Reorganize is costly in terms of db load, as it works in a single db transaction.
435                 // Try to limit how much needs to be done inside
436                 while (cursorIntermediate.prev != null && cursorIntermediate.prev.nChainTrust > bestBlockCursor.nChainTrust)
437                 {
438                     secondary.Add(cursorIntermediate);
439                     cursorIntermediate = cursorIntermediate.prev;
440                 }
441
442                 // Switch to new best branch
443                 if (!Reorganize(cursorIntermediate))
444                 {
445                     InvalidChainFound(cursor);
446                     return false; // reorganize failed
447                 }
448
449                 // Connect further blocks
450                 foreach (var currentCursor in secondary)
451                 {
452                     CBlock block;
453                     if (!currentCursor.ReadFromFile(ref fStreamReadWrite, out block))
454                     {
455                         // ReadFromDisk failed
456                         break;
457                     }
458
459                     // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
460                     if (!SetBestChainInner(currentCursor))
461                     {
462                         break;
463                     }
464                 }
465             }
466
467             bestBlockCursor = cursor;
468             nTimeBestReceived = Interop.GetTime();
469             nTransactionsUpdated++;
470
471             return true;
472         }
473
474         private void InvalidChainFound(CBlockStoreItem cursor)
475         {
476             throw new NotImplementedException();
477         }
478
479         private bool Reorganize(CBlockStoreItem cursorIntermediate)
480         {
481             // Find the fork
482             var fork = bestBlockCursor;
483             var longer = cursorIntermediate;
484
485             while (fork.ItemID != longer.ItemID)
486             {
487                 while (longer.nHeight > fork.nHeight)
488                 {
489                     if ((longer = longer.prev) == null)
490                     {
491                         return false; // longer.prev is null
492                     }
493                 }
494
495                 if (fork.ItemID == longer.ItemID)
496                 {
497                     break;
498                 }
499
500                 if ((fork = fork.prev) == null)
501                 {
502                     return false; // fork.prev is null
503                 }
504             }
505
506             // List of what to disconnect
507             var disconnect = new List<CBlockStoreItem>();
508             for (var cursor = bestBlockCursor; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
509             {
510                 disconnect.Add(cursor);
511             }
512
513             // List of what to connect
514             var connect = new List<CBlockStoreItem>();
515             for (var cursor = cursorIntermediate; cursor.ItemID != fork.ItemID; cursor = cursor.prev)
516             {
517                 connect.Add(cursor);
518             }
519             connect.Reverse();
520
521             // Disconnect shorter branch
522             var txResurrect = new List<CTransaction>();
523             foreach (var blockCursor in disconnect)
524             {
525                 CBlock block;
526                 if (!blockCursor.ReadFromFile(ref fStreamReadWrite, out block))
527                 {
528                     return false; // ReadFromFile for disconnect failed.
529                 }
530                 if (!DisconnectBlock(blockCursor, ref block))
531                 {
532                     return false; // DisconnectBlock failed.
533                 }
534
535                 // Queue memory transactions to resurrect
536                 foreach (var tx in block.vtx)
537                 {
538                     if (!tx.IsCoinBase && !tx.IsCoinStake)
539                     {
540                         txResurrect.Add(tx);
541                     }
542                 }
543             }
544
545
546             // Connect longer branch
547             var txDelete = new List<CTransaction>();
548             foreach (var cursor in connect)
549             {
550                 CBlock block;
551                 if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
552                 {
553                     return false; // ReadFromDisk for connect failed
554                 }
555
556                 if (!ConnectBlock(cursor, ref block))
557                 {
558                     // Invalid block
559                     return false; // ConnectBlock failed
560                 }
561
562                 // Queue memory transactions to delete
563                 foreach (var tx in block.vtx)
564                 {
565                     txDelete.Add(tx);
566                 }
567             }
568
569             if (!UpdateTopChain(cursorIntermediate))
570             {
571                 return false; // UpdateTopChain failed
572             }
573
574             // Resurrect memory transactions that were in the disconnected branch
575             foreach (var tx in txResurrect)
576             {
577                 mapUnconfirmedTx.TryAdd(tx.Hash, tx);
578             }
579
580             // Delete redundant memory transactions that are in the connected branch
581             foreach (var tx in txDelete)
582             {
583                 CTransaction dummy;
584                 mapUnconfirmedTx.TryRemove(tx.Hash, out dummy);
585             }
586
587             return true; // Done
588         }
589
590         private bool DisconnectBlock(CBlockStoreItem blockCursor, ref CBlock block)
591         {
592             throw new NotImplementedException();
593         }
594
595         private bool SetBestChainInner(CBlockStoreItem cursor)
596         {
597             uint256 hash = cursor.Hash;
598             CBlock block;
599             if (!cursor.ReadFromFile(ref fStreamReadWrite, out block))
600             {
601                 return false; // Unable to read block from file.
602             }
603
604             // Adding to current best branch
605             if (!ConnectBlock(cursor, ref block) || !UpdateTopChain(cursor))
606             {
607                 InvalidChainFound(cursor);
608                 return false;
609             }
610
611             // Add to current best branch
612             cursor.prev.next = cursor;
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(ref inputs);
681                     if (nSigOps > CBlock.nMaxSigOps)
682                     {
683                         return false; // too many sigops
684                     }
685
686                     ulong nTxValueIn = tx.GetValueIn(ref 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, ref inputs, ref queued, ref 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, ref Dictionary<COutPoint, TxOutItem> inputs, ref Dictionary<COutPoint, TxOutItem> queued, ref 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                 dbConn.Rollback();
891
892                 return false;
893             }
894
895             return true;
896         }
897
898         /// <summary>
899         /// GEt block by hash.
900         /// </summary>
901         /// <param name="blockHash">Block hash</param>
902         /// <param name="block">Block object reference</param>
903         /// <param name="nBlockPos">Block position reference</param>
904         /// <returns>Result</returns>
905         public bool GetBlock(uint256 blockHash, ref CBlock block, ref long nBlockPos)
906         {
907             CBlockStoreItem cursor;
908
909             if (!blockMap.TryGetValue(blockHash, out cursor))
910             {
911                 return false; // Unable to fetch block cursor
912             }
913
914             nBlockPos = cursor.nBlockPos;
915
916             return cursor.ReadFromFile(ref fStreamReadWrite, out block);
917         }
918
919         /// <summary>
920         /// Get block and transaction by transaction hash.
921         /// </summary>
922         /// <param name="TxID">Transaction hash</param>
923         /// <param name="block">Block reference</param>
924         /// <param name="nBlockPos">Block position reference</param>
925         /// <returns>Result of operation</returns>
926         public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref long nBlockPos)
927         {
928             var queryResult = dbConn.Query<CBlockStoreItem>("select b.* from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID);
929
930             if (queryResult.Count == 1)
931             {
932                 CBlockStoreItem blockCursor = queryResult[0];
933
934                 return blockCursor.ReadFromFile(ref fStreamReadWrite, out block);
935             }
936
937             // Tx not found
938
939             return false;
940         }
941
942         public bool GetOutputs(uint256 transactionHash, out Dictionary<COutPoint, TxOutItem> txouts, bool fUnspentOnly=true)
943         {
944             txouts = null;
945
946             var queryParams = new object[] { (byte[])transactionHash, fUnspentOnly ? OutputFlags.AVAILABLE : (OutputFlags.AVAILABLE | OutputFlags.SPENT) };
947             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);
948
949             if (queryResult.Count != 0)
950             {
951                 txouts = new Dictionary<COutPoint, TxOutItem>();
952
953                 foreach (var o in queryResult)
954                 {
955                     var outpointKey = new COutPoint(transactionHash, o.nOut);
956                     var outpointData = o;
957
958                     txouts.Add(outpointKey, outpointData);
959                 }
960
961                 // There are some unspent inputs.
962                 return true;
963             }
964
965             // This transaction has been spent completely.
966             return false;
967         }
968
969         public bool WriteNodes(ref CMerkleNode[] merkleNodes)
970         {
971             
972
973             return true;
974         }
975
976         /// <summary>
977         /// Get block cursor from map.
978         /// </summary>
979         /// <param name="blockHash">block hash</param>
980         /// <returns>Cursor or null</returns>
981         public CBlockStoreItem GetMapCursor(uint256 blockHash)
982         {
983             if (blockHash == 0)
984             {
985                 // Genesis block has zero prevHash and no parent.
986                 return null;
987             }
988
989             CBlockStoreItem cursor = null;
990             blockMap.TryGetValue(blockHash, out cursor);
991
992             return cursor;
993         }
994
995         /// <summary>
996         /// Load cursor from database.
997         /// </summary>
998         /// <param name="blockHash">Block hash</param>
999         /// <returns>Block cursor object</returns>
1000         public CBlockStoreItem GetDBCursor(uint256 blockHash)
1001         {
1002             // Trying to get cursor from the database.
1003             var QueryBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])blockHash);
1004
1005             if (QueryBlockCursor.Count == 1)
1006             {
1007                 return QueryBlockCursor[0];
1008             }
1009
1010             // Nothing found.
1011             return null;
1012         }
1013
1014         /// <summary>
1015         /// Update cursor in memory and on disk.
1016         /// </summary>
1017         /// <param name="cursor">Block cursor</param>
1018         /// <returns>Result</returns>
1019         public bool UpdateMapCursor(CBlockStoreItem cursor)
1020         {
1021             var original = blockMap[cursor.Hash];
1022             return blockMap.TryUpdate(cursor.Hash, cursor, original);
1023         }
1024
1025         /// <summary>
1026         /// Update cursor record in database.
1027         /// </summary>
1028         /// <param name="cursor">Block cursor object</param>
1029         /// <returns>Result</returns>
1030         public bool UpdateDBCursor(ref CBlockStoreItem cursor)
1031         {
1032             return dbConn.Update(cursor) != 0;
1033         }
1034
1035         public bool ProcessBlock(ref CBlock block)
1036         {
1037             var blockHash = block.header.Hash;
1038
1039             if (blockMap.ContainsKey(blockHash))
1040             {
1041                 // We already have this block.
1042                 return false;
1043             }
1044
1045             if (orphanMap.ContainsKey(blockHash))
1046             {
1047                 // We already have block in the list of orphans.
1048                 return false;
1049             }
1050
1051             // TODO: Limited duplicity on stake and reserialization of block signature
1052
1053             if (!block.CheckBlock(true, true, true))
1054             {
1055                 // Preliminary checks failure.
1056                 return false;
1057             }
1058
1059             if (block.IsProofOfStake)
1060             {
1061                 if (!block.SignatureOK)
1062                 {
1063                     // Proof-of-Stake signature validation failure.
1064                     return false;
1065                 }
1066
1067                 // TODO: proof-of-stake validation
1068
1069                 uint256 hashProofOfStake = 0, targetProofOfStake = 0;
1070                 if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
1071                 {
1072                     return false; // do not error here as we expect this during initial block download
1073                 }
1074                 if (!mapProofOfStake.ContainsKey(blockHash)) 
1075                 {
1076                     // add to mapProofOfStake
1077                     mapProofOfStake.TryAdd(blockHash, hashProofOfStake);
1078                 }
1079
1080             }
1081
1082             // TODO: difficulty verification
1083
1084             // If don't already have its previous block, shunt it off to holding area until we get it
1085             if (!blockMap.ContainsKey(block.header.prevHash))
1086             {
1087                 if (block.IsProofOfStake)
1088                 {
1089                     // TODO: limit duplicity on stake
1090                 }
1091
1092                 var block2 = new CBlock(block);
1093                 orphanMap.TryAdd(blockHash, block2);
1094                 orphanMapByPrev.TryAdd(blockHash, block2);
1095
1096                 return true;
1097             }
1098
1099             // Store block to disk
1100             if (!AcceptBlock(ref block))
1101             {
1102                 // Accept failed
1103                 return false;
1104             }
1105
1106             // Recursively process any orphan blocks that depended on this one
1107             var orphansQueue = new List<uint256>();
1108             orphansQueue.Add(blockHash);
1109
1110             for (int i = 0; i < orphansQueue.Count; i++)
1111             {
1112                 var hashPrev = orphansQueue[i];
1113
1114                 foreach (var pair in orphanMap)
1115                 {
1116                     var orphanBlock = pair.Value;
1117
1118                     if (orphanBlock.header.prevHash == blockHash)
1119                     {
1120                         if (AcceptBlock(ref orphanBlock))
1121                         {
1122                             orphansQueue.Add(pair.Key);
1123                         }
1124
1125                         CBlock dummy1;
1126                         orphanMap.TryRemove(pair.Key, out dummy1);
1127                     }
1128                 }
1129
1130                 CBlock dummy2;
1131                 orphanMap.TryRemove(hashPrev, out dummy2);
1132             }
1133
1134             return true;
1135         }
1136
1137         public bool ParseBlockFile(string BlockFile = "bootstrap.dat")
1138         {
1139             // TODO: Rewrite completely.
1140
1141             var nOffset = 0L;
1142
1143             var buffer = new byte[CBlock.nMaxBlockSize]; // Max block size is 1Mb
1144             var intBuffer = new byte[4];
1145
1146             var fStream2 = File.OpenRead(BlockFile);
1147
1148             fStream2.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
1149
1150             while (fStream2.Read(buffer, 0, 4) == 4) // Read magic number
1151             {
1152                 var nMagic = BitConverter.ToUInt32(buffer, 0);
1153                 if (nMagic != 0xe5e9e8e4)
1154                 {
1155                     throw new Exception("Incorrect magic number.");
1156                 }
1157
1158                 var nBytesRead = fStream2.Read(buffer, 0, 4);
1159                 if (nBytesRead != 4)
1160                 {
1161                     throw new Exception("BLKSZ EOF");
1162                 }
1163
1164                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
1165
1166                 nOffset = fStream2.Position;
1167
1168                 nBytesRead = fStream2.Read(buffer, 0, nBlockSize);
1169
1170                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
1171                 {
1172                     throw new Exception("BLK EOF");
1173                 }
1174
1175                 var block = new CBlock(buffer);
1176                 var hash = block.header.Hash;
1177
1178                 if (blockMap.ContainsKey(hash))
1179                 {
1180                     continue;
1181                 }
1182
1183                 if (!ProcessBlock(ref block))
1184                 {
1185                     throw new Exception("Invalid block: " + block.header.Hash);
1186                 }
1187
1188                 int nCount = blockMap.Count;
1189                 Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
1190
1191                 /*
1192                 if (nCount % 100 == 0 && nCount != 0)
1193                 {
1194                     Console.WriteLine("Commit...");
1195                     dbConn.Commit();
1196                     dbConn.BeginTransaction();
1197                 }*/
1198             }
1199
1200             return true;
1201         }
1202
1203         ~CBlockStore()
1204         {
1205             Dispose(false);
1206         }
1207
1208         public void Dispose()
1209         {
1210             Dispose(true);
1211             GC.SuppressFinalize(this);
1212         }
1213
1214         protected virtual void Dispose(bool disposing)
1215         {
1216             if (!disposed)
1217             {
1218                 if (disposing)
1219                 {
1220                     // Free other state (managed objects).
1221
1222                     fStreamReadWrite.Dispose();
1223                 }
1224
1225                 if (dbConn != null)
1226                 {
1227                     dbConn.Close();
1228                     dbConn = null;
1229                 }
1230
1231                 disposed = true;
1232             }
1233         }
1234
1235     }
1236 }