/root/bitcoin/src/node/txreconciliation.cpp
| Line | Count | Source | 
| 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 <common/system.h> | 
| 8 |  | #include <logging.h> | 
| 9 |  | #include <util/check.h> | 
| 10 |  |  | 
| 11 |  | #include <unordered_map> | 
| 12 |  | #include <variant> | 
| 13 |  |  | 
| 14 |  |  | 
| 15 |  | namespace { | 
| 16 |  |  | 
| 17 |  | /** Static salt component used to compute short txids for sketch construction, see BIP-330. */ | 
| 18 |  | const std::string RECON_STATIC_SALT = "Tx Relay Salting"; | 
| 19 |  | const HashWriter RECON_SALT_HASHER = TaggedHash(RECON_STATIC_SALT); | 
| 20 |  |  | 
| 21 |  | /** | 
| 22 |  |  * Salt (specified by BIP-330) constructed from contributions from both peers. It is used | 
| 23 |  |  * to compute transaction short IDs, which are then used to construct a sketch representing a set | 
| 24 |  |  * of transactions we want to announce to the peer. | 
| 25 |  |  */ | 
| 26 |  | uint256 ComputeSalt(uint64_t salt1, uint64_t salt2) | 
| 27 | 0 | { | 
| 28 |  |     // According to BIP-330, salts should be combined in ascending order. | 
| 29 | 0 |     return (HashWriter(RECON_SALT_HASHER) << std::min(salt1, salt2) << std::max(salt1, salt2)).GetSHA256(); | 
| 30 | 0 | } | 
| 31 |  |  | 
| 32 |  | /** | 
| 33 |  |  * Keeps track of txreconciliation-related per-peer state. | 
| 34 |  |  */ | 
| 35 |  | class TxReconciliationState | 
| 36 |  | { | 
| 37 |  | public: | 
| 38 |  |     /** | 
| 39 |  |      * TODO: This field is public to ignore -Wunused-private-field. Make private once used in | 
| 40 |  |      * the following commits. | 
| 41 |  |      * | 
| 42 |  |      * Reconciliation protocol assumes using one role consistently: either a reconciliation | 
| 43 |  |      * initiator (requesting sketches), or responder (sending sketches). This defines our role, | 
| 44 |  |      * based on the direction of the p2p connection. | 
| 45 |  |      * | 
| 46 |  |      */ | 
| 47 |  |     bool m_we_initiate; | 
| 48 |  |  | 
| 49 |  |     /** | 
| 50 |  |      * TODO: These fields are public to ignore -Wunused-private-field. Make private once used in | 
| 51 |  |      * the following commits. | 
| 52 |  |      * | 
| 53 |  |      * These values are used to salt short IDs, which is necessary for transaction reconciliations. | 
| 54 |  |      */ | 
| 55 |  |     uint64_t m_k0, m_k1; | 
| 56 |  |  | 
| 57 | 0 |     TxReconciliationState(bool we_initiate, uint64_t k0, uint64_t k1) : m_we_initiate(we_initiate), m_k0(k0), m_k1(k1) {} | 
| 58 |  | }; | 
| 59 |  |  | 
| 60 |  | } // namespace | 
| 61 |  |  | 
| 62 |  | /** Actual implementation for TxReconciliationTracker's data structure. */ | 
| 63 |  | class TxReconciliationTracker::Impl | 
| 64 |  | { | 
| 65 |  | private: | 
| 66 |  |     mutable Mutex m_txreconciliation_mutex; | 
| 67 |  |  | 
| 68 |  |     // Local protocol version | 
| 69 |  |     uint32_t m_recon_version; | 
| 70 |  |  | 
| 71 |  |     /** | 
| 72 |  |      * Keeps track of txreconciliation states of eligible peers. | 
| 73 |  |      * For pre-registered peers, the locally generated salt is stored. | 
| 74 |  |      * For registered peers, the locally generated salt is forgotten, and the state (including | 
| 75 |  |      * "full" salt) is stored instead. | 
| 76 |  |      */ | 
| 77 |  |     std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex); | 
| 78 |  |  | 
| 79 |  | public: | 
| 80 | 0 |     explicit Impl(uint32_t recon_version) : m_recon_version(recon_version) {} | 
| 81 |  |  | 
| 82 |  |     uint64_t PreRegisterPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex) | 
| 83 | 0 |     { | 
| 84 | 0 |         AssertLockNotHeld(m_txreconciliation_mutex); | 
| 85 | 0 |         LOCK(m_txreconciliation_mutex); | 
| 86 |  | 
 | 
| 87 | 0 |         LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Pre-register peer=%d\n", peer_id); | 
| 88 | 0 |         const uint64_t local_salt{FastRandomContext().rand64()}; | 
| 89 |  |  | 
| 90 |  |         // We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's | 
| 91 |  |         // safe to assume we don't have this record yet. | 
| 92 | 0 |         Assume(m_states.emplace(peer_id, local_salt).second); | 
| 93 | 0 |         return local_salt; | 
| 94 | 0 |     } | 
| 95 |  |  | 
| 96 |  |     ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound, uint32_t peer_recon_version, | 
| 97 |  |                                               uint64_t remote_salt) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex) | 
| 98 | 0 |     { | 
| 99 | 0 |         AssertLockNotHeld(m_txreconciliation_mutex); | 
| 100 | 0 |         LOCK(m_txreconciliation_mutex); | 
| 101 | 0 |         auto recon_state = m_states.find(peer_id); | 
| 102 |  | 
 | 
| 103 | 0 |         if (recon_state == m_states.end()) return ReconciliationRegisterResult::NOT_FOUND; | 
| 104 |  |  | 
| 105 | 0 |         if (std::holds_alternative<TxReconciliationState>(recon_state->second)) { | 
| 106 | 0 |             return ReconciliationRegisterResult::ALREADY_REGISTERED; | 
| 107 | 0 |         } | 
| 108 |  |  | 
| 109 | 0 |         uint64_t local_salt = *std::get_if<uint64_t>(&recon_state->second); | 
| 110 |  |  | 
| 111 |  |         // If the peer supports the version which is lower than ours, we downgrade to the version | 
| 112 |  |         // it supports. For now, this only guarantees that nodes with future reconciliation | 
| 113 |  |         // versions have the choice of reconciling with this current version. However, they also | 
| 114 |  |         // have the choice to refuse supporting reconciliations if the common version is not | 
| 115 |  |         // satisfactory (e.g. too low). | 
| 116 | 0 |         const uint32_t recon_version{std::min(peer_recon_version, m_recon_version)}; | 
| 117 |  |         // v1 is the lowest version, so suggesting something below must be a protocol violation. | 
| 118 | 0 |         if (recon_version < 1) return ReconciliationRegisterResult::PROTOCOL_VIOLATION; | 
| 119 |  |  | 
| 120 | 0 |         LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Register peer=%d (inbound=%i)\n", | 
| 121 | 0 |                       peer_id, is_peer_inbound); | 
| 122 |  | 
 | 
| 123 | 0 |         const uint256 full_salt{ComputeSalt(local_salt, remote_salt)}; | 
| 124 | 0 |         recon_state->second = TxReconciliationState(!is_peer_inbound, full_salt.GetUint64(0), full_salt.GetUint64(1)); | 
| 125 | 0 |         return ReconciliationRegisterResult::SUCCESS; | 
| 126 | 0 |     } | 
| 127 |  |  | 
| 128 |  |     void ForgetPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex) | 
| 129 | 0 |     { | 
| 130 | 0 |         AssertLockNotHeld(m_txreconciliation_mutex); | 
| 131 | 0 |         LOCK(m_txreconciliation_mutex); | 
| 132 | 0 |         if (m_states.erase(peer_id)) { | 
| 133 | 0 |             LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Forget txreconciliation state of peer=%d\n", peer_id); | 
| 134 | 0 |         } | 
| 135 | 0 |     } | 
| 136 |  |  | 
| 137 |  |     bool IsPeerRegistered(NodeId peer_id) const EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex) | 
| 138 | 0 |     { | 
| 139 | 0 |         AssertLockNotHeld(m_txreconciliation_mutex); | 
| 140 | 0 |         LOCK(m_txreconciliation_mutex); | 
| 141 | 0 |         auto recon_state = m_states.find(peer_id); | 
| 142 | 0 |         return (recon_state != m_states.end() && | 
| 143 | 0 |                 std::holds_alternative<TxReconciliationState>(recon_state->second)); | 
| 144 | 0 |     } | 
| 145 |  | }; | 
| 146 |  |  | 
| 147 | 0 | TxReconciliationTracker::TxReconciliationTracker(uint32_t recon_version) : m_impl{std::make_unique<TxReconciliationTracker::Impl>(recon_version)} {} | 
| 148 |  |  | 
| 149 | 0 | TxReconciliationTracker::~TxReconciliationTracker() = default; | 
| 150 |  |  | 
| 151 |  | uint64_t TxReconciliationTracker::PreRegisterPeer(NodeId peer_id) | 
| 152 | 0 | { | 
| 153 | 0 |     return m_impl->PreRegisterPeer(peer_id); | 
| 154 | 0 | } | 
| 155 |  |  | 
| 156 |  | ReconciliationRegisterResult TxReconciliationTracker::RegisterPeer(NodeId peer_id, bool is_peer_inbound, | 
| 157 |  |                                                           uint32_t peer_recon_version, uint64_t remote_salt) | 
| 158 | 0 | { | 
| 159 | 0 |     return m_impl->RegisterPeer(peer_id, is_peer_inbound, peer_recon_version, remote_salt); | 
| 160 | 0 | } | 
| 161 |  |  | 
| 162 |  | void TxReconciliationTracker::ForgetPeer(NodeId peer_id) | 
| 163 | 0 | { | 
| 164 | 0 |     m_impl->ForgetPeer(peer_id); | 
| 165 | 0 | } | 
| 166 |  |  | 
| 167 |  | bool TxReconciliationTracker::IsPeerRegistered(NodeId peer_id) const | 
| 168 | 0 | { | 
| 169 | 0 |     return m_impl->IsPeerRegistered(peer_id); | 
| 170 | 0 | } |