bool IsRBFOptIn(const CTxMemPoolEntry &entry, CTxMemPool &pool) { AssertLockHeld(pool.cs); CTxMemPool::setEntries setAncestors; // First check the transaction itself. if (SignalsOptInRBF(entry.GetTx())) { return true; } // If this transaction is not in our mempool, then we can't be sure // we will know about all its inputs. if (!pool.exists(entry.GetTx().GetHash())) { throw std::runtime_error("Cannot determine RBF opt-in signal for non-mempool transaction\n"); } // If all the inputs have nSequence >= maxint-1, it still might be // signaled for RBF if any unconfirmed parents have signaled. uint64_t noLimit = std::numeric_limits<uint64_t>::max(); std::string dummy; pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); BOOST_FOREACH(CTxMemPool::txiter it, setAncestors) { if (SignalsOptInRBF(it->GetTx())) { return true; } } return false; }
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate) { LOCK(cs_feeEstimator); unsigned int txHeight = entry.GetHeight(); uint256 hash = entry.GetTx().GetHash(); if (mapMemPoolTxs.count(hash)) { LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n", hash.ToString().c_str()); return; } if (txHeight != nBestSeenHeight) { // Ignore side chains and re-orgs; assuming they are random they don't // affect the estimate. We'll potentially double count transactions in 1-block reorgs. // Ignore txs if BlockPolicyEstimator is not in sync with chainActive.Tip(). // It will be synced next time a block is processed. return; } // Only want to be updating estimates when our blockchain is synced, // otherwise we'll miscalculate how many blocks its taking to get included. if (!validFeeEstimate) { untrackedTxs++; return; } trackedTxs++; // Feerates are stored and reported as BTC-per-kb: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); mapMemPoolTxs[hash].blockHeight = txHeight; mapMemPoolTxs[hash].bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK()); }
bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate) { NotifyEntryAdded(entry.GetSharedTx()); // Add to memory pool without checking anything. // Used by AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); indexed_transaction_set::iterator newit = mapTx.insert(entry).first; mapLinks.insert(make_pair(newit, TxLinks())); // Update transaction for any feeDelta created by PrioritiseTransaction // TODO: refactor so that the fee delta is calculated before inserting // into mapTx. std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash); if (pos != mapDeltas.end()) { const CAmount &delta = pos->second; if (delta) { mapTx.modify(newit, update_fee_delta(delta)); } } // Update cachedInnerUsage to include contained transaction's usage. // (When we update the entry for in-mempool parents, memory usage will be // further updated.) cachedInnerUsage += entry.DynamicMemoryUsage(); const CTransaction& tx = newit->GetTx(); std::set<uint256> setParentTransactions; for (unsigned int i = 0; i < tx.vin.size(); i++) { mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, &tx)); setParentTransactions.insert(tx.vin[i].prevout.hash); } // Don't bother worrying about child transactions of this one. // Normal case of a new transaction arriving is that there can't be any // children, because such children would be orphans. // An exception to that is if a transaction enters that used to be in a block. // In that case, our disconnect block logic will call UpdateTransactionsFromBlock // to clean up the mess we're leaving here. // Update ancestors with information about this tx for (const uint256 &phash : setParentTransactions) { txiter pit = mapTx.find(phash); if (pit != mapTx.end()) { UpdateParent(newit, pit, true); } } UpdateAncestorsOf(true, newit, setAncestors); UpdateEntryForAncestors(newit, setAncestors); nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);} vTxHashes.emplace_back(tx.GetWitnessHash(), newit); newit->vTxHashesIdx = vTxHashes.size() - 1; return true; }
bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); mapTx[hash] = entry; const CTransaction& tx = mapTx[hash].GetTx(); for (unsigned int i = 0; i < tx.vin.size(); i++) mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); cachedInnerUsage += entry.DynamicMemoryUsage(); minerPolicyEstimator->processTransaction(entry, fCurrentEstimate); return true; }
void CNameMemPool::remove (const CTxMemPoolEntry& entry) { AssertLockHeld (pool.cs); if (entry.isNameRegistration ()) { const NameTxMap::iterator mit = mapNameRegs.find (entry.getName ()); assert (mit != mapNameRegs.end ()); mapNameRegs.erase (mit); } if (entry.isNameUpdate ()) { const NameTxMap::iterator mit = mapNameUpdates.find (entry.getName ()); assert (mit != mapNameUpdates.end ()); mapNameUpdates.erase (mit); } }
void CNameMemPool::addUnchecked (const uint256& hash, const CTxMemPoolEntry& entry) { AssertLockHeld (pool.cs); if (entry.isNameNew ()) { const valtype& newHash = entry.getNameNewHash (); const NameTxMap::const_iterator mit = mapNameNews.find (newHash); if (mit != mapNameNews.end ()) assert (mit->second == hash); else mapNameNews.insert (std::make_pair (newHash, hash)); } if (entry.isNameRegistration ()) { const valtype& name = entry.getName (); assert (mapNameRegs.count (name) == 0); mapNameRegs.insert (std::make_pair (name, hash)); } if (entry.isNameUpdate ()) { const valtype& name = entry.getName (); assert (mapNameUpdates.count (name) == 0); mapNameUpdates.insert (std::make_pair (name, hash)); } }
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate) { unsigned int txHeight = entry.GetHeight(); uint256 hash = entry.GetTx().GetHash(); if (mapMemPoolTxs[hash].stats != NULL) { LogPrint("estimatefee", "Blockpolicy error mempool tx %s already being tracked\n", hash.ToString().c_str()); return; } if (txHeight < nBestSeenHeight) { // Ignore side chains and re-orgs; assuming they are random they don't // affect the estimate. We'll potentially double count transactions in 1-block reorgs. return; } // Only want to be updating estimates when our blockchain is synced, // otherwise we'll miscalculate how many blocks its taking to get included. if (!fCurrentEstimate) return; if (!entry.WasClearAtEntry()) { // This transaction depends on other transactions in the mempool to // be included in a block before it will be able to be included, so // we shouldn't include it in our calculations return; } // Fees are stored and reported as XRE-per-kb: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); // Want the priority of the tx at confirmation. However we don't know // what that will be and its too hard to continue updating it // so use starting priority as a proxy double curPri = entry.GetPriority(txHeight); mapMemPoolTxs[hash].blockHeight = txHeight; LogPrint("estimatefee", "Blockpolicy mempool tx %s ", hash.ToString().substr(0,10)); // Record this as a priority estimate if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { mapMemPoolTxs[hash].stats = &priStats; mapMemPoolTxs[hash].bucketIndex = priStats.NewTx(txHeight, curPri); } // Record this as a fee estimate else if (isFeeDataPoint(feeRate, curPri)) { mapMemPoolTxs[hash].stats = &feeStats; mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); } else { LogPrint("estimatefee", "not adding"); } LogPrint("estimatefee", "\n"); }
void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) { if (!entry.WasClearAtEntry()) { // This transaction depended on other transactions in the mempool to // be included in a block before it was able to be included, so // we shouldn't include it in our calculations return; } // How many blocks did it take for miners to include this transaction? // blocksToConfirm is 1-based, so a transaction included in the earliest // possible block has confirmation count of 1 int blocksToConfirm = nBlockHeight - entry.GetHeight(); if (blocksToConfirm <= 0) { // This can't happen because we don't process transactions from a block with a height // lower than our greatest seen height LogPrint("estimatefee", "Blockpolicy error Transaction had negative blocksToConfirm\n"); return; } // Fees are stored and reported as XRE-per-kb: CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); // Want the priority of the tx at confirmation. The priority when it // entered the mempool could easily be very small and change quickly double curPri = entry.GetPriority(nBlockHeight); // Record this as a priority estimate if (entry.GetFee() == 0 || isPriDataPoint(feeRate, curPri)) { priStats.Record(blocksToConfirm, curPri); } // Record this as a fee estimate else if (isFeeDataPoint(feeRate, curPri)) { feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); } }
bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) const { LOCK(cs); setEntries parentHashes; const CTransaction &tx = entry.GetTx(); if (fSearchForParents) { // Get parents of this transaction that are in the mempool // GetMemPoolParents() is only valid for entries in the mempool, so we // iterate mapTx to find parents. for (unsigned int i = 0; i < tx.vin.size(); i++) { txiter piter = mapTx.find(tx.vin[i].prevout.hash); if (piter != mapTx.end()) { parentHashes.insert(piter); if (parentHashes.size() + 1 > limitAncestorCount) { errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount); return false; } } } } else { // If we're not searching for parents, we require this to be an // entry in the mempool already. txiter it = mapTx.iterator_to(entry); parentHashes = GetMemPoolParents(it); } size_t totalSizeWithAncestors = entry.GetTxSize(); while (!parentHashes.empty()) { txiter stageit = *parentHashes.begin(); setAncestors.insert(stageit); parentHashes.erase(stageit); totalSizeWithAncestors += stageit->GetTxSize(); if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > limitDescendantSize) { errString = strprintf("exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantSize); return false; } else if (stageit->GetCountWithDescendants() + 1 > limitDescendantCount) { errString = strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantCount); return false; } else if (totalSizeWithAncestors > limitAncestorSize) { errString = strprintf("exceeds ancestor size limit [limit: %u]", limitAncestorSize); return false; } const setEntries & setMemPoolParents = GetMemPoolParents(stageit); for (const txiter &phash : setMemPoolParents) { // If this is a new ancestor, add it. if (setAncestors.count(phash) == 0) { parentHashes.insert(phash); } if (parentHashes.size() + setAncestors.size() + 1 > limitAncestorCount) { errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount); return false; } } } return true; }