X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fmain.cpp;h=e95376e25cc97afad1e9a60008df11e16d5106a3;hp=c36665171538c067e6c38b259b26d4b00f0879e0;hb=9417ff30cae6ffffd1c4fc4f4b1a81551f04dbc5;hpb=5b1f0a0a62474909ea2c798bbd440d2790d042c5 diff --git a/src/main.cpp b/src/main.cpp index c366651..e95376e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -77,7 +77,11 @@ 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; +int64_t nBroadcastInterval = 24 * 60 * 60; extern enum Checkpoints::CPMode CheckpointsMode; @@ -147,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 @@ -1032,13 +1034,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 @@ -1055,7 +1057,7 @@ int64_t GetProofOfWorkReward(unsigned int nBits, int64_t nFees) } // miner's coin stake reward based on nBits and coin age spent (coin-days) -int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) +int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTime, bool bCoinYearOnly) { int64_t nRewardCoinYear, nSubsidy, nSubsidyLimit = 10 * COIN; @@ -1078,9 +1080,6 @@ int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int 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()); - if(!fTestNet && nTime < STAKECURVE_SWITCH_TIME) { // @@ -2228,38 +2227,31 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c // These are checks that are independent of context // that can be verified before saving an orphan block. + set uniqueTx; // tx hashes + unsigned int nSigOps = 0; // total sigops + // Size limits 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")); - // Check proof of work matches claimed amount - if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) - return DoS(50, error("CheckBlock() : proof of work failed")); - - // Check timestamp - if (GetBlockTime() > FutureDrift(GetAdjustedTime())) - return error("CheckBlock() : block timestamp too far in the future"); + bool fProofOfStake = IsProofOfStake(); // First transaction must be coinbase, the rest must not be - if (vtx.empty() || !vtx[0].IsCoinBase()) + if (!vtx[0].IsCoinBase()) return DoS(100, error("CheckBlock() : first tx is not coinbase")); - // Check coinbase timestamp - if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime)) - return DoS(50, error("CheckBlock() : coinbase timestamp is too late")); - - for (unsigned int i = 1; i < vtx.size(); i++) - { - if (vtx[i].IsCoinBase()) - return DoS(100, error("CheckBlock() : more than one coinbase")); + if (!vtx[0].CheckTransaction()) + return DoS(vtx[0].nDoS, error("CheckBlock() : CheckTransaction failed on coinbase")); - // Check transaction timestamp - if (GetBlockTime() < (int64_t)vtx[i].nTime) - return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); - } + uniqueTx.insert(vtx[0].GetHash()); + nSigOps += vtx[0].GetLegacySigOpCount(); - if (IsProofOfStake()) + if (fProofOfStake) { + // Proof-of-STake related checkings. Note that we know here that 1st transactions is coinstake. We don't need + // check the type of 1st transaction because it's performed earlier by IsProofOfStake() + + // nNonce must be zero for proof-of-stake blocks if (nNonce != 0) return DoS(100, error("CheckBlock() : non-zero nonce in proof-of-stake block")); @@ -2267,13 +2259,6 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty()) return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block")); - // Second transaction must be coinstake, the rest must not be - if (vtx.empty() || !vtx[1].IsCoinStake()) - return DoS(100, error("CheckBlock() : second tx is not coinstake")); - for (unsigned int i = 2; i < vtx.size(); i++) - if (vtx[i].IsCoinStake()) - return DoS(100, error("CheckBlock() : more than one coinstake")); - // Check coinstake timestamp if (GetBlockTime() != (int64_t)vtx[1].nTime) return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%" PRId64 " nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); @@ -2281,30 +2266,63 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c // NovaCoin: check proof-of-stake block signature if (fCheckSig && !CheckBlockSignature()) return DoS(100, error("CheckBlock() : bad proof-of-stake block signature")); + + if (!vtx[1].CheckTransaction()) + return DoS(vtx[1].nDoS, error("CheckBlock() : CheckTransaction failed on coinstake")); + + uniqueTx.insert(vtx[1].GetHash()); + nSigOps += vtx[1].GetLegacySigOpCount(); } + else + { + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + return DoS(50, error("CheckBlock() : proof of work failed")); - // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) + // Check timestamp + if (GetBlockTime() > FutureDrift(GetAdjustedTime())) + return error("CheckBlock() : block timestamp too far in the future"); + + // Check coinbase timestamp + if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime)) + return DoS(50, error("CheckBlock() : coinbase timestamp is too late")); + } + + // Iterate all transactions starting from second for proof-of-stake block + // or first for proof-of-work block + for (unsigned int i = fProofOfStake ? 2 : 1; i < vtx.size(); i++) { + const CTransaction& tx = vtx[i]; + + // Reject coinbase transactions at non-zero index + if (tx.IsCoinBase()) + return DoS(100, error("CheckBlock() : coinbase at wrong index")); + + // Reject coinstake transactions at index != 1 + if (tx.IsCoinStake()) + return DoS(100, error("CheckBlock() : coinstake at wrong index")); + + // Check transaction timestamp + if (GetBlockTime() < (int64_t)tx.nTime) + return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); + + // Check transaction consistency if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); + + // Add transaction hash into list of unique transaction IDs + uniqueTx.insert(tx.GetHash()); + + // Calculate sigops count + nSigOps += tx.GetLegacySigOpCount(); } // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: - set uniqueTx; - BOOST_FOREACH(const CTransaction& tx, vtx) - { - uniqueTx.insert(tx.GetHash()); - } if (uniqueTx.size() != vtx.size()) return DoS(100, error("CheckBlock() : duplicate transaction")); - unsigned int nSigOps = 0; - BOOST_FOREACH(const CTransaction& tx, vtx) - { - nSigOps += tx.GetLegacySigOpCount(); - } + // Reject block if validation would consume too much resources. if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); @@ -2364,7 +2382,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"); @@ -2545,17 +2563,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)); @@ -2604,56 +2622,6 @@ 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 int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp - - CKey key; - CTransaction txCoinStake; - int64_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 { @@ -2745,6 +2713,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) @@ -3913,7 +3893,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Keep-alive ping. We send a nonce of zero because we don't use it anywhere // right now. - if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) { + if (pto->nLastSend && GetTime() - pto->nLastSend > nPingInterval && pto->vSend.empty()) { uint64_t nonce = 0; if (pto->nVersion > BIP0031_VERSION) pto->PushMessage("ping", nonce); @@ -3932,7 +3912,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Address refresh broadcast static int64_t nLastRebroadcast; - if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) + if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > nBroadcastInterval)) { { LOCK(cs_vNodes); @@ -4068,3 +4048,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