X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=20f499defc6b925a2f2e9c4d3f8903d1aadabc04;hb=f7e0391e3fc1cd71f62e6fa289cdea6b6093dc9b;hp=8e6b788cfb525e006c21f973f279d927de21cd14;hpb=cb8b65d8d76fe05f3e700cfb55d1a3bd51fbe55d;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 8e6b788..20f499d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,7 +77,7 @@ int64 nHPSTimerStart; // Settings int64 nTransactionFee = MIN_TX_FEE; - +bool fStakeUsePooledKeys = false; ////////////////////////////////////////////////////////////////////////////// // @@ -998,7 +998,7 @@ int64 GetProofOfWorkReward(unsigned int nBits) // miner's coin stake reward based on nBits and coin age spent (coin-days) int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) { - int64 nRewardCoinYear, nSubsidy; + int64 nRewardCoinYear, nSubsidy, nSubsidyLimit = 10 * COIN; if(fTestNet || nTime > STAKE_SWITCH_TIME) { @@ -1032,8 +1032,8 @@ int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTi // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/6) // - bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit; - bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget; + bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue; + bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; } else { @@ -1045,11 +1045,11 @@ int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTi // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3) // - bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnTargetLimit; - bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget; + bnMidPart = bnMidValue * bnMidValue * bnMidValue; + bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; } - if (bnMidPart > bnRewardPart) + if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget) bnUpperBound = bnMidValue; else bnLowerBound = bnMidValue; @@ -1073,6 +1073,17 @@ int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTi else nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8); + // Set reasonable reward limit for large inputs since 20 Oct 2013 + // + // This will stimulate large holders to use smaller inputs, that's good for the network protection + if(fTestNet || STAKECURVE_SWITCH_TIME < nTime) + { + if (fDebug && GetBoolArg("-printcreation") && nSubsidyLimit < nSubsidy) + printf("GetProofOfStakeReward(): %s is greater than %s, coinstake reward will be truncated\n", FormatMoney(nSubsidy).c_str(), FormatMoney(nSubsidyLimit).c_str()); + + nSubsidy = min(nSubsidy, nSubsidyLimit); + } + if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d" nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); return nSubsidy; @@ -1093,13 +1104,10 @@ int64 inline GetTargetSpacingWorkMax(int nHeight, unsigned int nTime) } // -// minimum amount of work that could possibly be required nTime after -// minimum work required was nBase +// maximum nBits value could possible be required nTime after // -unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) +unsigned int ComputeMaxBits(CBigNum bnTargetLimit, unsigned int nBase, int64 nTime) { - CBigNum bnTargetLimit = bnProofOfWorkLimit; - CBigNum bnResult; bnResult.SetCompact(nBase); bnResult *= 2; @@ -1114,6 +1122,25 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) return bnResult.GetCompact(); } +// +// minimum amount of work that could possibly be required nTime after +// minimum proof-of-work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) +{ + return ComputeMaxBits(bnProofOfWorkLimit, nBase, nTime); +} + +// +// minimum amount of stake that could possibly be required nTime after +// minimum proof-of-stake required was nBase +// +unsigned int ComputeMinStake(unsigned int nBase, int64 nTime, unsigned int nBlockTime) +{ + return ComputeMaxBits(GetProofOfStakeLimit(0, nBlockTime), nBase, nTime); +} + + // ppcoin: find last block index up to pindex const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake) { @@ -1574,8 +1601,8 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) { - // Check it again in case a previous version let a bad block in - if (!CheckBlock(!fJustCheck, !fJustCheck)) + // Check it again in case a previous version let a bad block in, but skip BlockSig checking + if (!CheckBlock(!fJustCheck, !fJustCheck, false)) return false; // Do not allow blocks that contain transactions which 'overwrite' older transactions, @@ -2031,7 +2058,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust(); // ppcoin: compute stake entropy bit for stake modifier - if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight))) + if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime))) return error("AddToBlockIndex() : SetStakeEntropyBit() failed"); // ppcoin: record proof-of-stake hash value @@ -2088,7 +2115,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) -bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const +bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const { // These are checks that are independent of context // that can be verified before saving an orphan block. @@ -2097,24 +2124,6 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); - // Special short-term limits to avoid 10,000 BDB lock limit: - if (GetBlockTime() < LOCKS_SWITCH_TIME) - { - // Rule is: #unique txids referenced <= 4,500 - // ... to prevent 10,000 BDB lock exhaustion on old clients - set setTxIn; - for (size_t i = 0; i < vtx.size(); i++) - { - setTxIn.insert(vtx[i].GetHash()); - if (i == 0) continue; // skip coinbase txin - BOOST_FOREACH(const CTxIn& txin, vtx[i].vin) - setTxIn.insert(txin.prevout.hash); - } - size_t nTxids = setTxIn.size(); - if (nTxids > 4500) - return error("CheckBlock() : maxlocks violation"); - } - // Check proof of work matches claimed amount if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return DoS(50, error("CheckBlock() : proof of work failed")); @@ -2150,6 +2159,10 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const // Check coinstake timestamp if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime)) return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); + + // NovaCoin: check proof-of-stake block signature + if (fCheckSig && !CheckBlockSignature(true)) + return DoS(100, error("CheckBlock() : bad proof-of-stake block signature")); } else { @@ -2158,9 +2171,26 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const // Check coinbase reward if (vtx[0].GetValueOut() > (GetProofOfWorkReward(nBits) - nFee)) - return DoS(50, error("CheckBlock() : coinbase reward exceeded %s > %s", - FormatMoney(vtx[0].GetValueOut()).c_str(), - FormatMoney(GetProofOfWorkReward(nBits) - nFee).c_str())); + return DoS(50, error("CheckBlock() : coinbase reward exceeded (actual=%"PRI64d" vs calculated=%"PRI64d")", + vtx[0].GetValueOut(), + GetProofOfWorkReward(nBits) - nFee)); + + // Should we check proof-of-work block signature or not? + // + // * Always skip on TestNet + // * Perform checking for the first 9689 blocks + // * Perform checking since last checkpoint until 20 Sep 2013 (will be removed after) + + if(!fTestNet && fCheckSig) + { + bool isAfterCheckpoint = (GetBlockTime() > Checkpoints::GetLastCheckpointTime()); + bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME); + bool checkPoWSig = (isAfterCheckpoint && GetBlockTime() < CHAINCHECKS_SWITCH_TIME); + + // NovaCoin: check proof-of-work block signature + if ((checkEntropySig || checkPoWSig) && !CheckBlockSignature(false)) + return DoS(100, error("CheckBlock() : bad proof-of-work block signature")); + } } // Check transactions @@ -2196,12 +2226,6 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); - // NovaCoin: check proof-of-stake block signature - if (IsProofOfStake() || (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME)) - { - if (!CheckBlockSignature()) - return DoS(100, error("CheckBlock() : bad block signature")); - } return true; } @@ -2237,22 +2261,9 @@ bool CBlock::AcceptBlock() if (!Checkpoints::CheckHardened(nHeight, hash)) return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight)); - // ppcoin: check that the block satisfies synchronized checkpoint - if (!Checkpoints::CheckSync(hash, pindexPrev)) - { - if(!GetBoolArg("-nosynccheckpoints", false)) - { - return error("AcceptBlock() : rejected by synchronized checkpoint"); - } - else - { - strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!"); - } - } - - // Reject block.nVersion < 3 blocks since 95% threshold on mainNet and always on testNet: - if (nVersion < 3 && ((!fTestNet && nHeight > 14060) || (fTestNet && nHeight > 0))) - return error("CheckBlock() : rejected nVersion < 3 block"); + // Check that the block satisfies synchronized checkpoint + if (!GetBoolArg("-nosynccheckpoints", false) && !Checkpoints::CheckSync(hash, pindexPrev)) + return error("AcceptBlock() : rejected by synchronized checkpoint"); // Enforce rule that the coinbase starts with serialized block height CScript expect = CScript() << nHeight; @@ -2403,8 +2414,8 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) // ppcoin: verify hash target and signature of coinstake tx if (pblock->IsProofOfStake()) { - uint256 hashProofOfStake = 0; - if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake)) + uint256 hashProofOfStake = 0, targetProofOfStake = 0; + if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake, targetProofOfStake)) { printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); return false; // do not error here as we expect this during initial block download @@ -2421,7 +2432,12 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); CBigNum bnRequired; - bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, pblock->IsProofOfStake())->nBits, deltaTime)); + + if (pblock->IsProofOfStake()) + bnRequired.SetCompact(ComputeMinStake(GetLastBlockIndex(pcheckpoint, true)->nBits, deltaTime, pblock->nTime)); + else + bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, false)->nBits, deltaTime)); + if (bnNewBlock > bnRequired) { if (pfrom) @@ -2556,7 +2572,7 @@ bool CBlock::SignBlock(const CKeyStore& keystore) } // ppcoin: check block signature -bool CBlock::CheckBlockSignature() const +bool CBlock::CheckBlockSignature(bool fProofOfStake) const { if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) return vchBlockSig.empty(); @@ -2564,7 +2580,7 @@ bool CBlock::CheckBlockSignature() const vector vSolutions; txnouttype whichType; - if(IsProofOfStake()) + if(fProofOfStake) { const CTxOut& txout = vtx[1].vout[1]; @@ -2958,9 +2974,10 @@ string GetWarnings(string strFor) strStatusBar = strMiscWarning; } - // ppcoin: should not enter safe mode for longer invalid chain - // ppcoin: if sync-checkpoint is too old do not enter safe mode - if (Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) && !fTestNet && !IsInitialBlockDownload()) + // * Should not enter safe mode for longer invalid chain + // * If sync-checkpoint is too old do not enter safe mode + // * Do not display warning if -nosynccheckpoints specified + if (!GetBoolArg("-nosynccheckpoints", false) && Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) && !fTestNet && !IsInitialBlockDownload()) { nPriority = 100; strStatusBar = "WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers."; @@ -2984,7 +3001,7 @@ string GetWarnings(string strFor) nPriority = alert.nPriority; strStatusBar = alert.strStatusBar; if (nPriority > 1000) - strRPC = strStatusBar; // ppcoin: safe mode for high alert + strRPC = strStatusBar; } } } @@ -3568,10 +3585,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "getaddr") { + // Don't return addresses older than nCutOff timestamp + int64 nCutOff = GetTime() - (nNodeLifespan * 24 * 60 * 60); pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) - pfrom->PushAddress(addr); + if(addr.nTime > nCutOff) + pfrom->PushAddress(addr); } @@ -4099,8 +4119,6 @@ public: // fProofOfStake: try (best effort) to make a proof-of-stake block CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) { - CReserveKey reservekey(pwallet); - // Create new block auto_ptr pblock(new CBlock()); if (!pblock.get()) @@ -4111,7 +4129,14 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); txNew.vout.resize(1); - txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + + if (!fProofOfStake) + { + CReserveKey reservekey(pwallet); + txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; + } + else + txNew.vout[0].SetEmpty(); // Add our coinbase tx as first transaction pblock->vtx.push_back(txNew); @@ -4121,10 +4146,6 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); - // Special compatibility rule before 20 Aug: limit size to 500,000 bytes: - if (GetAdjustedTime() < LOCKS_SWITCH_TIME) - nBlockMaxSize = std::min(nBlockMaxSize, (unsigned int)(MAX_BLOCK_SIZE_GEN)); - // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); @@ -4160,7 +4181,6 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - nMaxClockDrift)) { // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp - pblock->vtx[0].vout[0].SetEmpty(); pblock->vtx[0].nTime = txCoinStake.nTime; pblock->vtx.push_back(txCoinStake); } @@ -4451,12 +4471,14 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) uint256 hash = pblock->GetHash(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); - if (hash > hashTarget && pblock->IsProofOfWork()) - return error("BitcoinMiner : proof-of-work not meeting target"); + if(!pblock->IsProofOfWork()) + return error("CheckWork() : %s is not a proof-of-work block", hash.GetHex().c_str()); + + if (hash > hashTarget) + return error("CheckWork() : proof-of-work not meeting target"); //// debug print - printf("BitcoinMiner:\n"); - printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); + printf("CheckWork() : new proof-of-stake block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); @@ -4464,7 +4486,7 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { LOCK(cs_main); if (pblock->hashPrevBlock != hashBestChain) - return error("BitcoinMiner : generated block is stale"); + return error("CheckWork() : generated block is stale"); // Remove key from key pool reservekey.KeepKey(); @@ -4477,24 +4499,60 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock)) - return error("BitcoinMiner : ProcessBlock, block not accepted"); + return error("CheckWork() : ProcessBlock, block not accepted"); + } + + return true; +} + +bool CheckStake(CBlock* pblock, CWallet& wallet) +{ + uint256 proofHash = 0, hashTarget = 0; + uint256 hash = pblock->GetHash(); + + if(!pblock->IsProofOfStake()) + return error("CheckStake() : %s is not a proof-of-stake block", hash.GetHex().c_str()); + + // verify hash target and signature of coinstake tx + if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, proofHash, hashTarget)) + return error("CheckStake() : proof-of-stake checking failed"); + + //// debug print + printf("CheckStake() : new proof-of-stake block found \n hash: %s \nproofhash: %s \ntarget: %s\n", hash.GetHex().c_str(), proofHash.GetHex().c_str(), hashTarget.GetHex().c_str()); + pblock->print(); + printf("out %s\n", FormatMoney(pblock->vtx[1].GetValueOut()).c_str()); + + // Found a solution + { + LOCK(cs_main); + if (pblock->hashPrevBlock != hashBestChain) + return error("CheckStake() : generated block is stale"); + + // Track how many getdata requests this block gets + { + LOCK(wallet.cs_wallet); + wallet.mapRequestCount[pblock->GetHash()] = 0; + } + + // Process this block the same as if we had received it from another node + if (!ProcessBlock(NULL, pblock)) + return error("CheckStake() : ProcessBlock, block not accepted"); } return true; } -void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) +void StakeMiner(CWallet *pwallet) { SetThreadPriority(THREAD_PRIORITY_LOWEST); // Make this thread recognisable as the mining thread - RenameThread("bitcoin-miner"); + RenameThread("novacoin-miner"); - // Each thread has its own key and counter - CReserveKey reservekey(pwallet); + // Each thread has its own counter unsigned int nExtraNonce = 0; - while (fProofOfStake) + while (true) { if (fShutdown) return; @@ -4503,8 +4561,6 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) Sleep(1000); if (fShutdown) return; - if (!fProofOfStake) - return; } while (pwallet->IsLocked()) @@ -4519,30 +4575,28 @@ void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) // CBlockIndex* pindexPrev = pindexBest; - auto_ptr pblock(CreateNewBlock(pwallet, fProofOfStake)); + auto_ptr pblock(CreateNewBlock(pwallet, true)); if (!pblock.get()) return; IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); - if (fProofOfStake) + if(pblock->IsProofOfStake()) { - // ppcoin: if proof-of-stake block found then process block - if (pblock->IsProofOfStake()) + // Trying to sign a block + if (!pblock->SignBlock(*pwalletMain)) { - if (!pblock->SignBlock(*pwalletMain)) - { - strMintWarning = strMintMessage; - continue; - } - strMintWarning = ""; - printf("StakeMiner : 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); + strMintWarning = strMintMessage; + continue; } - Sleep(500); - continue; + + strMintWarning = ""; + SetThreadPriority(THREAD_PRIORITY_NORMAL); + CheckStake(pblock.get(), *pwalletMain); + SetThreadPriority(THREAD_PRIORITY_LOWEST); } + + Sleep(500); + continue; } }