#include "db.h"
#include "net.h"
#include "init.h"
+#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
// 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<CWallet*> setpwalletRegistered;
map<uint256, CBlockIndex*> 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;
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
+ // 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)
+ return false;
+ if (!txin.scriptSig.IsPushOnly())
+ return false;
+ }
+ BOOST_FOREACH(const CTxOut& txout, vout)
+ if (!::IsStandard(txout.scriptPubKey))
+ return false;
+ return true;
+}
+
+//
+// Check transaction inputs, and make sure any
+// pay-to-script-hash transactions are evaluating IsStandard scripts
+//
+// Why bother? To avoid denial-of-service attacks; an attacker
+// 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::AreInputsStandard(const std::map<uint256, std::pair<CTxIndex, CTransaction> >& mapInputs) const
+{
+ if (fTestNet)
+ return true; // Allow non-standard on testnet
+
+ for (int i = 0; i < vin.size(); i++)
+ {
+ COutPoint prevout = vin[i].prevout;
+
+ std::map<uint256, std::pair<CTxIndex, CTransaction> >::const_iterator mi = mapInputs.find(prevout.hash);
+ if (mi == mapInputs.end())
+ return false;
+
+ const CTransaction& txPrev = (mi->second).second;
+ assert(prevout.n < txPrev.vout.size());
+
+ vector<vector<unsigned char> > vSolutions;
+ txnouttype whichType;
+ // get the scriptPubKey corresponding to this input:
+ const CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey;
+ if (!Solver(prevScript, whichType, vSolutions))
+ return false;
+ if (whichType == TX_SCRIPTHASH)
+ {
+ vector<vector<unsigned char> > stack;
+
+ if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
+ return false;
+ if (stack.empty())
+ return false;
+ CScript subscript(stack.back().begin(), stack.back().end());
+ if (!::IsStandard(subscript))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int
+CTransaction::GetLegacySigOpCount() const
+{
+ int nSigOps = 0;
+ BOOST_FOREACH(const CTxIn& txin, vin)
+ {
+ nSigOps += txin.scriptSig.GetSigOpCount(false);
+ }
+ BOOST_FOREACH(const CTxOut& txout, vout)
+ {
+ nSigOps += txout.scriptPubKey.GetSigOpCount(false);
+ }
+ return nSigOps;
+}
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
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<int>::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");
if (fCheckInputs)
{
- // Check against previous transactions
+ map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
map<uint256, CTxIndex> 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 pay-to-script-hash 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
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)
for (int i = 0; i < vin.size(); i++)
mapNextTx[vin[i].prevout] = CInPoint(&mapTransactions[hash], i);
nTransactionsUpdated++;
+ ++nPooledTx;
}
return true;
}
mapNextTx.erase(txin.prevout);
mapTransactions.erase(GetHash());
nTransactionsUpdated++;
+ --nPooledTx;
}
return true;
}
-int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const
+int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
{
if (hashBlock == 0 || nIndex == -1)
return 0;
fMerkleVerified = true;
}
- nHeightRet = pindex->nHeight;
+ pindexRet = pindex;
return pindexBest->nHeight - pindex->nHeight + 1;
}
bool IsInitialBlockDownload()
{
- if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold))
+ if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
return true;
static int64 nLastUpdate;
static CBlockIndex* pindexLastBest;
}
-bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
- CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee)
+bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
+ bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& 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<uint256, pair<CTxIndex, CTransaction> > inputs,
+ map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
+ const 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
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()));
// 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
+ bool fStrictPayToScriptHash = true;
+ if (fBlock)
+ {
+ // 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
+ fStrictPayToScriptHash = (pindexBlock->nTime >= nEvalSwitchTime);
+ }
+ // if !fBlock, then always be strict-- don't accept
+ // invalid-under-new-rules pay-to-script-hash transactions into
+ // our memory pool (don't relay them, don't include them
+ // in blocks we mine).
+
+ // 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())))
+ {
// Verify signature
- if (!VerifySignature(txPrev, *this, i))
+ if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
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
if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return DoS(100, error("ConnectInputs() : txin values out of range"));
+ // Calculate sigOps accurately:
+ nSigOpsRet += txPrev.vout[prevout.n].scriptPubKey.GetSigOpCount(vin[i].scriptSig);
+
// Mark outpoints as spent
txindex.vSpent[prevout.n] = posThisTx;
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i))
+ 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
map<uint256, CTxIndex> 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<uint256, pair<CTxIndex, CTransaction> > mapInputs;
+ if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
+ return false;
+
+ int nTxOps = 0;
+ if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nTxOps))
return false;
+
+ nSigOps += nTxOps;
+ if (nSigOps > MAX_BLOCK_SIGOPS)
+ return DoS(100, error("ConnectBlock() : too many sigops"));
+ // There is a different MAX_BLOCK_SIGOPS check in AcceptBlock();
+ // a block must satisfy both to make it into the best-chain
+ // (AcceptBlock() is always called before ConnectBlock())
}
+
// Write queued txindex changes
for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
{
}
+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();
}
// Update best block in wallet (so we can detect restored wallets)
- if (!IsInitialBlockDownload())
+ bool fIsInitialDownload = IsInitialBlockDownload();
+ if (!fIsInitialDownload)
{
const CBlockLocator locator(pindexNew);
::SetBestChain(locator);
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;
}
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)
+ // Pre-pay-to-script-hash (before version 0.6), this is how sigops
+ // were counted; there is another check in ConnectBlock when
+ // transaction inputs are fetched to count pay-to-script-hash sigops:
+ int nSigOps = 0;
+ BOOST_FOREACH(const CTransaction& tx, vtx)
+ {
+ nSigOps += tx.GetLegacySigOpCount();
+ }
+ if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
// Check merkleroot
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
- static map<unsigned int, vector<unsigned char> > mapReuseKey;
+ static map<CService, vector<unsigned char> > mapReuseKey;
RandAddSeedPerfmon();
if (fDebug) {
printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
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)
{
}
// 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))
else if (strCommand == "verack")
{
- pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
+ pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
}
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<uint256, CNode*> mapMix;
BOOST_FOREACH(CNode* pnode, vNodes)
/// 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);
}
};
+uint64 nLastBlockTx = 0;
+uint64 nLastBlockSize = 0;
+
CBlock* CreateNewBlock(CReserveKey& reservekey)
{
CBlockIndex* pindexPrev = pindexBest;
// Collect transactions into block
map<uint256, CTxIndex> mapTestPool;
uint64 nBlockSize = 1000;
- int nBlockSigOps = 100;
+ uint64 nBlockTx = 0;
+ int nBlockSigOps1 = 100; // pre-0.6 count of sigOps
+ int nBlockSigOps2 = 100; // post-0.6 count of sigOps
while (!mapPriority.empty())
{
// Take highest priority transaction off priority queue
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)
+
+ // Legacy limits on sigOps:
+ int nTxSigOps1 = tx.GetLegacySigOpCount();
+ if (nBlockSigOps1 + nTxSigOps1 >= 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<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
- if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
+ map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
+ if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
+ continue;
+
+ int nTxSigOps2 = 0;
+ if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps2, nMinFee))
+ continue;
+ if (nBlockSigOps2 + nTxSigOps2 >= MAX_BLOCK_SIGOPS)
continue;
swap(mapTestPool, mapTestPoolTmp);
// Added
pblock->vtx.push_back(tx);
nBlockSize += nTxSize;
- nBlockSigOps += nTxSigOps;
+ ++nBlockTx;
+ nBlockSigOps1 += nTxSigOps1;
+ nBlockSigOps2 += nTxSigOps2;
// Add transactions that depend on this one to the priority queue
uint256 hash = tx.GetHash();
}
}
}
+
+ nLastBlockTx = nBlockTx;
+ nLastBlockSize = nBlockSize;
+ printf("CreateNewBlock(): total size %lu\n", nBlockSize);
+
}
pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
}
++nExtraNonce;
pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce);
+
+ // Put "/P2SH/" in the coinbase so everybody can tell when
+ // a majority of miners support it
+ const char* pszP2SH = "/P2SH/";
+ pblock->vtx[0].vin[0].scriptSig += CScript() << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH));
+ assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
+
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
}