X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fmain.cpp;fp=src%2Fmain.cpp;h=d72a131a5f598d1cbfebad3bd95c36151226e92b;hp=e6f94210b986ffcd0173c75d26eb809468e19de9;hb=a93ab877877925c60b2dbf56bdde8aa46b6b7391;hpb=d0fe14ffecda4af98ffe7b1523f9a903bf7518a0 diff --git a/src/main.cpp b/src/main.cpp index e6f9421..d72a131 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,6 @@ #include "db.h" #include "net.h" #include "init.h" -#include "cryptopp/sha.h" #include #include @@ -31,7 +30,6 @@ map mapNextTx; map mapBlockIndex; uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download" CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainWork = 0; @@ -40,6 +38,8 @@ uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; +CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have + map mapOrphanBlocks; multimap mapOrphanBlocksByPrev; @@ -64,16 +64,14 @@ int fUseUPnP = false; #endif - - - - - ////////////////////////////////////////////////////////////////////////////// // // dispatching functions // +// These functions dispatch to one or all registered wallets + + void RegisterWallet(CWallet* pwalletIn) { CRITICAL_BLOCK(cs_setpwalletRegistered) @@ -90,6 +88,7 @@ void UnregisterWallet(CWallet* pwalletIn) } } +// check whether the passed transaction is from us bool static IsFromMe(CTransaction& tx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -98,6 +97,7 @@ bool static IsFromMe(CTransaction& tx) return false; } +// get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -106,42 +106,49 @@ bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) return false; } +// erases transaction with the given hash from all wallets void static EraseFromWallets(uint256 hash) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->EraseFromWallet(hash); } +// make sure all wallets know about the given transaction, in the given block void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } +// notify wallets about a new best chain void static SetBestChain(const CBlockLocator& loc) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->SetBestChain(loc); } +// notify wallets about an updated transaction void static UpdatedTransaction(const uint256& hashTx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->UpdatedTransaction(hashTx); } +// dump all wallets void static PrintWallets(const CBlock& block) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->PrintWallet(block); } +// notify wallets about an incoming inventory (for request counts) void static Inventory(const uint256& hash) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->Inventory(hash); } +// ask wallets to resend their transactions void static ResendWalletTransactions() { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) @@ -314,24 +321,24 @@ bool CTransaction::CheckTransaction() const { // Basic checks that don't depend on any context if (vin.empty()) - return error("CTransaction::CheckTransaction() : vin empty"); + return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) - return error("CTransaction::CheckTransaction() : vout empty"); + return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CTransaction::CheckTransaction() : size limits failed"); + return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; BOOST_FOREACH(const CTxOut& txout, vout) { if (txout.nValue < 0) - return error("CTransaction::CheckTransaction() : txout.nValue negative"); + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative")); if (txout.nValue > MAX_MONEY) - return error("CTransaction::CheckTransaction() : txout.nValue too high"); + return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return error("CTransaction::CheckTransaction() : txout total out of range"); + return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs @@ -346,13 +353,13 @@ bool CTransaction::CheckTransaction() const if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) - return error("CTransaction::CheckTransaction() : coinbase script size"); + return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) - return error("CTransaction::CheckTransaction() : prevout is null"); + return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; @@ -368,7 +375,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi // Coinbase is only valid in a block, not as a loose transaction if (IsCoinBase()) - return error("AcceptToMemoryPool() : coinbase as individual tx"); + return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx")); // To help v0.1.5 clients who would see it as a negative number if ((int64)nLockTime > INT_MAX) @@ -436,7 +443,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi // 34 bytes because a TxOut is: // 20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length if (GetSigOpCount() > nSize / 34 || nSize < 100) - return error("AcceptToMemoryPool() : nonstandard transaction"); + return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount"); int64 nFees = GetValueIn(mapInputs)-GetValueOut(); @@ -788,9 +795,15 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits) return true; } +// Return maximum amount of blocks that other nodes claim to have +int GetNumBlocksOfPeers() +{ + return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); +} + bool IsInitialBlockDownload() { - if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold)) + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; @@ -940,7 +953,7 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes // Revisit this if/when transaction replacement is implemented and allows // adding inputs: fInvalid = true; - return error("FetchInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()); + return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); } } @@ -1009,7 +1022,7 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, CTransaction& txPrev = inputs[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) - return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()); + return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); // If prev is coinbase, check that it's matured if (txPrev.IsCoinBase()) @@ -1018,24 +1031,31 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); // Check for conflicts (double-spend) + // This doesn't trigger the DoS code on purpose; if it did, it would make it easier + // for an attacker to attempt to split the network. if (!txindex.vSpent[prevout.n].IsNull()) return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); // Check for negative or overflow input values nValueIn += txPrev.vout[prevout.n].nValue; if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return error("ConnectInputs() : txin values out of range"); + return DoS(100, error("ConnectInputs() : txin values out of range")); - // Verify signature - if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) + // Skip ECDSA signature verification when connecting blocks (fBlock=true) + // before the last blockchain checkpoint. This is safe because block merkle hashes are + // still computed and checked, and any change will be caught at the next checkpoint. + if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) { - // only during transition phase for P2SH: do not invoke (external) - // anti-DoS code for potentially old clients relaying bad P2SH - // transactions - if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0)) - return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + // Verify signature + if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) + { + // only during transition phase for P2SH: do not invoke anti-DoS code for + // potentially old clients relaying bad P2SH transactions + if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0)) + return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); - return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); + return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } } // Mark outpoints as spent @@ -1049,15 +1069,15 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs, } if (nValueIn < GetValueOut()) - return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()); + return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) - return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()); + return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); nFees += nTxFee; if (!MoneyRange(nFees)) - return error("ConnectInputs() : nFees out of range"); + return DoS(100, error("ConnectInputs() : nFees out of range")); } return true; @@ -1176,7 +1196,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) { nSigOps += tx.GetSigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) - return error("ConnectBlock() : too many sigops"); + return DoS(100, error("ConnectBlock() : too many sigops")); CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); nTxPos += ::GetSerializeSize(tx, SER_DISK); @@ -1195,7 +1215,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(mapInputs); if (nSigOps > MAX_BLOCK_SIGOPS) - return error("ConnectBlock() : too many sigops"); + return DoS(100, error("ConnectBlock() : too many sigops")); } nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); @@ -1450,11 +1470,11 @@ bool CBlock::CheckBlock() const // Size limits if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE) - return error("CheckBlock() : size limits failed"); + return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (!CheckProofOfWork(GetHash(), nBits)) - return error("CheckBlock() : proof of work failed"); + return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) @@ -1462,23 +1482,23 @@ bool CBlock::CheckBlock() const // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) - return error("CheckBlock() : first tx is not coinbase"); + return DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) - return error("CheckBlock() : more than one coinbase"); + return DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.CheckTransaction()) - return error("CheckBlock() : CheckTransaction failed"); + return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); // Check that it's not full of nonstandard transactions if (GetSigOpCount() > MAX_BLOCK_SIGOPS) - return error("CheckBlock() : out-of-bounds SigOpCount"); + return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot if (hashMerkleRoot != BuildMerkleTree()) - return error("CheckBlock() : hashMerkleRoot mismatch"); + return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } @@ -1493,13 +1513,13 @@ bool CBlock::AcceptBlock() // Get prev block index map::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) - return error("AcceptBlock() : prev block not found"); + return DoS(10, error("AcceptBlock() : prev block not found")); CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; // Check proof of work if (nBits != GetNextWorkRequired(pindexPrev, this)) - return error("AcceptBlock() : incorrect proof of work"); + return DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) @@ -1508,11 +1528,11 @@ bool CBlock::AcceptBlock() // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) - return error("AcceptBlock() : contains a non-final transaction"); + return DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckBlock(nHeight, hash)) - return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight); + return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight)); // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK))) @@ -1535,7 +1555,7 @@ bool CBlock::AcceptBlock() return true; } -bool static ProcessBlock(CNode* pfrom, CBlock* pblock) +bool ProcessBlock(CNode* pfrom, CBlock* pblock) { // Check for duplicate uint256 hash = pblock->GetHash(); @@ -1555,6 +1575,8 @@ bool static ProcessBlock(CNode* pfrom, CBlock* pblock) int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; if (deltaTime < 0) { + if (pfrom) + pfrom->Misbehaving(100); return error("ProcessBlock() : block with timestamp before last checkpoint"); } CBigNum bnNewBlock; @@ -1563,6 +1585,8 @@ bool static ProcessBlock(CNode* pfrom, CBlock* pblock) bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); if (bnNewBlock > bnRequired) { + if (pfrom) + pfrom->Misbehaving(100); return error("ProcessBlock() : block with too little proof-of-work"); } } @@ -2002,7 +2026,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { // Each connection can only send one version message if (pfrom->nVersion != 0) + { + pfrom->Misbehaving(1); return false; + } int64 nTime; CAddress addrMe; @@ -2080,12 +2107,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fSuccessfullyConnected = true; printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight); + + cPeerBlockCounts.input(pfrom->nStartingHeight); } else if (pfrom->nVersion == 0) { // Must have a version message before anything else + pfrom->Misbehaving(1); return false; } @@ -2107,7 +2137,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000) return true; if (vAddr.size() > 1000) + { + pfrom->Misbehaving(20); return error("message addr size() = %d", vAddr.size()); + } // Store the new addresses CAddrDB addrDB; @@ -2165,7 +2198,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; if (vInv.size() > 50000) + { + pfrom->Misbehaving(20); return error("message inv size() = %d", vInv.size()); + } CTxDB txdb("r"); BOOST_FOREACH(const CInv& inv, vInv) @@ -2194,7 +2230,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vector vInv; vRecv >> vInv; if (vInv.size() > 50000) + { + pfrom->Misbehaving(20); return error("message getdata size() = %d", vInv.size()); + } BOOST_FOREACH(const CInv& inv, vInv) { @@ -2371,6 +2410,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (nEvicted > 0) printf("mapOrphan overflow, removed %d tx\n", nEvicted); } + if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); } @@ -2387,6 +2427,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (ProcessBlock(pfrom, &block)) mapAlreadyAskedFor.erase(inv); + if (block.nDoS) pfrom->Misbehaving(block.nDoS); } @@ -2830,15 +2871,25 @@ int static FormatHashBlocks(void* pbuffer, unsigned int len) return blocks; } -using CryptoPP::ByteReverse; - static const unsigned int pSHA256InitState[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; -inline void SHA256Transform(void* pstate, void* pinput, const void* pinit) +void SHA256Transform(void* pstate, void* pinput, const void* pinit) { - memcpy(pstate, pinit, 32); - CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput); + SHA256_CTX ctx; + unsigned char data[64]; + + SHA256_Init(&ctx); + + for (int i = 0; i < 16; i++) + ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); + + for (int i = 0; i < 8; i++) + ctx.h[i] = ((uint32_t*)pinit)[i]; + + SHA256_Update(&ctx, data, sizeof(data)); + for (int i = 0; i < 8; i++) + ((uint32_t*)pstate)[i] = ctx.h[i]; } // @@ -3156,7 +3207,6 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) return error("BitcoinMiner : ProcessBlock, block not accepted"); } - Sleep(2000); return true; }