mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-02 19:19:41 +01:00
Remove the transaction from the list of transactions to broadcast after we receive it from the network. Only remove the transaction if it is the same as the one we sent: has the same wtxid (and it follows the same txid). Don't remove transactions that have the same txid and different wtxid. Such transactions show that some of the private broadcast recipients malleated the witness and the transaction made it back to us. The witness could be either: * invalid, in which case the transaction will not be accepted in anybody's pool; or * valid, in which case either the original or the malleated transaction will make it to nodes' mempools and eventually be mined. Our response is to keep broadcasting the original. If the malleated transaction wins then we will eventually stop broadcasting the original when it gets stale and gets removed from the "to broadcast" storage cause it is not acceptable in our mempool.
115 lines
3.3 KiB
C++
115 lines
3.3 KiB
C++
// Copyright (c) 2023-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or https://opensource.org/license/mit/.
|
|
|
|
#include <private_broadcast.h>
|
|
#include <util/check.h>
|
|
|
|
#include <algorithm>
|
|
|
|
bool PrivateBroadcast::Add(const CTransactionRef& tx)
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
const bool inserted{m_transactions.try_emplace(tx).second};
|
|
return inserted;
|
|
}
|
|
|
|
std::optional<size_t> PrivateBroadcast::Remove(const CTransactionRef& tx)
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
const auto handle{m_transactions.extract(tx)};
|
|
if (handle) {
|
|
const auto p{DerivePriority(handle.mapped())};
|
|
return p.num_confirmed;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<CTransactionRef> PrivateBroadcast::PickTxForSend(const NodeId& will_send_to_nodeid)
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
|
|
const auto it{std::ranges::max_element(
|
|
m_transactions,
|
|
[](const auto& a, const auto& b) { return a < b; },
|
|
[](const auto& el) { return DerivePriority(el.second); })};
|
|
|
|
if (it != m_transactions.end()) {
|
|
auto& [tx, sent_to]{*it};
|
|
sent_to.emplace_back(will_send_to_nodeid, NodeClock::now());
|
|
return tx;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<CTransactionRef> PrivateBroadcast::GetTxForNode(const NodeId& nodeid)
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
const auto tx_and_status{GetSendStatusByNode(nodeid)};
|
|
if (tx_and_status.has_value()) {
|
|
return tx_and_status.value().tx;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
void PrivateBroadcast::NodeConfirmedReception(const NodeId& nodeid)
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
const auto tx_and_status{GetSendStatusByNode(nodeid)};
|
|
if (tx_and_status.has_value()) {
|
|
tx_and_status.value().send_status.confirmed = NodeClock::now();
|
|
}
|
|
}
|
|
|
|
bool PrivateBroadcast::DidNodeConfirmReception(const NodeId& nodeid)
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
const auto tx_and_status{GetSendStatusByNode(nodeid)};
|
|
if (tx_and_status.has_value()) {
|
|
return tx_and_status.value().send_status.confirmed.has_value();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PrivateBroadcast::HavePendingTransactions()
|
|
EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
|
{
|
|
LOCK(m_mutex);
|
|
return !m_transactions.empty();
|
|
}
|
|
|
|
PrivateBroadcast::Priority PrivateBroadcast::DerivePriority(const std::vector<SendStatus>& sent_to)
|
|
{
|
|
Priority p;
|
|
p.num_picked = sent_to.size();
|
|
for (const auto& send_status : sent_to) {
|
|
p.last_picked = std::max(p.last_picked, send_status.picked);
|
|
if (send_status.confirmed.has_value()) {
|
|
++p.num_confirmed;
|
|
p.last_confirmed = std::max(p.last_confirmed, send_status.confirmed.value());
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
std::optional<PrivateBroadcast::TxAndSendStatusForNode> PrivateBroadcast::GetSendStatusByNode(const NodeId& nodeid)
|
|
EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
|
|
{
|
|
AssertLockHeld(m_mutex);
|
|
for (auto& [tx, sent_to] : m_transactions) {
|
|
for (auto& send_status : sent_to) {
|
|
if (send_status.nodeid == nodeid) {
|
|
return TxAndSendStatusForNode{.tx = tx, .send_status = send_status};
|
|
}
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|