Skip to content

Commit 3fcf78e

Browse files
committed
p2p: Announce reconciliation support
If we're connecting to the peer which might support transaction reconciliation, we announce we want to reconcile with them. We store the reconciliation salt so that when the peer responds with their salt, we are able to compute the full reconciliation salt. This behavior is enabled with a CLI flag.
1 parent 24e36fa commit 3fcf78e

File tree

8 files changed

+173
-2
lines changed

8 files changed

+173
-2
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ BITCOIN_CORE_H = \
209209
node/minisketchwrapper.h \
210210
node/psbt.h \
211211
node/transaction.h \
212+
node/txreconciliation.h \
212213
node/utxo_snapshot.h \
213214
node/validation_cache_args.h \
214215
noui.h \
@@ -396,6 +397,7 @@ libbitcoin_node_a_SOURCES = \
396397
node/minisketchwrapper.cpp \
397398
node/psbt.cpp \
398399
node/transaction.cpp \
400+
node/txreconciliation.cpp \
399401
node/utxo_snapshot.cpp \
400402
node/validation_cache_args.cpp \
401403
noui.cpp \

src/init.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <node/mempool_args.h>
4646
#include <node/mempool_persist_args.h>
4747
#include <node/miner.h>
48+
#include <node/txreconciliation.h>
4849
#include <node/validation_cache_args.h>
4950
#include <policy/feerate.h>
5051
#include <policy/fees.h>
@@ -484,6 +485,7 @@ void SetupServerArgs(ArgsManager& argsman)
484485
argsman.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
485486
argsman.AddArg("-networkactive", "Enable all P2P network activity (default: 1). Can be changed by the setnetworkactive RPC command", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
486487
argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
488+
argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
487489
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
488490
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
489491
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);

src/net_processing.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <netbase.h>
2121
#include <netmessagemaker.h>
2222
#include <node/blockstorage.h>
23+
#include <node/txreconciliation.h>
2324
#include <policy/fees.h>
2425
#include <policy/policy.h>
2526
#include <policy/settings.h>
@@ -703,6 +704,7 @@ class PeerManagerImpl final : public PeerManager
703704
ChainstateManager& m_chainman;
704705
CTxMemPool& m_mempool;
705706
TxRequestTracker m_txrequest GUARDED_BY(::cs_main);
707+
std::unique_ptr<TxReconciliationTracker> m_txreconciliation;
706708

707709
/** The height of the best chain */
708710
std::atomic<int> m_best_height{-1};
@@ -1776,6 +1778,11 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman,
17761778
m_mempool(pool),
17771779
m_ignore_incoming_txs(ignore_incoming_txs)
17781780
{
1781+
// While Erlay support is incomplete, it must be enabled explicitly via -txreconciliation.
1782+
// This argument can go away after Erlay support is complete.
1783+
if (gArgs.GetBoolArg("-txreconciliation", DEFAULT_TXRECONCILIATION_ENABLE)) {
1784+
m_txreconciliation = std::make_unique<TxReconciliationTracker>();
1785+
}
17791786
}
17801787

17811788
void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
@@ -3236,8 +3243,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
32363243
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDADDRV2));
32373244
}
32383245

3239-
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
3240-
32413246
pfrom.m_has_all_wanted_services = HasAllDesirableServiceFlags(nServices);
32423247
peer->m_their_services = nServices;
32433248
pfrom.SetAddrLocal(addrMe);
@@ -3262,6 +3267,25 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
32623267
if (fRelay) pfrom.m_relays_txs = true;
32633268
}
32643269

3270+
if (greatest_common_version >= WTXID_RELAY_VERSION && m_txreconciliation) {
3271+
// Per BIP-330, we announce txreconciliation support if:
3272+
// - protocol version per the VERSION message supports WTXID_RELAY;
3273+
// - we intended to exchange transactions over this connection while establishing it
3274+
// and the peer indicated support for transaction relay in the VERSION message;
3275+
// - we are not in -blocksonly mode.
3276+
if (pfrom.m_relays_txs && !m_ignore_incoming_txs) {
3277+
const uint64_t recon_salt = m_txreconciliation->PreRegisterPeer(pfrom.GetId());
3278+
// We suggest our txreconciliation role (initiator/responder) based on
3279+
// the connection direction.
3280+
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::SENDTXRCNCL,
3281+
!pfrom.IsInboundConn(),
3282+
pfrom.IsInboundConn(),
3283+
TXRECONCILIATION_VERSION, recon_salt));
3284+
}
3285+
}
3286+
3287+
m_connman.PushMessage(&pfrom, msg_maker.Make(NetMsgType::VERACK));
3288+
32653289
// Potentially mark this peer as a preferred download peer.
32663290
{
32673291
LOCK(cs_main);

src/node/txreconciliation.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <node/txreconciliation.h>
6+
7+
#include <util/check.h>
8+
#include <util/system.h>
9+
10+
#include <unordered_map>
11+
#include <variant>
12+
13+
14+
namespace {
15+
16+
/**
17+
* Keeps track of txreconciliation-related per-peer state.
18+
*/
19+
class TxReconciliationState
20+
{
21+
};
22+
23+
} // namespace
24+
25+
/** Actual implementation for TxReconciliationTracker's data structure. */
26+
class TxReconciliationTracker::Impl
27+
{
28+
private:
29+
mutable Mutex m_txreconciliation_mutex;
30+
31+
/**
32+
* Keeps track of txreconciliation states of eligible peers.
33+
* For pre-registered peers, the locally generated salt is stored.
34+
* For registered peers, the locally generated salt is forgotten, and the state (including
35+
* "full" salt) is stored instead.
36+
*/
37+
std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex);
38+
39+
public:
40+
explicit Impl() {}
41+
42+
uint64_t PreRegisterPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
43+
{
44+
AssertLockNotHeld(m_txreconciliation_mutex);
45+
LOCK(m_txreconciliation_mutex);
46+
// We do not support txreconciliation salt/version updates.
47+
assert(m_states.find(peer_id) == m_states.end());
48+
49+
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Pre-register peer=%d\n", peer_id);
50+
const uint64_t local_salt{GetRand(UINT64_MAX)};
51+
52+
// We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's
53+
// safe to assume we don't have this record yet.
54+
Assert(m_states.emplace(peer_id, local_salt).second);
55+
return local_salt;
56+
}
57+
};
58+
59+
TxReconciliationTracker::TxReconciliationTracker() : m_impl{std::make_unique<TxReconciliationTracker::Impl>()} {}
60+
61+
TxReconciliationTracker::~TxReconciliationTracker() = default;
62+
63+
uint64_t TxReconciliationTracker::PreRegisterPeer(NodeId peer_id)
64+
{
65+
return m_impl->PreRegisterPeer(peer_id);
66+
}

src/node/txreconciliation.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) 2022 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_NODE_TXRECONCILIATION_H
6+
#define BITCOIN_NODE_TXRECONCILIATION_H
7+
8+
#include <net.h>
9+
#include <sync.h>
10+
11+
#include <memory>
12+
#include <tuple>
13+
14+
/** Whether transaction reconciliation protocol should be enabled by default. */
15+
static constexpr bool DEFAULT_TXRECONCILIATION_ENABLE{false};
16+
/** Supported transaction reconciliation protocol version */
17+
static constexpr uint32_t TXRECONCILIATION_VERSION{1};
18+
19+
/**
20+
* Transaction reconciliation is a way for nodes to efficiently announce transactions.
21+
* This object keeps track of all txreconciliation-related communications with the peers.
22+
* The high-level protocol is:
23+
* 0. Txreconciliation protocol handshake.
24+
* 1. Once we receive a new transaction, add it to the set instead of announcing immediately.
25+
* 2. At regular intervals, a txreconciliation initiator requests a sketch from a peer, where a
26+
* sketch is a compressed representation of short form IDs of the transactions in their set.
27+
* 3. Once the initiator received a sketch from the peer, the initiator computes a local sketch,
28+
* and combines the two sketches to attempt finding the difference in *sets*.
29+
* 4a. If the difference was not larger than estimated, see SUCCESS below.
30+
* 4b. If the difference was larger than estimated, initial txreconciliation fails. The initiator
31+
* requests a larger sketch via an extension round (allowed only once).
32+
* - If extension succeeds (a larger sketch is sufficient), see SUCCESS below.
33+
* - If extension fails (a larger sketch is insufficient), see FAILURE below.
34+
*
35+
* SUCCESS. The initiator knows full symmetrical difference and can request what the initiator is
36+
* missing and announce to the peer what the peer is missing.
37+
*
38+
* FAILURE. The initiator notifies the peer about the failure and announces all transactions from
39+
* the corresponding set. Once the peer received the failure notification, the peer
40+
* announces all transactions from their set.
41+
42+
* This is a modification of the Erlay protocol (https://arxiv.org/abs/1905.10518) with two
43+
* changes (sketch extensions instead of bisections, and an extra INV exchange round), both
44+
* are motivated in BIP-330.
45+
*/
46+
class TxReconciliationTracker
47+
{
48+
private:
49+
class Impl;
50+
const std::unique_ptr<Impl> m_impl;
51+
52+
public:
53+
explicit TxReconciliationTracker();
54+
~TxReconciliationTracker();
55+
56+
/**
57+
* Step 0. Generates initial part of the state (salt) required to reconcile txs with the peer.
58+
* The salt is used for short ID computation required for txreconciliation.
59+
* The function returns the salt.
60+
* A peer can't participate in future txreconciliations without this call.
61+
* This function must be called only once per peer.
62+
*/
63+
uint64_t PreRegisterPeer(NodeId peer_id);
64+
};
65+
66+
#endif // BITCOIN_NODE_TXRECONCILIATION_H

src/protocol.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const char *CFHEADERS="cfheaders";
4444
const char *GETCFCHECKPT="getcfcheckpt";
4545
const char *CFCHECKPT="cfcheckpt";
4646
const char *WTXIDRELAY="wtxidrelay";
47+
const char *SENDTXRCNCL="sendtxrcncl";
4748
} // namespace NetMsgType
4849

4950
/** All known message types. Keep this in the same order as the list of
@@ -84,6 +85,7 @@ const static std::string allNetMessageTypes[] = {
8485
NetMsgType::GETCFCHECKPT,
8586
NetMsgType::CFCHECKPT,
8687
NetMsgType::WTXIDRELAY,
88+
NetMsgType::SENDTXRCNCL,
8789
};
8890
const static std::vector<std::string> allNetMessageTypesVec(std::begin(allNetMessageTypes), std::end(allNetMessageTypes));
8991

src/protocol.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,14 @@ extern const char* CFCHECKPT;
258258
* @since protocol version 70016 as described by BIP 339.
259259
*/
260260
extern const char* WTXIDRELAY;
261+
/**
262+
* Contains 2 1-byte bools, a 4-byte version number and an 8-byte salt.
263+
* The 2 booleans indicate that a node is willing to participate in transaction
264+
* reconciliation, respectively as an initiator or as a receiver.
265+
* The salt is used to compute short txids needed for efficient
266+
* txreconciliation, as described by BIP 330.
267+
*/
268+
extern const char* SENDTXRCNCL;
261269
}; // namespace NetMsgType
262270

263271
/* Get a vector of all valid message types (see above) */

src/test/fuzz/process_message.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ FUZZ_TARGET_MSG(pong);
130130
FUZZ_TARGET_MSG(sendaddrv2);
131131
FUZZ_TARGET_MSG(sendcmpct);
132132
FUZZ_TARGET_MSG(sendheaders);
133+
FUZZ_TARGET_MSG(sendtxrcncl);
133134
FUZZ_TARGET_MSG(tx);
134135
FUZZ_TARGET_MSG(verack);
135136
FUZZ_TARGET_MSG(version);

0 commit comments

Comments
 (0)