Add stake modifier checkpoints & do some cleanup
[NovacoinLibrary.git] / Novacoin / CBlockStore.cs
index 1d0d1a9..7439235 100644 (file)
@@ -85,7 +85,7 @@ namespace Novacoin
 
 
         private ConcurrentDictionary<COutPoint, uint> mapStakeSeen = new ConcurrentDictionary<COutPoint, uint>();
-        private ConcurrentDictionary<COutPoint, uint> mapStakeSeenOrphan = new ConcurrentDictionary<COutPoint, uint>();
+        private ConcurrentDictionary<Tuple<COutPoint, uint>, uint256> mapStakeSeenOrphan = new ConcurrentDictionary<Tuple<COutPoint, uint>, uint256>();
 
 
         /// <summary>
@@ -359,9 +359,12 @@ namespace Novacoin
             }
 
             itemTemplate.SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
-            itemTemplate.nStakeModifierChecksum = StakeModifier.GetStakeModifierChecksum(ref itemTemplate);
 
-            // TODO: verify stake modifier checkpoints
+            var nChecksum = StakeModifier.GetModifierChecksum(ref itemTemplate);
+            if (!ModifierCheckpoints.Verify(itemTemplate.nHeight, nChecksum))
+            {
+                return false; // Stake modifier checkpoints mismatch
+            }
 
             // Add to index
             if (block.IsProofOfStake)
@@ -645,12 +648,12 @@ namespace Novacoin
                 return false; // Invalid block found.
             }
 
-            bool fScriptChecks = cursor.nHeight >= Checkpoints.TotalBlocksEstimate;
+            bool fScriptChecks = cursor.nHeight >= HashCheckpoints.TotalBlocksEstimate;
             var scriptFlags = scriptflag.SCRIPT_VERIFY_NOCACHE | scriptflag.SCRIPT_VERIFY_P2SH;
 
-            ulong nFees = 0;
-            ulong nValueIn = 0;
-            ulong nValueOut = 0;
+            long nFees = 0;
+            long nValueIn = 0;
+            long nValueOut = 0;
             uint nSigOps = 0;
 
             var queuedMerkleNodes = new Dictionary<uint256, CMerkleNode>();
@@ -706,8 +709,8 @@ namespace Novacoin
                         return false; // too many sigops
                     }
 
-                    ulong nTxValueIn = tx.GetValueIn(ref inputs);
-                    ulong nTxValueOut = tx.nValueOut;
+                    long nTxValueIn = tx.GetValueIn(ref inputs);
+                    long nTxValueOut = tx.nValueOut;
 
                     nValueIn += nTxValueIn;
                     nValueOut += nTxValueOut;
@@ -741,7 +744,7 @@ namespace Novacoin
 
             if (!block.IsProofOfStake)
             {
-                ulong nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees);
+                long nBlockReward = CBlock.GetProofOfWorkReward(cursor.nBits, nFees);
 
                 // Check coinbase reward
                 if (block.vtx[0].nValueOut > nBlockReward)
@@ -750,8 +753,8 @@ namespace Novacoin
                 }
             }
 
-            cursor.nMint = (long)(nValueOut - nValueIn + nFees);
-            cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + (long)nValueOut - (long)nValueIn;
+            cursor.nMint = nValueOut - nValueIn + nFees;
+            cursor.nMoneySupply = (cursor.prev != null ? cursor.prev.nMoneySupply : 0) + nValueOut - nValueIn;
 
             if (!UpdateDBCursor(ref cursor))
             {
@@ -858,8 +861,8 @@ namespace Novacoin
 
             if (!tx.IsCoinBase)
             {
-                ulong nValueIn = 0;
-                ulong nFees = 0;
+                long nValueIn = 0;
+                long nFees = 0;
                 for (uint i = 0; i < tx.vin.Length; i++)
                 {
                     var prevout = tx.vin[i].prevout;
@@ -965,16 +968,16 @@ namespace Novacoin
 
                 if (tx.IsCoinStake)
                 {
-                    // ppcoin: coin stake tx earns reward instead of paying fee
-                    ulong nCoinAge;
+                    // Coin stake tx earns reward instead of paying fee
+                    long nCoinAge;
                     if (!tx.GetCoinAge(ref inputs, out nCoinAge))
                     {
                         return false; // unable to get coin age for coinstake
                     }
 
-                    ulong nReward = tx.nValueOut - nValueIn;
+                    long nReward = tx.nValueOut - nValueIn;
 
-                    ulong nCalculatedReward = CBlock.GetProofOfStakeReward(nCoinAge, cursorBlock.nBits, tx.nTime) - tx.GetMinFee(1, false, CTransaction.MinFeeMode.GMF_BLOCK) + CTransaction.nCent;
+                    long nCalculatedReward = CBlock.GetProofOfStakeReward(nCoinAge, cursorBlock.nBits, tx.nTime) - tx.GetMinFee(1, false, CTransaction.MinFeeMode.GMF_BLOCK) + CTransaction.nCent;
 
                     if (nReward > nCalculatedReward)
                     {
@@ -989,7 +992,7 @@ namespace Novacoin
                     }
 
                     // Tally transaction fees
-                    ulong nTxFee = nValueIn - tx.nValueOut;
+                    long nTxFee = nValueIn - tx.nValueOut;
                     if (nTxFee < 0)
                     {
                         return false; // nTxFee < 0
@@ -1072,6 +1075,12 @@ namespace Novacoin
                 }
             }
 
+            // Check that the block chain matches the known block chain up to a checkpoint
+            if (!HashCheckpoints.Verify(nHeight, nHash))
+            {
+                return false;  // rejected by checkpoint lock-in
+            }
+
             // TODO: Enforce rule that the coinbase starts with serialized block height
 
             // Write block to file.
@@ -1093,7 +1102,7 @@ namespace Novacoin
         }
 
         /// <summary>
-        /// GEt block by hash.
+        /// Get block by hash.
         /// </summary>
         /// <param name="blockHash">Block hash</param>
         /// <param name="block">Block object reference</param>
@@ -1281,12 +1290,6 @@ 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;
@@ -1309,12 +1312,22 @@ namespace Novacoin
             {
                 if (block.IsProofOfStake)
                 {
-                    // TODO: limit duplicity on stake
+                    var proof = block.ProofOfStake;
+
+                    // Limited duplicity on stake: prevents block flood attack
+                    // Duplicate stake allowed only when there is orphan child block
+                    if (mapStakeSeenOrphan.ContainsKey(proof) && !orphanMapByPrev.ContainsKey(blockHash))
+                    {
+                        return false; // duplicate proof-of-stake
+                    }
+                    else
+                    {
+                        mapStakeSeenOrphan.TryAdd(proof, blockHash);
+                    }
                 }
 
-                var block2 = new CBlock(block);
-                orphanMap.TryAdd(blockHash, block2);
-                orphanMapByPrev.TryAdd(blockHash, block2);
+                orphanMap.TryAdd(blockHash, block);
+                orphanMapByPrev.TryAdd(blockHash, block);
 
                 return true;
             }
@@ -1326,32 +1339,39 @@ namespace Novacoin
                 return false;
             }
 
-            // Recursively process any orphan blocks that depended on this one
-            var orphansQueue = new List<uint256>();
-            orphansQueue.Add(blockHash);
-
-            for (int i = 0; i < orphansQueue.Count; i++)
+            if (orphanMapByPrev.Count > 0)
             {
-                var hashPrev = orphansQueue[i];
+                // Recursively process any orphan blocks that depended on this one
 
-                foreach (var pair in orphanMap)
+                var orphansQueue = new List<uint256>();
+                orphansQueue.Add(blockHash);
+
+                for (int i = 0; i < orphansQueue.Count; i++)
                 {
-                    var orphanBlock = pair.Value;
+                    var hashPrev = orphansQueue[i];
 
-                    if (orphanBlock.header.prevHash == blockHash)
+                    foreach (var pair in orphanMapByPrev)
                     {
-                        if (AcceptBlock(ref orphanBlock))
+                        var orphanBlock = pair.Value;
+
+                        if (orphanBlock.header.prevHash == blockHash)
                         {
-                            orphansQueue.Add(pair.Key);
-                        }
+                            if (AcceptBlock(ref orphanBlock))
+                            {
+                                orphansQueue.Add(pair.Key);
+                            }
+
+                            CBlock dummy1;
+                            orphanMap.TryRemove(pair.Key, out dummy1);
 
-                        CBlock dummy1;
-                        orphanMap.TryRemove(pair.Key, out dummy1);
+                            uint256 dummyHash;
+                            mapStakeSeenOrphan.TryRemove(orphanBlock.ProofOfStake, out dummyHash);
+                        }
                     }
-                }
 
-                CBlock dummy2;
-                orphanMap.TryRemove(hashPrev, out dummy2);
+                    CBlock dummy2;
+                    orphanMapByPrev.TryRemove(hashPrev, out dummy2);
+                }
             }
 
             return true;