multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
map<uint256, CDataStream*> mapOrphanTransactions;
-multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev;
+map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
// mapOrphanTransactions
//
-void AddOrphanTx(const CDataStream& vMsg)
+bool AddOrphanTx(const CDataStream& vMsg)
{
CTransaction tx;
CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
- return;
+ return false;
+
+ CDataStream* pvMsg = new CDataStream(vMsg);
- CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg);
+ // Ignore big transactions, to avoid a
+ // send-big-orphans memory exhaustion attack. If a peer has a legitimate
+ // large transaction with a missing parent then we assume
+ // it will rebroadcast it later, after the parent transaction(s)
+ // have been mined or received.
+ // 10,000 orphans, each of which is at most 5,000 bytes big is
+ // at most 500 megabytes of orphans:
+ if (pvMsg->size() > 5000)
+ {
+ delete pvMsg;
+ printf("ignoring large orphan tx (size: %u, hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str());
+ return false;
+ }
+
+ mapOrphanTransactions[hash] = pvMsg;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg));
+ mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
+
+ printf("stored orphan tx %s (mapsz %u)\n", hash.ToString().substr(0,10).c_str(),
+ mapOrphanTransactions.size());
+ return true;
}
void static EraseOrphanTx(uint256 hash)
CDataStream(*pvMsg) >> tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
- for (multimap<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash);
- mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);)
- {
- if ((*mi).second == pvMsg)
- mapOrphanTransactionsByPrev.erase(mi++);
- else
- mi++;
- }
+ mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
+ if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
+ mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
delete pvMsg;
mapOrphanTransactions.erase(hash);
}
-int LimitOrphanTxSize(int nMaxOrphans)
+unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
- int nEvicted = 0;
+ unsigned int nEvicted = 0;
while (mapOrphanTransactions.size() > nMaxOrphans)
{
// Evict a random orphan:
- std::vector<unsigned char> randbytes(32);
- RAND_bytes(&randbytes[0], 32);
- uint256 randomhash(randbytes);
+ uint256 randomhash = GetRandHash();
map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
if (!Solver(prevScript, whichType, vSolutions))
return false;
int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
+ if (nArgsExpected < 0)
+ return false;
// Transactions with extra stuff in their scriptSigs are
// non-standard. Note that this EvalScript() call will
return false;
if (whichType2 == TX_SCRIPTHASH)
return false;
- nArgsExpected += ScriptSigArgsExpected(whichType2, vSolutions2);
+
+ int tmpExpected;
+ tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
+ if (tmpExpected < 0)
+ return false;
+ nArgsExpected += tmpExpected;
}
- if (stack.size() != nArgsExpected)
+ if (stack.size() != (unsigned int)nArgsExpected)
return false;
}
return true;
}
-int
+unsigned int
CTransaction::GetLegacySigOpCount() const
{
- int nSigOps = 0;
+ unsigned int nSigOps = 0;
BOOST_FOREACH(const CTxIn& txin, vin)
{
nSigOps += txin.scriptSig.GetSigOpCount(false);
hashBlock = pblock->GetHash();
// Locate the transaction
- for (nIndex = 0; nIndex < pblock->vtx.size(); nIndex++)
+ for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++)
if (pblock->vtx[nIndex] == *(CTransaction*)this)
break;
- if (nIndex == pblock->vtx.size())
+ if (nIndex == (int)pblock->vtx.size())
{
vMerkleBranch.clear();
nIndex = -1;
return false;
}
-bool CWalletTx::AcceptWalletTransaction()
+bool CWalletTx::AcceptWalletTransaction()
{
CTxDB txdb("r");
return AcceptWalletTransaction(txdb);
}
-int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
+unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
{
if (IsCoinBase())
return 0;
- int nSigOps = 0;
+ unsigned int nSigOps = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
const CTxOut& prevout = GetOutputFor(vin[i], inputs);
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);
+ // Check for negative or overflow input values
+ nValueIn += txPrev.vout[prevout.n].nValue;
+ if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
+ return DoS(100, error("ConnectInputs() : txin values out of range"));
+
+ }
+ // The first loop above does all the inexpensive checks.
+ // Only if ALL inputs pass do we perform expensive ECDSA signature checks.
+ // Helps prevent CPU exhaustion attacks.
+ for (unsigned int i = 0; i < vin.size(); i++)
+ {
+ COutPoint prevout = vin[i].prevout;
+ assert(inputs.count(prevout.hash) > 0);
+ CTxIndex& txindex = inputs[prevout.hash].first;
+ CTransaction& txPrev = inputs[prevout.hash].second;
+
// 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 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.
map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
- int nSigOps = 0;
+ unsigned int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx)
{
nSigOps += tx.GetLegacySigOpCount();
if (!tx.CheckTransaction())
return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
- int nSigOps = 0;
+ // Check for duplicate txids. This is caught by ConnectInputs(),
+ // but catching it earlier avoids a potential DoS attack:
+ set<uint256> 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();
// at a time so the setAddrKnowns of the chosen nodes prevent repeats
static uint256 hashSalt;
if (hashSalt == 0)
- RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt));
+ hashSalt = GetRandHash();
int64 hashAddr = addr.GetHash();
uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60));
hashRand = Hash(BEGIN(hashRand), END(hashRand));
else if (strCommand == "tx")
{
vector<uint256> vWorkQueue;
+ vector<uint256> vEraseQueue;
CDataStream vMsg(vRecv);
CTxDB txdb("r");
CTransaction tx;
RelayMessage(inv, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
+ vEraseQueue.push_back(inv.hash);
// Recursively process any orphan transactions that depended on this one
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
uint256 hashPrev = vWorkQueue[i];
- for (multimap<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev.lower_bound(hashPrev);
- mi != mapOrphanTransactionsByPrev.upper_bound(hashPrev);
+ for (map<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin();
+ mi != mapOrphanTransactionsByPrev[hashPrev].end();
++mi)
{
const CDataStream& vMsg = *((*mi).second);
CTransaction tx;
CDataStream(vMsg) >> tx;
CInv inv(MSG_TX, tx.GetHash());
+ bool fMissingInputs2 = false;
- if (tx.AcceptToMemoryPool(txdb, true))
+ if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
{
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
SyncWithWallets(tx, NULL, true);
RelayMessage(inv, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
+ vEraseQueue.push_back(inv.hash);
+ }
+ else if (!fMissingInputs2)
+ {
+ // invalid orphan
+ vEraseQueue.push_back(inv.hash);
+ printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
}
}
}
- BOOST_FOREACH(uint256 hash, vWorkQueue)
+ BOOST_FOREACH(uint256 hash, vEraseQueue)
EraseOrphanTx(hash);
}
else if (fMissingInputs)
{
- printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
AddOrphanTx(vMsg);
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
- int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
+ unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
if (nEvicted > 0)
- printf("mapOrphan overflow, removed %d tx\n", nEvicted);
+ printf("mapOrphan overflow, removed %u tx\n", nEvicted);
}
if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
}
int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
if (vRecv.end() - pstart < nHeaderSize)
{
- if (vRecv.size() > nHeaderSize)
+ if ((int)vRecv.size() > nHeaderSize)
{
printf("\n\nPROCESSMESSAGE MESSAGESTART NOT FOUND\n\n");
vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
if (pto->nVersion == 0)
return true;
- // Keep-alive ping. We send a nonce of zero because we don't use it anywhere
+ // 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->nVersion > BIP0031_VERSION)
// 1/4 of tx invs blast to all immediately
static uint256 hashSalt;
if (hashSalt == 0)
- RAND_bytes((unsigned char*)&hashSalt, sizeof(hashSalt));
+ hashSalt = GetRandHash();
uint256 hashRand = inv.hash ^ hashSalt;
hashRand = Hash(BEGIN(hashRand), END(hashRand));
bool fTrickleWait = ((hashRand & 3) != 0);
ctx.h[i] = ((uint32_t*)pinit)[i];
SHA256_Update(&ctx, data, sizeof(data));
- for (int i = 0; i < 8; i++)
+ for (int i = 0; i < 8; i++)
((uint32_t*)pstate)[i] = ctx.h[i];
}
if ((nNonce & 0xffff) == 0)
{
nHashesDone = 0xffff+1;
- return -1;
+ return (unsigned int) -1;
}
}
}
continue;
// Legacy limits on sigOps:
- int nTxSigOps = tx.GetLegacySigOpCount();
+ unsigned int nTxSigOps = tx.GetLegacySigOpCount();
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
(char*)&hash, nHashesDone);
// Check if something found
- if (nNonceFound != -1)
+ if (nNonceFound != (unsigned int) -1)
{
for (unsigned int i = 0; i < sizeof(hash)/4; i++)
((unsigned int*)&hash)[i] = ByteReverse(((unsigned int*)&hash)[i]);