Transaction relay privacy bugfix #14220

pull sdaftuar wants to merge 3 commits into bitcoin:master from sdaftuar:2018-09-txrelay changing 5 files +127 −5
  1. sdaftuar commented at 1:39 pm on September 14, 2018: member

    This PR does two one things:

    • Fixes a privacy issue where if we relay a transaction to peer A (but not peer B), peer B can request the transaction and we’ll provide it. Instead, we should wait until we announce to peer B before providing the transaction.

    * Respond to getdata requests for transactions that are ancestors of newly-announced transactions. Currently, our software will request the parents of an orphan transaction from the peer who announced the orphan, but if our peer is running a recent Bitcoin Core version, the request would typically be dropped. So this should improve propagation for transaction chains, particularly for nodes that are just starting up.

    This PR also includes a test that transaction chains relay to peers that weren’t connected when a parent was broadcast.

  2. fanquake added the label P2P on Sep 14, 2018
  3. sdaftuar force-pushed on Sep 14, 2018
  4. DrahtBot commented at 12:46 pm on September 15, 2018: member
  5. gmaxwell commented at 7:15 am on September 16, 2018: contributor
    concept ACK
  6. in test/functional/p2p_txchain_relay.py:37 in ba6c0a1686 outdated
    32+        self.sync_all()
    33+        self.nodes[1].generate(100)
    34+        self.sync_all()
    35+
    36+        # Node0 should now have 1 utxo.
    37+        assert self.nodes[0].getbalance() > 10 # Should be 50, but doesn't matter
    


    conscott commented at 2:36 pm on September 16, 2018:

    nit: Comment doesn’t seem to match the assert. Could just add…

    0assert_equal(len(self.nodes[0].listunspent()), 1)
    
  7. conscott commented at 3:17 pm on September 16, 2018: contributor
    Tested ACK b723d95fa08c8058f4ab32cdd5329b4df3aac036
  8. in src/net_processing.cpp:160 in 68657b8ae3 outdated
    153@@ -153,7 +154,12 @@ namespace {
    154     std::atomic<int64_t> g_last_tip_update(0);
    155 
    156     /** Relay map */
    157-    typedef std::map<uint256, CTransactionRef> MapRelay;
    158+    struct RelayEntry {
    159+        RelayEntry(CTransactionRef &&tx) : m_txref(tx) {}
    160+        CTransactionRef m_txref;
    161+        std::unordered_set<NodeId> m_node_map;
    


    promag commented at 8:50 pm on September 16, 2018:

    Commit “Don’t relay tx data to peers until after tx announcement”

    Doesn’t seem quite obvious that std::set is worst, why did you picked std::unordered_set?


    promag commented at 8:55 pm on September 16, 2018:

    Commit “Don’t relay tx data to peers until after tx announcement”

    nit, s/_map/_set?


    sdaftuar commented at 7:56 pm on September 24, 2018:
    Constant time lookup seemed better to me than log_n lookup but I dunno, is there a reason you think std::set is better here? I suspect it doesn’t really matter.

    promag commented at 8:25 pm on September 26, 2018:
    I dunno too. Considering the size is small I agree it doesn’t matter.
  9. in src/net_processing.cpp:158 in 68657b8ae3 outdated
    153@@ -153,7 +154,12 @@ namespace {
    154     std::atomic<int64_t> g_last_tip_update(0);
    155 
    156     /** Relay map */
    157-    typedef std::map<uint256, CTransactionRef> MapRelay;
    158+    struct RelayEntry {
    159+        RelayEntry(CTransactionRef &&tx) : m_txref(tx) {}
    


    promag commented at 8:54 pm on September 16, 2018:

    Commit “Don’t relay tx data to peers until after tx announcement”

    From developer notes:

    0- By default, declare single-argument constructors `explicit`.
    1
    2  - *Rationale*: This is a precaution to avoid unintended conversions that might
    3    arise when single-argument constructors are used as implicit conversion
    4    functions.
    
  10. in src/net_processing.cpp:3619 in 68657b8ae3 outdated
    3615@@ -3610,7 +3616,10 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
    3616                             vRelayExpiration.pop_front();
    3617                         }
    3618 
    3619-                        auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx)));
    3620+                        auto ret = mapRelay.insert(std::make_pair(hash, RelayEntry(std::move(txinfo.tx))));
    


    promag commented at 8:55 pm on September 16, 2018:

    Commit “Don’t relay tx data to peers until after tx announcement”

    nit, could use emplace(k,v) since you are touching this line.

    This is moved and updated in the next commit.

  11. in src/net_processing.cpp:170 in 3c227a6bf9 outdated
    163@@ -164,6 +164,18 @@ namespace {
    164     /** Expiration-time ordered list of (expire time, relay map entry) pairs. */
    165     std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration GUARDED_BY(cs_main);
    166 
    167+    void AddToMapRelay(CTransactionRef &&txref, NodeId peer, int64_t time_now) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
    168+    {
    169+        const uint256& hash = txref->GetHash();
    170+        auto ret = mapRelay.emplace(std::make_pair(hash, RelayEntry(std::move(txref))));
    


    promag commented at 8:57 pm on September 16, 2018:

    Commit “Add ancestors of announced transactions to mapRelay”

    nit, could drop std::make_pair.

  12. MarcoFalke commented at 8:40 pm on September 18, 2018: member
    utACK. I wrote a test in #14240 that should only pass with your first commit.
  13. in src/net_processing.cpp:158 in b723d95fa0 outdated
    153@@ -153,11 +154,28 @@ namespace {
    154     std::atomic<int64_t> g_last_tip_update(0);
    155 
    156     /** Relay map */
    157-    typedef std::map<uint256, CTransactionRef> MapRelay;
    158+    struct RelayEntry {
    159+        RelayEntry(CTransactionRef &&tx) : m_txref(tx) {}
    


    practicalswift commented at 7:04 pm on September 22, 2018:
    02018-09-22 21:02:10 cpplint(pr=14220): src/net_processing.cpp:158:  Single-parameter constructors should be marked explicit.  [runtime/explicit] [5]
    
  14. MarcoFalke commented at 7:29 pm on September 24, 2018: member
    @sdaftuar Do you feel like splitting this up? I believe one of the fixes is a bugfix, the other a feature.
  15. sdaftuar force-pushed on Sep 25, 2018
  16. sdaftuar commented at 12:06 pm on September 25, 2018: member

    @MarcoFalke This PR is now just the bugfix along with the test you wrote (thanks!), I’ll separately PR the new feature.

    I believe I’ve also incorporated all the PR comments that are relevant for the bugfix commit.

  17. sdaftuar renamed this:
    Transaction relay improvements
    Transaction relay privacy bugfix
    on Sep 25, 2018
  18. MarcoFalke commented at 5:51 pm on September 25, 2018: member
    Tested ACK 736462c26de7a267bdf75c1f86bf12932c604b3d (I wrote the test, that fails without this patch)
  19. MarcoFalke commented at 5:56 pm on September 25, 2018: member
    Is this for backport?
  20. sdaftuar commented at 6:07 pm on September 25, 2018: member

    I don’t think we need to backport this (in particular I wouldn’t want to interfere with the 0.17.0 release), as the bug has been present for so long, there’s no urgency. And I tend to think that changes like this to the p2p behavior are better to sit in master for a while before appearing in a release anyway.

    Any idea what is going on with the appveyor test run?

  21. sdaftuar commented at 6:42 pm on September 25, 2018: member

    I guess it’s worth pointing out as well, that this change without the improvement in #14318 may well worsen relay of transactions that are part of chains a bit. For instance, if a peer of ours asks for the parent of a transaction we announced, then as long as that parent is in mapRelay – ie it was recently announced – then we’d provide it, even if that peer wasn’t connected to us at the time the parent transaction was announced.

    I have no idea how common that sort of thing is, but rather than spend too much time wondering about it, I’d say it’s better to not backport this change (and arguably I shouldn’t have unbundled this PR from #14318).

  22. jamesob commented at 7:13 pm on September 26, 2018: member

    utACK https://github.com/bitcoin/bitcoin/pull/14220/commits/736462c26de7a267bdf75c1f86bf12932c604b3d

    In plain English, I’d characterize this change as “don’t respond to a GETDATA(txn) for a peer who you haven’t previously sent INV(txn).”

  23. in test/functional/p2p_leak_tx.py:50 in 736462c26d outdated
    45+        # 0.285714
    46+        #
    47+        # Where,
    48+        # * f_in is the pdf of the exponential distribution for inbound peers,
    49+        #   with lambda_in = 1 / INVENTORY_BROADCAST_INTERVAL = 1/5
    50+        # * F_out is the cdf of the expon. distribuiton for outbound peers,
    


    jamesob commented at 7:16 pm on September 26, 2018:
    nit: distribuiton (spelling)
  24. in test/functional/p2p_leak_tx.py:89 in 736462c26d outdated
    80+                measured_leak += 1
    81+
    82+        measured_leak /= REPEATS
    83+        self.log.info('Measured leak of {}'.format(measured_leak))
    84+
    85+        assert_greater_than(EXPECTED_MEASURED_LEAK, measured_leak)
    


    jamesob commented at 7:16 pm on September 26, 2018:
    This test, being stochastic, looks like it’ll fail spuriously. I guess it if happens too often, we can up REPEATS or something.

    MarcoFalke commented at 7:30 pm on September 26, 2018:

    Someone could run it for 1000 times and see if it fails at all

    0./test/functional/test_runner.py -j 15 $(for i in {0..999}; do echo p2p_leak_tx ; done)
    

    sdaftuar commented at 5:36 pm on September 27, 2018:
    Looks like it fails about 9% of the time for me. :(

    MarcoFalke commented at 3:28 am on September 28, 2018:

    Sorry for that. I haven’t actually run the test substantially after writing. I seems you could set EXPECTED_MEASURED_LEAK = .42 or so without making the test useless. (On master the leak is always 1.00, so setting the expected measured leak to any number less than 1 should be fine)

    figure_1 (measured leak on x-axis)


    sdaftuar commented at 9:04 pm on September 28, 2018:
    I bumped it up to 0.45, and it failed once in 1000 runs, so i bumped it up to 0.50 and ran it 5000 times with no failures. I have not really looked at the test or analyzed the statistics at all to verify that this makes sense.
  25. DrahtBot added the label Needs rebase on Sep 27, 2018
  26. sdaftuar force-pushed on Sep 27, 2018
  27. sdaftuar commented at 6:04 pm on September 27, 2018: member
    Rebased, but I think we need to adjust the test so that it has a drastically lower error rate, or drop it from this PR. @MarcoFalke thoughts?
  28. DrahtBot removed the label Needs rebase on Sep 27, 2018
  29. in test/functional/test_framework/messages.py:1255 in 47bf6e9622 outdated
    1251+
    1252+    def __repr__(self):
    1253+        return "msg_notfound(vec=%s)" % (repr(self.vec))
    1254+
    1255+
    1256+class msg_sendheaders():
    


    MarcoFalke commented at 1:09 am on September 28, 2018:
    nit: unrelated change?

    sdaftuar commented at 9:04 pm on September 28, 2018:
    fixed
  30. MarcoFalke approved
  31. MarcoFalke commented at 3:30 am on September 28, 2018: member
    re-ACK 47bf6e9622
  32. DrahtBot commented at 10:49 am on September 28, 2018: member
    Coverage Change (pull 14220) Reference (master)
    Lines +0.0094 % 87.0361 %
    Functions -0.0130 % 84.1130 %
    Branches +0.0047 % 51.5451 %
  33. sdaftuar force-pushed on Sep 28, 2018
  34. MarcoFalke commented at 1:26 am on October 1, 2018: member
    re-ACK 320a85e3427685d4bed9d585c3a3c45e288d5a30
  35. jamesob approved
  36. DrahtBot added the label Needs rebase on Oct 8, 2018
  37. in test/functional/p2p_leak_tx.py:89 in 320a85e342 outdated
    84+                measured_leak += 1
    85+
    86+        measured_leak /= REPEATS
    87+        self.log.info('Measured leak of {}'.format(measured_leak))
    88+
    89+        assert_greater_than(EXPECTED_MEASURED_LEAK, measured_leak)
    


    jnewbery commented at 4:53 am on October 8, 2018:

    I don’t understand what this test is supposed to be testing. Is it:

    (i) that we don’t leak txs to inbound peers that we haven’t yet announced to (as stated in the docstring); or (ii) that txs are probabilistically announced to outbound peers before inbound peers.

    If (i), can’t we just test that for every tx, either we receive a NOTFOUND message for the tx or we’ve already received an INV message for the tx?

    The assert_greater_than() call here seems to be simply testing (ii). Or maybe I’m misunderstanding?


    MarcoFalke commented at 8:23 am on October 8, 2018:

    Makes sense! That makes the test easier to understand as well:

    (to be applied on current HEAD commit of this branch)

     0diff --git a/test/functional/p2p_leak_tx.py b/test/functional/p2p_leak_tx.py
     1index 21d1d4ed2d..a0ceaedd67 100755
     2--- a/test/functional/p2p_leak_tx.py
     3+++ b/test/functional/p2p_leak_tx.py
     4@@ -13,10 +13,17 @@ from test_framework.util import (
     5     wait_until,
     6 )
     7 
     8+import time
     9+
    10+
    11+class P2PNode(P2PDataStore):
    12+    def on_inv(self, msg):
    13+        pass
    14+
    15 
    16 class P2PLeakTxTest(BitcoinTestFramework):
    17     def set_test_params(self):
    18-        self.num_nodes = 2
    19+        self.num_nodes = 1
    20 
    21     def skip_test_if_missing_module(self):
    22         self.skip_if_no_wallet()
    23@@ -26,67 +33,28 @@ class P2PLeakTxTest(BitcoinTestFramework):
    24         gen_node.generate(1)
    25         self.sync_all()
    26 
    27-        inbound_peer = self.nodes[0].add_p2p_connection(P2PDataStore())  # An "attacking" inbound peer
    28-        outbound_peer = self.nodes[1]  # Our outbound peer
    29+        inbound_peer = self.nodes[0].add_p2p_connection(P2PNode())  # An "attacking" inbound peer
    30 
    31-        # In an adversarial setting we can generally assume that inbound peers
    32-        # are more likely to spy on us than outbound peers. Thus, on average,
    33-        # we announce transactions first to outbound peers, then to (all)
    34-        # inbound peers. Inbound peers must not be able to successfully request a
    35-        # transaction if they haven't yet received the announcement for it.
    36-        #
    37-        # With only one outbound peer, we expect that a tx is first announced
    38-        # to (all) inbound peers (and thus present a potential leak) in 28.5% of
    39-        # the cases.
    40-        #
    41-        # Probability( time_ann_inbound < time_ann_outbound )                 =
    42-        # ∫ f_in(x)                           * F_out(x)                   dx =
    43-        # ∫ (lambda_in * exp(-lambda_in * x)) * (1 - exp(-lambda_out * x)) dx =
    44-        # 0.285714
    45-        #
    46-        # Where,
    47-        # * f_in is the pdf of the exponential distribution for inbound peers,
    48-        #   with lambda_in = 1 / INVENTORY_BROADCAST_INTERVAL = 1/5
    49-        # * F_out is the cdf of the expon. distribution for outbound peers,
    50-        #   with lambda_out = 1 / (INVENTORY_BROADCAST_INTERVAL >> 1) = 1/2
    51-        #
    52-        # Due to measurement delays, the actual monte-carlo leak is a bit
    53-        # higher. Assume a total delay of 0.6 s (Includes network delays and
    54-        # rpc delay to poll the raw mempool)
    55-        #
    56-        # Probability( time_ann_inbound < time_ann_outbound + 0.6 )           =
    57-        # ∫ f_in(x)                           * F_out(x + 0.6)             dx =
    58-        # ∫ (lambda_in * exp(-lambda_in * x)) * (1 - exp(-lambda_out * (x+.6))) dx =
    59-        # 0.366485
    60-        # EXPECTED_MEASURED_LEAK = .366485
    61-        # Because this test is empirical and our testing framework isn't set up
    62-        # to handle tests that fail with some expected likelihood, we bump this
    63-        # value up to decrease the false positive rate.
    64-        EXPECTED_MEASURED_LEAK = .50
    65-
    66-        REPEATS = 100
    67-        measured_leak = 0
    68-        self.log.info('Start simulation for {} repeats'.format(REPEATS))
    69+        REPEATS = 10
    70+        self.log.info('Start test for {} repeats'.format(REPEATS))
    71         for i in range(REPEATS):
    72             self.log.debug('Run {}/{}'.format(i, REPEATS))
    73             txid = gen_node.sendtoaddress(gen_node.getnewaddress(), 0.033)
    74+
    75+            time.sleep(5)
    76+
    77             want_tx = msg_getdata()
    78             want_tx.inv.append(CInv(t=1, h=int(txid, 16)))
    79-
    80-            wait_until(lambda: txid in outbound_peer.getrawmempool(), lock=mininode_lock)
    81             inbound_peer.send_message(want_tx)
    82             inbound_peer.sync_with_ping()
    83 
    84             if inbound_peer.last_message.get('notfound'):
    85+                self.log.debug('tx {} was not yet announced to us.'.format(txid))
    86                 assert_equal(inbound_peer.last_message['notfound'].vec[0].hash, int(txid, 16))
    87                 inbound_peer.last_message.pop('notfound')
    88             else:
    89-                measured_leak += 1
    90-
    91-        measured_leak /= REPEATS
    92-        self.log.info('Measured leak of {}'.format(measured_leak))
    93-
    94-        assert_greater_than(EXPECTED_MEASURED_LEAK, measured_leak)
    95+                self.log.debug('tx {} was announced to us.'.format(txid))
    96+                assert int(txid, 16) in [inv.hash for inv in inbound_peer.last_message['inv'].inv]
    97 
    98 
    99 if __name__ == '__main__':
    

    jnewbery commented at 9:06 am on October 8, 2018:

    Concept ACK the change to the test!

    Can we add multiple P2P connections to the node to get more test repeats in parallel, or does the change to tx propagation in #13298 mean that we’ll announce to all those peers at the same time and not get any additional benefit?


    MarcoFalke commented at 9:15 am on October 8, 2018:

    Currently we can only easily add inbound mininode peers (which are in the same bucket for tx propagation), so right now it couldn’t run in parallel that way.

    I am happy to adjust the test as soon as we can add outbound connections to mininode peers.


    sdaftuar commented at 2:58 am on October 9, 2018:
    Thanks!
  38. sdaftuar force-pushed on Oct 9, 2018
  39. Don't relay tx data to peers until after tx announcement
    Prior to this commit, we'd respond with tx data for anything in mapRelay,
    regardless of whether the requesting peer was one that we'd sent an INV to
    for the transaction in question.
    
    Close this privacy leak by maintaining a set of peers to which we've
    relayed each transaction in mapRelay.
    f979abd138
  40. [qa] Test inter-bucket privacy leakage a4e0c3b090
  41. sdaftuar force-pushed on Oct 9, 2018
  42. sdaftuar commented at 2:57 am on October 9, 2018: member
    Picked up Marco’s latest test and rebased on master due to the test_runner.py conflict.
  43. Satisfy the linter de9662b7b8
  44. sdaftuar commented at 3:06 am on October 9, 2018: member

    Added a commit because of this:

    0./test/functional/p2p_leak_tx.py:8:1: F401 'test_framework.mininode.mininode_lock' imported but unused
    1./test/functional/p2p_leak_tx.py:10:1: F401 'test_framework.util.assert_greater_than' imported but unused
    2./test/functional/p2p_leak_tx.py:10:1: F401 'test_framework.util.wait_until' imported but unused
    
  45. DrahtBot removed the label Needs rebase on Oct 9, 2018
  46. MarcoFalke commented at 3:30 am on October 9, 2018: member
    Feel free to just squash these trivial fixups in
  47. in src/net_processing.cpp:1294 in de9662b7b8
    1289@@ -1284,8 +1290,8 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
    1290             bool push = false;
    1291             auto mi = mapRelay.find(inv.hash);
    1292             int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
    1293-            if (mi != mapRelay.end()) {
    1294-                connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
    1295+            if (mi != mapRelay.end() && mi->second.m_node_set.count(pfrom->GetId())) {
    1296+                connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second.m_txref));
    


    jnewbery commented at 6:31 am on October 10, 2018:

    I think a comment here would be helpful (since mi->second.m_node_set.count(pfrom->GetId()) isn’t immediately obvious when read outside the context of this PR). Something like:

    0// Only send the transaction if we've previously announced it to this peer
    
  48. in src/net_processing.cpp:1296 in de9662b7b8
    1289@@ -1284,8 +1290,8 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm
    1290             bool push = false;
    1291             auto mi = mapRelay.find(inv.hash);
    1292             int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0);
    1293-            if (mi != mapRelay.end()) {
    1294-                connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second));
    1295+            if (mi != mapRelay.end() && mi->second.m_node_set.count(pfrom->GetId())) {
    1296+                connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second.m_txref));
    1297                 push = true;
    1298             } else if (pfrom->timeLastMempoolReq) {
    


    jnewbery commented at 6:32 am on October 10, 2018:
    Note that we can leak mempool contents (and lose privacy) to a peer if it sends us a MEMPOOL message. That is unchanged by this PR, but the logic here is a bit confusing and we should probably deprecate support for MEMPOOL in future.

    sipa commented at 7:47 am on October 17, 2018:
    One relatively easy solution (I think), is to only announce transactions that have been longer than say 60s in the mempool at the time the MEMPOOL message is received, and to enforce the same time restriction in this block of code (so you can’t fetch transactions that were received less than 60s before the last MEMPOOL request).
  49. in test/functional/p2p_leak_tx.py:1 in de9662b7b8
    0@@ -0,0 +1,59 @@
    1+#!/usr/bin/env python3
    


    jnewbery commented at 6:41 am on October 10, 2018:

    I’ve now reviewed this test. Sadly, it doesn’t test the new code (and does not fail on master).

    Since the node-under-test has only one peer, the transaction won’t get added to mapRelay until we send the INV to that peer. That means in the if/else branches at the bottom of this test:

    • if the node has already sent us an INV for the tx, then it’ll be in the mapRelay and the node will send us the TX.
    • if the node has not yet sent us an INV for the tx, then it won’t be in the mapRelay and we’ll fail mi != mapRelay.end() and not even reach && mi->second.m_node_set.count(pfrom->GetId())

    To test the new code, we want the to be in mapRelay, but the peer to not be in the RelayEntry. That’s not possible if the node has only one peer.

    Perhaps we should remove the test from this PR and add it under a future PR. If you do so, please tag me in that PR and I’ll commit to reviewing the test.

  50. jnewbery changes_requested
  51. jnewbery commented at 6:42 am on October 10, 2018: member

    utACK the code change with a request for an additional comment.

    The test appears to me to be broken. Perhaps we should split it out into a separate PR.

  52. jnewbery commented at 8:34 am on October 10, 2018: member
    One other question: do we know how large mapRelay is expected to grow? Do we know how much additional memory will be used by changing the CTransactionRef to a RelayEntry?
  53. jnewbery commented at 2:43 pm on October 15, 2018: member
    I’ve written an alternative test here: https://github.com/jnewbery/bitcoin/tree/pr14220.1 which reliably tests the new behaviour and fails on master.
  54. in src/net_processing.cpp:158 in f979abd138 outdated
    153@@ -153,7 +154,12 @@ namespace {
    154     std::atomic<int64_t> g_last_tip_update(0);
    155 
    156     /** Relay map */
    157-    typedef std::map<uint256, CTransactionRef> MapRelay;
    158+    struct RelayEntry {
    159+        explicit RelayEntry(CTransactionRef &&tx) : m_txref(tx) {}
    


    sipa commented at 7:35 am on October 17, 2018:
    Use m_txref(std::move(tx)) here. tx in this context is not an rvalue reference (as it’s been bound to variable), it’s only initialized by accepting an rvalue reference.

    sdaftuar commented at 5:07 pm on October 17, 2018:
    I had no idea, thank you for pointing this out!
  55. sdaftuar commented at 5:08 pm on October 17, 2018: member

    One other question: do we know how large mapRelay is expected to grow? Do we know how much additional memory will be used by changing the CTransactionRef to a RelayEntry?

    mapRelay is bounded by our rate-limited transaction relay algorithm, but on further thought, this approach may potentially use quite a bit more memory than I originally realized.

    I’ll close this PR for now and re-open when I have an alternative that I think is worth pursuing.

  56. sdaftuar closed this on Oct 17, 2018

  57. in src/net_processing.cpp:160 in de9662b7b8
    153@@ -153,7 +154,12 @@ namespace {
    154     std::atomic<int64_t> g_last_tip_update(0);
    155 
    156     /** Relay map */
    157-    typedef std::map<uint256, CTransactionRef> MapRelay;
    158+    struct RelayEntry {
    159+        explicit RelayEntry(CTransactionRef &&tx) : m_txref(tx) {}
    160+        CTransactionRef m_txref;
    161+        std::unordered_set<NodeId> m_node_set;
    


    MarcoFalke commented at 2:19 pm on October 21, 2018:
    There are at most 9 buckets, so a set of outbound NodeIds and a flag for inbound ones might demand less memory?
  58. MarcoFalke locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-11-17 18:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me