← index

[DEFUNCT] Post-clustermempool Package RBF

An archive of delvingbitcoin.org · view original topic →

Pieter Wuille · #1 ·

Follow-up to [DEFUNCT] Cluster Mempool Package RBF sketch. Superseded by Post-clustermempool package RBF: per-chunk processing.

Design for post-clustermempool Package RBF rules.

A new package comes in (consisting of a set of transactions PKG). Ignore for now how the P2P side happens.

(At this point the package is valid)

In the above, wherever a linearization occurs, fail the package if it involves a cluster that exceeds cluster count limit. Also fail the package whenever a cluster would be created that exceeds the cluster size limit.

Open questions

Gregory Sanders · #2 ·

How does minrelay fit into this paradigm?

I think this links back to our conversation yesterday. If you have two same-unit-size txs, A and B, where A is 0-fee, B is 1 fee, and mempoolminfee is 1, then B would be accepted when submitted by itself, but rejected as a package A+B. In this scenario of course, we’re only considering “chunks” since we’re not connecting the package to the mempool clusters.

Once this check is run, we know everything in these chunks “pays for itself”, and can continue on safely, even if A+B ends up with a chunk score less than minrelay.

I assume this is where scripts are being run as well?

What kind of failures are possible here if any?

I assume TimeToSize is another step we want, after 12.

edit: misunderstood the Q2 scenario, deleted

Anthony Towns · #3 ·

Anything done before script-validation isn’t really DoS protection, it’s just an optimisation?

“At this point the package is desirable” perhaps? We’ve established it’s got a nice feerate, etc, but not done the consensus checks from 12, so it may actually be garbage. Could call it the “qualification” stage or something.

I think we should do something along these lines for packages received over p2p, as otherwise it seems like something you could use for targeted tx eviction? But that could be as simple as restricting p2p package relay to packages made up of a single tx and its ancestors?

Pictures:

graph TD
    A[A: 100]-->B[B: 4]
    A-->C[C: 5]
    C'[ C': 6] x-- conflicts --x C

Treating A+B+C as a unit has your mempool do this:

graph LR
  m1[C': 6] -- +ABC --> m2[A, C, B: 100, 5, 4] -- +C --> m3[A,C', B: 100, 6, 4]

Doing ancestor-first package acceptance would work for this case: your mempool would be:

graph LR
  m1[C': 6] -- +A --> m2[A, C': 100, 6] -- rej C --> m3[A, C': 100, 6] -- +B --> m4[A,C', B: 100, 6, 4]

Maybe you can do ancestor-first package acceptance via chunking? Once you linearise NEW go through the chunks with new txs in order, and add just those txs to the mempool; updating OLD, NEW and CON as you go? If you get a chunk that’s not worth adding to the mempool (or that fails validation), abort completely.

Pieter Wuille · #4 ·

I’m not sure I understand the example. Do A and B depend on each other? Is one in the mempool already?

Indeed.

None, if all goes well. The consensus checks are there for (a) priming the script/validation cache and (b) being absolutely sure that no cases exist where standardness checks permit something that consensus does not.

Indeed.

Do you mean doing this pre-eviction here (as opposed to later) is an optimization? If so, I may agree. But not removing (and not otherwise outlawing) sub-mempoolminfee stuff in packages would constitute a DoS risk, I believe (free relay by continuously replacing the bottom tx in the mempool at \epsilon feerate increments). This gets stopped by increasing mempoolminfee above the evicted transactions’ feerate when eviction happens, so that anything new accepted into it is (for the time being) strictly higher feerate.

Unless our code is broken, standardness checks (done in (10)) are strictly more restrictive than the consensus checks in (12). These last ones are just for priming the validation cache and a last-ditch protection against bugs in standardness validation that could make it less restrictive than consensus.

There are many things that can be done to find a better combination of OLD and NEW things than exactly the package as relayed indicates. They include:

But all of these are either incomplete, high-bandwidth, or computationally infeasible for all but the smallest cases. We could do relatively cheap improvements, but they will still permit “undesirable” more complex situations like this. Or we can restrict cluster/replacement sizes so much that we can always find the optimal combination.

However, I’d rather not get into a discussion of what approaches to consider without knowing what good enough is. Are there attacks possible without any improvements over the package relayed as-is? Note that if the peer is malicious, it’s ok to do something suboptimal as long as it doesn’t prevent us from accepting the “right” solution later from an honest peer. And if it’s good enough, maybe we don’t need to bother with patching up simple and obvious suboptimalities. And if it’s not good enough, then patching only the simple cases likely still leaves other cases that are still not enough.

Gregory Sanders · #5 · · in reply to #4

Yes a simple cluster of size two. My point was whether or not A is in the mempool already changes the result of an attempt to submit A+B. If A is already there, A is deduplicated and B is accepted, if not, both A and B are rejected.

Gregory Sanders · #6 · · in reply to #4

For readers are home, this is also how it’s done today. I’d forgotten.

Anthony Towns · #7 · · in reply to #4

“There are levels of [good enough] that we are prepared to accept”

Something that works moderately better than what we have today, that only changes the edge cases would be “good enough” in one sense – it doesn’t even have to support package RBF at all to achieve that, eg?

I’d would like to end up with something that can offer some level of an anti-pinning guarantee; like:

That requires that an honest peer actually knows enough to even try relaying the right solution. Are we assuming we’re reconciling the top of the mempool, or rebroadcasting txs when we notice conflicts or something? I don’t really think relying on a good samaritan running custom software is really good enough there…

Depends – if it’s possible to define what the simple cases are, and design your wallet/protocol so you only hit the simple cases, then I think that could be “good enough”.

Anthony Towns · #8 · · in reply to #7

FWIW, I lightly convinced myself that trying to find the best subset of a proposed package is probably too hard (ie, depends on the state of all affected clusters in the mempool, and if replacement is involved, there’s no nice greedy way of working out which part to try first).

So in that case, I think maybe something like this could work?

I think that approach would minimise the spread of unnecessary-evictions, while also being fairly simple. Changing “ancestor” packages to “chunk” packages is a bit hand-wavy, but I don’t think requires different messages compared to the current bip 331 draft?

Push-relay of chunk packages might allow you to propagate hard-to-find optimal chunks across the network though. One way to get “infinite computation” might be to use the p2p network as a distributed system – each node picks a random cluster that’s near the top of the mempool to optimise every now and then, and if they find something good, they spread it out to everyone.

Gregory Sanders · #9 · · in reply to #8

some nodes could also simply choose to run optimal sort up to max cluster size and gossip these

Pieter Wuille · #10 ·

Forum organization question: @sdaftuar and I have a new proposal to replace this one (pretty much the “do it per chunk” like AJ suggested before) with some justification and also a new example that shows its limitations. Should I just replace the description, or is it better to start a new topic?

EDIT: see new topic Post-clustermempool package RBF: per-chunk processing

Anthony Towns · #11 · · in reply to #10

New topic seems like the right choice; it’s also possible to turn a post into a “wiki” but I haven’t tried that out