mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-13 03:09:37 +01:00
p2p: Functions to add/remove wtxids to tx reconciliation sets
They will be used later on.
This commit is contained in:
parent
7e42a50631
commit
b84348a8fc
3 changed files with 158 additions and 1 deletions
|
@ -7,6 +7,7 @@
|
||||||
#include <common/system.h>
|
#include <common/system.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <util/check.h>
|
#include <util/check.h>
|
||||||
|
#include <util/hasher.h>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
@ -48,6 +49,14 @@ public:
|
||||||
*/
|
*/
|
||||||
uint64_t m_k0, m_k1;
|
uint64_t m_k0, m_k1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store all wtxids which we would announce to the peer (policy checks passed, etc.)
|
||||||
|
* in this set instead of announcing them right away. When reconciliation time comes, we will
|
||||||
|
* compute a compressed representation of this set ("sketch") and use it to efficiently
|
||||||
|
* reconcile this set with a set on the peer's side.
|
||||||
|
*/
|
||||||
|
std::unordered_set<Wtxid, SaltedTxidHasher> m_local_set;
|
||||||
|
|
||||||
TxReconciliationState(bool we_initiate, uint64_t k0, uint64_t k1) : m_we_initiate(we_initiate), m_k0(k0), m_k1(k1) {}
|
TxReconciliationState(bool we_initiate, uint64_t k0, uint64_t k1) : m_we_initiate(we_initiate), m_k0(k0), m_k1(k1) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,6 +79,15 @@ private:
|
||||||
*/
|
*/
|
||||||
std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex);
|
std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex);
|
||||||
|
|
||||||
|
TxReconciliationState* GetRegisteredPeerState(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(m_txreconciliation_mutex)
|
||||||
|
{
|
||||||
|
AssertLockHeld(m_txreconciliation_mutex);
|
||||||
|
auto salt_or_state = m_states.find(peer_id);
|
||||||
|
if (salt_or_state == m_states.end()) return nullptr;
|
||||||
|
|
||||||
|
return std::get_if<TxReconciliationState>(&salt_or_state->second);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Impl(uint32_t recon_version) : m_recon_version(recon_version) {}
|
explicit Impl(uint32_t recon_version) : m_recon_version(recon_version) {}
|
||||||
|
|
||||||
|
@ -115,10 +133,60 @@ public:
|
||||||
peer_id, is_peer_inbound);
|
peer_id, is_peer_inbound);
|
||||||
|
|
||||||
const uint256 full_salt{ComputeSalt(local_salt, remote_salt)};
|
const uint256 full_salt{ComputeSalt(local_salt, remote_salt)};
|
||||||
recon_state->second = TxReconciliationState(!is_peer_inbound, full_salt.GetUint64(0), full_salt.GetUint64(1));
|
|
||||||
|
auto new_state = TxReconciliationState(!is_peer_inbound, full_salt.GetUint64(0), full_salt.GetUint64(1));;
|
||||||
|
m_states.erase(recon_state);
|
||||||
|
bool emplaced = m_states.emplace(peer_id, std::move(new_state)).second;
|
||||||
|
Assume(emplaced);
|
||||||
|
|
||||||
return ReconciliationRegisterResult::SUCCESS;
|
return ReconciliationRegisterResult::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AddToSet(NodeId peer_id, const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
|
||||||
|
{
|
||||||
|
AssertLockNotHeld(m_txreconciliation_mutex);
|
||||||
|
LOCK(m_txreconciliation_mutex);
|
||||||
|
auto peer_state = GetRegisteredPeerState(peer_id);
|
||||||
|
if (!peer_state) return false;
|
||||||
|
|
||||||
|
// Transactions which don't make it to the set due to the limit are announced via fan-out.
|
||||||
|
if (peer_state->m_local_set.size() >= MAX_RECONSET_SIZE) {
|
||||||
|
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Reconciliation set maximum size reached for peer=%d.\n", peer_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The caller currently keeps track of the per-peer transaction announcements, so it
|
||||||
|
// should not attempt to add same tx to the set twice. However, if that happens, we will
|
||||||
|
// simply ignore it.
|
||||||
|
if (peer_state->m_local_set.insert(wtxid).second) {
|
||||||
|
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Added %s to the reconciliation set for peer=%d. "
|
||||||
|
"Now the set contains %i transactions.\n",
|
||||||
|
wtxid.ToString(), peer_id, peer_state->m_local_set.size());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryRemovingFromSet(NodeId peer_id, const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
|
||||||
|
{
|
||||||
|
AssertLockNotHeld(m_txreconciliation_mutex);
|
||||||
|
LOCK(m_txreconciliation_mutex);
|
||||||
|
auto peer_state = GetRegisteredPeerState(peer_id);
|
||||||
|
if (!peer_state) return false;
|
||||||
|
|
||||||
|
auto removed = peer_state->m_local_set.erase(wtxid) > 0;
|
||||||
|
if (removed) {
|
||||||
|
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Removed %s from the reconciliation set for peer=%d. "
|
||||||
|
"Now the set contains %i transactions.\n",
|
||||||
|
wtxid.ToString(), peer_id, peer_state->m_local_set.size());
|
||||||
|
} else {
|
||||||
|
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Couldn't remove %s from the reconciliation set for peer=%d. "
|
||||||
|
"Transaction not found\n",
|
||||||
|
wtxid.ToString(), peer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
void ForgetPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
|
void ForgetPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(m_txreconciliation_mutex);
|
AssertLockNotHeld(m_txreconciliation_mutex);
|
||||||
|
@ -128,6 +196,9 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For calls within this class use GetRegisteredPeerState instead.
|
||||||
|
*/
|
||||||
bool IsPeerRegistered(NodeId peer_id) const EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
|
bool IsPeerRegistered(NodeId peer_id) const EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(m_txreconciliation_mutex);
|
AssertLockNotHeld(m_txreconciliation_mutex);
|
||||||
|
@ -153,6 +224,16 @@ ReconciliationRegisterResult TxReconciliationTracker::RegisterPeer(NodeId peer_i
|
||||||
return m_impl->RegisterPeer(peer_id, is_peer_inbound, peer_recon_version, remote_salt);
|
return m_impl->RegisterPeer(peer_id, is_peer_inbound, peer_recon_version, remote_salt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TxReconciliationTracker::AddToSet(NodeId peer_id, const Wtxid& wtxid)
|
||||||
|
{
|
||||||
|
return m_impl->AddToSet(peer_id, wtxid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TxReconciliationTracker::TryRemovingFromSet(NodeId peer_id, const Wtxid& wtxid)
|
||||||
|
{
|
||||||
|
return m_impl->TryRemovingFromSet(peer_id, wtxid);
|
||||||
|
}
|
||||||
|
|
||||||
void TxReconciliationTracker::ForgetPeer(NodeId peer_id)
|
void TxReconciliationTracker::ForgetPeer(NodeId peer_id)
|
||||||
{
|
{
|
||||||
m_impl->ForgetPeer(peer_id);
|
m_impl->ForgetPeer(peer_id);
|
||||||
|
|
|
@ -14,6 +14,12 @@
|
||||||
/** Supported transaction reconciliation protocol version */
|
/** Supported transaction reconciliation protocol version */
|
||||||
static constexpr uint32_t TXRECONCILIATION_VERSION{1};
|
static constexpr uint32_t TXRECONCILIATION_VERSION{1};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of wtxids stored in a peer local set, bounded to protect the memory use of
|
||||||
|
* reconciliation sets and short ids mappings, and CPU used for sketch computation.
|
||||||
|
*/
|
||||||
|
constexpr size_t MAX_RECONSET_SIZE = 3000;
|
||||||
|
|
||||||
enum class ReconciliationRegisterResult {
|
enum class ReconciliationRegisterResult {
|
||||||
NOT_FOUND,
|
NOT_FOUND,
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
|
@ -74,6 +80,20 @@ public:
|
||||||
ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound,
|
ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound,
|
||||||
uint32_t peer_recon_version, uint64_t remote_salt);
|
uint32_t peer_recon_version, uint64_t remote_salt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step 1. Add a new transaction we want to announce to the peer to the local reconciliation set
|
||||||
|
* of the peer, so that it will be reconciled later, unless the set limit is reached.
|
||||||
|
* Returns whether the transaction appears in the set.
|
||||||
|
*/
|
||||||
|
bool AddToSet(NodeId peer_id, const Wtxid& wtxid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Before Step 2, we might want to remove a wtxid from the reconciliation set, for example if
|
||||||
|
* the peer just announced the transaction to us.
|
||||||
|
* Returns whether the wtxid was removed.
|
||||||
|
*/
|
||||||
|
bool TryRemovingFromSet(NodeId peer_id, const Wtxid& wtxid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to forget txreconciliation-related state of the peer (if we previously stored any).
|
* Attempts to forget txreconciliation-related state of the peer (if we previously stored any).
|
||||||
* After this, we won't be able to reconcile transactions with the peer.
|
* After this, we won't be able to reconcile transactions with the peer.
|
||||||
|
|
|
@ -81,4 +81,60 @@ BOOST_AUTO_TEST_CASE(IsPeerRegisteredTest)
|
||||||
BOOST_CHECK(!tracker.IsPeerRegistered(peer_id0));
|
BOOST_CHECK(!tracker.IsPeerRegistered(peer_id0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(AddToSetTest)
|
||||||
|
{
|
||||||
|
TxReconciliationTracker tracker(TXRECONCILIATION_VERSION);
|
||||||
|
NodeId peer_id0 = 0;
|
||||||
|
FastRandomContext frc{/*fDeterministic=*/true};
|
||||||
|
|
||||||
|
Wtxid wtxid{Wtxid::FromUint256(frc.rand256())};
|
||||||
|
|
||||||
|
BOOST_REQUIRE(!tracker.IsPeerRegistered(peer_id0));
|
||||||
|
BOOST_REQUIRE(!tracker.AddToSet(peer_id0, wtxid));
|
||||||
|
|
||||||
|
tracker.PreRegisterPeer(peer_id0);
|
||||||
|
BOOST_REQUIRE_EQUAL(tracker.RegisterPeer(peer_id0, true, 1, 1), ReconciliationRegisterResult::SUCCESS);
|
||||||
|
BOOST_CHECK(tracker.IsPeerRegistered(peer_id0));
|
||||||
|
|
||||||
|
BOOST_REQUIRE(tracker.AddToSet(peer_id0, wtxid));
|
||||||
|
|
||||||
|
tracker.ForgetPeer(peer_id0);
|
||||||
|
Wtxid wtxid2{Wtxid::FromUint256(frc.rand256())};
|
||||||
|
BOOST_REQUIRE(!tracker.AddToSet(peer_id0, wtxid2));
|
||||||
|
|
||||||
|
NodeId peer_id1 = 1;
|
||||||
|
tracker.PreRegisterPeer(peer_id1);
|
||||||
|
BOOST_REQUIRE_EQUAL(tracker.RegisterPeer(peer_id1, true, 1, 1), ReconciliationRegisterResult::SUCCESS);
|
||||||
|
BOOST_CHECK(tracker.IsPeerRegistered(peer_id1));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MAX_RECONSET_SIZE; ++i)
|
||||||
|
BOOST_REQUIRE(tracker.AddToSet(peer_id1, Wtxid::FromUint256(frc.rand256())));
|
||||||
|
BOOST_REQUIRE(!tracker.AddToSet(peer_id1, Wtxid::FromUint256(frc.rand256())));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(TryRemovingFromSetTest)
|
||||||
|
{
|
||||||
|
TxReconciliationTracker tracker(TXRECONCILIATION_VERSION);
|
||||||
|
NodeId peer_id0 = 0;
|
||||||
|
FastRandomContext frc{/*fDeterministic=*/true};
|
||||||
|
|
||||||
|
Wtxid wtxid{Wtxid::FromUint256(frc.rand256())};
|
||||||
|
|
||||||
|
BOOST_REQUIRE(!tracker.IsPeerRegistered(peer_id0));
|
||||||
|
BOOST_REQUIRE(!tracker.TryRemovingFromSet(peer_id0, wtxid));
|
||||||
|
|
||||||
|
tracker.PreRegisterPeer(peer_id0);
|
||||||
|
BOOST_REQUIRE_EQUAL(tracker.RegisterPeer(peer_id0, true, 1, 1), ReconciliationRegisterResult::SUCCESS);
|
||||||
|
BOOST_CHECK(tracker.IsPeerRegistered(peer_id0));
|
||||||
|
|
||||||
|
BOOST_REQUIRE(!tracker.TryRemovingFromSet(peer_id0, wtxid));
|
||||||
|
BOOST_REQUIRE(tracker.AddToSet(peer_id0, wtxid));
|
||||||
|
BOOST_REQUIRE(tracker.TryRemovingFromSet(peer_id0, wtxid));
|
||||||
|
BOOST_REQUIRE(!tracker.TryRemovingFromSet(peer_id0, wtxid));
|
||||||
|
|
||||||
|
BOOST_REQUIRE(tracker.AddToSet(peer_id0, wtxid));
|
||||||
|
tracker.ForgetPeer(peer_id0);
|
||||||
|
BOOST_REQUIRE(!tracker.TryRemovingFromSet(peer_id0, wtxid));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Add table
Reference in a new issue