The PR branch HEAD was 038f751 at the time of this review club meeting.
Notes
Hiding links between wallet addresses and IP addresses is a key part of
Bitcoin privacy. Many techniques exist to help users obfuscate their IP
address when submitting their own transactions, and various P2P changes have
been proposed with the goal of hiding transaction origins.
Beyond initial broadcast, rebroadcast behavior can also leak information. If
a node rebroadcasts its own wallet transactions differently from transactions
received from its peers, adversaries can use this information to infer
transaction origins even if the initial broadcast revealed nothing. We have
discussed rebroadcast in previous review clubs,
#16698 and
#18038.
The rebroadcast projectâs goal is to improve privacy by making node
rebroadcast behavior for wallet transactions indistinguishable from that of
other peersâ transactions.
#21061 adds a TxRebroadcast
module responsible for selecting transactions to be rebroadcast and keeping
track of how many times each transaction has been rebroadcast. After each
block, the module uses the miner and other heuristics to select transactions
from the mempool that it believes âshouldâ have been included in the block and
reannounces them (disabled by default for now).
Rebroadcasts happen once per new block. The set of transactions to be
rebroadcast is calculated as follows:
The node regularly estimates the minimum feerate for transactions to be
included in the next block, m_cached_fee_rate.
When a new block arrives, the transactions included in the block are
removed from the mempool. The node then uses BlockAssembler to calculate
which transactions (with a total weight up to 3/4 of the block maximum)
from the mempool are more than 30 minutes old and have a minimum feerate
of m_cached_fee_rate. This results in a set of transactions that our
node would have included in the last block.
The rebroadcast attempt tracker, m_attempt_tracker, tracks how many
times and how recently weâve attempted to rebroadcast a transaction so
that we donât spam the network with re-announcements.
In what scenarios might a user want to rebroadcast their transaction? Why
shouldnât each wallet just be solely responsible for rebroadcasting its own
transactions?
How does the rebroadcast module decide which transactions to rebroadcast
(TxRebroadcastHandler::GetRebroadcastTransactions())?
In what scenarios would a miner include different transactions from our
BlockAssembler? More specifically, when might the miner exclude a
transaction, and when might it include a transaction yours doesnât?
Why might we want to keep a transaction in our rebroadcast attempt tracker
even after removing it from our mempool? (Hint: what happens if we expire a
transaction from our mempool and then our peer rebroadcasts it to us? When
might this happen?)
When should we remove transactions from our rebroadcast attempt tracker? How
does the code ensure that the tracker doesnât grow unbounded?
How is the estimated minimum feerate for inclusion in a block,
m_cached_fee_rate, calculated? Why not just calculate the feerate of the
lowest-feerate transaction in the most recently mined block?
<b10c> transactions expire after 14 days, can get size-limited if more higher fee trasnactions are there - or can be removed in a block which is later reorged (the reorged chain does not contain the transaction)
<_0x0ff> It rebrodcasts tx that are: older than 30min, txfee > m_cached_fee_rate (calcualted via BlockAssembler), hasnt been rebroadcasted >= MAX_REBROADCAST_COUNT (6) and wasn't rebroadcasted in the last MIN_REATTEMPT_INTERVAL (4h).
<svav> A dusting attack is an attack in which a trace amount of cryptocurrency, called dust, is sent to a large number of wallet addresses with the purpose of "un-masking" or de-anonymizing the addresses. Dusting attacks are tactics utilized by both criminals and law enforcement agencies.
<amiti> here's a scenario where the filters wouldn't return any txns to rebroadcast: at time 0, the fee rate cache runs, identifies min fee rate. at time 1 a block comes in and picks up all our mempool txns above this fee rate. when we go to connect the tip, we don't have any remaining txns above the calculated min fee rate.
<svav> tracks how many times and how recently weâve attempted to rebroadcast a transaction so that we donât spam the network with re-announcements.
<glozow> mempool expiry is 2 weeks, while the attempt tracker expiry is ~3 months. why aren't they the same? -> there must be some reasons why we'd keep a tx in the attempt tracker after the mempool has forgotten about them
<sipa_> the last time a consensus rule change was introduced that changed something that wasn't already very widespread nonstandard was BIP113 i believe
<jnewbery> sishir: imagine both node A and node B have not upgraded and both consider the tx valid. If there's no way to prevent rebroadcasting, they'll just continue to rebroadcast the tx to each other.
<lightlike> though if you send >500 of these txes, they would still never forget even with the tracker, so I'm thinking the tracker only helps when this unintentional, not in an attack case or does it?
<glozow> svav: right, so let's see how the 3month expiry helps. let's say you expire a tx from mempool after 2 weeks, and you keep it in your rebroadcast attempt tracker. what happens if you see the tx again?
<_0x0ff> it will folow the same rules as it did before it got removed, which means we might not rebroadcast it immidiatelly after receiving it (if conditions for rebrodcasting arent met)
<amiti> glozow: yeah the limit is slightly arbitrary right now, just to pick a starting point. I think the most relevant will be observing the mechanism out in the wild & seeing if this limit is useful
<glozow> hopefully we feel comfortable moving to the next question? In general, when should we remove transactions from our rebroadcast attempt tracker?
<jnewbery> another potential change could be to move the txids into a rolling filter if they reach MAX_REBROADCAST_COUNT, since at that point we only need a test for inclusion
<glozow> ok I think we have time to do part of the last question: How is the estimated minimum feerate for inclusion in a block, `m_cached_fee_rate`, calculated?
<_0x0ff> It uses `BlockAssembler::minTxFeeRate()` which calculates a min fee that would still be included in the next mined block. This approach is better because it calculates fees based on the future mintxfee and not the past.