|
|
@ -449,31 +449,32 @@ bool CTransaction::CheckTransaction() const |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) |
|
|
|
bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, |
|
|
|
|
|
|
|
bool* pfMissingInputs) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (pfMissingInputs) |
|
|
|
if (pfMissingInputs) |
|
|
|
*pfMissingInputs = false; |
|
|
|
*pfMissingInputs = false; |
|
|
|
|
|
|
|
|
|
|
|
if (!CheckTransaction()) |
|
|
|
if (!tx.CheckTransaction()) |
|
|
|
return error("AcceptToMemoryPool() : CheckTransaction failed"); |
|
|
|
return error("CTxMemPool::accept() : CheckTransaction failed"); |
|
|
|
|
|
|
|
|
|
|
|
// Coinbase is only valid in a block, not as a loose transaction
|
|
|
|
// Coinbase is only valid in a block, not as a loose transaction
|
|
|
|
if (IsCoinBase()) |
|
|
|
if (tx.IsCoinBase()) |
|
|
|
return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx")); |
|
|
|
return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); |
|
|
|
|
|
|
|
|
|
|
|
// To help v0.1.5 clients who would see it as a negative number
|
|
|
|
// To help v0.1.5 clients who would see it as a negative number
|
|
|
|
if ((int64)nLockTime > std::numeric_limits<int>::max()) |
|
|
|
if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) |
|
|
|
return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet"); |
|
|
|
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); |
|
|
|
|
|
|
|
|
|
|
|
// Rather not work on nonstandard transactions (unless -testnet)
|
|
|
|
// Rather not work on nonstandard transactions (unless -testnet)
|
|
|
|
if (!fTestNet && !IsStandard()) |
|
|
|
if (!fTestNet && !tx.IsStandard()) |
|
|
|
return error("AcceptToMemoryPool() : nonstandard transaction type"); |
|
|
|
return error("CTxMemPool::accept() : nonstandard transaction type"); |
|
|
|
|
|
|
|
|
|
|
|
// Do we already have it?
|
|
|
|
// Do we already have it?
|
|
|
|
uint256 hash = GetHash(); |
|
|
|
uint256 hash = tx.GetHash(); |
|
|
|
{ |
|
|
|
{ |
|
|
|
LOCK(mempool.cs); |
|
|
|
LOCK(cs); |
|
|
|
if (mempool.mapTx.count(hash)) |
|
|
|
if (mapTx.count(hash)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
if (fCheckInputs) |
|
|
|
if (fCheckInputs) |
|
|
@ -482,10 +483,10 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi |
|
|
|
|
|
|
|
|
|
|
|
// Check for conflicts with in-memory transactions
|
|
|
|
// Check for conflicts with in-memory transactions
|
|
|
|
CTransaction* ptxOld = NULL; |
|
|
|
CTransaction* ptxOld = NULL; |
|
|
|
for (int i = 0; i < vin.size(); i++) |
|
|
|
for (int i = 0; i < tx.vin.size(); i++) |
|
|
|
{ |
|
|
|
{ |
|
|
|
COutPoint outpoint = vin[i].prevout; |
|
|
|
COutPoint outpoint = tx.vin[i].prevout; |
|
|
|
if (mempool.mapNextTx.count(outpoint)) |
|
|
|
if (mapNextTx.count(outpoint)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Disable replacement feature for now
|
|
|
|
// Disable replacement feature for now
|
|
|
|
return false; |
|
|
|
return false; |
|
|
@ -493,15 +494,15 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi |
|
|
|
// Allow replacing with a newer version of the same transaction
|
|
|
|
// Allow replacing with a newer version of the same transaction
|
|
|
|
if (i != 0) |
|
|
|
if (i != 0) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
ptxOld = mempool.mapNextTx[outpoint].ptx; |
|
|
|
ptxOld = mapNextTx[outpoint].ptx; |
|
|
|
if (ptxOld->IsFinal()) |
|
|
|
if (ptxOld->IsFinal()) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
if (!IsNewerThan(*ptxOld)) |
|
|
|
if (!tx.IsNewerThan(*ptxOld)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
for (int i = 0; i < vin.size(); i++) |
|
|
|
for (int i = 0; i < tx.vin.size(); i++) |
|
|
|
{ |
|
|
|
{ |
|
|
|
COutPoint outpoint = vin[i].prevout; |
|
|
|
COutPoint outpoint = tx.vin[i].prevout; |
|
|
|
if (!mempool.mapNextTx.count(outpoint) || mempool.mapNextTx[outpoint].ptx != ptxOld) |
|
|
|
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
break; |
|
|
|
break; |
|
|
@ -513,29 +514,29 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi |
|
|
|
MapPrevTx mapInputs; |
|
|
|
MapPrevTx mapInputs; |
|
|
|
map<uint256, CTxIndex> mapUnused; |
|
|
|
map<uint256, CTxIndex> mapUnused; |
|
|
|
bool fInvalid = false; |
|
|
|
bool fInvalid = false; |
|
|
|
if (!FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) |
|
|
|
if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (fInvalid) |
|
|
|
if (fInvalid) |
|
|
|
return error("AcceptToMemoryPool() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); |
|
|
|
return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); |
|
|
|
if (pfMissingInputs) |
|
|
|
if (pfMissingInputs) |
|
|
|
*pfMissingInputs = true; |
|
|
|
*pfMissingInputs = true; |
|
|
|
return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); |
|
|
|
return error("CTxMemPool::accept() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check for non-standard pay-to-script-hash in inputs
|
|
|
|
// Check for non-standard pay-to-script-hash in inputs
|
|
|
|
if (!AreInputsStandard(mapInputs) && !fTestNet) |
|
|
|
if (!tx.AreInputsStandard(mapInputs) && !fTestNet) |
|
|
|
return error("AcceptToMemoryPool() : nonstandard transaction input"); |
|
|
|
return error("CTxMemPool::accept() : nonstandard transaction input"); |
|
|
|
|
|
|
|
|
|
|
|
// Note: if you modify this code to accept non-standard transactions, then
|
|
|
|
// Note: if you modify this code to accept non-standard transactions, then
|
|
|
|
// you should add code here to check that the transaction does a
|
|
|
|
// you should add code here to check that the transaction does a
|
|
|
|
// reasonable number of ECDSA signature verifications.
|
|
|
|
// reasonable number of ECDSA signature verifications.
|
|
|
|
|
|
|
|
|
|
|
|
int64 nFees = GetValueIn(mapInputs)-GetValueOut(); |
|
|
|
int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); |
|
|
|
unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK); |
|
|
|
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK); |
|
|
|
|
|
|
|
|
|
|
|
// Don't accept it if it can't get into a block
|
|
|
|
// Don't accept it if it can't get into a block
|
|
|
|
if (nFees < GetMinFee(1000, true, GMF_RELAY)) |
|
|
|
if (nFees < tx.GetMinFee(1000, true, GMF_RELAY)) |
|
|
|
return error("AcceptToMemoryPool() : not enough fees"); |
|
|
|
return error("CTxMemPool::accept() : not enough fees"); |
|
|
|
|
|
|
|
|
|
|
|
// Continuously rate-limit free transactions
|
|
|
|
// Continuously rate-limit free transactions
|
|
|
|
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
|
|
|
|
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
|
|
|
@ -554,8 +555,8 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi |
|
|
|
nLastTime = nNow; |
|
|
|
nLastTime = nNow; |
|
|
|
// -limitfreerelay unit is thousand-bytes-per-minute
|
|
|
|
// -limitfreerelay unit is thousand-bytes-per-minute
|
|
|
|
// At default rate it would take over a month to fill 1GB
|
|
|
|
// At default rate it would take over a month to fill 1GB
|
|
|
|
if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(*this)) |
|
|
|
if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx)) |
|
|
|
return error("AcceptToMemoryPool() : free transaction rejected by rate limiter"); |
|
|
|
return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); |
|
|
|
if (fDebug) |
|
|
|
if (fDebug) |
|
|
|
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); |
|
|
|
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); |
|
|
|
dFreeCount += nSize; |
|
|
|
dFreeCount += nSize; |
|
|
@ -564,21 +565,21 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi |
|
|
|
|
|
|
|
|
|
|
|
// Check against previous transactions
|
|
|
|
// Check against previous transactions
|
|
|
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
|
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
|
|
if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) |
|
|
|
if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); |
|
|
|
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Store transaction in memory
|
|
|
|
// Store transaction in memory
|
|
|
|
{ |
|
|
|
{ |
|
|
|
LOCK(mempool.cs); |
|
|
|
LOCK(cs); |
|
|
|
if (ptxOld) |
|
|
|
if (ptxOld) |
|
|
|
{ |
|
|
|
{ |
|
|
|
printf("AcceptToMemoryPool() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); |
|
|
|
printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); |
|
|
|
mempool.remove(*ptxOld); |
|
|
|
remove(*ptxOld); |
|
|
|
} |
|
|
|
} |
|
|
|
mempool.addUnchecked(*this); |
|
|
|
addUnchecked(tx); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
///// are we sure this is ok when loading transactions or restoring block txes
|
|
|
|
///// are we sure this is ok when loading transactions or restoring block txes
|
|
|
@ -586,15 +587,20 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi |
|
|
|
if (ptxOld) |
|
|
|
if (ptxOld) |
|
|
|
EraseFromWallets(ptxOld->GetHash()); |
|
|
|
EraseFromWallets(ptxOld->GetHash()); |
|
|
|
|
|
|
|
|
|
|
|
printf("AcceptToMemoryPool(): accepted %s\n", hash.ToString().substr(0,10).c_str()); |
|
|
|
printf("CTxMemPool::accept() : accepted %s\n", hash.ToString().substr(0,10).c_str()); |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool CTxMemPool::addUnchecked(CTransaction &tx) |
|
|
|
bool CTxMemPool::addUnchecked(CTransaction &tx) |
|
|
|
{ |
|
|
|
{ |
|
|
|
printf("AcceptToMemoryPoolUnchecked(): size %lu\n", mapTx.size()); |
|
|
|
printf("addUnchecked(): size %lu\n", mapTx.size()); |
|
|
|
// Add to memory pool without checking anything. Don't call this directly,
|
|
|
|
// Add to memory pool without checking anything. Don't call this directly,
|
|
|
|
// call AcceptToMemoryPool to properly check the transaction first.
|
|
|
|
// call CTxMemPool::accept to properly check the transaction first.
|
|
|
|
{ |
|
|
|
{ |
|
|
|
LOCK(cs); |
|
|
|
LOCK(cs); |
|
|
|
uint256 hash = tx.GetHash(); |
|
|
|
uint256 hash = tx.GetHash(); |
|
|
|