mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
Merge bitcoin/bitcoin#28364: log: log wtxids when possible, add TXPACKAGES category
a3b55c94b9
[doc] move comment about AlreadyHaveTx DoS score to the right place (glozow)3b8c17838a
[log] add more logs related to orphan handling (glozow)51b3275cd1
[log] add category TXPACKAGES for orphanage and package relay (glozow)a33dde1e41
[log] include wtxid in tx {relay,validation,orphanage} logging (glozow) Pull request description: This was taken from #28031 (see #27463 for project tracking). - Log wtxids in addition to txids when possible. This allows us to track the fate of a transaction from inv to mempool accept/reject through logs. - Additional orphan-related logging to make testing and debugging easier. Suggested in https://github.com/bitcoin/bitcoin/pull/28031#pullrequestreview-1531022386 and https://github.com/bitcoin/bitcoin/pull/28031#discussion_r1269622220 - Add `TXPACKAGES` category for logging. - Move a nearby comment block that was in the wrong place. ACKs for top commit: instagibbs: reACKa3b55c94b9
achow101: ACKa3b55c94b9
brunoerg: crACKa3b55c94b9
mzumsande: Code review ACKa3b55c94b9
Tree-SHA512: 21884ef7c2ea2fd006e715574a9dd3e6cbbe8f82d62c6187fe1d39aad5a834051203fda5f355a06ca40c3e2b9561aec50d7c922a662b1edc96f7b552c9f4b24d
This commit is contained in:
commit
5666966dff
6 changed files with 63 additions and 35 deletions
|
@ -182,6 +182,7 @@ const CLogCategoryDesc LogCategories[] =
|
|||
{BCLog::BLOCKSTORAGE, "blockstorage"},
|
||||
{BCLog::TXRECONCILIATION, "txreconciliation"},
|
||||
{BCLog::SCAN, "scan"},
|
||||
{BCLog::TXPACKAGES, "txpackages"},
|
||||
{BCLog::ALL, "1"},
|
||||
{BCLog::ALL, "all"},
|
||||
};
|
||||
|
@ -286,6 +287,8 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
|
|||
return "txreconciliation";
|
||||
case BCLog::LogFlags::SCAN:
|
||||
return "scan";
|
||||
case BCLog::LogFlags::TXPACKAGES:
|
||||
return "txpackages";
|
||||
case BCLog::LogFlags::ALL:
|
||||
return "all";
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace BCLog {
|
|||
BLOCKSTORAGE = (1 << 26),
|
||||
TXRECONCILIATION = (1 << 27),
|
||||
SCAN = (1 << 28),
|
||||
TXPACKAGES = (1 << 29),
|
||||
ALL = ~(uint32_t)0,
|
||||
};
|
||||
enum class Level {
|
||||
|
|
|
@ -2919,9 +2919,15 @@ bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
|
|||
const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
|
||||
const TxValidationState& state = result.m_state;
|
||||
const uint256& orphanHash = porphanTx->GetHash();
|
||||
const uint256& orphan_wtxid = porphanTx->GetWitnessHash();
|
||||
|
||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
|
||||
LogPrint(BCLog::TXPACKAGES, " accepted orphan tx %s (wtxid=%s)\n", orphanHash.ToString(), orphan_wtxid.ToString());
|
||||
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (wtxid=%s) (poolsz %u txn, %u kB)\n",
|
||||
peer.m_id,
|
||||
orphanHash.ToString(),
|
||||
orphan_wtxid.ToString(),
|
||||
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
|
||||
RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
|
||||
m_orphanage.AddChildrenToWorkSet(*porphanTx);
|
||||
m_orphanage.EraseTx(orphanHash);
|
||||
|
@ -2931,8 +2937,14 @@ bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
|
|||
return true;
|
||||
} else if (state.GetResult() != TxValidationResult::TX_MISSING_INPUTS) {
|
||||
if (state.IsInvalid()) {
|
||||
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s from peer=%d. %s\n",
|
||||
LogPrint(BCLog::TXPACKAGES, " invalid orphan tx %s (wtxid=%s) from peer=%d. %s\n",
|
||||
orphanHash.ToString(),
|
||||
orphan_wtxid.ToString(),
|
||||
peer.m_id,
|
||||
state.ToString());
|
||||
LogPrint(BCLog::MEMPOOLREJ, "%s (wtxid=%s) from peer=%d was not accepted: %s\n",
|
||||
orphanHash.ToString(),
|
||||
orphan_wtxid.ToString(),
|
||||
peer.m_id,
|
||||
state.ToString());
|
||||
// Maybe punish peer that gave us an invalid orphan tx
|
||||
|
@ -2940,7 +2952,7 @@ bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
|
|||
}
|
||||
// Has inputs but not accepted to mempool
|
||||
// Probably non-standard or insufficient fee
|
||||
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
|
||||
LogPrint(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", orphanHash.ToString(), orphan_wtxid.ToString());
|
||||
if (state.GetResult() != TxValidationResult::TX_WITNESS_STRIPPED) {
|
||||
// We can add the wtxid of this transaction to our reject filter.
|
||||
// Do not add txids of witness transactions or witness-stripped
|
||||
|
@ -4115,12 +4127,29 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
// permission, even if they were already in the mempool, allowing
|
||||
// the node to function as a gateway for nodes hidden behind it.
|
||||
if (!m_mempool.exists(GenTxid::Txid(tx.GetHash()))) {
|
||||
LogPrintf("Not relaying non-mempool transaction %s from forcerelay peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
|
||||
LogPrintf("Not relaying non-mempool transaction %s (wtxid=%s) from forcerelay peer=%d\n",
|
||||
tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), pfrom.GetId());
|
||||
} else {
|
||||
LogPrintf("Force relaying tx %s from peer=%d\n", tx.GetHash().ToString(), pfrom.GetId());
|
||||
LogPrintf("Force relaying tx %s (wtxid=%s) from peer=%d\n",
|
||||
tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), pfrom.GetId());
|
||||
RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
|
||||
}
|
||||
}
|
||||
// If a tx is detected by m_recent_rejects it is ignored. Because we haven't
|
||||
// submitted the tx to our mempool, we won't have computed a DoS
|
||||
// score for it or determined exactly why we consider it invalid.
|
||||
//
|
||||
// This means we won't penalize any peer subsequently relaying a DoSy
|
||||
// tx (even if we penalized the first peer who gave it to us) because
|
||||
// we have to account for m_recent_rejects showing false positives. In
|
||||
// other words, we shouldn't penalize a peer if we aren't *sure* they
|
||||
// submitted a DoSy tx.
|
||||
//
|
||||
// Note that m_recent_rejects doesn't just record DoSy or invalid
|
||||
// transactions, but any tx not accepted by the mempool, which may be
|
||||
// due to node policy (vs. consensus). So we can't blanket penalize a
|
||||
// peer simply for relaying a tx that our m_recent_rejects has caught,
|
||||
// regardless of false positives.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4137,9 +4166,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
|
||||
pfrom.m_last_tx_time = GetTime<std::chrono::seconds>();
|
||||
|
||||
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
|
||||
LogPrint(BCLog::MEMPOOL, "AcceptToMemoryPool: peer=%d: accepted %s (wtxid=%s) (poolsz %u txn, %u kB)\n",
|
||||
pfrom.GetId(),
|
||||
tx.GetHash().ToString(),
|
||||
tx.GetWitnessHash().ToString(),
|
||||
m_mempool.size(), m_mempool.DynamicMemoryUsage() / 1000);
|
||||
|
||||
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
|
||||
|
@ -4191,7 +4221,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
// DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
|
||||
m_orphanage.LimitOrphans(m_opts.max_orphan_txs);
|
||||
} else {
|
||||
LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
|
||||
LogPrint(BCLog::MEMPOOL, "not keeping orphan with rejected parents %s (wtxid=%s)\n",
|
||||
tx.GetHash().ToString(),
|
||||
tx.GetWitnessHash().ToString());
|
||||
// We will continue to reject this tx since it has rejected
|
||||
// parents so avoid re-requesting it from other peers.
|
||||
// Here we add both the txid and the wtxid, as we know that
|
||||
|
@ -4238,25 +4270,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
}
|
||||
}
|
||||
|
||||
// If a tx has been detected by m_recent_rejects, we will have reached
|
||||
// this point and the tx will have been ignored. Because we haven't
|
||||
// submitted the tx to our mempool, we won't have computed a DoS
|
||||
// score for it or determined exactly why we consider it invalid.
|
||||
//
|
||||
// This means we won't penalize any peer subsequently relaying a DoSy
|
||||
// tx (even if we penalized the first peer who gave it to us) because
|
||||
// we have to account for m_recent_rejects showing false positives. In
|
||||
// other words, we shouldn't penalize a peer if we aren't *sure* they
|
||||
// submitted a DoSy tx.
|
||||
//
|
||||
// Note that m_recent_rejects doesn't just record DoSy or invalid
|
||||
// transactions, but any tx not accepted by the mempool, which may be
|
||||
// due to node policy (vs. consensus). So we can't blanket penalize a
|
||||
// peer simply for relaying a tx that our m_recent_rejects has caught,
|
||||
// regardless of false positives.
|
||||
|
||||
if (state.IsInvalid()) {
|
||||
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
|
||||
LogPrint(BCLog::MEMPOOLREJ, "%s (wtxid=%s) from peer=%d was not accepted: %s\n",
|
||||
tx.GetHash().ToString(),
|
||||
tx.GetWitnessHash().ToString(),
|
||||
pfrom.GetId(),
|
||||
state.ToString());
|
||||
MaybePunishNodeForTx(pfrom.GetId(), state);
|
||||
|
|
|
@ -21,6 +21,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|||
LOCK(m_mutex);
|
||||
|
||||
const uint256& hash = tx->GetHash();
|
||||
const uint256& wtxid = tx->GetWitnessHash();
|
||||
if (m_orphans.count(hash))
|
||||
return false;
|
||||
|
||||
|
@ -34,7 +35,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|||
unsigned int sz = GetTransactionWeight(*tx);
|
||||
if (sz > MAX_STANDARD_TX_WEIGHT)
|
||||
{
|
||||
LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
|
||||
LogPrint(BCLog::TXPACKAGES, "ignoring large orphan tx (size: %u, txid: %s, wtxid: %s)\n", sz, hash.ToString(), wtxid.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,7 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|||
m_outpoint_to_orphan_it[txin.prevout].insert(ret.first);
|
||||
}
|
||||
|
||||
LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
|
||||
LogPrint(BCLog::TXPACKAGES, "stored orphan tx %s (wtxid=%s) (mapsz %u outsz %u)\n", hash.ToString(), wtxid.ToString(),
|
||||
m_orphans.size(), m_outpoint_to_orphan_it.size());
|
||||
return true;
|
||||
}
|
||||
|
@ -83,6 +84,8 @@ int TxOrphanage::EraseTxNoLock(const uint256& txid)
|
|||
m_orphan_list[old_pos] = it_last;
|
||||
it_last->second.list_pos = old_pos;
|
||||
}
|
||||
const auto& wtxid = it->second.tx->GetWitnessHash();
|
||||
LogPrint(BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s)\n", txid.ToString(), wtxid.ToString());
|
||||
m_orphan_list.pop_back();
|
||||
m_wtxid_to_orphan_it.erase(it->second.tx->GetWitnessHash());
|
||||
|
||||
|
@ -106,7 +109,7 @@ void TxOrphanage::EraseForPeer(NodeId peer)
|
|||
nErased += EraseTxNoLock(maybeErase->second.tx->GetHash());
|
||||
}
|
||||
}
|
||||
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
|
||||
if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan tx from peer=%d\n", nErased, peer);
|
||||
}
|
||||
|
||||
void TxOrphanage::LimitOrphans(unsigned int max_orphans)
|
||||
|
@ -132,7 +135,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans)
|
|||
}
|
||||
// Sweep again 5 minutes after the next entry that expires in order to batch the linear scan.
|
||||
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
|
||||
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
|
||||
if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan tx due to expiration\n", nErased);
|
||||
}
|
||||
FastRandomContext rng;
|
||||
while (m_orphans.size() > max_orphans)
|
||||
|
@ -142,7 +145,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans)
|
|||
EraseTxNoLock(m_orphan_list[randompos]->first);
|
||||
++nEvicted;
|
||||
}
|
||||
if (nEvicted > 0) LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
|
||||
if (nEvicted > 0) LogPrint(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
|
||||
}
|
||||
|
||||
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
|
||||
|
@ -159,6 +162,8 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
|
|||
std::set<uint256>& orphan_work_set = m_peer_work_set.try_emplace(elem->second.fromPeer).first->second;
|
||||
// Add this tx to the work set
|
||||
orphan_work_set.insert(elem->first);
|
||||
LogPrint(BCLog::TXPACKAGES, "added %s (wtxid=%s) to peer %d workset\n",
|
||||
tx.GetHash().ToString(), tx.GetWitnessHash().ToString(), elem->second.fromPeer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -233,6 +238,6 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
|
|||
for (const uint256& orphanHash : vOrphanErase) {
|
||||
nErased += EraseTxNoLock(orphanHash);
|
||||
}
|
||||
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
|
||||
LogPrint(BCLog::TXPACKAGES, "Erased %d orphan tx included or conflicted by block\n", nErased);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1079,9 +1079,11 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
|||
// Remove conflicting transactions from the mempool
|
||||
for (CTxMemPool::txiter it : ws.m_all_conflicting)
|
||||
{
|
||||
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s additional fees, %d delta bytes\n",
|
||||
LogPrint(BCLog::MEMPOOL, "replacing tx %s (wtxid=%s) with %s (wtxid=%s) for %s additional fees, %d delta bytes\n",
|
||||
it->GetTx().GetHash().ToString(),
|
||||
it->GetTx().GetWitnessHash().ToString(),
|
||||
hash.ToString(),
|
||||
tx.GetWitnessHash().ToString(),
|
||||
FormatMoney(ws.m_modified_fees - ws.m_conflicting_fees),
|
||||
(int)entry->GetTxSize() - (int)ws.m_conflicting_size);
|
||||
TRACE7(mempool, replaced,
|
||||
|
|
|
@ -106,7 +106,7 @@ class P2PPermissionsTests(BitcoinTestFramework):
|
|||
|
||||
self.log.debug("Check that node[1] will send the tx to node[0] even though it is already in the mempool")
|
||||
self.connect_nodes(1, 0)
|
||||
with self.nodes[1].assert_debug_log(["Force relaying tx {} from peer=0".format(txid)]):
|
||||
with self.nodes[1].assert_debug_log(["Force relaying tx {} (wtxid={}) from peer=0".format(txid, tx.getwtxid())]):
|
||||
p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
|
||||
self.wait_until(lambda: txid in self.nodes[0].getrawmempool())
|
||||
|
||||
|
@ -119,14 +119,14 @@ class P2PPermissionsTests(BitcoinTestFramework):
|
|||
[tx],
|
||||
self.nodes[1],
|
||||
success=False,
|
||||
reject_reason='{} from peer=0 was not accepted: txn-mempool-conflict'.format(txid)
|
||||
reject_reason='{} (wtxid={}) from peer=0 was not accepted: txn-mempool-conflict'.format(txid, tx.getwtxid())
|
||||
)
|
||||
|
||||
p2p_rebroadcast_wallet.send_txs_and_test(
|
||||
[tx],
|
||||
self.nodes[1],
|
||||
success=False,
|
||||
reject_reason='Not relaying non-mempool transaction {} from forcerelay peer=0'.format(txid)
|
||||
reject_reason='Not relaying non-mempool transaction {} (wtxid={}) from forcerelay peer=0'.format(txid, tx.getwtxid())
|
||||
)
|
||||
|
||||
def checkpermission(self, args, expectedPermissions):
|
||||
|
|
Loading…
Add table
Reference in a new issue