mirror of
https://github.com/twisterarmy/twister-core.git
synced 2025-01-11 07:17:53 +00:00
cleaning block/tx checking
This commit is contained in:
parent
41f5e9f846
commit
97ba3b787c
415
src/main.cpp
415
src/main.cpp
@ -724,35 +724,6 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicts with in-memory transactions
|
|
||||||
CTransaction* ptxOld = NULL;
|
|
||||||
/*
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
|
||||||
{
|
|
||||||
COutPoint outpoint = tx.vin[i].prevout;
|
|
||||||
if (mapNextTx.count(outpoint))
|
|
||||||
{
|
|
||||||
// Disable replacement feature for now
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Allow replacing with a newer version of the same transaction
|
|
||||||
if (i != 0)
|
|
||||||
return false;
|
|
||||||
ptxOld = 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 (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
{
|
{
|
||||||
CCoinsView dummy;
|
CCoinsView dummy;
|
||||||
CCoinsViewCache view(dummy);
|
CCoinsViewCache view(dummy);
|
||||||
@ -763,25 +734,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
|
|||||||
view.SetBackend(viewMemPool);
|
view.SetBackend(viewMemPool);
|
||||||
|
|
||||||
// do we already have it?
|
// do we already have it?
|
||||||
|
// [MF] TODO: use hash(tx.userName)
|
||||||
if (view.HaveCoins(hash))
|
if (view.HaveCoins(hash))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// do all inputs exist?
|
|
||||||
// Note that this does not check for the presence of actual outputs (see the next check for that),
|
|
||||||
// only helps filling in pfMissingInputs (to determine missing vs spent).
|
|
||||||
/*
|
|
||||||
BOOST_FOREACH(const CTxIn txin, tx.vin) {
|
|
||||||
if (!view.HaveCoins(txin.prevout.hash)) {
|
|
||||||
if (pfMissingInputs)
|
|
||||||
*pfMissingInputs = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// are the actual inputs available?
|
|
||||||
if (!view.HaveInputs(tx))
|
|
||||||
return state.Invalid(error("CTxMemPool::accept() : inputs already spent"));
|
|
||||||
|
|
||||||
// Bring the best block into scope
|
// Bring the best block into scope
|
||||||
view.GetBestBlock();
|
view.GetBestBlock();
|
||||||
|
|
||||||
@ -789,14 +745,6 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
|
|||||||
view.SetBackend(dummy);
|
view.SetBackend(dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for non-standard pay-to-script-hash in inputs
|
|
||||||
if (!TestNet() && !AreInputsStandard(tx, view))
|
|
||||||
return error("CTxMemPool::accept() : nonstandard transaction input");
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
|
||||||
// Continuously rate-limit free transactions
|
// Continuously rate-limit free transactions
|
||||||
@ -821,30 +769,15 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr
|
|||||||
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
|
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
|
||||||
dFreeCount += nSize;
|
dFreeCount += nSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check against previous transactions
|
|
||||||
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
||||||
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC))
|
|
||||||
{
|
|
||||||
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store transaction in memory
|
// Store transaction in memory
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
if (ptxOld)
|
|
||||||
{
|
|
||||||
printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
|
|
||||||
remove(*ptxOld);
|
|
||||||
}
|
|
||||||
addUnchecked(hash, tx);
|
addUnchecked(hash, 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
|
||||||
// If updated, erase old tx from wallet
|
|
||||||
if (ptxOld)
|
|
||||||
EraseFromWallets(ptxOld->GetHash());
|
|
||||||
SyncWithWallets(hash, tx, NULL, true);
|
SyncWithWallets(hash, tx, NULL, true);
|
||||||
|
|
||||||
printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
|
printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
|
||||||
@ -1428,24 +1361,6 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
|
|||||||
|
|
||||||
bool CCoinsViewCache::HaveInputs(const CTransaction& tx)
|
bool CCoinsViewCache::HaveInputs(const CTransaction& tx)
|
||||||
{
|
{
|
||||||
if (!tx.IsSpamMessage()) {
|
|
||||||
/* [MF]
|
|
||||||
// first check whether information about the prevout hash is available
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
|
||||||
if (!HaveCoins(prevout.hash))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// then check whether the actual outputs are available
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
|
||||||
const CCoins &coins = GetCoins(prevout.hash);
|
|
||||||
if (!coins.IsAvailable(prevout.n))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1466,90 +1381,6 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks)
|
|
||||||
{
|
|
||||||
if (!tx.IsSpamMessage())
|
|
||||||
{
|
|
||||||
/* [MF]
|
|
||||||
if (pvChecks)
|
|
||||||
pvChecks->reserve(tx.vin.size());
|
|
||||||
|
|
||||||
// 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 (!inputs.HaveInputs(tx))
|
|
||||||
return state.Invalid(error("CheckInputs() : %s inputs unavailable", tx.GetHash().ToString().c_str()));
|
|
||||||
|
|
||||||
// While checking, GetBestBlock() refers to the parent block.
|
|
||||||
// This is also true for mempool checks.
|
|
||||||
int nSpendHeight = inputs.GetBestBlock()->nHeight + 1;
|
|
||||||
int64 nValueIn = 0;
|
|
||||||
int64 nFees = 0;
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
|
||||||
{
|
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
|
||||||
const CCoins &coins = inputs.GetCoins(prevout.hash);
|
|
||||||
|
|
||||||
// If prev is coinbase, check that it's matured
|
|
||||||
if (coins.IsCoinBase()) {
|
|
||||||
if (nSpendHeight - coins.nHeight < COINBASE_MATURITY)
|
|
||||||
return state.Invalid(error("CheckInputs() : tried to spend coinbase at depth %d", nSpendHeight - coins.nHeight));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for negative or overflow input values
|
|
||||||
nValueIn += coins.vout[prevout.n].nValue;
|
|
||||||
if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
|
||||||
return state.DoS(100, error("CheckInputs() : txin values out of range"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nValueIn < GetValueOut(tx))
|
|
||||||
return state.DoS(100, error("CheckInputs() : %s value in < value out", tx.GetHash().ToString().c_str()));
|
|
||||||
|
|
||||||
// Tally transaction fees
|
|
||||||
int64 nTxFee = nValueIn - GetValueOut(tx);
|
|
||||||
if (nTxFee < 0)
|
|
||||||
return state.DoS(100, error("CheckInputs() : %s nTxFee < 0", tx.GetHash().ToString().c_str()));
|
|
||||||
nFees += nTxFee;
|
|
||||||
if (!MoneyRange(nFees))
|
|
||||||
return state.DoS(100, error("CheckInputs() : nFees 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.
|
|
||||||
|
|
||||||
// Skip ECDSA signature verification when connecting blocks
|
|
||||||
// before the last block chain checkpoint. This is safe because block merkle hashes are
|
|
||||||
// still computed and checked, and any change will be caught at the next checkpoint.
|
|
||||||
if (fScriptChecks) {
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
|
||||||
const CCoins &coins = inputs.GetCoins(prevout.hash);
|
|
||||||
|
|
||||||
// Verify signature
|
|
||||||
CScriptCheck check(coins, tx, i, flags, 0);
|
|
||||||
if (pvChecks) {
|
|
||||||
pvChecks->push_back(CScriptCheck());
|
|
||||||
check.swap(pvChecks->back());
|
|
||||||
} else if (!check()) {
|
|
||||||
if (flags & SCRIPT_VERIFY_STRICTENC) {
|
|
||||||
// For now, check whether the failure was caused by non-canonical
|
|
||||||
// encodings or not; if so, don't trigger DoS protection.
|
|
||||||
CScriptCheck check(coins, tx, i, flags & (~SCRIPT_VERIFY_STRICTENC), 0);
|
|
||||||
if (check())
|
|
||||||
return state.Invalid();
|
|
||||||
}
|
|
||||||
return state.DoS(100,false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
|
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
|
||||||
{
|
{
|
||||||
assert(pindex == view.GetBestBlock());
|
assert(pindex == view.GetBestBlock());
|
||||||
@ -1683,81 +1514,23 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate();
|
|
||||||
|
|
||||||
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
|
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
|
||||||
// unless those are already completely spent.
|
|
||||||
// If such overwrites are allowed, coinbases and transactions depending upon those
|
|
||||||
// can be duplicated to remove the ability to spend the first instance -- even after
|
|
||||||
// being sent to another address.
|
|
||||||
// See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
|
|
||||||
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
|
|
||||||
// already refuses previously-known transaction ids entirely.
|
|
||||||
// This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC.
|
|
||||||
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
|
|
||||||
// two in the chain that violate it. This prevents exploiting the issue against nodes in their
|
|
||||||
// initial block download.
|
|
||||||
bool fEnforceBIP30 = (!pindex->phashBlock) || // Enforce on CreateNewBlock invocations which don't have a hash.
|
|
||||||
!((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
|
|
||||||
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
|
|
||||||
if (fEnforceBIP30) {
|
|
||||||
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||||
uint256 hash = block.GetTxHash(i);
|
uint256 hash = block.GetTxHash(i);
|
||||||
if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
|
if (view.HaveCoins(hash))
|
||||||
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
|
return state.DoS(100, error("ConnectBlock() : tried to overwrite transaction"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// BIP16 didn't become active until Apr 1 2012
|
|
||||||
int64 nBIP16SwitchTime = 1333238400;
|
|
||||||
bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
|
|
||||||
|
|
||||||
unsigned int flags = SCRIPT_VERIFY_NOCACHE |
|
|
||||||
(fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE);
|
|
||||||
|
|
||||||
CBlockUndo blockundo;
|
CBlockUndo blockundo;
|
||||||
|
|
||||||
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
|
|
||||||
|
|
||||||
int64 nStart = GetTimeMicros();
|
int64 nStart = GetTimeMicros();
|
||||||
int64 nFees = 0;
|
|
||||||
int nInputs = 0;
|
|
||||||
unsigned int nSigOps = 0;
|
|
||||||
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
|
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
|
||||||
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||||
vPos.reserve(block.vtx.size());
|
vPos.reserve(block.vtx.size());
|
||||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||||
{
|
{
|
||||||
const CTransaction &tx = block.vtx[i];
|
const CTransaction &tx = block.vtx[i];
|
||||||
/* [MF]
|
|
||||||
nInputs += tx.vin.size();
|
|
||||||
nSigOps += GetLegacySigOpCount(tx);
|
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
|
||||||
return state.DoS(100, error("ConnectBlock() : too many sigops"));
|
|
||||||
|
|
||||||
if (!tx.IsCoinBase())
|
|
||||||
{
|
|
||||||
if (!view.HaveInputs(tx))
|
|
||||||
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"));
|
|
||||||
|
|
||||||
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 += GetP2SHSigOpCount(tx, view);
|
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
|
||||||
return state.DoS(100, error("ConnectBlock() : too many sigops"));
|
|
||||||
}
|
|
||||||
|
|
||||||
nFees += view.GetValueIn(tx)-GetValueOut(tx);
|
|
||||||
|
|
||||||
std::vector<CScriptCheck> vChecks;
|
|
||||||
if (!CheckInputs(tx, state, view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL))
|
|
||||||
return false;
|
|
||||||
control.Add(vChecks);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
CTxUndo txundo;
|
CTxUndo txundo;
|
||||||
UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i));
|
UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i));
|
||||||
if (!tx.IsSpamMessage())
|
if (!tx.IsSpamMessage())
|
||||||
@ -1768,16 +1541,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
|||||||
}
|
}
|
||||||
int64 nTime = GetTimeMicros() - nStart;
|
int64 nTime = GetTimeMicros() - nStart;
|
||||||
if (fBenchmark)
|
if (fBenchmark)
|
||||||
printf("- 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));
|
printf("- Connect %u transactions: %.2fms (%.3fms/tx)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size());
|
||||||
|
|
||||||
if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees))
|
|
||||||
return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees)));
|
|
||||||
|
|
||||||
if (!control.Wait())
|
|
||||||
return state.DoS(100, false);
|
|
||||||
int64 nTime2 = GetTimeMicros() - nStart;
|
|
||||||
if (fBenchmark)
|
|
||||||
printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
|
|
||||||
|
|
||||||
if (fJustCheck)
|
if (fJustCheck)
|
||||||
return true;
|
return true;
|
||||||
@ -2180,14 +1944,6 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
|
|||||||
if (uniqueTx.size() != block.vtx.size())
|
if (uniqueTx.size() != block.vtx.size())
|
||||||
return state.DoS(100, error("CheckBlock() : duplicate transaction"));
|
return state.DoS(100, error("CheckBlock() : duplicate transaction"));
|
||||||
|
|
||||||
unsigned int nSigOps = 0;
|
|
||||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
|
||||||
{
|
|
||||||
nSigOps += GetLegacySigOpCount(tx);
|
|
||||||
}
|
|
||||||
if (nSigOps > MAX_BLOCK_SIGOPS)
|
|
||||||
return state.DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
|
|
||||||
|
|
||||||
// Check merkle root
|
// Check merkle root
|
||||||
if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree())
|
if (fCheckMerkleRoot && block.hashMerkleRoot != block.BuildMerkleTree())
|
||||||
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
|
return state.DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
|
||||||
@ -4186,6 +3942,8 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
|
|||||||
return NULL;
|
return NULL;
|
||||||
txNew.pubKey << pubkey << OP_CHECKSIG;
|
txNew.pubKey << pubkey << OP_CHECKSIG;
|
||||||
|
|
||||||
|
// [MF] TODO: fix nNonce
|
||||||
|
|
||||||
// Add our coinbase tx as first transaction
|
// Add our coinbase tx as first transaction
|
||||||
pblock->vtx.push_back(txNew);
|
pblock->vtx.push_back(txNew);
|
||||||
pblocktemplate->vTxFees.push_back(-1); // updated at end
|
pblocktemplate->vTxFees.push_back(-1); // updated at end
|
||||||
@ -4207,7 +3965,6 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
|
|||||||
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
|
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
|
||||||
|
|
||||||
// Collect memory pool transactions into the block
|
// Collect memory pool transactions into the block
|
||||||
int64 nFees = 0;
|
|
||||||
{
|
{
|
||||||
LOCK2(cs_main, mempool.cs);
|
LOCK2(cs_main, mempool.cs);
|
||||||
CBlockIndex* pindexPrev = pindexBest;
|
CBlockIndex* pindexPrev = pindexBest;
|
||||||
@ -4216,193 +3973,49 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
|
|||||||
// Priority order to process transactions
|
// Priority order to process transactions
|
||||||
list<COrphan> vOrphan; // list memory doesn't move
|
list<COrphan> vOrphan; // list memory doesn't move
|
||||||
map<uint256, vector<COrphan*> > mapDependers;
|
map<uint256, vector<COrphan*> > mapDependers;
|
||||||
bool fPrintPriority = GetBoolArg("-printpriority", false);
|
|
||||||
|
|
||||||
// This vector will be sorted into a priority queue:
|
// Collect transactions into block
|
||||||
vector<TxPriority> vecPriority;
|
uint64 nBlockSize = 1000;
|
||||||
vecPriority.reserve(mempool.mapTx.size());
|
uint64 nBlockTx = 0;
|
||||||
|
|
||||||
for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
|
for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
|
||||||
{
|
{
|
||||||
CTransaction& tx = (*mi).second;
|
CTransaction& tx = (*mi).second;
|
||||||
if (tx.IsSpamMessage())
|
if (tx.IsSpamMessage())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
COrphan* porphan = NULL;
|
// This should never happen; all transactions in the memory are new
|
||||||
double dPriority = 0;
|
if( view.HaveCoins(tx.GetHash()) ) {
|
||||||
int64 nTotalIn = 0;
|
printf("ERROR: mempool transaction already exists\n");
|
||||||
bool fMissingInputs = false;
|
if (fDebug) assert("mempool transaction already exists" == 0);
|
||||||
/* [MF]
|
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
|
||||||
{
|
|
||||||
// Read prev transaction
|
|
||||||
if (!view.HaveCoins(txin.prevout.hash))
|
|
||||||
{
|
|
||||||
// This should never happen; all transactions in the memory
|
|
||||||
// pool should connect to either transactions in the chain
|
|
||||||
// or other transactions in the memory pool.
|
|
||||||
if (!mempool.mapTx.count(txin.prevout.hash))
|
|
||||||
{
|
|
||||||
printf("ERROR: mempool transaction missing input\n");
|
|
||||||
if (fDebug) assert("mempool transaction missing input" == 0);
|
|
||||||
fMissingInputs = true;
|
|
||||||
if (porphan)
|
|
||||||
vOrphan.pop_back();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has to wait for dependencies
|
|
||||||
if (!porphan)
|
|
||||||
{
|
|
||||||
// Use list for automatic deletion
|
|
||||||
vOrphan.push_back(COrphan(&tx));
|
|
||||||
porphan = &vOrphan.back();
|
|
||||||
}
|
|
||||||
mapDependers[txin.prevout.hash].push_back(porphan);
|
|
||||||
porphan->setDependsOn.insert(txin.prevout.hash);
|
|
||||||
nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const CCoins &coins = view.GetCoins(txin.prevout.hash);
|
|
||||||
|
|
||||||
int64 nValueIn = coins.vout[txin.prevout.n].nValue;
|
|
||||||
nTotalIn += nValueIn;
|
|
||||||
|
|
||||||
int nConf = pindexPrev->nHeight - coins.nHeight + 1;
|
|
||||||
|
|
||||||
dPriority += (double)nValueIn * nConf;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (fMissingInputs) continue;
|
|
||||||
|
|
||||||
// Priority is sum(valuein * age) / txsize
|
|
||||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
dPriority /= nTxSize;
|
|
||||||
|
|
||||||
// This is a more accurate fee-per-kilobyte than is used by the client code, because the
|
|
||||||
// client code rounds up the size to the nearest 1K. That's good, because it gives an
|
|
||||||
// incentive to create smaller transactions.
|
|
||||||
double dFeePerKb = double(nTotalIn-GetValueOut(tx)) / (double(nTxSize)/1000.0);
|
|
||||||
|
|
||||||
if (porphan)
|
|
||||||
{
|
|
||||||
porphan->dPriority = dPriority;
|
|
||||||
porphan->dFeePerKb = dFeePerKb;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect transactions into block
|
|
||||||
uint64 nBlockSize = 1000;
|
|
||||||
uint64 nBlockTx = 0;
|
|
||||||
int nBlockSigOps = 100;
|
|
||||||
bool fSortedByFee = (nBlockPrioritySize <= 0);
|
|
||||||
|
|
||||||
TxPriorityCompare comparer(fSortedByFee);
|
|
||||||
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
|
||||||
|
|
||||||
while (!vecPriority.empty())
|
|
||||||
{
|
|
||||||
// Take highest priority transaction off the priority queue:
|
|
||||||
double dPriority = vecPriority.front().get<0>();
|
|
||||||
double dFeePerKb = vecPriority.front().get<1>();
|
|
||||||
CTransaction& tx = *(vecPriority.front().get<2>());
|
|
||||||
|
|
||||||
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
|
||||||
vecPriority.pop_back();
|
|
||||||
|
|
||||||
// Size limits
|
// Size limits
|
||||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
if (nBlockSize + nTxSize >= nBlockMaxSize)
|
if (nBlockSize + nTxSize >= nBlockMaxSize)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Legacy limits on sigOps:
|
|
||||||
unsigned int nTxSigOps = GetLegacySigOpCount(tx);
|
|
||||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Skip free transactions if we're past the minimum block size:
|
|
||||||
if (fSortedByFee && (nBlockSize + nTxSize >= nBlockMinSize))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Prioritize by fee once past the priority size or we run out of high-priority
|
|
||||||
// transactions:
|
|
||||||
if (!fSortedByFee &&
|
|
||||||
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
|
|
||||||
{
|
|
||||||
fSortedByFee = true;
|
|
||||||
comparer = TxPriorityCompare(fSortedByFee);
|
|
||||||
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!view.HaveInputs(tx))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int64 nTxFees = view.GetValueIn(tx)-GetValueOut(tx);
|
|
||||||
|
|
||||||
nTxSigOps += GetP2SHSigOpCount(tx, view);
|
|
||||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CTxUndo txundo;
|
CTxUndo txundo;
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash);
|
UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1, hash);
|
||||||
|
|
||||||
// Added
|
// Added
|
||||||
pblock->vtx.push_back(tx);
|
pblock->vtx.push_back(tx);
|
||||||
pblocktemplate->vTxFees.push_back(nTxFees);
|
|
||||||
pblocktemplate->vTxSigOps.push_back(nTxSigOps);
|
|
||||||
nBlockSize += nTxSize;
|
nBlockSize += nTxSize;
|
||||||
++nBlockTx;
|
++nBlockTx;
|
||||||
nBlockSigOps += nTxSigOps;
|
|
||||||
nFees += nTxFees;
|
|
||||||
|
|
||||||
if (fPrintPriority)
|
|
||||||
{
|
|
||||||
printf("priority %.1f feeperkb %.1f txid %s\n",
|
|
||||||
dPriority, dFeePerKb, tx.GetHash().ToString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add transactions that depend on this one to the priority queue
|
|
||||||
if (mapDependers.count(hash))
|
|
||||||
{
|
|
||||||
BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
|
|
||||||
{
|
|
||||||
if (!porphan->setDependsOn.empty())
|
|
||||||
{
|
|
||||||
porphan->setDependsOn.erase(hash);
|
|
||||||
if (porphan->setDependsOn.empty())
|
|
||||||
{
|
|
||||||
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx));
|
|
||||||
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nLastBlockTx = nBlockTx;
|
nLastBlockTx = nBlockTx;
|
||||||
nLastBlockSize = nBlockSize;
|
nLastBlockSize = nBlockSize;
|
||||||
printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
|
printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
|
||||||
|
|
||||||
/* [MF]
|
|
||||||
pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
|
|
||||||
*/
|
|
||||||
pblocktemplate->vTxFees[0] = -nFees;
|
|
||||||
|
|
||||||
// Fill in header
|
// Fill in header
|
||||||
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
||||||
|
pblock->nHeight = pindexPrev->nHeight + 1;
|
||||||
UpdateTime(*pblock, pindexPrev);
|
UpdateTime(*pblock, pindexPrev);
|
||||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
|
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
|
||||||
pblock->nNonce = 0;
|
pblock->nNonce = 0;
|
||||||
/* [MF]
|
|
||||||
pblock->vtx[0].vin[0].scriptSig = CScript() << OP_0 << OP_0;
|
|
||||||
*/
|
|
||||||
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
|
|
||||||
|
|
||||||
CBlockIndex indexDummy(*pblock);
|
CBlockIndex indexDummy(*pblock);
|
||||||
indexDummy.pprev = pindexPrev;
|
indexDummy.pprev = pindexPrev;
|
||||||
|
@ -306,13 +306,6 @@ inline bool AllowFree(double dPriority)
|
|||||||
return dPriority > COIN * 144 / 250;
|
return dPriority > COIN * 144 / 250;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
|
|
||||||
// This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
|
|
||||||
// instead of being performed inline.
|
|
||||||
bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCache &view, bool fScriptChecks = true,
|
|
||||||
unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC,
|
|
||||||
std::vector<CScriptCheck> *pvChecks = NULL);
|
|
||||||
|
|
||||||
// Apply the effects of this transaction on the UTXO set represented by view
|
// Apply the effects of this transaction on the UTXO set represented by view
|
||||||
bool UpdateCoins(const CTransaction& tx, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash);
|
bool UpdateCoins(const CTransaction& tx, CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user