Don't check proof-of-stake signature twice.
[NovacoinLibrary.git] / Novacoin / CBlockStore.cs
index f4342a2..6b03f08 100644 (file)
@@ -208,10 +208,13 @@ 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();
             }
         }
 
-        public bool GetTxOutCursor(COutPoint outpoint, ref TxOutItem txOutCursor)
+        public bool GetTxOutCursor(COutPoint outpoint, out TxOutItem txOutCursor)
         {
             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);
 
@@ -224,10 +227,12 @@ namespace Novacoin
 
             // Tx not found
 
+            txOutCursor = null;
+
             return false;
         }
         
-        public bool FetchInputs(CTransaction tx, ref Dictionary<COutPoint, TxOutItem> queued, ref Dictionary<COutPoint, TxOutItem> inputs, bool IsBlock, out bool Invalid)
+        public bool FetchInputs(ref CTransaction tx, ref Dictionary<COutPoint, TxOutItem> queued, ref Dictionary<COutPoint, TxOutItem> inputs, bool IsBlock, out bool Invalid)
         {
             Invalid = false;
 
@@ -274,6 +279,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
@@ -340,17 +350,6 @@ namespace Novacoin
                 return false; // SetStakeEntropyBit() failed
             }
 
-            // Save proof-of-stake hash value
-            if (itemTemplate.IsProofOfStake)
-            {
-                uint256 hashProofOfStake;
-                if (!GetProofOfStakeHash(blockHash, out hashProofOfStake))
-                {
-                    return false;  // hashProofOfStake not found 
-                }
-                itemTemplate.hashProofOfStake = hashProofOfStake;
-            }
-
             // compute stake modifier
             long nStakeModifier = 0;
             bool fGeneratedStakeModifier = false;
@@ -360,7 +359,7 @@ namespace Novacoin
             }
 
             itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
-            itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(itemTemplate);
+            itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(ref itemTemplate);
 
             // TODO: verify stake modifier checkpoints
 
@@ -371,6 +370,14 @@ namespace Novacoin
 
                 itemTemplate.prevoutStake = block.vtx[1].vin[0].prevout;
                 itemTemplate.nStakeTime = block.vtx[1].nTime;
+
+                // Save proof-of-stake hash value
+                uint256 hashProofOfStake;
+                if (!GetProofOfStakeHash(ref blockHash, out hashProofOfStake))
+                {
+                    return false;  // hashProofOfStake not found 
+                }
+                itemTemplate.hashProofOfStake = hashProofOfStake;
             }
 
             if (!itemTemplate.WriteToFile(ref fStreamReadWrite, ref block))
@@ -467,6 +474,11 @@ namespace Novacoin
             nTimeBestReceived = Interop.GetTime();
             nTransactionsUpdated++;
 
+            if (!UpdateTopChain(cursor))
+            {
+                return false; // unable to set top chain node.
+            }
+
             return true;
         }
 
@@ -541,7 +553,6 @@ namespace Novacoin
                 }
             }
 
-
             // Connect longer branch
             var txDelete = new List<CTransaction>();
             foreach (var cursor in connect)
@@ -608,7 +619,13 @@ namespace Novacoin
             }
 
             // Add to current best branch
-            cursor.prev.next = cursor;
+            var prevCursor = cursor.prev;
+            prevCursor.next = cursor;
+
+            if (!UpdateDBCursor(ref prevCursor))
+            {
+                return false; // unable to update
+            }
 
             // Delete redundant memory transactions
             foreach (var tx in block.vtx)
@@ -675,7 +692,7 @@ namespace Novacoin
                 else
                 {
                     bool Invalid;
-                    if (!FetchInputs(tx, ref queuedOutputs, ref inputs, true, out Invalid))
+                    if (!FetchInputs(ref tx, ref queuedOutputs, ref inputs, true, out Invalid))
                     {
                         return false; // Unable to fetch some inputs.
                     }
@@ -700,7 +717,7 @@ namespace Novacoin
                         nFees += nTxValueIn - nTxValueOut;
                     }
 
-                    if (!ConnectInputs(tx, ref inputs, ref queuedOutputs, ref cursor, true, fScriptChecks, scriptFlags))
+                    if (!ConnectInputs(ref tx, ref inputs, ref queuedOutputs, ref cursor, true, fScriptChecks, scriptFlags))
                     {
                         return false;
                     }
@@ -834,12 +851,10 @@ namespace Novacoin
             return true;
         }
 
-        private bool ConnectInputs(CTransaction tx, ref Dictionary<COutPoint, TxOutItem> inputs, ref Dictionary<COutPoint, TxOutItem> queued, ref CBlockStoreItem cursorBlock, bool fBlock, bool fScriptChecks, scriptflag scriptFlags)
+        private bool ConnectInputs(ref CTransaction tx, ref Dictionary<COutPoint, TxOutItem> inputs, ref Dictionary<COutPoint, TxOutItem> queued, ref CBlockStoreItem cursorBlock, bool fBlock, bool fScriptChecks, scriptflag scriptFlags)
         {
-            // Take over previous transactions' spent pointers
+            // 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
-            // fMiner is true when called from the internal bitcoin miner
-            // ... both are false when called from CTransaction::AcceptToMemoryPool
 
             if (!tx.IsCoinBase)
             {
@@ -852,26 +867,44 @@ namespace Novacoin
                     var input = inputs[prevout];
 
                     CBlockStoreItem parentBlockCursor;
-                    var merkleItem = GetMerkleCursor(input, out parentBlockCursor);
 
-                    if (merkleItem == null)
+                    if (input.nMerkleNodeID == -1)
                     {
-                        return false; // Unable to find merkle node
-                    }
+                        // This input seems as is confirmed by the same block.
 
-                    // If prev is coinbase or coinstake, check that it's matured
-                    if (merkleItem.IsCoinBase || merkleItem.IsCoinStake)
-                    {
-                        if (cursorBlock.nHeight - parentBlockCursor.nHeight < NetInfo.nGeneratedMaturity)
+                        if (!queued.ContainsKey(prevout))
                         {
-                            return false; // tried to spend non-matured generation input.
+                            return false; // No such output has been queued by this block.
                         }
-                    }
 
-                    // check transaction timestamp
-                    if (merkleItem.nTime > tx.nTime)
+                        // TODO: Ensure that neither coinbase nor coinstake outputs are 
+                        //    available for spending in the generation block.
+                    }
+                    else
                     {
-                        return false; // transaction timestamp earlier than input transaction
+                        // 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
@@ -917,7 +950,16 @@ namespace Novacoin
                     // Write back
                     if (fBlock)
                     {
-                        queued.Add(prevout, input);
+                        if (input.nMerkleNodeID != -1)
+                        {
+                            // Input has been confirmed earlier.
+                            queued.Add(prevout, input);
+                        }
+                        else
+                        {
+                            // Input has been confirmed by current block.
+                            queued[prevout] = input;
+                        }
                     }
                 }
 
@@ -987,7 +1029,7 @@ namespace Novacoin
         /// <param name="blockHash">Block hash</param>
         /// <param name="hashProofOfStake">Proof-of-stake hash</param>
         /// <returns>Proof-of-Stake hash value</returns>
-        private bool GetProofOfStakeHash(uint256 blockHash, out uint256 hashProofOfStake)
+        private bool GetProofOfStakeHash(ref uint256 blockHash, out uint256 hashProofOfStake)
         {
             return mapProofOfStake.TryGetValue(blockHash, out hashProofOfStake);
         }
@@ -1002,7 +1044,7 @@ namespace Novacoin
                 return false;
             }
 
-            CBlockStoreItem prevBlockCursor = null;
+            CBlockStoreItem prevBlockCursor;
             if (!blockMap.TryGetValue(block.header.prevHash, out prevBlockCursor))
             {
                 // Unable to get the cursor.
@@ -1078,14 +1120,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);
             }
 
@@ -1234,16 +1281,10 @@ namespace Novacoin
 
             if (block.IsProofOfStake)
             {
-                if (!block.SignatureOK)
-                {
-                    // Proof-of-Stake signature validation failure.
-                    return false;
-                }
-
                 // 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
                 }
@@ -1362,15 +1403,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
-
-                /*
-                if (nCount % 100 == 0 && nCount != 0)
-                {
-                    Console.WriteLine("Commit...");
-                    dbConn.Commit();
-                    dbConn.BeginTransaction();
-                }*/
+                Console.WriteLine("nCount={0}, Hash={1}, NumTx={2}, Time={3}", nCount, block.header.Hash, block.vtx.Length, DateTime.Now);
             }
 
             return true;