Changeset View
Changeset View
Standalone View
Standalone View
src/net_processing.cpp
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
| Show First 20 Lines • Show All 4,120 Lines • ▼ Show 20 Lines | void PeerManagerImpl::ProcessInvalidTx(NodeId nodeid, | ||||
| LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", | LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", | ||||
| txid.ToString(), nodeid, state.ToString()); | txid.ToString(), nodeid, state.ToString()); | ||||
| if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { | if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (m_avalanche && m_avalanche->m_preConsensus && | |||||
| state.GetResult() == TxValidationResult::TX_AVALANCHE_RECONSIDERABLE) { | |||||
| return; | |||||
| } | |||||
| if (state.GetResult() == TxValidationResult::TX_PACKAGE_RECONSIDERABLE) { | if (state.GetResult() == TxValidationResult::TX_PACKAGE_RECONSIDERABLE) { | ||||
| // If the result is TX_PACKAGE_RECONSIDERABLE, add it to | // If the result is TX_PACKAGE_RECONSIDERABLE, add it to | ||||
| // m_recent_rejects_package_reconsiderable because we should not | // m_recent_rejects_package_reconsiderable because we should not | ||||
| // download or submit this transaction by itself again, but may submit | // download or submit this transaction by itself again, but may submit | ||||
| // it as part of a package later. | // it as part of a package later. | ||||
| m_recent_rejects_package_reconsiderable.insert(txid); | m_recent_rejects_package_reconsiderable.insert(txid); | ||||
| } else { | } else { | ||||
| m_recent_rejects.insert(txid); | m_recent_rejects.insert(txid); | ||||
| ▲ Show 20 Lines • Show All 1,672 Lines • ▼ Show 20 Lines | if (msg_type == NetMsgType::TX) { | ||||
| package_result.m_state.ToString()); | package_result.m_state.ToString()); | ||||
| ProcessPackageResult(package_to_validate.value(), | ProcessPackageResult(package_to_validate.value(), | ||||
| package_result); | package_result); | ||||
| } | } | ||||
| } | } | ||||
| if (state.GetResult() == | if (state.GetResult() == | ||||
| TxValidationResult::TX_AVALANCHE_RECONSIDERABLE) { | TxValidationResult::TX_AVALANCHE_RECONSIDERABLE) { | ||||
| // Once added to the conflicting pool, a tx is considered | |||||
| // AlreadyHave, and we shouldn't request it anymore. | |||||
| m_txrequest.ForgetInvId(tx.GetId()); | |||||
| unsigned int nEvicted{0}; | unsigned int nEvicted{0}; | ||||
| bool haveConflictingTx{false}; | |||||
| // NO_THREAD_SAFETY_ANALYSIS because of g_msgproc_mutex required in | // NO_THREAD_SAFETY_ANALYSIS because of g_msgproc_mutex required in | ||||
| // the lambda for m_rng | // the lambda for m_rng | ||||
| m_mempool.withConflicting( | m_mempool.withConflicting( | ||||
| [&](TxConflicting &conflicting) NO_THREAD_SAFETY_ANALYSIS { | [&](TxConflicting &conflicting) NO_THREAD_SAFETY_ANALYSIS { | ||||
| conflicting.AddTx(ptx, pfrom.GetId()); | conflicting.AddTx(ptx, pfrom.GetId()); | ||||
| nEvicted = | nEvicted = | ||||
| conflicting.LimitTxs(m_opts.max_conflicting_txs, m_rng); | conflicting.LimitTxs(m_opts.max_conflicting_txs, m_rng); | ||||
| haveConflictingTx = conflicting.HaveTx(ptx->GetId()); | |||||
| }); | }); | ||||
| if (nEvicted > 0) { | if (nEvicted > 0) { | ||||
| LogPrint(BCLog::TXPACKAGES, | LogPrint(BCLog::TXPACKAGES, | ||||
| "conflicting pool overflow, removed %u tx\n", | "conflicting pool overflow, removed %u tx\n", | ||||
| nEvicted); | nEvicted); | ||||
| } | } | ||||
| if (m_avalanche && m_avalanche->m_preConsensus && | |||||
| haveConflictingTx) { | |||||
| m_avalanche->addToReconcile(ptx); | |||||
| } | |||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| if (msg_type == NetMsgType::CMPCTBLOCK) { | if (msg_type == NetMsgType::CMPCTBLOCK) { | ||||
| // Ignore cmpctblock received while importing | // Ignore cmpctblock received while importing | ||||
| if (m_chainman.m_blockman.LoadingBlocks()) { | if (m_chainman.m_blockman.LoadingBlocks()) { | ||||
| ▲ Show 20 Lines • Show All 867 Lines • ▼ Show 20 Lines | if (msg_type == NetMsgType::AVARESPONSE) { | ||||
| if (auto pitem = std::get_if<const CTransactionRef>(&item)) { | if (auto pitem = std::get_if<const CTransactionRef>(&item)) { | ||||
| const CTransactionRef tx = *pitem; | const CTransactionRef tx = *pitem; | ||||
| assert(tx != nullptr); | assert(tx != nullptr); | ||||
| const TxId &txid = tx->GetId(); | const TxId &txid = tx->GetId(); | ||||
| logVoteUpdate(u, "tx", txid); | logVoteUpdate(u, "tx", txid); | ||||
| switch (u.getStatus()) { | switch (u.getStatus()) { | ||||
| case avalanche::VoteStatus::Rejected: | case avalanche::VoteStatus::Rejected: { | ||||
| break; | |||||
| case avalanche::VoteStatus::Invalid: { | |||||
| // Remove from the mempool and the finalized tree, as | // Remove from the mempool and the finalized tree, as | ||||
roqqit: Is the "and the finalized tree" part true? Shouldn't this never happen? | |||||
| // well as all the children txs. | // well as all the children txs. | ||||
| // FIXME Remember the tx has been invalidated so we | LOCK2(cs_main, m_mempool.cs); | ||||
| // don't poll for it again and again. | if (m_mempool.exists(txid)) { | ||||
| LOCK(m_mempool.cs); | |||||
| auto it = m_mempool.GetIter(txid); | |||||
| if (it.has_value()) { | |||||
| m_mempool.removeRecursive( | m_mempool.removeRecursive( | ||||
| *tx, MemPoolRemovalReason::AVALANCHE); | *tx, MemPoolRemovalReason::AVALANCHE); | ||||
| std::vector<CTransactionRef> conflictingTxs = | |||||
| m_mempool.withConflicting( | |||||
| [&tx](const TxConflicting &conflicting) { | |||||
| return conflicting.GetConflictTxs(tx); | |||||
| }); | |||||
| if (conflictingTxs.size() > 0) { | |||||
| // Pull the first tx only, erase the others so | |||||
| // they can be re-downloaded if needed. | |||||
| auto result = m_chainman.ProcessTransaction( | |||||
| conflictingTxs[0]); | |||||
| assert(result.m_state.IsValid()); | |||||
| } | |||||
| m_mempool.withConflicting( | |||||
| [&conflictingTxs, | |||||
| &tx](TxConflicting &conflicting) { | |||||
| for (const auto &conflictingTx : | |||||
| conflictingTxs) { | |||||
| conflicting.EraseTx( | |||||
| conflictingTx->GetId()); | |||||
| } | |||||
| // Note that we don't store the descendants, | |||||
| // which should be re-downloaded. This could | |||||
| // be optimized but we will have to manage | |||||
| // the topological ordering. | |||||
| conflicting.AddTx(tx, NO_NODE); | |||||
| }); | |||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case avalanche::VoteStatus::Accepted: | case avalanche::VoteStatus::Invalid: { | ||||
| m_mempool.withConflicting( | |||||
| [&txid](TxConflicting &conflicting) { | |||||
| conflicting.EraseTx(txid); | |||||
| }); | |||||
| WITH_LOCK(cs_main, m_recent_rejects.insert(txid)); | |||||
| break; | break; | ||||
| case avalanche::VoteStatus::Finalized: { | } | ||||
| LOCK(m_mempool.cs); | case avalanche::VoteStatus::Finalized: | ||||
| // fallthrough | |||||
| case avalanche::VoteStatus::Accepted: { | |||||
| { | |||||
| LOCK2(cs_main, m_mempool.cs); | |||||
| if (m_mempool.withConflicting( | |||||
| [&txid](const TxConflicting &conflicting) { | |||||
| return conflicting.HaveTx(txid); | |||||
| })) { | |||||
| // Swap conflicting txs from/to the mempool | |||||
| std::vector<CTransactionRef> | |||||
| mempool_conflicting_txs; | |||||
| for (const auto &txin : tx->vin) { | |||||
| // Find the conflicting txs | |||||
| if (CTransactionRef conflict = | |||||
| m_mempool.GetConflictTx( | |||||
| txin.prevout)) { | |||||
| mempool_conflicting_txs.push_back( | |||||
| std::move(conflict)); | |||||
| } | |||||
| } | |||||
| m_mempool.removeConflicts(*tx); | |||||
| auto result = m_chainman.ProcessTransaction(tx); | |||||
| assert(result.m_state.IsValid()); | |||||
| m_mempool.withConflicting( | |||||
| [&txid, &mempool_conflicting_txs]( | |||||
| TxConflicting &conflicting) { | |||||
| conflicting.EraseTx(txid); | |||||
| // Store the first tx only, the others | |||||
| // can be re-downloaded if needed. | |||||
| if (mempool_conflicting_txs.size() > | |||||
| 0) { | |||||
| conflicting.AddTx( | |||||
| mempool_conflicting_txs[0], | |||||
| NO_NODE); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| if (u.getStatus() == avalanche::VoteStatus::Finalized) { | |||||
| LOCK2(cs_main, m_mempool.cs); | |||||
| auto it = m_mempool.GetIter(txid); | auto it = m_mempool.GetIter(txid); | ||||
| if (!it.has_value()) { | if (!it.has_value()) { | ||||
| LogPrint(BCLog::AVALANCHE, | LogPrint( | ||||
| BCLog::AVALANCHE, | |||||
| "Error: finalized tx (%s) is not in the " | "Error: finalized tx (%s) is not in the " | ||||
| "mempool\n", | "mempool\n", | ||||
| txid.ToString()); | txid.ToString()); | ||||
| break; | break; | ||||
| } | } | ||||
| m_mempool.setAvalancheFinalized(**it); | m_mempool.setAvalancheFinalized(**it); | ||||
| // NO_THREAD_SAFETY_ANALYSIS because | |||||
| // m_recent_rejects requires cs_main in the lambda | |||||
| m_mempool.withConflicting( | |||||
| [&](TxConflicting &conflicting) | |||||
| NO_THREAD_SAFETY_ANALYSIS { | |||||
| std::vector<CTransactionRef> | |||||
| conflictingTxs = | |||||
| conflicting.GetConflictTxs(tx); | |||||
| for (const auto &conflictingTx : | |||||
| conflictingTxs) { | |||||
| m_recent_rejects.insert( | |||||
| conflictingTx->GetId()); | |||||
| conflicting.EraseTx( | |||||
| conflictingTx->GetId()); | |||||
| } | |||||
| }); | |||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| case avalanche::VoteStatus::Stale: | case avalanche::VoteStatus::Stale: | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 2,064 Lines • Show Last 20 Lines | |||||
Is the "and the finalized tree" part true? Shouldn't this never happen?