Save proof-of-stake kernel hash.
[NovacoinLibrary.git] / Novacoin / CBlockStore.cs
index 2299e15..f0c830f 100644 (file)
@@ -26,6 +26,8 @@ using SQLite.Net.Interop;
 using SQLite.Net.Platform.Generic;
 using System.Collections.Generic;
 using System.Text;
+using System.Diagnostics.Contracts;
+using System.Linq;
 
 namespace Novacoin
 {
@@ -206,6 +208,9 @@ namespace Novacoin
 
                 // Load data about the top node.
                 ChainParams = dbConn.Table<ChainState>().First();
+
+                genesisBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", (byte[])NetInfo.nHashGenesisBlock).First();
+                bestBlockCursor = dbConn.Query<CBlockStoreItem>("select * from [BlockStorage] where [Hash] = ?", ChainParams.HashBestChain).First();
             }
         }
 
@@ -224,8 +229,7 @@ namespace Novacoin
 
             return false;
         }
-
-
+        
         public bool FetchInputs(CTransaction tx, ref Dictionary<COutPoint, TxOutItem> queued, ref Dictionary<COutPoint, TxOutItem> inputs, bool IsBlock, out bool Invalid)
         {
             Invalid = false;
@@ -233,17 +237,17 @@ namespace Novacoin
             if (tx.IsCoinBase)
             {
                 // Coinbase transactions have no inputs to fetch.
-                return true; 
+                return true;
             }
 
             StringBuilder queryBuilder = new StringBuilder();
-            
+
             queryBuilder.Append("select o.*, m.[TransactionHash] from [Outputs] o left join [MerkleNodes] m on (m.[nMerkleNodeID] = o.[nMerkleNodeID]) where ");
 
             for (var i = 0; i < tx.vin.Length; i++)
             {
-                queryBuilder.AppendFormat(" {0} (m.[TransactionHash] = x'{1}' and o.[OutputNumber] = x'{2}')", 
-                    (i > 0 ? "or" : string.Empty), Interop.ToHex(tx.vin[i].prevout.hash), 
+                queryBuilder.AppendFormat(" {0} (m.[TransactionHash] = x'{1}' and o.[OutputNumber] = x'{2}')",
+                    (i > 0 ? "or" : string.Empty), Interop.ToHex(tx.vin[i].prevout.hash),
                     Interop.ToHex(VarInt.EncodeVarInt(tx.vin[i].prevout.n)
                 ));
             }
@@ -257,12 +261,10 @@ namespace Novacoin
                     return false; // Already spent
                 }
 
-                var inputsKey =  new COutPoint(item.TransactionHash, item.nOut);
-
-                item.IsSpent = true;
+                var inputsKey = new COutPoint(item.TransactionHash, item.nOut);
 
                 // Add output data to dictionary
-                inputs.Add(inputsKey, (TxOutItem) item);
+                inputs.Add(inputsKey, item.getTxOutItem());
             }
 
             if (queryResults.Count < tx.vin.Length)
@@ -275,6 +277,11 @@ namespace Novacoin
                     {
                         var outPoint = txin.prevout;
 
+                        if (inputs.ContainsKey(outPoint))
+                        {
+                            continue; // We have already seen this input.
+                        }
+
                         if (!queued.ContainsKey(outPoint))
                         {
                             return false; // No such transaction
@@ -284,7 +291,7 @@ namespace Novacoin
                         inputs.Add(outPoint, queued[outPoint]);
 
                         // Mark output as spent
-                        queued[outPoint].IsSpent = true;                        
+                        // queued[outPoint].IsSpent = true;
                     }
                 }
                 else
@@ -307,7 +314,7 @@ namespace Novacoin
 
                             return false; // nOut is out of range
                         }
-                        
+
                         // TODO: return inputs from map 
                         throw new NotImplementedException();
 
@@ -322,7 +329,6 @@ namespace Novacoin
 
         private bool AddItemToIndex(ref CBlockStoreItem itemTemplate, ref CBlock block)
         {
-            var writer = new BinaryWriter(fStreamReadWrite).BaseStream;
             uint256 blockHash = itemTemplate.Hash;
 
             if (blockMap.ContainsKey(blockHash))
@@ -331,6 +337,9 @@ namespace Novacoin
                 return false;
             }
 
+            // Begin transaction
+            dbConn.BeginTransaction();
+
             // Compute chain trust score
             itemTemplate.nChainTrust = (itemTemplate.prev != null ? itemTemplate.prev.nChainTrust : 0) + itemTemplate.nBlockTrust;
 
@@ -370,9 +379,10 @@ namespace Novacoin
 
                 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
                 itemTemplate.nStakeTime = block.vtx[1].nTime;
+                itemTemplate.hashProofOfStake = mapProofOfStake[blockHash];
             }
 
-            if (!itemTemplate.WriteToFile(ref writer, ref block))
+            if (!itemTemplate.WriteToFile(ref fStreamReadWrite, ref block))
             {
                 return false;
             }
@@ -384,7 +394,7 @@ namespace Novacoin
 
             // Get last RowID.
             itemTemplate.ItemID = dbPlatform.SQLiteApi.LastInsertRowid(dbConn.Handle);
-            
+
             if (!blockMap.TryAdd(blockHash, itemTemplate))
             {
                 return false; // blockMap add failed
@@ -400,6 +410,9 @@ namespace Novacoin
                 }
             }
 
+            // Commit transaction
+            dbConn.Commit();
+
             return true;
         }
 
@@ -463,6 +476,11 @@ namespace Novacoin
             nTimeBestReceived = Interop.GetTime();
             nTransactionsUpdated++;
 
+            if (!UpdateTopChain(cursor))
+            {
+                return false; // unable to set top chain node.
+            }
+
             return true;
         }
 
@@ -566,9 +584,6 @@ namespace Novacoin
                 return false; // UpdateTopChain failed
             }
 
-            // Make sure it's successfully written to disk 
-            dbConn.Commit();
-
             // Resurrect memory transactions that were in the disconnected branch
             foreach (var tx in txResurrect)
             {
@@ -607,9 +622,13 @@ namespace Novacoin
             }
 
             // Add to current best branch
-            cursor.prev.next = cursor;
+            var prevCursor = cursor.prev;
+            prevCursor.next = cursor;
 
-            dbConn.Commit();
+            if (!UpdateDBCursor(ref prevCursor))
+            {
+                return false; // unable to update
+            }
 
             // Delete redundant memory transactions
             foreach (var tx in block.vtx)
@@ -621,7 +640,7 @@ namespace Novacoin
             return true;
         }
 
-        private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck=false)
+        private bool ConnectBlock(CBlockStoreItem cursor, ref CBlock block, bool fJustCheck = false)
         {
             // Check it again in case a previous version let a bad block in, but skip BlockSig checking
             if (!block.CheckBlock(!fJustCheck, !fJustCheck, false))
@@ -638,13 +657,20 @@ namespace Novacoin
             uint nSigOps = 0;
 
             var queuedMerkleNodes = new Dictionary<uint256, CMerkleNode>();
-            var queued = new Dictionary<COutPoint, TxOutItem>();
+            var queuedOutputs = new Dictionary<COutPoint, TxOutItem>();
 
             for (var nTx = 0; nTx < block.vtx.Length; nTx++)
             {
                 var tx = block.vtx[nTx];
                 var hashTx = tx.Hash;
-                var nTxPos = cursor.nBlockPos + block.GetTxOffset(nTx);
+
+                if (!queuedMerkleNodes.ContainsKey(hashTx))
+                {
+                    var nTxPos = cursor.nBlockPos + block.GetTxOffset(nTx);
+                    var mNode = new CMerkleNode(cursor.ItemID, nTxPos, tx);
+
+                    queuedMerkleNodes.Add(hashTx, mNode);
+                }
 
                 Dictionary<COutPoint, TxOutItem> txouts;
                 if (GetOutputs(hashTx, out txouts))
@@ -669,7 +695,7 @@ namespace Novacoin
                 else
                 {
                     bool Invalid;
-                    if (!FetchInputs(tx, ref queued, ref inputs, true, out Invalid))
+                    if (!FetchInputs(tx, ref queuedOutputs, ref inputs, true, out Invalid))
                     {
                         return false; // Unable to fetch some inputs.
                     }
@@ -677,13 +703,13 @@ namespace Novacoin
                     // Add in sigops done by pay-to-script-hash inputs;
                     // this is to prevent a "rogue miner" from creating
                     // an incredibly-expensive-to-validate block.
-                    nSigOps += tx.GetP2SHSigOpCount(inputs);
+                    nSigOps += tx.GetP2SHSigOpCount(ref inputs);
                     if (nSigOps > CBlock.nMaxSigOps)
                     {
                         return false; // too many sigops
                     }
 
-                    ulong nTxValueIn = tx.GetValueIn(inputs);
+                    ulong nTxValueIn = tx.GetValueIn(ref inputs);
                     ulong nTxValueOut = tx.nValueOut;
 
                     nValueIn += nTxValueIn;
@@ -694,7 +720,7 @@ namespace Novacoin
                         nFees += nTxValueIn - nTxValueOut;
                     }
 
-                    if (!ConnectInputs(tx, inputs, queued, cursor, fScriptChecks, scriptFlags))
+                    if (!ConnectInputs(tx, ref inputs, ref queuedOutputs, ref cursor, true, fScriptChecks, scriptFlags))
                     {
                         return false;
                     }
@@ -702,20 +728,17 @@ namespace Novacoin
 
                 for (var i = 0u; i < tx.vout.Length; i++)
                 {
-                    var mNode = new CMerkleNode(cursor.ItemID, nTxPos, tx);
-                    queuedMerkleNodes.Add(hashTx, mNode);
-
                     var outKey = new COutPoint(hashTx, i);
-                    var outData = new TxOutItem();
-
-                    outData.nValue = tx.vout[i].nValue;
-                    outData.scriptPubKey = tx.vout[i].scriptPubKey;
-                    outData.nOut = i;
-
-
-                    outData.IsSpent = false;
+                    var outData = new TxOutItem()
+                    {
+                        nMerkleNodeID = -1,
+                        nValue = tx.vout[i].nValue,
+                        scriptPubKey = tx.vout[i].scriptPubKey,
+                        IsSpent = false,
+                        nOut = i
+                    };
 
-                    queued.Add(outKey, outData);
+                    queuedOutputs.Add(outKey, outData);
                 }
             }
 
@@ -730,7 +753,7 @@ namespace Novacoin
                 }
             }
 
-            cursor.nMint = (long) (nValueOut - nValueIn + nFees);
+            cursor.nMint = (long)(nValueOut - nValueIn + nFees);
             cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + (long)nValueOut - (long)nValueIn;
 
             if (!UpdateDBCursor(ref cursor))
@@ -743,51 +766,75 @@ namespace Novacoin
                 return true;
             }
 
+            // Flush merkle nodes.
+            var savedMerkleNodes = new Dictionary<uint256, CMerkleNode>();
+            foreach (var merklePair in queuedMerkleNodes)
+            {
+                var merkleNode = merklePair.Value;
+
+                if (!SaveMerkleNode(ref merkleNode))
+                {
+                    // Unable to save merkle tree cursor.
+                    return false;
+                }
+
+                savedMerkleNodes.Add(merklePair.Key, merkleNode);
+            }
+
             // Write queued transaction changes
-            var actualMerkleNodes = new Dictionary<uint256, CMerkleNode>();
-            var queuedOutpointItems = new List<TxOutItem>();
-            foreach(KeyValuePair<COutPoint, TxOutItem> outPair in queued)
+            var newOutpointItems = new List<TxOutItem>();
+            var updatedOutpointItems = new List<TxOutItem>();
+            foreach (var outPair in queuedOutputs)
             {
-                uint256 txID = outPair.Key.hash;
-                CMerkleNode merkleNode;
+                var outItem = outPair.Value;
 
-                if (actualMerkleNodes.ContainsKey(txID))
+                if (outItem.nMerkleNodeID == -1)
                 {
-                    merkleNode = actualMerkleNodes[txID];
+                    // This outpoint doesn't exist yet, adding to insert list.
+
+                    outItem.nMerkleNodeID = savedMerkleNodes[outPair.Key.hash].nMerkleNodeID;
+                    newOutpointItems.Add(outItem);
                 }
                 else
                 {
-                    merkleNode = queuedMerkleNodes[txID];
-                    if (!SaveMerkleNode(ref merkleNode))
-                    {
-                        // Unable to save merkle tree cursor.
-                        return false;
-                    }
-                    actualMerkleNodes.Add(txID, merkleNode);
-                }
+                    // This outpount already exists, adding to update list.
 
-                var outItem = outPair.Value;
-                outItem.nMerkleNodeID = merkleNode.nMerkleNodeID;
+                    updatedOutpointItems.Add(outItem);
+                }
+            }
 
-                queuedOutpointItems.Add(outItem);
+            if (updatedOutpointItems.Count != 0 && !UpdateOutpoints(ref updatedOutpointItems))
+            {
+                return false; // Unable to update outpoints
             }
 
-            if (!SaveOutpoints(ref queuedOutpointItems))
+            if (newOutpointItems.Count != 0 && !InsertOutpoints(ref newOutpointItems))
             {
-                return false; // Unable to save outpoints
+                return false; // Unable to insert outpoints
             }
 
             return true;
         }
 
         /// <summary>
-        /// Insert set of outpoints
+        /// Insert set of new outpoints
+        /// </summary>
+        /// <param name="newOutpointItems">List of TxOutItem objects.</param>
+        /// <returns>Result</returns>
+        private bool InsertOutpoints(ref List<TxOutItem> newOutpointItems)
+        {
+            return (dbConn.InsertAll(newOutpointItems, false) != 0);
+        }
+
+
+        /// <summary>
+        /// Update set of outpoints
         /// </summary>
         /// <param name="queuedOutpointItems">List of TxOutItem objects.</param>
         /// <returns>Result</returns>
-        private bool SaveOutpoints(ref List<TxOutItem> queuedOutpointItems)
+        private bool UpdateOutpoints(ref List<TxOutItem> updatedOutpointItems)
         {
-            return dbConn.InsertAll(queuedOutpointItems, false) != 0;
+            return (dbConn.UpdateAll(updatedOutpointItems, false) != 0);
         }
 
         /// <summary>
@@ -807,11 +854,164 @@ namespace Novacoin
             return true;
         }
 
-        private bool ConnectInputs(CTransaction tx, Dictionary<COutPoint, TxOutItem> inputs, Dictionary<COutPoint, TxOutItem> queued, CBlockStoreItem cursor, bool fScriptChecks, scriptflag scriptFlags)
+        private bool ConnectInputs(CTransaction tx, ref Dictionary<COutPoint, TxOutItem> inputs, ref Dictionary<COutPoint, TxOutItem> queued, ref CBlockStoreItem cursorBlock, bool fBlock, bool fScriptChecks, scriptflag scriptFlags)
         {
-            throw new NotImplementedException();
+            // Take over previous transactions' spent items
+            // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
+
+            if (!tx.IsCoinBase)
+            {
+                ulong nValueIn = 0;
+                ulong nFees = 0;
+                for (uint i = 0; i < tx.vin.Length; i++)
+                {
+                    var prevout = tx.vin[i].prevout;
+                    Contract.Assert(inputs.ContainsKey(prevout));
+                    var input = inputs[prevout];
+
+                    CBlockStoreItem parentBlockCursor;
+
+                    if (input.nMerkleNodeID == -1)
+                    {
+                        // This input seems as is confirmed by the same block.
+
+                        if (!queued.ContainsKey(prevout))
+                        {
+                            return false; // No such output has been queued by this block.
+                        }
+
+                        // TODO: Ensure that neither coinbase nor coinstake outputs are 
+                        //    available for spending in the generation block.
+                    }
+                    else
+                    {
+                        // This input has been confirmed by one of the earlier accepted blocks.
+
+                        var merkleItem = GetMerkleCursor(input, out parentBlockCursor);
+
+                        if (merkleItem == null)
+                        {
+                            return false; // Unable to find merkle node
+                        }
+
+                        // If prev is coinbase or coinstake, check that it's matured
+                        if (merkleItem.IsCoinBase || merkleItem.IsCoinStake)
+                        {
+                            if (cursorBlock.nHeight - parentBlockCursor.nHeight < NetInfo.nGeneratedMaturity)
+                            {
+                                return false; // tried to spend non-matured generation input.
+                            }
+                        }
+
+                        // check transaction timestamp
+                        if (merkleItem.nTime > tx.nTime)
+                        {
+                            return false; // transaction timestamp earlier than input transaction
+                        }
+                    }
+
+                    // Check for negative or overflow input values
+                    nValueIn += input.nValue;
+                    if (!CTransaction.MoneyRange(input.nValue) || !CTransaction.MoneyRange(nValueIn))
+                    {
+                        return false; // txin values out of range
+                    }
+
+                }
+
+                // The first loop above does all the inexpensive checks.
+                // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
+                // Helps prevent CPU exhaustion attacks.
+                for (int i = 0; i < tx.vin.Length; i++)
+                {
+                    var prevout = tx.vin[i].prevout;
+                    Contract.Assert(inputs.ContainsKey(prevout));
+                    var input = inputs[prevout];
+
+                    // Check for conflicts (double-spend)
+                    if (input.IsSpent)
+                    {
+                        return false;
+                    }
+
+                    // Skip ECDSA signature verification when connecting blocks (fBlock=true)
+                    // before the last blockchain checkpoint. This is safe because block merkle hashes are
+                    // still computed and checked, and any change will be caught at the next checkpoint.
+                    if (fScriptChecks)
+                    {
+                        // Verify signature
+                        if (!ScriptCode.VerifyScript(tx.vin[i].scriptSig, input.scriptPubKey, tx, i, (int)scriptflag.SCRIPT_VERIFY_P2SH, 0))
+                        {
+                            return false; // VerifyScript failed.
+                        }
+                    }
+
+                    // Mark outpoint as spent
+                    input.IsSpent = true;
+                    inputs[prevout] = input;
+
+                    // Write back
+                    if (fBlock)
+                    {
+                        if (input.nMerkleNodeID != -1)
+                        {
+                            // Input has been confirmed earlier.
+                            queued.Add(prevout, input);
+                        }
+                        else
+                        {
+                            // Input has been confirmed by current block.
+                            queued[prevout] = input;
+                        }
+                    }
+                }
+
+                if (tx.IsCoinStake)
+                {
+                    // ppcoin: coin stake tx earns reward instead of paying fee
+                    ulong nCoinAge;
+                    if (!tx.GetCoinAge(ref inputs, out nCoinAge))
+                    {
+                        return false; // unable to get coin age for coinstake
+                    }
+
+                    ulong nReward = tx.nValueOut - nValueIn;
+
+                    ulong nCalculatedReward = CBlock.GetProofOfStakeReward(nCoinAge, cursorBlock.nBits, tx.nTime) - tx.GetMinFee(1, false, CTransaction.MinFeeMode.GMF_BLOCK) + CTransaction.nCent;
+
+                    if (nReward > nCalculatedReward)
+                    {
+                        return false; // coinstake pays too much
+                    }
+                }
+                else
+                {
+                    if (nValueIn < tx.nValueOut)
+                    {
+                        return false; // value in < value out
+                    }
+
+                    // Tally transaction fees
+                    ulong nTxFee = nValueIn - tx.nValueOut;
+                    if (nTxFee < 0)
+                    {
+                        return false; // nTxFee < 0
+                    }
+
+                    nFees += nTxFee;
+
+                    if (!CTransaction.MoneyRange(nFees))
+                    {
+                        return false; // nFees out of range
+                    }
+                }
+                
+            }
+
+            return true;
         }
 
+
         /// <summary>
         /// Set new top node or current best chain.
         /// </summary>
@@ -887,6 +1087,8 @@ namespace Novacoin
 
             if (!AddItemToIndex(ref itemTemplate, ref block))
             {
+                dbConn.Rollback();
+
                 return false;
             }
 
@@ -921,14 +1123,19 @@ namespace Novacoin
         /// <param name="block">Block reference</param>
         /// <param name="nBlockPos">Block position reference</param>
         /// <returns>Result of operation</returns>
-        public bool GetBlockByTransactionID(uint256 TxID, ref CBlock block, ref long nBlockPos)
+        public bool GetBlockByTransactionID(uint256 TxID, out CBlock block, out long nBlockPos)
         {
+            block = null;
+            nBlockPos = -1;
+
             var queryResult = dbConn.Query<CBlockStoreItem>("select b.* from [BlockStorage] b left join [MerkleNodes] m on (b.[ItemID] = m.[nParentBlockID]) where m.[TransactionHash] = ?", (byte[])TxID);
 
             if (queryResult.Count == 1)
             {
                 CBlockStoreItem blockCursor = queryResult[0];
 
+                nBlockPos = blockCursor.nBlockPos;
+
                 return blockCursor.ReadFromFile(ref fStreamReadWrite, out block);
             }
 
@@ -964,13 +1171,6 @@ namespace Novacoin
             return false;
         }
 
-        public bool WriteNodes(ref CMerkleNode[] merkleNodes)
-        {
-            
-
-            return true;
-        }
-
         /// <summary>
         /// Get block cursor from map.
         /// </summary>
@@ -991,6 +1191,34 @@ namespace Novacoin
         }
 
         /// <summary>
+        /// Get merkle node cursor by output metadata.
+        /// </summary>
+        /// <param name="item">Output metadata object</param>
+        /// <returns>Merkle node cursor or null</returns>
+        public CMerkleNode GetMerkleCursor(TxOutItem item, out CBlockStoreItem blockCursor)
+        {
+            blockCursor = null;
+
+            // Trying to get cursor from the database.
+            var QueryMerkleCursor = dbConn.Query<CMerkleNode>("select * from [MerkleNodes] where [nMerkleNodeID] = ?", item.nMerkleNodeID);
+
+            if (QueryMerkleCursor.Count == 1)
+            {
+                var merkleNode = QueryMerkleCursor[0];
+
+                // Search for block
+                var results = blockMap.Where(x => x.Value.ItemID == merkleNode.nParentBlockID).Select(x => x.Value).ToArray();
+
+                blockCursor = results[0];
+
+                return merkleNode;
+            }
+
+            // Nothing found.
+            return null;
+        }
+
+        /// <summary>
         /// Load cursor from database.
         /// </summary>
         /// <param name="blockHash">Block hash</param>
@@ -1065,7 +1293,7 @@ namespace Novacoin
                 // TODO: proof-of-stake validation
 
                 uint256 hashProofOfStake = 0, targetProofOfStake = 0;
-                if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, ref hashProofOfStake, ref targetProofOfStake))
+                if (!StakeModifier.CheckProofOfStake(block.vtx[1], block.header.nBits, out hashProofOfStake, out targetProofOfStake))
                 {
                     return false; // do not error here as we expect this during initial block download
                 }
@@ -1142,11 +1370,10 @@ namespace Novacoin
             var intBuffer = new byte[4];
 
             var fStream2 = File.OpenRead(BlockFile);
-            var readerForBlocks = new BinaryReader(fStream2).BaseStream;
 
-            readerForBlocks.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
+            fStream2.Seek(nOffset, SeekOrigin.Begin); // Seek to previous offset + previous block length
 
-            while (readerForBlocks.Read(buffer, 0, 4) == 4) // Read magic number
+            while (fStream2.Read(buffer, 0, 4) == 4) // Read magic number
             {
                 var nMagic = BitConverter.ToUInt32(buffer, 0);
                 if (nMagic != 0xe5e9e8e4)
@@ -1154,7 +1381,7 @@ namespace Novacoin
                     throw new Exception("Incorrect magic number.");
                 }
 
-                var nBytesRead = readerForBlocks.Read(buffer, 0, 4);
+                var nBytesRead = fStream2.Read(buffer, 0, 4);
                 if (nBytesRead != 4)
                 {
                     throw new Exception("BLKSZ EOF");
@@ -1162,9 +1389,9 @@ namespace Novacoin
 
                 var nBlockSize = BitConverter.ToInt32(buffer, 0);
 
-                nOffset = readerForBlocks.Position;
+                nOffset = fStream2.Position;
 
-                nBytesRead = readerForBlocks.Read(buffer, 0, nBlockSize);
+                nBytesRead = fStream2.Read(buffer, 0, nBlockSize);
 
                 if (nBytesRead == 0 || nBytesRead != nBlockSize)
                 {
@@ -1185,7 +1412,7 @@ namespace Novacoin
                 }
 
                 int nCount = blockMap.Count;
-                Console.WriteLine("nCount={0}, Hash={1}, Time={2}", nCount, block.header.Hash, DateTime.Now); // Commit on each 100th block
+                Console.WriteLine("nCount={0}, Hash={1}, NumTx={2}, Time={3}", nCount, block.header.Hash, block.vtx.Length, DateTime.Now); // Commit on each 100th block
 
                 /*
                 if (nCount % 100 == 0 && nCount != 0)
@@ -1196,8 +1423,6 @@ namespace Novacoin
                 }*/
             }
 
-            dbConn.Commit();
-
             return true;
         }