|
|
@ -379,21 +379,6 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Amount of bitcoins spent by the transaction.
|
|
|
|
|
|
|
|
@return sum of all outputs (note: does not include fees) |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
int64_t GetValueOut(const CTransaction& tx) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int64_t nValueOut = 0; |
|
|
|
|
|
|
|
BOOST_FOREACH(const CTxOut& txout, tx.vout) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
nValueOut += txout.nValue; |
|
|
|
|
|
|
|
if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut)) |
|
|
|
|
|
|
|
throw std::runtime_error("GetValueOut() : value out of range"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nValueOut; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Check transaction inputs, and make sure any
|
|
|
|
// Check transaction inputs, and make sure any
|
|
|
|
// pay-to-script-hash transactions are evaluating IsStandard scripts
|
|
|
|
// pay-to-script-hash transactions are evaluating IsStandard scripts
|
|
|
@ -660,7 +645,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
// Check for conflicts with in-memory transactions
|
|
|
|
// Check for conflicts with in-memory transactions
|
|
|
|
CTransaction* ptxOld = NULL; |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
LOCK(pool.cs); // protect pool.mapNextTx
|
|
|
|
LOCK(pool.cs); // protect pool.mapNextTx
|
|
|
|
for (unsigned int i = 0; i < tx.vin.size(); i++) |
|
|
|
for (unsigned int i = 0; i < tx.vin.size(); i++) |
|
|
@ -670,22 +654,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Disable replacement feature for now
|
|
|
|
// Disable replacement feature for now
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
// Allow replacing with a newer version of the same transaction
|
|
|
|
|
|
|
|
if (i != 0) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
ptxOld = pool.mapNextTx[outpoint].ptx; |
|
|
|
|
|
|
|
if (IsFinalTx(*ptxOld)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!tx.IsNewerThan(*ptxOld)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
for (unsigned int i = 0; i < tx.vin.size(); i++) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
COutPoint outpoint = tx.vin[i].prevout; |
|
|
|
|
|
|
|
if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -734,8 +702,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa |
|
|
|
// 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_t nFees = view.GetValueIn(tx)-GetValueOut(tx); |
|
|
|
int64_t nValueIn = view.GetValueIn(tx); |
|
|
|
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); |
|
|
|
int64_t nValueOut = tx.GetValueOut(); |
|
|
|
|
|
|
|
int64_t nFees = nValueIn-nValueOut; |
|
|
|
|
|
|
|
double dPriority = view.GetPriority(tx, chainActive.Height()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height()); |
|
|
|
|
|
|
|
unsigned int nSize = entry.GetTxSize(); |
|
|
|
|
|
|
|
|
|
|
|
// Don't accept it if it can't get into a block
|
|
|
|
// Don't accept it if it can't get into a block
|
|
|
|
int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); |
|
|
|
int64_t txMinFee = GetMinFee(tx, nSize, true, GMF_RELAY); |
|
|
@ -779,22 +752,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa |
|
|
|
{ |
|
|
|
{ |
|
|
|
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); |
|
|
|
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Store transaction in memory
|
|
|
|
// Store transaction in memory
|
|
|
|
{ |
|
|
|
pool.addUnchecked(hash, entry); |
|
|
|
if (ptxOld) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); |
|
|
|
|
|
|
|
pool.remove(*ptxOld); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
pool.addUnchecked(hash, tx); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
///// are we sure this is ok when loading transactions or restoring block txes
|
|
|
|
|
|
|
|
// If updated, erase old tx from wallet
|
|
|
|
|
|
|
|
if (ptxOld) |
|
|
|
|
|
|
|
g_signals.EraseTransaction(ptxOld->GetHash()); |
|
|
|
|
|
|
|
g_signals.SyncTransaction(hash, tx, NULL); |
|
|
|
g_signals.SyncTransaction(hash, tx, NULL); |
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
@ -1370,12 +1331,12 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (nValueIn < GetValueOut(tx)) |
|
|
|
if (nValueIn < tx.GetValueOut()) |
|
|
|
return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), |
|
|
|
return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()), |
|
|
|
REJECT_INVALID, "in < out"); |
|
|
|
REJECT_INVALID, "in < out"); |
|
|
|
|
|
|
|
|
|
|
|
// Tally transaction fees
|
|
|
|
// Tally transaction fees
|
|
|
|
int64_t nTxFee = nValueIn - GetValueOut(tx); |
|
|
|
int64_t nTxFee = nValueIn - tx.GetValueOut(); |
|
|
|
if (nTxFee < 0) |
|
|
|
if (nTxFee < 0) |
|
|
|
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), |
|
|
|
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()), |
|
|
|
REJECT_INVALID, "fee < 0"); |
|
|
|
REJECT_INVALID, "fee < 0"); |
|
|
@ -1628,7 +1589,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C |
|
|
|
REJECT_INVALID, "too many sigops"); |
|
|
|
REJECT_INVALID, "too many sigops"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
nFees += view.GetValueIn(tx)-GetValueOut(tx); |
|
|
|
nFees += view.GetValueIn(tx)-tx.GetValueOut(); |
|
|
|
|
|
|
|
|
|
|
|
std::vector<CScriptCheck> vChecks; |
|
|
|
std::vector<CScriptCheck> vChecks; |
|
|
|
if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) |
|
|
|
if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL)) |
|
|
@ -1648,10 +1609,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C |
|
|
|
if (fBenchmark) |
|
|
|
if (fBenchmark) |
|
|
|
LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); |
|
|
|
LogPrintf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); |
|
|
|
|
|
|
|
|
|
|
|
if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) |
|
|
|
if (block.vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) |
|
|
|
return state.DoS(100, |
|
|
|
return state.DoS(100, |
|
|
|
error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", |
|
|
|
error("ConnectBlock() : coinbase pays too much (actual=%"PRId64" vs limit=%"PRId64")", |
|
|
|
GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)), |
|
|
|
block.vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees)), |
|
|
|
REJECT_INVALID, "coinbase too large"); |
|
|
|
REJECT_INVALID, "coinbase too large"); |
|
|
|
|
|
|
|
|
|
|
|
if (!control.Wait()) |
|
|
|
if (!control.Wait()) |
|
|
|