PPCoin: Fix bug in target adjustment since 32e6e50a
[novacoin.git] / src / main.cpp
index 818c677..9fd19e3 100644 (file)
@@ -756,7 +756,7 @@ unsigned int static GetNextTargetRequired(const CBlockIndex* pindexLast, bool fP
     // ppcoin: target change every block
     // ppcoin: retarget with exponential moving toward target spacing
     CBigNum bnNew;
-    bnNew.SetCompact(pindexLast->nBits);
+    bnNew.SetCompact(pindexPrev->nBits);
     bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
     bnNew /= ((nInterval + 1) * nTargetSpacing);
 
@@ -1284,17 +1284,23 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
 
 // ppcoin: coinstake must meet hash target according to the protocol:
 // at least one input must meet the formula
-//     hash(nBits + txPrev.block.nTime + txPrev.nTime + nTime) < bnTarget * nCoinDay
+//     hash(nBits + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDay
 // this ensures that the chance of getting a coinstake is proportional to the
 // amount of coin age one owns.
 // The reason this hash is chosen is the following:
-//   nBits: encodes all past block timestamps, prevents computing hash in advance
-//   txPrev.block.nTime: prevent nodes from guessing a good timestamp to generate
-//       transaction for future advantage
-//   txPrev.nTime: prevent nodes from meeting target simultaneously
-//   block/tx hash should not be used here as they can be generated in vast quatities
-//       so as to generate blocks faster, degrading the system back into a proof-of-work
-//       situation.
+//   nBits: encodes all past block timestamps, making computing hash in advance
+//          more difficult
+//   txPrev.block.nTime: prevent nodes from guessing a good timestamp to
+//                       generate transaction for future advantage
+//   txPrev.offset: offset of txPrev inside block, to reduce the chance of 
+//                  nodes generating coinstake at the same time
+//   txPrev.nTime: reduce the chance of nodes generating coinstake at the same
+//                 time
+//   txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
+//                  generating coinstake at the same time
+//   block/tx hash should not be used here as they can be generated in vast
+//   quantities so as to generate blocks faster, degrading the system back into
+//   a proof-of-work situation.
 //
 bool CTransaction::CheckProofOfStake(CTxDB& txdb, unsigned int nBits) const
 {
@@ -1325,7 +1331,7 @@ bool CTransaction::CheckProofOfStake(CTxDB& txdb, unsigned int nBits) const
         CBigNum bnCoinDay = CBigNum(nValueIn) * (nTime-txPrev.nTime) / COIN / (24 * 60 * 60);
         // Calculate hash
         CDataStream ss(SER_GETHASH, VERSION);
-        ss << nBits << block.nTime << txPrev.nTime << nTime;
+        ss << nBits << block.nTime << (txindex.pos.nTxPos - txindex.pos.nBlockPos) << txPrev.nTime << txin.prevout.n << nTime;
         if (CBigNum(Hash(ss.begin(), ss.end())) <= bnCoinDay * bnTargetPerCoinDay)
             return true;
     }
@@ -1540,7 +1546,16 @@ bool CBlock::AcceptBlock()
     CBlockIndex* pindexPrev = (*mi).second;
     int nHeight = pindexPrev->nHeight+1;
 
-    // Check proof of work
+    // ppcoin: check for coinstake duplicate
+    if (IsProofOfStake())
+    {   // check if coinstake is already connected; that would imply the owner
+        // of the coinstake sent multiple blocks with the same coinstake
+        CTxIndex txindex;
+        if (CTxDB("r").ReadTxIndex(vtx[1].GetHash(), txindex))
+            return error("AcceptBlock() : block %s has duplicate coinstake %s", hash.ToString().c_str(), vtx[1].GetHash().ToString().c_str());
+    }
+
+    // Check proof-of-work or proof-of-stake
     if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
         return DoS(100, error("AcceptBlock() : incorrect proof-of-work/proof-of-stake"));
 
@@ -1608,7 +1623,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
         bnNewBlock.SetCompact(pblock->nBits);
         CBigNum bnRequired;
         bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
-        if (bnNewBlock > bnRequired)
+        if (pblock->IsProofOfWork() && bnNewBlock > bnRequired)
         {
             pfrom->Misbehaving(100);
             return error("ProcessBlock() : block with too little proof-of-work");
@@ -1851,11 +1866,12 @@ void PrintBlockTree()
         // print item
         CBlock block;
         block.ReadFromDisk(pindex);
-        printf("%d (%u,%u) %s  %s  tx %d",
+        printf("%d (%u,%u) %s  %08lx  %s  tx %d",
             pindex->nHeight,
             pindex->nFile,
             pindex->nBlockPos,
             block.GetHash().ToString().substr(0,20).c_str(),
+            block.nBits,
             DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
             block.vtx.size());
 
@@ -2964,7 +2980,6 @@ public:
 
 CBlock* CreateNewBlock(CWallet* pwallet)
 {
-    CBlockIndex* pindexPrev = pindexBest;
     CReserveKey reservekey(pwallet);
 
     // Create new block
@@ -2983,12 +2998,27 @@ CBlock* CreateNewBlock(CWallet* pwallet)
     pblock->vtx.push_back(txNew);
 
     // ppcoin: if coinstake available add coinstake tx
-    pblock->nBits = GetNextTargetRequired(pindexPrev, true);
-    CTransaction txCoinStake;
-    if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, pblock->nBits, txCoinStake))
-        pblock->vtx.push_back(txCoinStake);
-    else
-        pblock->nBits = GetNextTargetRequired(pindexPrev, false);
+    static unsigned int nLastCoinStakeCheckTime = GetAdjustedTime() - nMaxClockDrift;  // only initialized at startup
+    CBlockIndex* pindexPrev = pindexBest;
+    while (nLastCoinStakeCheckTime < GetAdjustedTime())
+    {
+        pindexPrev = pindexBest;  // get best block again to avoid getting stale
+        pblock->nBits = GetNextTargetRequired(pindexPrev, true);
+        static CCriticalSection cs;
+        CTransaction txCoinStake;
+        CRITICAL_BLOCK(cs)
+        {
+            nLastCoinStakeCheckTime++;
+            txCoinStake.nTime = nLastCoinStakeCheckTime;
+        }
+        if (pwallet->CreateCoinStake(txNew.vout[0].scriptPubKey, pblock->nBits, txCoinStake))
+        {
+            pblock->vtx.push_back(txCoinStake);
+            break;
+        }
+    }
+
+    pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake());
 
     // Collect memory pool transactions into the block
     int64 nFees = 0;
@@ -3189,12 +3219,12 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
     uint256 hash = pblock->GetHash();
     uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
 
-    if (hash > hashTarget)
-        return false;
+    if (hash > hashTarget && pblock->IsProofOfWork())
+        return error("BitcoinMiner : proof-of-work not meeting target");
 
     //// debug print
     printf("BitcoinMiner:\n");
-    printf("proof-of-work found  \n  hash: %s  \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
+    printf("new block found  \n  hash: %s  \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
     pblock->print();
     printf("%s ", DateTimeStrFormat("%x %H:%M", GetTime()).c_str());
     printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
@@ -3256,8 +3286,21 @@ void static BitcoinMiner(CWallet *pwallet)
         auto_ptr<CBlock> pblock(CreateNewBlock(pwallet));
         if (!pblock.get())
             return;
+
         IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
 
+        // 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));
+            printf("BitcoinMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); 
+            SetThreadPriority(THREAD_PRIORITY_NORMAL);
+            CheckWork(pblock.get(), *pwalletMain, reservekey);
+            SetThreadPriority(THREAD_PRIORITY_LOWEST);
+            continue;
+        }
+
         printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());