X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=e3e4fd8fa5ac4467fab6fd67251e7abb74d63700;hb=3814af1ef1a10f25e7a76929edfd3abdb0ab791d;hp=7853aa38685b25448577740f1d4c4613171ff67e;hpb=035eee8428bf34e5a57edf6a3898a782d682a779;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 7853aa3..e3e4fd8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,6 +79,10 @@ const string strMessageMagic = "NovaCoin Signed Message:\n"; int64_t nTransactionFee = MIN_TX_FEE; int64_t nMinimumInputValue = MIN_TX_FEE; +// Ping and address broadcast intervals +int64_t nPingInterval = 30 * 60; +int64_t nBroadcastInterval = 24 * 60 * 60; + extern enum Checkpoints::CPMode CheckpointsMode; ////////////////////////////////////////////////////////////////////////////// @@ -309,10 +313,14 @@ bool CTransaction::IsStandard(string& strReason) const txnouttype whichType; BOOST_FOREACH(const CTxIn& txin, vin) { - // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG - // pay-to-script-hash, which is 3 ~80-byte signatures, 3 - // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) + // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed + // keys. (remember the 520 byte limit on redeemScript size) That works + // out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)=1624 + // bytes of scriptSig, which we round off to 1650 bytes for some minor + // future-proofing. That's also enough to spend a 20-of-20 + // CHECKMULTISIG scriptPubKey, though such a scriptPubKey is not + // considered standard) + if (txin.scriptSig.size() > 1650) { strReason = "scriptsig-size"; return false; @@ -547,13 +555,9 @@ bool CTransaction::CheckTransaction() const int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const { - // Use new fees approach if we are on test network or - // switch date has been reached - bool fNewApproach = fTestNet || nTime > FEE_SWITCH_TIME; - int64_t nMinTxFee = MIN_TX_FEE, nMinRelayTxFee = MIN_RELAY_TX_FEE; - if(!fNewApproach || IsCoinStake()) + if(IsCoinStake()) { // Enforce 0.01 as minimum fee for old approach or coinstake nMinTxFee = CENT; @@ -566,42 +570,31 @@ int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum G unsigned int nNewBlockSize = nBlockSize + nBytes; int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; - if (fNewApproach) + if (fAllowFree) { - if (fAllowFree) + if (nBlockSize == 1) { - if (nBlockSize == 1) - { - // Transactions under 1K are free - if (nBytes < 1000) - nMinFee = 0; - } - else - { - // Free transaction area - if (nNewBlockSize < 27000) - nMinFee = 0; - } + // Transactions under 1K are free + if (nBytes < 1000) + nMinFee = 0; + } + else + { + // Free transaction area + if (nNewBlockSize < 27000) + nMinFee = 0; } - - // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for - // each non empty output which is less than 0.01 - // - // It's safe to ignore empty outputs here, because these inputs are allowed - // only for coinbase and coinstake transactions. - BOOST_FOREACH(const CTxOut& txout, vout) - if (txout.nValue < CENT && !txout.IsEmpty()) - nMinFee += nBaseFee; - } - else if (nMinFee < nBaseFee) - { - // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if - // any output is less than 0.01 - BOOST_FOREACH(const CTxOut& txout, vout) - if (txout.nValue < CENT) - nMinFee = nBaseFee; } + // To limit dust spam, require additional MIN_TX_FEE/MIN_RELAY_TX_FEE for + // each non empty output which is less than 0.01 + // + // It's safe to ignore empty outputs here, because these inputs are allowed + // only for coinbase and coinstake transactions. + BOOST_FOREACH(const CTxOut& txout, vout) + if (txout.nValue < CENT && !txout.IsEmpty()) + nMinFee += nBaseFee; + // Raise the price as the block approaches full if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) { @@ -1066,7 +1059,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; @@ -1274,13 +1267,14 @@ bool IsInitialBlockDownload() return true; static int64_t nLastUpdate; static CBlockIndex* pindexLastBest; + int64_t nCurrentTime = GetTime(); if (pindexBest != pindexLastBest) { pindexLastBest = pindexBest; - nLastUpdate = GetTime(); + nLastUpdate = nCurrentTime; } - return (GetTime() - nLastUpdate < 10 && - pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); + return (nCurrentTime - nLastUpdate < 10 && + pindexBest->GetBlockTime() < nCurrentTime - 24 * 60 * 60); } void static InvalidChainFound(CBlockIndex* pindexNew) @@ -2176,7 +2170,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->nTime))) + if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight))) return error("AddToBlockIndex() : SetStakeEntropyBit() failed"); // ppcoin: record proof-of-stake hash value @@ -2224,7 +2218,9 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) hashPrevBestCoinBase = vtx[0].GetHash(); } - uiInterface.NotifyBlocksChanged(); + static int8_t counter = 0; + if( (++counter & 0x0F) == 0 || !IsInitialBlockDownload()) // repaint every 16 blocks if not in initial block download + uiInterface.NotifyBlocksChanged(); return true; } @@ -2236,38 +2232,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")); @@ -2275,61 +2264,70 @@ 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)); // NovaCoin: check proof-of-stake block signature - if (fCheckSig && !CheckBlockSignature(true)) + 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 { - // 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) + // Check proof of work matches claimed amount + if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + return DoS(50, error("CheckBlock() : proof of work failed")); - if(!fTestNet && fCheckSig) - { - bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME); + // Check timestamp + if (GetBlockTime() > FutureDrift(GetAdjustedTime())) + return error("CheckBlock() : block timestamp too far in the future"); - // NovaCoin: check proof-of-work block signature - if (checkEntropySig && !CheckBlockSignature(false)) - return DoS(100, error("CheckBlock() : bad proof-of-work block signature")); - } + // Check coinbase timestamp + if (GetBlockTime() < PastDrift((int64_t)vtx[0].nTime)) + return DoS(50, error("CheckBlock() : coinbase timestamp is too late")); } - // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) + // 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")); @@ -2337,7 +2335,6 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); - return true; } @@ -2390,7 +2387,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"); @@ -2525,7 +2522,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) 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 - if (!pblock->CheckBlock()) + if (!pblock->CheckBlock(true, true, (pblock->nTime > Checkpoints::GetLastCheckpointTime()))) return error("ProcessBlock() : CheckBlock FAILED"); // ppcoin: verify hash target and signature of coinstake tx @@ -2571,17 +2568,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)); @@ -2643,11 +2640,11 @@ bool CBlock::SignBlock(CWallet& wallet) if (IsProofOfStake()) return true; - static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp + static uint32_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp CKey key; CTransaction txCoinStake; - int64_t nSearchTime = txCoinStake.nTime; // search to current time + uint32_t nSearchTime = txCoinStake.nTime; // search to current time if (nSearchTime > nLastCoinStakeSearchTime) { @@ -2681,55 +2678,27 @@ bool CBlock::SignBlock(CWallet& wallet) } // ppcoin: check block signature -bool CBlock::CheckBlockSignature(bool fProofOfStake) const +bool CBlock::CheckBlockSignature() const { - if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) - return vchBlockSig.empty(); + if (IsProofOfWork()) + return true; vector vSolutions; txnouttype whichType; - if(fProofOfStake) - { - const CTxOut& txout = vtx[1].vout[1]; + const CTxOut& txout = vtx[1].vout[1]; - if (!Solver(txout.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); - } - } - else + if (!Solver(txout.scriptPubKey, whichType, vSolutions)) + return false; + if (whichType == TX_PUBKEY) { - for(unsigned int i = 0; i < vtx[0].vout.size(); i++) - { - const CTxOut& txout = vtx[0].vout[i]; - - if (!Solver(txout.scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_PUBKEY) - { - // Verify - valtype& vchPubKey = vSolutions[0]; - CKey key; - if (!key.SetPubKey(vchPubKey)) - continue; - if (vchBlockSig.empty()) - continue; - if(!key.Verify(GetHash(), vchBlockSig)) - continue; - - return true; - } - } + valtype& vchPubKey = vSolutions[0]; + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + if (vchBlockSig.empty()) + return false; + return key.Verify(GetHash(), vchBlockSig); } return false; } @@ -2799,6 +2768,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) @@ -3967,7 +3948,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); @@ -3986,7 +3967,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);