X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=bf7dbe805dc8ea7dabace77871ca1156c61cb771;hb=340f0876eabcabefa77588585f7c8f29954dcb48;hp=832a0f9240265052de3543a9e95d5bf062f22ab0;hpb=eb5fff9e16b2c3e94835cd3a8897318472df2374;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 832a0f9..bf7dbe8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "db.h" #include "net.h" #include "init.h" +#include #include #include @@ -17,6 +18,11 @@ using namespace boost; // Global state // +// Name of client reported in the 'version' message. Report the same name +// for both bitcoind and bitcoin-qt, to make it harder for attackers to +// target servers or GUI users specifically. +const std::string CLIENT_NAME("bitcoin-qt"); + CCriticalSection cs_setpwalletRegistered; set setpwalletRegistered; @@ -30,7 +36,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; @@ -241,6 +246,68 @@ bool CTransaction::ReadFromDisk(COutPoint prevout) return ReadFromDisk(txdb, prevout, txindex); } +bool CTransaction::IsStandard() const +{ + BOOST_FOREACH(const CTxIn& txin, vin) + { + // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG + // in an OP_EVAL, which is 3 ~80-byte signatures, 3 + // ~65-byte public keys, plus a few script ops. + if (txin.scriptSig.size() > 500) + return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size()); + if (!txin.scriptSig.IsPushOnly()) + return error("nonstandard txin (opcodes other than PUSH): %s", txin.scriptSig.ToString().c_str()); + } + BOOST_FOREACH(const CTxOut& txout, vout) + if (!::IsStandard(txout.scriptPubKey)) + return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); + return true; +} + +// +// Check transaction inputs, and make sure any +// OP_EVAL transactions are evaluating IsStandard scripts +// +// Why bother? To avoid denial-of-service attacks; an attacker +// can submit a standard DUP HASH... OP_EVAL transaction, +// which will get accepted into blocks. The script being +// EVAL'ed can be anything; an attacker could use a very +// expensive-to-check-upon-redemption script like: +// DUP CHECKSIG DROP ... repeated 100 times... OP_1 +// +bool CTransaction::AreInputsStandard(std::map > mapInputs) const +{ + if (fTestNet) + return true; // Allow non-standard on testnet + + for (int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + assert(mapInputs.count(prevout.hash) > 0); + CTransaction& txPrev = mapInputs[prevout.hash].second; + assert(prevout.n < txPrev.vout.size()); + + vector > vSolutions; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return error("nonstandard txin (spending nonstandard txout %s)", prevScript.ToString().c_str()); + if (whichType == TX_SCRIPTHASH) + { + vector > stack; + int nUnused; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, nUnused)) + return false; + CScript subscript(stack.back().begin(), stack.back().end()); + if (!::IsStandard(subscript)) + return error("nonstandard txin (nonstandard OP_EVAL subscript %s)", subscript.ToString().c_str()); + } + } + + return true; +} + int CMerkleTx::SetMerkleBranch(const CBlock* pblock) @@ -361,18 +428,9 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi 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) + if ((int64)nLockTime > std::numeric_limits::max()) return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); - // Safety limits - unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); - // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service - // attacks disallow transactions with more than one SigOp per 34 bytes. - // 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() : transaction with out-of-bounds SigOpCount"); - // Rather not work on nonstandard transactions (unless -testnet) if (!fTestNet && !IsStandard()) return error("AcceptToMemoryPool() : nonstandard transaction type"); @@ -416,18 +474,37 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi if (fCheckInputs) { - // Check against previous transactions + map > mapInputs; map mapUnused; + if (!FetchInputs(txdb, mapUnused, false, false, mapInputs)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); + } + + // Check for non-standard OP_EVALs in inputs + if (!AreInputsStandard(mapInputs)) + return error("AcceptToMemoryPool() : nonstandard transaction input"); + + // Check against previous transactions int64 nFees = 0; - if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false)) + int nSigOps = 0; + if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps)) { if (pfMissingInputs) *pfMissingInputs = true; return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } + // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service + // attacks disallow transactions with more than one SigOp per 65 bytes. + // 65 bytes because that is the minimum size of an ECDSA signature + unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); + if (nSigOps > nSize / 65 || nSize < 100) + return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount"); // Don't accept it if it can't get into a block - if (nFees < GetMinFee(1000, true, true)) + if (nFees < GetMinFee(1000, true, GMF_RELAY)) return error("AcceptToMemoryPool() : not enough fees"); // Continuously rate-limit free transactions @@ -482,8 +559,11 @@ bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs) return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs); } +uint64 nPooledTx = 0; + bool CTransaction::AddToMemoryPoolUnchecked() { + printf("AcceptToMemoryPoolUnchecked(): size %lu\n", mapTransactions.size()); // Add to memory pool without checking anything. Don't call this directly, // call AcceptToMemoryPool to properly check the transaction first. CRITICAL_BLOCK(cs_mapTransactions) @@ -493,6 +573,7 @@ bool CTransaction::AddToMemoryPoolUnchecked() for (int i = 0; i < vin.size(); i++) mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); nTransactionsUpdated++; + ++nPooledTx; } return true; } @@ -507,6 +588,7 @@ bool CTransaction::RemoveFromMemoryPool() mapNextTx.erase(txin.prevout); mapTransactions.erase(GetHash()); nTransactionsUpdated++; + --nPooledTx; } return true; } @@ -516,7 +598,7 @@ bool CTransaction::RemoveFromMemoryPool() -int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; @@ -537,7 +619,7 @@ int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const fMerkleVerified = true; } - nHeightRet = pindex->nHeight; + pindexRet = pindex; return pindexBest->nHeight - pindex->nHeight + 1; } @@ -659,11 +741,32 @@ int64 static GetBlockValue(int nHeight, int64 nFees) return nSubsidy + nFees; } +static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks +static const int64 nTargetSpacing = 10 * 60; +static const int64 nInterval = nTargetTimespan / nTargetSpacing; + +// +// minimum amount of work that could possibly be required nTime after +// minimum work required was nBase +// +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) +{ + CBigNum bnResult; + bnResult.SetCompact(nBase); + while (nTime > 0 && bnResult < bnProofOfWorkLimit) + { + // Maximum 400% adjustment... + bnResult *= 4; + // ... in best-case exactly 4-times-normal target time + nTime -= nTargetTimespan*4; + } + if (bnResult > bnProofOfWorkLimit) + bnResult = bnProofOfWorkLimit; + return bnResult.GetCompact(); +} + unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast) { - const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks - const int64 nTargetSpacing = 10 * 60; - const int64 nInterval = nTargetTimespan / nTargetSpacing; // Genesis block if (pindexLast == NULL) @@ -729,7 +832,7 @@ int GetNumBlocksOfPeers() bool IsInitialBlockDownload() { - if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold)) + if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; @@ -800,8 +903,72 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb) } -bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPool, CDiskTxPos posThisTx, - CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee) +bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, + bool fBlock, bool fMiner, map >& inputsRet) +{ + if (IsCoinBase()) + return true; // Coinbase transactions have no inputs to fetch. + + for (int i = 0; i < vin.size(); i++) + { + COutPoint prevout = vin[i].prevout; + if (inputsRet.count(prevout.hash)) + continue; // Got it already + + // Read txindex + CTxIndex& txindex = inputsRet[prevout.hash].first; + bool fFound = true; + if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) + { + // Get txindex from current proposed changes + txindex = mapTestPool.find(prevout.hash)->second; + } + else + { + // Read txindex from txdb + fFound = txdb.ReadTxIndex(prevout.hash, txindex); + } + if (!fFound && (fBlock || fMiner)) + return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + + // Read txPrev + CTransaction& txPrev = inputsRet[prevout.hash].second; + if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) + { + // Get prev tx from single transactions in memory + CRITICAL_BLOCK(cs_mapTransactions) + { + if (!mapTransactions.count(prevout.hash)) + return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + txPrev = mapTransactions[prevout.hash]; + } + if (!fFound) + txindex.vSpent.resize(txPrev.vout.size()); + } + else + { + // Get prev tx from disk + if (!txPrev.ReadFromDisk(txindex.pos)) + return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); + } + } + + // Make sure all prevout.n's are valid: + for (int i = 0; i < vin.size(); i++) + { + const COutPoint prevout = vin[i].prevout; + const CTxIndex& txindex = inputsRet[prevout.hash].first; + const CTransaction& txPrev = inputsRet[prevout.hash].second; + if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) + 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())); + } + + return true; +} + +bool CTransaction::ConnectInputs(map > inputs, + map& mapTestPool, CDiskTxPos posThisTx, + CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee) { // Take over previous transactions' spent pointers // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain @@ -813,43 +980,9 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo for (int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; - - // Read txindex - CTxIndex txindex; - bool fFound = true; - if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) - { - // Get txindex from current proposed changes - txindex = mapTestPool[prevout.hash]; - } - else - { - // Read txindex from txdb - fFound = txdb.ReadTxIndex(prevout.hash, txindex); - } - if (!fFound && (fBlock || fMiner)) - return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - - // Read txPrev - CTransaction txPrev; - if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) - { - // Get prev tx from single transactions in memory - CRITICAL_BLOCK(cs_mapTransactions) - { - if (!mapTransactions.count(prevout.hash)) - return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - txPrev = mapTransactions[prevout.hash]; - } - if (!fFound) - txindex.vSpent.resize(txPrev.vout.size()); - } - else - { - // Get prev tx from disk - if (!txPrev.ReadFromDisk(txindex.pos)) - return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); - } + assert(inputs.count(prevout.hash) > 0); + CTxIndex& txindex = inputs[prevout.hash].first; + CTransaction& txPrev = inputs[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) 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())); @@ -860,13 +993,31 @@ bool CTransaction::ConnectInputs(CTxDB& txdb, map& mapTestPoo if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); - // Skip ECDSA signature verification when connecting blocks (fBlock=true) during initial download - // (before the last blockchain checkpoint). This is safe because block merkle hashes are + // 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 && IsInitialBlockDownload())) + if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) + { + bool fStrictOpEval = true; + // This code should be removed when OP_EVAL has + // a majority of hashing power on the network. + if (fBlock) + { + // To avoid being on the short end of a block-chain split, + // interpret OP_EVAL as a NO_OP until blocks with timestamps + // after opevaltime: + int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); // Feb 1, 2012 + fStrictOpEval = (pindexBlock->nTime >= nEvalSwitchTime); + } + // if !fBlock, then always be strict-- don't accept + // invalid-under-new-rules OP_EVAL transactions into + // our memory pool (don't relay them, don't include them + // in blocks we mine). + // Verify signature - if (!VerifySignature(txPrev, *this, i)) + if (!VerifySignature(txPrev, *this, i, nSigOpsRet, fStrictOpEval)) return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier @@ -939,7 +1090,8 @@ bool CTransaction::ClientConnectInputs() return false; // Verify signature - if (!VerifySignature(txPrev, *this, i)) + int nUnused = 0; + if (!VerifySignature(txPrev, *this, i, nUnused, false)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of @@ -997,14 +1149,21 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) map mapQueuedChanges; int64 nFees = 0; + int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); nTxPos += ::GetSerializeSize(tx, SER_DISK); - if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false)) + map > mapInputs; + if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs)) + return false; + if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps)) return false; + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); } + // Write queued txindex changes for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { @@ -1125,6 +1284,14 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) } +static void +runCommand(std::string strCommand) +{ + int nErr = ::system(strCommand.c_str()); + if (nErr) + printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr); +} + bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) { uint256 hash = GetHash(); @@ -1168,7 +1335,8 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) } // Update best block in wallet (so we can detect restored wallets) - if (!IsInitialBlockDownload()) + bool fIsInitialDownload = IsInitialBlockDownload(); + if (!fIsInitialDownload) { const CBlockLocator locator(pindexNew); ::SetBestChain(locator); @@ -1183,6 +1351,14 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) nTransactionsUpdated++; printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + std::string strCmd = GetArg("-blocknotify", ""); + + if (!fIsInitialDownload && !strCmd.empty()) + { + boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + return true; } @@ -1265,8 +1441,21 @@ bool CBlock::CheckBlock() const if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); - // Check that it's not full of nonstandard transactions - if (GetSigOpCount() > MAX_BLOCK_SIGOPS) + // This code should be removed when a compatibility-breaking block chain split has passed. + // Compatibility check for old clients that counted sigops differently: + int nSigOps = 0; + BOOST_FOREACH(const CTransaction& tx, vtx) + { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nSigOps += txin.scriptSig.GetSigOpCount(); + } + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nSigOps += txout.scriptPubKey.GetSigOpCount(); + } + } + if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot @@ -1340,6 +1529,28 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) if (!pblock->CheckBlock()) return error("ProcessBlock() : CheckBlock FAILED"); + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); + if (pcheckpoint && pblock->hashPrevBlock != hashBestChain) + { + // Extra checks to prevent "fill up memory by spamming with bogus blocks" + int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; + if (deltaTime < 0) + { + pfrom->Misbehaving(100); + return error("ProcessBlock() : block with timestamp before last checkpoint"); + } + CBigNum bnNewBlock; + bnNewBlock.SetCompact(pblock->nBits); + CBigNum bnRequired; + bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime)); + if (bnNewBlock > bnRequired) + { + pfrom->Misbehaving(100); + return error("ProcessBlock() : block with too little proof-of-work"); + } + } + + // If don't already have its previous block, shunt it off to holding area until we get it if (!mapBlockIndex.count(pblock->hashPrevBlock)) { @@ -1743,7 +1954,7 @@ unsigned char pchMessageStart[4] = { 0xf9, 0xbe, 0xb4, 0xd9 }; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { - static map > mapReuseKey; + static map > mapReuseKey; RandAddSeedPerfmon(); if (fDebug) { printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); @@ -1799,14 +2010,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - AddTimeData(pfrom->addr.ip, nTime); + AddTimeData(pfrom->addr, nTime); // Change version if (pfrom->nVersion >= 209) pfrom->PushMessage("verack"); - pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION)); + pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); if (pfrom->nVersion < 209) - pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); if (!pfrom->fInbound) { @@ -1827,7 +2038,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } // Ask the first connected node for block updates - static int nAskedForBlocks; + static int nAskedForBlocks = 0; if (!pfrom->fClient && (pfrom->nVersion < 32000 || pfrom->nVersion >= 32400) && (nAskedForBlocks < 1 || vNodes.size() <= 1)) @@ -1859,7 +2070,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "verack") { - pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION)); + pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); } @@ -1905,7 +2116,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) static uint256 hashSalt; if (hashSalt == 0) RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt)); - uint256 hashRand = hashSalt ^ (((int64)addr.ip)<<32) ^ ((GetTime()+addr.ip)/(24*60*60)); + int64 hashAddr = addr.GetHash(); + uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap mapMix; BOOST_FOREACH(CNode* pnode, vNodes) @@ -2204,12 +2416,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) /// we have a chance to check the order here // Keep giving the same key to the same ip until they use it - if (!mapReuseKey.count(pfrom->addr.ip)) - pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr.ip], true); + if (!mapReuseKey.count(pfrom->addr)) + pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); // Send back approval of order and pubkey to use CScript scriptPubKey; - scriptPubKey << mapReuseKey[pfrom->addr.ip] << OP_CHECKSIG; + scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); } @@ -2680,6 +2892,9 @@ public: }; +uint64 nLastBlockTx = 0; +uint64 nLastBlockSize = 0; + CBlock* CreateNewBlock(CReserveKey& reservekey) { CBlockIndex* pindexPrev = pindexBest; @@ -2767,6 +2982,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Collect transactions into block map mapTestPool; uint64 nBlockSize = 1000; + uint64 nBlockTx = 0; int nBlockSigOps = 100; while (!mapPriority.empty()) { @@ -2779,18 +2995,21 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK); if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) continue; - int nTxSigOps = tx.GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) - continue; // Transaction fee required depends on block size bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority)); - int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true); + int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK); // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency map mapTestPoolTmp(mapTestPool); - if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee)) + map > mapInputs; + if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs)) + continue; + int nTxSigOps = 0; + if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee)) + continue; + if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; swap(mapTestPool, mapTestPoolTmp); @@ -2798,6 +3017,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) pblock->vtx.push_back(tx); nBlockSize += nTxSize; nBlockSigOps += nTxSigOps; + ++nBlockTx; // Add transactions that depend on this one to the priority queue uint256 hash = tx.GetHash(); @@ -2814,6 +3034,11 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) } } } + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + printf("CreateNewBlock(): total size %lu\n", nBlockSize); + } pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); @@ -2839,6 +3064,13 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& } ++nExtraNonce; pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce); + + // Put "OP_EVAL" in the coinbase so everybody can tell when + // a majority of miners support it + const char* pOpEvalName = GetOpName(OP_EVAL); + pblock->vtx[0].vin[0].scriptSig += CScript() << std::vector(pOpEvalName, pOpEvalName+strlen(pOpEvalName)); + assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); + pblock->hashMerkleRoot = pblock->BuildMerkleTree(); }