-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Dandelion transaction relay (BIP 156) #13947
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This is completely untested, so TODO:
|
@@ -755,6 +778,10 @@ class CNode | |||
// Our address, as reported by the peer | |||
CService addrLocal; | |||
mutable CCriticalSection cs_addrLocal; | |||
|
|||
/** Our current dandelion destination */ | |||
CConnman::DandelionPeer* m_dandelion_destination /* GUARDED_BY(CConnman::cs_dandelion_peers) */ {nullptr}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any drawbacks from uncommenting the GUARDED_BY(…)
annotation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The drawback was that it didn't compile for me
src/init.cpp
Outdated
@@ -1027,6 +1031,12 @@ bool AppInitParameterInteraction() | |||
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex()); | |||
} | |||
|
|||
{ | |||
const int64_t pct = gArgs.GetArg("-dandelion", DEFAULT_DANDELION_STEM_PERCENTAGE); | |||
if (pct < 0 || 100 < pct) return InitError(_("-dandelion must be 0 or a percentage less than 100")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should -dandelion=100
generate an error message? From the error message it looks like it should, but not from the logic :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% is allowed to always extend the stem by one hop. So the interval is [0, 100].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then the error message should be -dandelion must be 0 or a percentage up to 100
or something equivalent?
Note to reviewers: This pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
@@ -33,6 +33,7 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.17.0**): | |||
* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)). | |||
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)). | |||
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)). | |||
* [`BIP 156`](https://github.com/bitcoin/bips/blob/master/bip-0156.mediawiki): Dandelion transaction relay is supported as of **v0.18.0** ([PR 13947](https://github.com/bitcoin/bitcoin/pull/13947)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link https://github.com/bitcoin/bips/blob/master/bip-0156.mediawiki gives error 404 page not found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@clodhopp that's because the BIP PR hasn't been merged, you can see it here: bitcoin/bips#703
Once merged, the link should work
This code doesn't seem to include a "stempool"; how does it deal with dandelion transactions that depend on other dandelion transactions? ATMP will fail for those. |
Continued here https://botbot.me/freenode/bitcoin-core-dev/2018-08-12/?msg=103212266&page=2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just left some initial comments from a quick-read of the code. As discussed on IRC we still need to figure out what the right behavior is for transaction chains and replacement transactions (and combinations of the two), and generally how to prevent DoS attacks during stem routing.
If we use a stempool to address those issues, then I'm not sure I understand how we could share a stempool across multiple inbound peers without leaking dandelion routing information to an adversary (and if we have separate stempools for each inbound peer, then that sounds like a lot of overhead).
if (gArgs.SoftSetBoolArg("-whitelistrelay", false)) | ||
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__); | ||
if (gArgs.SoftSetBoolArg("-dandelion", false)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we use SoftSetBoolArg()
like this elsewhere, on arguments that take numeric values? It was not clear to me that this worked until I saw that SoftSetBoolArg converts the false
to std::string("0")
. (Neat, though.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to not mix up values, this should set 0 instead of false, even if false would be converted automatically to 0.
src/net.h
Outdated
@@ -803,7 +847,19 @@ class CNode | |||
nRefCount--; | |||
} | |||
|
|||
/** If this peer is not yet connected or soon to be disconnected */ | |||
bool IsEphermal() const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: IsEphemeral
src/net_processing.cpp
Outdated
else if (strCommand == NetMsgType::TX_DANDELION) { | ||
// Stop processing the transaction early if | ||
// dandelion is disabled and peer is either not whitelisted or whitelistrelay is off | ||
if (!CNode::IsDandelionEnabled()&& (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some IRC discussion around not extending whitelist-relay behavior further with dandelion processing:
https://botbot.me/freenode/bitcoin-core-dev/2018-08-13/?msg=103271568&page=3
src/net_processing.cpp
Outdated
return true; | ||
} | ||
|
||
if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above comment about dropping this behavior for whitelisted peers.
* The dandelion tx message transmits a single transaction. | ||
* @see https://github.com/bitcoin/bips/blob/master/bip-0156.mediawiki | ||
*/ | ||
extern const char* TX_DANDELION; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in the past, we've usually bumped the protocol version when introducing new p2p messages so that we only send the new messages (like ACCEPT_DANDELION) to peers that are signaling that they've upgraded. Not sure how important that is...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that we'd never send a TX_DANDELION
to a peer that never sent us ACCEPT_DANDELION
in the first place. So I don't think this is an issue at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps my comment was not placed on the right line of this file; I was thinking of the dandelionacc message. With sendheaders and I think compact blocks, I believe we bumped the protocol version and only tried to send the new p2p messages (even for handshaking/negotiating the protocol to use) to peers with that higher version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I think it is a lot easier to do this without enforcing a global network version bump.
Note that the dandelionacc
message is properly read by all nodes that implement dandelion (regardless of their network protocol version) or choose to ignore that message. And for all other nodes (that are completely unaware of dandelion) they simply treat this as unknown message (once per connection) in their logs. I think this one debug log is not enough reason to go through the hassle of bumping the protocol version.
bitcoin/src/net_processing.cpp
Line 2942 in 63f8b01
LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->GetId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a service bit?
CValidationState state; | ||
bool missing_inputs; | ||
if (AcceptToMemoryPool(mempool, state, tx, &missing_inputs, nullptr /* lRemovedTxn */, false /* bypass_limits */, 0 /* nAbsurdFee */, true /* test_accept */)) { | ||
RelayDandelionTransaction(mempool, pfrom, tx, msgMaker, connman); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should queue transactions for relay to a peer, rather than doing a direct send as is done here. Otherwise transaction relay can fill up our send buffers and delay block relay.
Further our current transaction relay system batches transactions for relay and, at each broadcast time, we sort the to-be-relayed transactions for the purposes of prioritising transactions that have fewer ancestors and higher feerate. I think being able to prioritise transactions with higher feerate for relay is probably useful (to prevent low-feerate transactions from harming propagation times of higher feerate transactions).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's also better for privacy against traffic analysis to batch things.
src/net_processing.cpp
Outdated
@@ -1908,6 +1967,11 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr | |||
State(pfrom->GetId())->fPreferHeaders = true; | |||
} | |||
|
|||
else if (strCommand == NetMsgType::ACCEPT_DANDELION) { | |||
pfrom->m_accept_dandelion = true; | |||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should write something to debug log here.
2728324
to
be9db89
Compare
0526b2d
to
b508ed1
Compare
src/init.cpp
Outdated
@@ -399,6 +399,7 @@ void SetupServerArgs() | |||
gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION); | |||
gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION); | |||
gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION); | |||
gArgs.AddArg("-dandelion", strprintf("Probability to extend the stem in Dandelion transaction relay by one hop (default: %d, 0 to disable dandelion)", DEFAULT_DANDELION_STEM_PERCENTAGE), false, OptionsCategory::CONNECTION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A simple "probability" should a number between 0 and 1. The variable used as default and the code below suggest a percentage is used instead. I think it would make sense to document it as such as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this as well: normally I’d expect a probability parameter to be in the interval [0.0, 1.0]
. Are we trying to be consistent with other parameters taking probabilities as [0, 100]
here, or what is the rationale? :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that is the case, it should at least be mentioned in the help line.
Probability in % to extend ..
if (gArgs.SoftSetBoolArg("-whitelistrelay", false)) | ||
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__); | ||
if (gArgs.SoftSetBoolArg("-dandelion", false)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to not mix up values, this should set 0 instead of false, even if false would be converted automatically to 0.
src/net.cpp
Outdated
{ | ||
from->m_dandelion_destination = nullptr; | ||
|
||
if (!CNode::IsDandelionEnabled()) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check should not be necessary, failing it means there's a bug. I'd suggest either using an assertion or something more severe than this or just removing the check.
@@ -317,6 +327,16 @@ class CConnman | |||
*/ | |||
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds); | |||
|
|||
using DandelionPeer = std::pair<CNode*, /* use count */ unsigned>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please elaborate a bit more on the documentation of the integer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is better to create a struct? This integer will look much better with a name. It is very hard to read all those peer.first
and peer.second
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */ | ||
static const int PING_INTERVAL = 2 * 60; | ||
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */ | ||
static const int TIMEOUT_INTERVAL = 20 * 60; | ||
/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/ | ||
static const int FEELER_INTERVAL = 120; | ||
/** Pick new dandelion peers once every 10 minutes or 600 seconds. */ | ||
static constexpr int INTERVAL_DANDELION = 600; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention seems to be XXX_INTERVAL
instead of INTERVAL_XXX
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, why 10 minutes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevenroose as per the BIP: https://github.com/bitcoin/bips/blob/master/bip-0156.mediawiki#periodic-dandelion-route-shuffling (the value suggested in the BIP is best discussed on the bitcoin-dev mailinglist)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sjors this would do:
/** As per BIP156, pick new dandelion peers once every 10 minutes or 600 seconds. */
|
||
/** Default for -dandelion stem percentage */ | ||
static constexpr int64_t DEFAULT_DANDELION_STEM_PERCENTAGE = 90; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 90? This is like the most important parameter of the dandelion algorithm, I think it deserves some motivation..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevenroose the BIP specifies that:
When relaying a Dandelion transaction along a Dandelion route, there is a 10% chance that the Dandelion transaction becomes a typical Bitcoin transaction and is therefore relayed via diffusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Sjors So I'd suggest referring to the BIP in the comment of that variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I'm trying to say with this comments is that this is supposed to be an implementation of the BIP, so it should refer to the BIP for logic/params etc. It has happened too often that a BIP is just a description of the implementation.
}); | ||
} | ||
|
||
static void FluffTransaction(CTxMemPool& tx_pool, const CNode* from, const CTransactionRef& ptx, CConnman* connman) | ||
{ | ||
LogPrint(BCLog::DANDELION, "fluff\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps mention txid?
static void RelayDandelionTransaction(CTxMemPool& tx_pool, CNode* node_from, const CTransactionRef& ptx, const CNetMsgMaker& msg_maker, CConnman* connman) | ||
{ | ||
bool fluff = GetRand(100 * 100) / 100. >= CNode::m_dandelion_stem_pct_threshold; | ||
if (fluff) return FluffTransaction(tx_pool, node_from, ptx, connman); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this check also be made for the wallet's own transactions? Or should a forced non-fluff be done in that case?
src/net_processing.cpp
Outdated
} | ||
|
||
LogPrint(BCLog::DANDELION, "dandelion relay to peer=%d\n", dest->GetId()); | ||
connman->PushMessage(dest, msg_maker.Make(DANDELION_SEND_FLAGS, NetMsgType::TX_DANDELION, *ptx)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong indentation since after return.
* The dandelion tx message transmits a single transaction. | ||
* @see https://github.com/bitcoin/bips/blob/master/bip-0156.mediawiki | ||
*/ | ||
extern const char* TX_DANDELION; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a service bit?
8252b0e
to
ce8c6d6
Compare
Shouldn't the functional test from the reference implementation (dandelion-org@d043e36#diff-21ab9447686d819eeeb7668ce8011e0d) run more-or-less unchanged on top of this branch? |
m_nodes_dandelion.clear(); | ||
for (CNode* node : all_nodes) { | ||
// Ignore inbound nodes | ||
if (node->fInbound) continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why ignore inbound nodes? Does this mean non-listening dandelion nodes send only their own transactions and that the peer who receives such will know with undeniable certainty that the non-listening node initiated the transaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inbound nodes are much more likely to be snooping peers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While that is true, aren't there solutions that do not make dandelion break firewalled nodes' privacy? For example, send only to outgoing peers if you originated the transaction, but send to incoming as well for forwarded txes? Perhaps weigh the outgoing peers more heavily than incoming peers when selecting a random peer to forward to (either by assigning a weight to individual peers based on incoming/outgoing status, or by first randomly selecting the outgoing or incoming pool with a fixed weighting before selecting a peer from whichever pool).
These commits could be documented more in general, I'm finding it hard to review when there are no descriptions about what is being implemented. |
It has been mentioned several times that common stempool might leak dandelion routing information to an adversary. Can someone help me understand how exactly? Also, what is the current status of this issue? I would like to offer some help. For example with tests |
@jb55 BIP156 should explain a lot of things. |
@scravy I'm more interested in documentation of the implementation itself, how each commit relates to a part of the bip, etc. It's much harder to verify an implementation without that, especially with important privacy/security features. I don't think we should be making it harder on reviewers than it needs to be. |
@jb55 Totally agree. All the non-trivial pieces of code should explain themselves, refer to the BIP when relevant, etc.. |
}); | ||
} | ||
|
||
static void FluffTransaction(CTxMemPool& tx_pool, const CNode* from, const CTransactionRef& ptx, CConnman* connman) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wandering... FluffTransaction is logically equivalent to receiving a transaction, right?
Then shouldn't we also execute all this code on fluff?
|
||
if (CNode::IsDandelionEnabled()) { | ||
// Shuffle dandelion destinations | ||
m_thread_shuffle_dandelion = std::thread(&TraceThread<std::function<void()>>, "dandelionshuff", [&] { ThreadShuffleDandelionDestinations(); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I tried this PR in action - dandelion was turned off for the first epoch. I believe this happens because we do the very first shuffle when there are no connections. Maybe the first shuffle should be delayed until someone connects?
from->m_cache_expiry.pop(); | ||
} while (from->m_cache_expiry.top() < now_millis); | ||
|
||
for (auto it = from->m_cache_dandelion.cbegin(); it != from->m_cache_dandelion.cend(); ++it) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An idea: if you define m_cache_expiry
as std::map<int64_t, CTransactionRef>
- you will get the same 'Constant time lookup' but you won't need to walk the whole cache - expired transactions are already available
// Dandelion relay (at least one hop) | ||
const int64_t now_micros = GetTimeMicros(); | ||
const int64_t expiry = now_micros + EXPIRE_DANDELION_CACHE * 1000 * 1000 + PoissonNextSend(now_micros, EXPIRE_DANDELION_CACHE_AVG_ADD); | ||
node_from->m_cache_dandelion.emplace(std::make_pair(ptx->GetWitnessHash(), std::make_pair(ptx, expiry))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
node_from->m_cache_dandelion.emplace(ptx->GetWitnessHash(), std::make_pair(ptx, expiry))
does absolutely the same, but shorter.
I believe this explanation of the DDoS possibilities on Dandelion by @sdaftuar belongs here: regarding option b), maybe dynamically adjusting stem % according to fullness of the stempool/cache could be a solution? at worst, we would fluff all the time just like a regular tx spam attack. |
I closed this for now because it is non-trivial to allow for all wallet behavior (e.g. cpfp and rbf) while not opening DOS vectors. I think a simpler first step would be to go for "Dandelion Light" or "Dandelion one-hop". That approach would not need any DOS protection, since the only sender is our own wallet and the next hop would already fluff the tx (and send it back to us, so that our wallet can know it made it into the mempool eventually) |
Dandelion transaction relay (BIP156) original source available at bitcoin/bitcoin#13947. Ported forward from Bitcoin 0.17 - looks to be written in a much neater manner. Signed-off-by: barrystyle <baz@tiltpool.com>
Dandelion transaction relay (BIP156) original source available at bitcoin/bitcoin#13947. Ported forward from Bitcoin 0.17 - looks to be written in a much neater manner. Signed-off-by: barrystyle <baz@tiltpool.com>
Removed up for grabs. |
static constexpr int DANDELION_SEND_FLAGS{0}; // Always send as witness tx | ||
|
||
// Check if the next peer would fluff (assume same probability) | ||
bool force_fluff = GetRand(100 * 100) / 100. >= CNode::m_dandelion_stem_pct_threshold; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doubles the odds of the next node fluffing -- 10% chance that we thought they should fluff, but if we don't think they should fluff, they'll still make their own decision, giving another 9% (=90%*10%) chance that they'll think they should fluff for a total of 19%.
(necroreviewing!)
The implementation of BIP 156 (Dandelion transaction relay) is done in three steps:
Add the protocol messages
dandeliontx
, which contains a transaction serialized with all witness datadandelionacc
, which is always empty and only used to signal dandelion support to other peersShuffle dandelion stems
We shuffle a list of all peers that sent us the message that they accept dandelion txs (
dandelionacc
). This list can be used on demand by nodes that want to forward a dandelion transaction. (Actual receiving of dandelion txs is done in the next step).Also, we add a command line option
-dandelion
, which can be used to opt out of all dandelion features.Keep a cache of recent dandelion transactions
Dandelion transactions are never added to the global tx pool and only forwarded to one peer (the next hop in the stem). To guard against the next peer going offline, we keep a cache of recent dandelion transactions that are not yet fluffed (added to the mempool) or mined. Entries in this cache expire after some random timeout and transition to fluff phase.