X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fmain.cpp;h=9e13f7a36e3aa43f4664da6f860185965619872e;hb=2bc4fd609ca00d5a5cb0b6b3eba5f35cb334b967;hp=1b5bf528079f2b51c9317a8c346e0f9dfae2c57e;hpb=d7062ef1bd3319689d402027131f2c46efeddcb6;p=novacoin.git diff --git a/src/main.cpp b/src/main.cpp index 1b5bf52..9e13f7a 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 @@ -35,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; @@ -53,6 +53,9 @@ map mapOrphanTransactions; multimap mapOrphanTransactionsByPrev; +const string strMessageMagic = "Bitcoin Signed Message:\n"; + + double dHashesPerSec; int64 nHPSTimerStart; @@ -250,54 +253,56 @@ bool CTransaction::IsStandard() const { BOOST_FOREACH(const CTxIn& txin, vin) { - // Biggest 'standard' txin is a 2-signature 2-of-3 escrow - // in an OP_EVAL, which is 2 ~80-byte signatures, 3 + // 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() > 400) - return error("nonstandard txin, size %d\n", txin.scriptSig.size()); + if (txin.scriptSig.size() > 500) + return false; if (!txin.scriptSig.IsPushOnly()) - return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); + return false; } BOOST_FOREACH(const CTxOut& txout, vout) if (!::IsStandard(txout.scriptPubKey)) - return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str()); + return false; return true; } // // Check transaction inputs, and make sure any -// OP_EVAL transactions are evaluating IsStandard scripts +// pay-to-script-hash 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 +// can submit a standard HASH... OP_EQUAL transaction, +// which will get accepted into blocks. The redemption +// script 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::IsStandardInputs(std::map > mapInputs) const +bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const { - if (fTestNet) - return true; // Allow non-standard on testnet + if (IsCoinBase()) + return true; // Coinbases don't use vin normally 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; + const CTxOut& prev = GetOutputFor(vin[i], mapInputs); vector > vSolutions; - txntype whichType; - if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions)) + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + const CScript& prevScript = prev.scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) return false; if (whichType == TX_SCRIPTHASH) { vector > stack; - int nUnused; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, nUnused)) + + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) + return false; + if (stack.empty()) return false; - const vector& subscript = stack.back(); - if (!::IsStandard(CScript(subscript.begin(), subscript.end()))) + CScript subscript(stack.back().begin(), stack.back().end()); + if (!::IsStandard(subscript)) return false; } } @@ -305,6 +310,20 @@ bool CTransaction::IsStandardInputs(std::map INT_MAX) + if ((int64)nLockTime > std::numeric_limits::max()) return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) @@ -471,37 +490,31 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi if (fCheckInputs) { - map > mapInputs; + MapPrevTx mapInputs; map mapUnused; - if (!FetchInputs(txdb, mapUnused, false, false, mapInputs)) + bool fInvalid = false; + if (!FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { + if (fInvalid) + return error("AcceptToMemoryPool() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); 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 (!IsStandardInputs(mapInputs)) + // Check for non-standard pay-to-script-hash in inputs + if (!AreInputsStandard(mapInputs) && !fTestNet) return error("AcceptToMemoryPool() : nonstandard transaction input"); - // Check against previous transactions - int64 nFees = 0; - 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 + // Note: if you modify this code to accept non-standard transactions, then + // you should add code here to check that the transaction does a + // reasonable number of ECDSA signature verifications. + + int64 nFees = GetValueIn(mapInputs)-GetValueOut(); 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 @@ -528,6 +541,13 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi dFreeCount += nSize; } } + + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) + { + return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); + } } // Store transaction in memory @@ -556,8 +576,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) @@ -567,6 +590,7 @@ bool CTransaction::AddToMemoryPoolUnchecked() for (int i = 0; i < vin.size(); i++) mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i); nTransactionsUpdated++; + ++nPooledTx; } return true; } @@ -581,6 +605,7 @@ bool CTransaction::RemoveFromMemoryPool() mapNextTx.erase(txin.prevout); mapTransactions.erase(GetHash()); nTransactionsUpdated++; + --nPooledTx; } return true; } @@ -824,7 +849,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; @@ -896,11 +921,17 @@ bool CTransaction::DisconnectInputs(CTxDB& txdb) bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTestPool, - bool fBlock, bool fMiner, map >& inputsRet) + bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) { + // FetchInputs can return false either because we just haven't seen some inputs + // (in which case the transaction should be stored as an orphan) + // or because the transaction is malformed (in which case the transaction should + // be dropped). If tx is definitely invalid, fInvalid will be set to true. + fInvalid = false; + if (IsCoinBase()) return true; // Coinbase transactions have no inputs to fetch. - + for (int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; @@ -944,12 +975,71 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes 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; + assert(inputsRet.count(prevout.hash) != 0); + 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()) + { + // Revisit this if/when transaction replacement is implemented and allows + // adding inputs: + fInvalid = true; + 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) +const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const +{ + MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); + if (mi == inputs.end()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); + + const CTransaction& txPrev = (mi->second).second; + if (input.prevout.n >= txPrev.vout.size()) + throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); + + return txPrev.vout[input.prevout.n]; +} + +int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const +{ + if (IsCoinBase()) + return 0; + + int64 nResult = 0; + for (int i = 0; i < vin.size(); i++) + { + nResult += GetOutputFor(vin[i], inputs).nValue; + } + return nResult; + +} + +int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const +{ + if (IsCoinBase()) + return 0; + + int nSigOps = 0; + for (int i = 0; i < vin.size(); i++) + { + const CTxOut& prevout = GetOutputFor(vin[i], inputs); + if (prevout.scriptPubKey.IsPayToScriptHash()) + nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); + } + return nSigOps; +} + +bool CTransaction::ConnectInputs(MapPrevTx inputs, + map& mapTestPool, const CDiskTxPos& posThisTx, + const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) { // 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 @@ -958,6 +1048,7 @@ bool CTransaction::ConnectInputs(map > inp if (!IsCoinBase()) { int64 nValueIn = 0; + int64 nFees = 0; for (int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; @@ -970,18 +1061,10 @@ bool CTransaction::ConnectInputs(map > inp // If prev is coinbase, check that it's matured if (txPrev.IsCoinBase()) - for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) + for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) 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 - // still computed and checked, and any change will be caught at the next checkpoint. - if (!(fBlock && IsInitialBlockDownload())) - // Verify signature - if (!VerifySignature(txPrev, *this, i, nSigOpsRet)) - 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 // for an attacker to attempt to split the network. @@ -993,6 +1076,16 @@ bool CTransaction::ConnectInputs(map > inp if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return DoS(100, error("ConnectInputs() : txin values out of range")); + // 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()))) + { + // Verify signature + if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) + return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } + // Mark outpoints as spent txindex.vSpent[prevout.n] = posThisTx; @@ -1010,24 +1103,11 @@ bool CTransaction::ConnectInputs(map > inp int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); - if (nTxFee < nMinFee) - return false; nFees += nTxFee; if (!MoneyRange(nFees)) return DoS(100, error("ConnectInputs() : nFees out of range")); } - if (fBlock) - { - // Add transaction to changes - mapTestPool[GetHash()] = CTxIndex(posThisTx, vout.size()); - } - else if (fMiner) - { - // Add transaction to test pool - mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size()); - } - return true; } @@ -1053,8 +1133,7 @@ bool CTransaction::ClientConnectInputs() return false; // Verify signature - int nUnused = 0; - if (!VerifySignature(txPrev, *this, i, nUnused)) + if (!VerifySignature(txPrev, *this, i, true, 0)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of @@ -1107,6 +1186,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) if (!CheckBlock()) return false; + // To avoid being on the short end of a block-chain split, + // don't do secondary validation of pay-to-script-hash transactions + // until blocks with timestamps after paytoscripthashtime: + int64 nEvalSwitchTime = GetArg("-paytoscripthashtime", 1329264000); // Feb 15, 2012 + bool fStrictPayToScriptHash = (pindex->nTime >= nEvalSwitchTime); + //// issue here: it doesn't know the version unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); @@ -1115,16 +1200,37 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { + nSigOps += tx.GetLegacySigOpCount(); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); nTxPos += ::GetSerializeSize(tx, SER_DISK); - 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")); + MapPrevTx mapInputs; + if (!tx.IsCoinBase()) + { + bool fInvalid; + if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) + return false; + + if (fStrictPayToScriptHash) + { + // Add in sigops done by pay-to-script-hash inputs; + // this is to prevent a "rogue miner" from creating + // an incredibly-expensive-to-validate block. + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + if (nSigOps > MAX_BLOCK_SIGOPS) + return DoS(100, error("ConnectBlock() : too many sigops")); + } + + nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut(); + + if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) + return false; + } + + mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size()); } // Write queued txindex changes @@ -1247,6 +1353,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(); @@ -1290,7 +1404,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); @@ -1305,6 +1420,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; } @@ -1387,19 +1510,10 @@ bool CBlock::CheckBlock() const if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); - // 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(); - } + nSigOps += tx.GetLegacySigOpCount(); } if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); @@ -1900,7 +2014,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()); @@ -1956,7 +2070,7 @@ 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) @@ -1984,7 +2098,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)) @@ -2062,7 +2176,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) @@ -2361,12 +2476,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); } @@ -2837,6 +2952,9 @@ public: }; +uint64 nLastBlockTx = 0; +uint64 nLastBlockSize = 0; + CBlock* CreateNewBlock(CReserveKey& reservekey) { CBlockIndex* pindexPrev = pindexBest; @@ -2924,6 +3042,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) // Collect transactions into block map mapTestPool; uint64 nBlockSize = 1000; + uint64 nBlockTx = 0; int nBlockSigOps = 100; while (!mapPriority.empty()) { @@ -2937,26 +3056,40 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN) continue; + // Legacy limits on sigOps: + int nTxSigOps = tx.GetLegacySigOpCount(); + 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); - map > mapInputs; - if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs)) + MapPrevTx mapInputs; + bool fInvalid; + if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) continue; - int nTxSigOps = 0; - if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee)) + + int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); + if (nFees < nMinFee) continue; + + nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; + + if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) + continue; + mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size()); swap(mapTestPool, mapTestPoolTmp); // Added pblock->vtx.push_back(tx); nBlockSize += nTxSize; + ++nBlockTx; nBlockSigOps += nTxSigOps; // Add transactions that depend on this one to the priority queue @@ -2974,6 +3107,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); @@ -2998,12 +3136,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& hashPrevBlock = pblock->hashPrevBlock; } ++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)); + pblock->vtx[0].vin[0].scriptSig = (CScript() << pblock->nTime << CBigNum(nExtraNonce)) + COINBASE_FLAGS; assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); pblock->hashMerkleRoot = pblock->BuildMerkleTree();