PPCoin: Sign block when communicating with miners via RPC 'getwork'
[novacoin.git] / src / main.cpp
index b8d83b2..263a9e9 100644 (file)
@@ -668,6 +668,15 @@ uint256 static GetOrphanRoot(const CBlock* pblock)
     return pblock->GetHash();
 }
 
+// ppcoin: find block wanted by given orphan block
+uint256 WantedByOrphan(const CBlock* pblockOrphan)
+{
+    // Work back to the first block in the orphan chain
+    while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock))
+        pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock];
+    return pblockOrphan->hashPrevBlock;
+}
+
 int64 static GetProofOfWorkReward(unsigned int nBits)
 {
     CBigNum bnSubsidyLimit = 9999 * COIN; // subsidy amount for difficulty 1
@@ -723,12 +732,12 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
 {
     CBigNum bnResult;
     bnResult.SetCompact(nBase);
+    bnResult *= 2;
     while (nTime > 0 && bnResult < bnProofOfWorkLimit)
     {
-        // Maximum 400% adjustment...
-        bnResult *= 4;
-        // ... in best-case exactly 4-times-normal target time
-        nTime -= nTargetTimespan*4;
+        // Maximum 200% adjustment per day...
+        bnResult *= 2;
+        nTime -= 24 * 60 * 60;
     }
     if (bnResult > bnProofOfWorkLimit)
         bnResult = bnProofOfWorkLimit;
@@ -1125,7 +1134,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex)
     return true;
 }
 
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
 {
     printf("REORGANIZE\n");
 
@@ -1191,8 +1200,6 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
     }
     if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
         return error("Reorganize() : WriteHashBestChain failed");
-    if (!txdb.WriteAutoCheckpoint(Checkpoints::GetNextAutoCheckpoint(pindexNew->nCheckpoint)))
-        return error("Reorganize() : WriteAutoCheckpoint failed");
 
     // Make sure it's successfully written to disk before changing memory structure
     if (!txdb.TxnCommit())
@@ -1228,7 +1235,6 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
     if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
     {
         txdb.WriteHashBestChain(hash);
-        txdb.WriteAutoCheckpoint(Checkpoints::GetNextAutoCheckpoint(pindexNew->nCheckpoint));
         if (!txdb.TxnCommit())
             return error("SetBestChain() : TxnCommit failed");
         pindexGenesisBlock = pindexNew;
@@ -1236,7 +1242,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
     else if (hashPrevBlock == hashBestChain)
     {
         // Adding to current best branch
-        if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash) || !txdb.WriteAutoCheckpoint(Checkpoints::GetNextAutoCheckpoint(pindexNew->nCheckpoint)))
+        if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
         {
             txdb.TxnAbort();
             InvalidChainFound(pindexNew);
@@ -1277,7 +1283,6 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
     nBestChainTrust = pindexNew->nChainTrust;
     nTimeBestReceived = GetTime();
     nTransactionsUpdated++;
-    Checkpoints::AdvanceAutoCheckpoint(pindexBest->nCheckpoint);
     printf("SetBestChain: new best=%s  height=%d  trust=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str());
 
     return true;
@@ -1437,10 +1442,6 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
     {
         pindexNew->pprev = (*miPrev).second;
         pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
-
-        // ppcoin: compute chain checkpoint
-        pindexNew->nCheckpoint = Checkpoints::GetNextChainCheckpoint(pindexNew->pprev);
-        assert (pindexNew->nCheckpoint >= pindexNew->pprev->nCheckpoint);
     }
 
     // ppcoin: compute chain trust score
@@ -1574,10 +1575,6 @@ bool CBlock::AcceptBlock()
     if (!Checkpoints::CheckHardened(nHeight, hash))
         return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lockin at %d", nHeight));
 
-    // ppcoin: check that the block satisfies automatic checkpoint
-    if (!Checkpoints::CheckAuto(pindexPrev))
-        return DoS(100, error("AcceptBlock() : rejected by automatic checkpoint at %d", Checkpoints::nAutoCheckpoint));
-
     // ppcoin: check that the block satisfies synchronized checkpoint
     if (!Checkpoints::CheckSync(hash, pindexPrev))
         return error("AcceptBlock() : rejected by synchronized checkpoint");
@@ -1617,7 +1614,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     // ppcoin: check proof-of-stake
     // Limited duplicity on stake: prevents block flood attack
     // Duplicate stake allowed only when there is orphan child block
-    if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash))
+    if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
         return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
 
     // Preliminary checks
@@ -1628,23 +1625,15 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     if (pblock->IsProofOfStake() && !pblock->vtx[1].CheckProofOfStake(pblock->nBits))
         return error("ProcessBlock() : check proof-of-stake failed for block %s", hash.ToString().c_str());
 
-    CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
-    if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
+    CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
+    if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
     {
         // Extra checks to prevent "fill up memory by spamming with bogus blocks"
         int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
-        if (deltaTime < 0)
-        {
-            pfrom->Misbehaving(100);
-            return error("ProcessBlock() : block with timestamp before last checkpoint");
-        }
         CBigNum bnNewBlock;
         bnNewBlock.SetCompact(pblock->nBits);
         CBigNum bnRequired;
-        if (pblock->IsProofOfWork())
-            bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
-        else
-            bnRequired = bnNewBlock; // TODO: Computer Min Stake Target Allowed
+        bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime));
 
         if (bnNewBlock > bnRequired)
         {
@@ -1664,7 +1653,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
         {
             // Limited duplicity on stake: prevents block flood attack
             // Duplicate stake allowed only when there is orphan child block
-            if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash))
+            if (setStakeSeenOrphan.count(pblock2->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
                 return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock2->GetProofOfStake().first.ToString().c_str(), pblock2->GetProofOfStake().second, hash.ToString().c_str());
             else
                 setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
@@ -1674,7 +1663,12 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
 
         // Ask this guy to fill in what we're missing
         if (pfrom)
+        {
             pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2));
+            // ppcoin: getblocks may not obtain the ancestor block rejected
+            // earlier by duplicate-stake check so we ask for it again directly
+            pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2)));
+        }
         return true;
     }
 
@@ -1849,11 +1843,26 @@ bool LoadBlockIndex(bool fAllowNew)
             return error("LoadBlockIndex() : genesis block not accepted");
 
         // ppcoin: initialize synchronized checkpoint
-        CTxDB txdbc;
-        if (!txdbc.WriteSyncCheckpoint(hashGenesisBlock))
+        if (!Checkpoints::WriteSyncCheckpoint(hashGenesisBlock))
             return error("LoadBlockIndex() : failed to init sync checkpoint");
-        txdbc.Close();
-        Checkpoints::hashSyncCheckpoint = hashGenesisBlock;
+    }
+
+    // ppcoin: if checkpoint master key changed must reset sync-checkpoint
+    {
+        CTxDB txdb;
+        string strPubKey = "";
+        if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
+        {
+            // write checkpoint master key to db
+            txdb.TxnBegin();
+            if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+                return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
+            if (!txdb.TxnCommit())
+                return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
+            if (!Checkpoints::ResetSyncCheckpoint())
+                return error("LoadBlockIndex() : failed to reset sync-checkpoint");
+        }
+        txdb.Close();
     }
 
     return true;
@@ -1973,6 +1982,12 @@ string GetWarnings(string strFor)
         strStatusBar = strRPC = "WARNING: Displayed transactions may not be correct!  You may need to upgrade, or other nodes may need to upgrade.";
     }
 
+    if (Checkpoints::hashInvalidCheckpoint != 0)
+    {
+        nPriority = 3000;
+        strStatusBar = strRPC = "WARNING: Invalid checkpoint found!  Displayed transactions may not be correct!  You may need to upgrade, or other nodes may need to upgrade.";
+    }
+
     // Alerts
     CRITICAL_BLOCK(cs_mapAlerts)
     {
@@ -3037,7 +3052,7 @@ public:
 };
 
 
-CBlock* CreateNewBlock(CWallet* pwallet)
+CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfWorkOnly)
 {
     CReserveKey reservekey(pwallet);
 
@@ -3059,24 +3074,28 @@ CBlock* CreateNewBlock(CWallet* pwallet)
     // ppcoin: if coinstake available add coinstake tx
     static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift + 60;  // only initialized at startup
     CBlockIndex* pindexPrev = pindexBest;
-    while (nLastCoinStakeCheckTime < GetAdjustedTime())
+
+    if (!fProofOfWorkOnly)
     {
-        pindexPrev = pindexBest;  // get best block again to avoid getting stale
-        pblock->nBits = GetNextTargetRequired(pindexPrev, true);
-        static CCriticalSection cs;
-        CTransaction txCoinStake;
-        CRITICAL_BLOCK(cs)
-        {
-            // mining may have been suspended for a while so 
-            // need to take max to satisfy the timestamp protocol
-            nLastCoinStakeCheckTime = max(++nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
-            txCoinStake.nTime = nLastCoinStakeCheckTime;
-        }
-        if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
+        while (nLastCoinStakeCheckTime < GetAdjustedTime())
         {
-            pblock->vtx.push_back(txCoinStake);
-            pblock->vtx[0].vout[0].SetEmpty();
-            break;
+            pindexPrev = pindexBest;  // get best block again to avoid getting stale
+            pblock->nBits = GetNextTargetRequired(pindexPrev, true);
+            static CCriticalSection cs;
+            CTransaction txCoinStake;
+            CRITICAL_BLOCK(cs)
+            {
+                // mining may have been suspended for a while so 
+                // need to take max to satisfy the timestamp protocol
+                nLastCoinStakeCheckTime = max(++nLastCoinStakeCheckTime, (unsigned int) (GetAdjustedTime() - nMaxClockDrift + 60));
+                txCoinStake.nTime = nLastCoinStakeCheckTime;
+            }
+            if (pwallet->CreateCoinStake(pblock->nBits, txCoinStake))
+            {
+                pblock->vtx.push_back(txCoinStake);
+                pblock->vtx[0].vout[0].SetEmpty();
+                break;
+            }
         }
     }
 
@@ -3355,8 +3374,11 @@ void static BitcoinMiner(CWallet *pwallet)
         // ppcoin: if proof-of-stake block found then process block
         if (pblock->IsProofOfStake())
         {
-            // should be able to sign block - assert here for now
-            assert(pblock->SignBlock(*pwalletMain));
+            if (!pblock->SignBlock(*pwalletMain))
+            {
+                error("BitcoinMiner: Unable to sign new proof-of-stake block");
+                return;
+            }
             printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); 
             SetThreadPriority(THREAD_PRIORITY_NORMAL);
             CheckWork(pblock.get(), *pwalletMain, reservekey);
@@ -3407,8 +3429,11 @@ void static BitcoinMiner(CWallet *pwallet)
                     // Found a solution
                     pblock->nNonce = ByteReverse(nNonceFound);
                     assert(hash == pblock->GetHash());
-                    // should be able to sign block - assert here for now
-                    assert(pblock->SignBlock(*pwalletMain));
+                    if (!pblock->SignBlock(*pwalletMain))
+                    {
+                        error("BitcoinMiner: Unable to sign new proof-of-work block");
+                        return;
+                    }
 
                     SetThreadPriority(THREAD_PRIORITY_NORMAL);
                     CheckWork(pblock.get(), *pwalletMain, reservekey);