X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=30222b5769d50fefbf1df8d7ceef508aafc974fb;hb=35d53cc54ae590fad75cf9fa038c0e0568157f7e;hp=9370ffaa97e8c7a30ee32df78fa4579cd00011e5;hpb=532b9005ab4fc02db3db424b4631fe395e0b9071;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 9370ffa..30222b5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,7 +77,7 @@ const string strMessageMagic = "NovaCoin Signed Message:\n"; // Settings int64_t nTransactionFee = MIN_TX_FEE; -int64_t nMinimumInputValue = MIN_TX_FEE; +int64_t nMinimumInputValue = MIN_TXOUT_AMOUNT; // Ping and address broadcast intervals int64_t nPingInterval = 30 * 60; @@ -139,7 +139,7 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, { if (!fConnect) { - // ppcoin: wallets need to refund inputs when disconnecting coinstake + // wallets need to refund inputs when disconnecting coinstake if (tx.IsCoinStake()) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -151,8 +151,6 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); - // Preloaded coins cache invalidation - fCoinsDataActual = false; } // notify wallets about a new best chain @@ -432,9 +430,14 @@ unsigned int CTransaction::GetLegacySigOpCount() const { unsigned int nSigOps = 0; - BOOST_FOREACH(const CTxIn& txin, vin) + if (!IsCoinBase() || nTime < COINBASE_SIGOPS_SWITCH_TIME) { - nSigOps += txin.scriptSig.GetSigOpCount(false); + // Coinbase scriptsigs are never executed, so there is + // no sense in calculation of sigops. + BOOST_FOREACH(const CTxIn& txin, vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(false); + } } BOOST_FOREACH(const CTxOut& txout, vout) { @@ -559,7 +562,7 @@ int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum G if(IsCoinStake()) { - // Enforce 0.01 as minimum fee for old approach or coinstake + // Enforce 0.01 as minimum fee for coinstake nMinTxFee = CENT; nMinRelayTxFee = CENT; } @@ -734,7 +737,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false, true, SIG_SWITCH_TIME < tx.nTime ? STRICT_FLAGS : SOFT_FLAGS)) + if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false, true, STRICT_FLAGS)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } @@ -1036,13 +1039,13 @@ int64_t GetProofOfWorkReward(unsigned int nBits, int64_t nFees) // Human readable form: // // nSubsidy = 100 / (diff ^ 1/6) + // + // Please note that we're using bisection to find an approximate solutuion CBigNum bnLowerBound = CENT; CBigNum bnUpperBound = bnSubsidyLimit; while (bnLowerBound + CENT <= bnUpperBound) { CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; - if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfWorkReward() : lower=%" PRId64 " upper=%" PRId64 " mid=%" PRId64 "\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget) bnUpperBound = bnMidValue; else @@ -1063,88 +1066,61 @@ int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTim { int64_t nRewardCoinYear, nSubsidy, nSubsidyLimit = 10 * COIN; - if(fTestNet || nTime > STAKE_SWITCH_TIME) - { - // Stage 2 of emission process is PoS-based. It will be active on mainNet since 20 Jun 2013. + // Stage 2 of emission process is mostly PoS-based. - CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest - CBigNum bnTarget; - bnTarget.SetCompact(nBits); - CBigNum bnTargetLimit = GetProofOfStakeLimit(0, nTime); - bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); + CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Base stake mint rate, 100% year interest + CBigNum bnTarget; + bnTarget.SetCompact(nBits); + CBigNum bnTargetLimit = GetProofOfStakeLimit(0, nTime); + bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); - // NovaCoin: A reasonably continuous curve is used to avoid shock to market + // A reasonably continuous curve is used to avoid shock to market - CBigNum bnLowerBound = 1 * CENT, // Lower interest bound is 1% per year - bnUpperBound = bnRewardCoinYearLimit, // Upper interest bound is 100% per year - bnMidPart, bnRewardPart; + CBigNum bnLowerBound = 1 * CENT, // Lower interest bound is 1% per year + bnUpperBound = bnRewardCoinYearLimit, // Upper interest bound is 100% per year + bnMidPart, bnRewardPart; - while (bnLowerBound + CENT <= bnUpperBound) - { - CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; - if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfStakeReward() : lower=%" PRId64 " upper=%" PRId64 " mid=%" PRId64 "\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); + while (bnLowerBound + CENT <= bnUpperBound) + { + CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; - if(!fTestNet && nTime < STAKECURVE_SWITCH_TIME) - { - // - // Until 20 Oct 2013: reward for coin-year is cut in half every 64x multiply of PoS difficulty - // - // (nRewardCoinYearLimit / nRewardCoinYear) ** 6 == bnProofOfStakeLimit / bnTarget - // - // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/6) - // - - bnMidPart = bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue; - bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; - } - else - { - // - // Since 20 Oct 2013: reward for coin-year is cut in half every 8x multiply of PoS difficulty - // - // (nRewardCoinYearLimit / nRewardCoinYear) ** 3 == bnProofOfStakeLimit / bnTarget - // - // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3) - // - - bnMidPart = bnMidValue * bnMidValue * bnMidValue; - bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; - } + // + // Reward for coin-year is cut in half every 8x multiply of PoS difficulty + // + // (nRewardCoinYearLimit / nRewardCoinYear) ** 3 == bnProofOfStakeLimit / bnTarget + // + // Human readable form: nRewardCoinYear = 1 / (posdiff ^ 1/3) + // - if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget) - bnUpperBound = bnMidValue; - else - bnLowerBound = bnMidValue; - } + bnMidPart = bnMidValue * bnMidValue * bnMidValue; + bnRewardPart = bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit; - nRewardCoinYear = bnUpperBound.getuint64(); - nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE); - } - else - { - // Old creation amount per coin-year, 5% fixed stake mint rate - nRewardCoinYear = 5 * CENT; + if (bnMidPart * bnTargetLimit > bnRewardPart * bnTarget) + bnUpperBound = bnMidValue; + else + bnLowerBound = bnMidValue; } + nRewardCoinYear = bnUpperBound.getuint64(); + nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, MAX_MINT_PROOF_OF_STAKE); + if(bCoinYearOnly) return nRewardCoinYear; nSubsidy = nCoinAge * nRewardCoinYear * 33 / (365 * 33 + 8); - // Set reasonable reward limit for large inputs since 20 Oct 2013 + // Set reasonable reward limit for large inputs // // 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") && 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=%" PRId64 " nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); + return nSubsidy; } @@ -1576,18 +1552,21 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map Checkpoints::GetLastCheckpointTime()) + { + unsigned int nTxSize = GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION); - unsigned int nTxSize = (nTime > VALIDATION_SWITCH_TIME || fTestNet) ? GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) : 0; + // coin stake tx earns reward instead of paying fee + uint64_t nCoinAge; + if (!GetCoinAge(txdb, nCoinAge)) + return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); - int64_t nReward = GetValueOut() - nValueIn; - int64_t nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT; + int64_t nReward = GetValueOut() - nValueIn; + int64_t nCalculatedReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee(1, false, GMF_BLOCK, nTxSize) + CENT; - if (nReward > nCalculatedReward) - return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%" PRId64 " vs calculated=%" PRId64 ")", nReward, nCalculatedReward)); + if (nReward > nCalculatedReward) + return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%" PRId64 " vs calculated=%" PRId64 ")", nReward, nCalculatedReward)); + } } else { @@ -1661,7 +1640,7 @@ bool CTransaction::ClientConnectInputs() bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) { // Disconnect in reverse order - for (int i = vtx.size()-1; i >= 0; i--) + for (size_t i = vtx.size()-1; i >= 0; i--) if (!vtx[i].DisconnectInputs(txdb)) return false; @@ -2387,7 +2366,7 @@ bool CBlock::AcceptBlock() // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); - unsigned int nFile = -1; + unsigned int nFile = std::numeric_limits::max(); unsigned int nBlockPos = 0; if (!WriteToDisk(nFile, nBlockPos)) return error("AcceptBlock() : WriteToDisk failed"); @@ -2418,21 +2397,9 @@ uint256 CBlockIndex::GetBlockTrust() const if (bnTarget <= 0) return 0; - /* Old protocol */ - if (!fTestNet && GetBlockTime() < CHAINCHECKS_SWITCH_TIME) - return (IsProofOfStake()? ((CBigNum(1)<<256) / (bnTarget+1)).getuint256() : 1); - - /* New protocol */ - - // Calculate work amount for block - uint256 nPoWTrust = (CBigNum(nPoWBase) / (bnTarget+1)).getuint256(); - - // Set nPowTrust to 1 if we are checking PoS block or PoW difficulty is too low - nPoWTrust = (IsProofOfStake() || nPoWTrust < 1) ? 1 : nPoWTrust; - - // Return nPoWTrust for the first 12 blocks + // Return 1 for the first 12 blocks if (pprev == NULL || pprev->nHeight < 12) - return nPoWTrust; + return 1; const CBlockIndex* currentIndex = pprev; @@ -2462,11 +2429,18 @@ uint256 CBlockIndex::GetBlockTrust() const } else { + // Calculate work amount for block + CBigNum bnPoWTrust = CBigNum(nPoWBase) / (bnTarget+1); + + // Set nPowTrust to 1 if PoW difficulty is too low + if (bnPoWTrust < 1) + bnPoWTrust = 1; + CBigNum bnLastBlockTrust = CBigNum(pprev->nChainTrust - pprev->pprev->nChainTrust); // Return nPoWTrust + 2/3 of previous block score if two parent blocks are not PoS blocks if (!(pprev->IsProofOfStake() && pprev->pprev->IsProofOfStake())) - return nPoWTrust + (2 * bnLastBlockTrust / 3).getuint256(); + return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256(); int nPoSCount = 0; @@ -2480,7 +2454,7 @@ uint256 CBlockIndex::GetBlockTrust() const // Return nPoWTrust + 2/3 of previous block score if less than 7 PoS blocks found if (nPoSCount < 7) - return nPoWTrust + (2 * bnLastBlockTrust / 3).getuint256(); + return (bnPoWTrust + 2 * bnLastBlockTrust / 3).getuint256(); bnTarget.SetCompact(pprev->nBits); @@ -2490,7 +2464,7 @@ uint256 CBlockIndex::GetBlockTrust() const CBigNum bnNewTrust = (CBigNum(1)<<256) / (bnTarget+1); // Return nPoWTrust + full trust score for previous block nBits - return nPoWTrust + bnNewTrust.getuint256(); + return (bnPoWTrust + bnNewTrust).getuint256(); } } @@ -2506,6 +2480,25 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns return (nFound >= nRequired); } +bool static ReserealizeBlockSignature(CBlock* pblock) +{ + if (pblock->IsProofOfWork()) + { + pblock->vchBlockSig.clear(); + return true; + } + + return CKey::ReserealizeSignature(pblock->vchBlockSig); +} + +bool static IsCanonicalBlockSignature(CBlock* pblock) +{ + if (pblock->IsProofOfWork()) + return pblock->vchBlockSig.empty(); + + return IsDERSignature(pblock->vchBlockSig); +} + bool ProcessBlock(CNode* pfrom, CBlock* pblock) { // Check for duplicate @@ -2515,12 +2508,18 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) if (mapOrphanBlocks.count(hash)) return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); - // ppcoin: check proof-of-stake + // 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) && !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()); + // Strip the garbage from newly received blocks, if we found some + if (!IsCanonicalBlockSignature(pblock)) { + if (!ReserealizeBlockSignature(pblock)) + printf("WARNING: ProcessBlock() : ReserealizeBlockSignature FAILED\n"); + } + // Preliminary checks if (!pblock->CheckBlock(true, true, (pblock->nTime > Checkpoints::GetLastCheckpointTime()))) return error("ProcessBlock() : CheckBlock FAILED"); @@ -2568,17 +2567,17 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) if (!mapBlockIndex.count(pblock->hashPrevBlock)) { printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); - CBlock* pblock2 = new CBlock(*pblock); // ppcoin: check proof-of-stake - if (pblock2->IsProofOfStake()) + if (pblock->IsProofOfStake()) { // 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) && !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()); + if (setStakeSeenOrphan.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) + return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); else - setStakeSeenOrphan.insert(pblock2->GetProofOfStake()); + setStakeSeenOrphan.insert(pblock->GetProofOfStake()); } + CBlock* pblock2 = new CBlock(*pblock); mapOrphanBlocks.insert(make_pair(hash, pblock2)); mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); @@ -2627,79 +2626,26 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) return true; } -// novacoin: attempt to generate suitable proof-of-stake -bool CBlock::SignBlock(CWallet& wallet) -{ - // if we are trying to sign - // something except proof-of-stake block template - if (!vtx[0].vout[0].IsEmpty()) - return false; - - // if we are trying to sign - // a complete proof-of-stake block - if (IsProofOfStake()) - return true; - - static uint32_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp - - CKey key; - CTransaction txCoinStake; - uint32_t nSearchTime = txCoinStake.nTime; // search to current time - - if (nSearchTime > nLastCoinStakeSearchTime) - { - if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key)) - { - if (txCoinStake.nTime >= max(pindexBest->GetMedianTimePast()+1, PastDrift(pindexBest->GetBlockTime()))) - { - // make sure coinstake would meet timestamp protocol - // as it would be the same as the block timestamp - vtx[0].nTime = nTime = txCoinStake.nTime; - nTime = max(pindexBest->GetMedianTimePast()+1, GetMaxTransactionTime()); - nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime())); - - // we have to make sure that we have no future timestamps in - // our transactions set - for (vector::iterator it = vtx.begin(); it != vtx.end();) - if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; } - - vtx.insert(vtx.begin() + 1, txCoinStake); - hashMerkleRoot = BuildMerkleTree(); - - // append a signature to our block - return key.Sign(GetHash(), vchBlockSig); - } - } - nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; - nLastCoinStakeSearchTime = nSearchTime; - } - - return false; -} - // ppcoin: check block signature bool CBlock::CheckBlockSignature() const { - if (IsProofOfWork()) - return true; + if (vchBlockSig.empty()) + return false; - vector vSolutions; txnouttype whichType; - - const CTxOut& txout = vtx[1].vout[1]; - - if (!Solver(txout.scriptPubKey, whichType, vSolutions)) + vector vSolutions; + if (!Solver(vtx[1].vout[1].scriptPubKey, whichType, vSolutions)) return false; + if (whichType == TX_PUBKEY) { valtype& vchPubKey = vSolutions[0]; CKey key; if (!key.SetPubKey(vchPubKey)) return false; - if (vchBlockSig.empty()) - return false; return key.Verify(GetHash(), vchBlockSig); } + return false; } @@ -2768,6 +2714,18 @@ FILE* AppendBlockFile(unsigned int& nFileRet) } } +void UnloadBlockIndex() +{ + mapBlockIndex.clear(); + setStakeSeen.clear(); + pindexGenesisBlock = NULL; + nBestHeight = 0; + nBestChainTrust = 0; + nBestInvalidTrust = 0; + hashBestChain = 0; + pindexBest = NULL; +} + bool LoadBlockIndex(bool fAllowNew) { if (fTestNet) @@ -3025,7 +2983,7 @@ bool LoadExternalBlockFile(FILE* fileIn) } } } - catch (std::exception &e) { + catch (const std::exception&) { printf("%s() : Deserialize or I/O error caught during load\n", BOOST_CURRENT_FUNCTION); } @@ -4091,3 +4049,25 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } return true; } + + +class CMainCleanup +{ +public: + CMainCleanup() {} + ~CMainCleanup() { + // block headers + std::map::iterator it1 = mapBlockIndex.begin(); + for (; it1 != mapBlockIndex.end(); it1++) + delete (*it1).second; + mapBlockIndex.clear(); + + // orphan blocks + std::map::iterator it2 = mapOrphanBlocks.begin(); + for (; it2 != mapOrphanBlocks.end(); it2++) + delete (*it2).second; + mapOrphanBlocks.clear(); + + // orphan transactions + } +} instance_of_cmaincleanup; \ No newline at end of file