Code Monkey home page Code Monkey logo

Comments (5)

amiller avatar amiller commented on August 13, 2024

There are maybe a few ways of doing this. It might be necessary to consider tradeoffs and work out just what privacy guarantee we'd be getting in each case.

One option is to have each node forget entirely about the stem transaction after relaying it. No inserting in mempool, not even inserting in filterInventoryKnown. The reason not to insert in filterInventoryKnown is because otherwise we might not participate in propagation during "fluff" mode when the transaction comes back around. Propagating via the stem but then "forgetting" seems consistent.

However, if we skip placing the transaction in mempool, then we would potentially relay the same transaction over again in stem mode. Or worse, we'd even relay "double spend" transactions. Doesn't this mean we'd be contributing to DoS? Maybe not.... one reason why this is OK is because "stem mode" never amplifies the propagation, i.e. we only propagate to 1 other peer, not all 30+ish of our peers. No exponential propagation begins until "fluff" mode, at which point ordinary mempool rules apply.

from bitcoin.

amiller avatar amiller commented on August 13, 2024

Ugh, I can't find an easy way to validate if a transaction could be accepted into mempool without actually placing it in mempool. Perhaps an option is to perform minimal validation of stem-propagated tx's? But then, there would be a problem if an A --stem --> B ---stem --> C, and then C determines the transaction is invalid and disconnects B. The logic for checking a transaction is too complicated for me to really want to copy a subset of that over.

Maybe it would be acceptable to add a bool dryrun=False option to AcceptToMempool?

from bitcoin.

amiller avatar amiller commented on August 13, 2024

Thinking through this yet more... there are some tricky interactions with other mechanisms in propagation, mainly a) replacement of txs in mempool by txs with higher fees, and b) handling chains of descendent transactions that might get processed out of order.

We're proposing to have stem transactions bypass mempool. So basically what we'd be doing is letting stem transactions propagate with only a subset of these checks, but get processed by mempool rules when they switch. So we would need to guarantee that as long as the subset of "stem checks" are passed, then the next node that receives it and puts it in mempool cannot assign a misbehavior score to the stem relayer.

from bitcoin.

amiller avatar amiller commented on August 13, 2024

Here's a new plan.... (after spending a day trying to implement this by avoiding mempool, it requires too much tricky duplication of behavior). Instead, we will actually insert transactions in mempool, and mask them another way, by setting a time-limited "embargo". Requests for mempool etc. during an embargo will simply skip over the dandelion transaction and hide it. Robustness will be enabled by expiring the embargo with a random delay per #9.

There is still a big challenge remaining in handling orphans and out-of-order transactions. If we treat each transaction independently for the sake of stem propagation, then it's possible for a child transaction to enter "fluff" mode before the parent. This will cause our peers to request the parent from us, even if we did not relay the INV for the parent (since the parent was in stem node).

This cannot be treated as just an edge case, since simple usage patterns (calling "sendtoaddress" multiple times in rapid succession) will typically create a chain of dependent transactions... so it's important these don't get stuck, and ideally still benefit from dandelion privacy.

So, here is a new suggestion:

  • If an accepted "stem" transaction depends on parents who are under "embargo," then this transaction is placed under embargo equal to the maximum of the embargo times for all the parents. This means that we skip the dandelion "coin flip" for a child transaction, if we have already made a coinflip for an earlier transaction.

  • This provides an extra reason to pick a "fixed" outgoing stem node, that does not change for each transaction, so descendent transactions get routed to the same place. If we have a "handover", where our current outgoing stem node disconnects or we decide to choose another, we should process the list of currently embargoed transactions, and either switch them all to fluff, or send them to the new stem node.

There are still several tricky cases though. Suppose A generates {tx1, tx2} that depend on each other. Then suppose we have:
A --[stem]{tx1,tx2}--> B --[stem]{tx1,tx2}--> C
and then shortly after
Attacker --[fluff]{tx2}--> A
If A has tx1 and tx2 under embargo, then it should try not to reveal to Attacker whether it has these. But, if A really does not have tx1, then the expected behavior should be to send getdata(tx1) to Attacker.

So in short, it's going to be really tricky to simultaneously 1) retain as much of the same policy of existing Bitcoin in terms of validation / DoS-scoring behavior, but also 2) avoid behaving differently whether we have seen a stem transaction or not.

from bitcoin.

amiller avatar amiller commented on August 13, 2024

OK, the plan described above is partially implemented in fee2593 (what an unfortunate commit hash!)

Items are requested even if they've been fetched before. This is accomplished by:

  1. having Dandelion INV message avoid putting an entry in "mapAlreadyAskedFor", which is the main obstacle that prevents future INV messages are responded to with GETDATA.
    fee2593#diff-9a82240fe7dfe86564178691cc57f2f1R2750
 +    if (inv.type == MSG_DANDELION_TX || inv.type == MSG_WITNESS_DANDELION_TX) {
 +	// Bypass the queue for dandelion transactions
 +	setDandelionAskFor.insert(inv.hash);
 +    } else {
 +	setDandelionAskFor.erase(inv.hash);
 +	if (it != mapAlreadyAskedFor.end())
 +	    mapAlreadyAskedFor.update(it, nRequestTime);
 +	else
 +	    mapAlreadyAskedFor.insert(std::make_pair(inv.hash, nRequestTime));
 +    }
  1. Skipping the filterInventoryKnown for dandelion INV items (and for transactions sent in response to a getdata in response to a dandelion INV
    fee2593#diff-eff7adeaec73a769788bb78858815c91R1674
 +                // Skip adding to inventory for dandelion tx
 +                if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)
 +                    pfrom->AddInventoryKnown(inv);
  1. Ignoring the AlreadyHave flag when deciding whether to invoke AskFor
    fee2593#diff-eff7adeaec73a769788bb78858815c91R1681
 +                    if (!fAlreadyHave || fEmbargoed) {
 +                        pfrom->AskFor(inv);
 +                    }

Furthermore, responses to "getdata" fee2593#diff-eff7adeaec73a769788bb78858815c91R1220 and "mempool"
fee2593#diff-eff7adeaec73a769788bb78858815c91R3220 omit the transaction if it is currently embargoed.

Finally, to avoid out of order problems, only 1 dandelion node is chosen (for minimalism here in this preliminary version, it's just the first outgoing connection), and a child is guaranteed to be routed as a "stem" if any of the inputs it depends on are currently treated as stems.
fee2593#diff-eff7adeaec73a769788bb78858815c91R971

Does this embargo mechanism provide adequate protection against the spy attacker probing the node's mempool? Here are the concerns I think are left:

  • In the worst case, the attacker is one of the recipients of the victim's transaction. In this case, the attacker could create a transaction that spends one of the coins, and send it to each node in order to tell whether it is relayed or placed in the orphans cache.
  • If the attacker cannot spend any of the transaction's coins, then the attacker could still create a bogus transaction that purports to spend one of them. This may be OK for us... we will need to be double check that the attacker cannot discriminate between a transaction that is rejected for invalid signature vs a transaction that is placed in the orphan cache.

from bitcoin.

Related Issues (17)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.