Part of #30289.
TxGraph’s fundamental responsibility is deciding the order of transactions in the mempool. It relies on the cluster_linearize.h code to optimize it, but there can and often will be many different orderings that are essentially equivalent from a quality perspective, so we have to pick one. At a high level, the solution will involve one or more of:
- Deciding based on internal identifiers (
Cluster::m_sequence,DepGraphIndex). This is very simple, but risks leaking information about transaction receive order. - Deciding randomly, which is private, but may interfere with relay expectations, block propagation, and ability to monitor network behavior.
- Deciding based on txid, which is private and deterministic, but risks incentivizing grinding to get an edge (though we haven’t really seen such behavior).
- Deciding based on size (e.g. prefer smaller transactions), which is somewhat related to quality, but not unconditionally (depending on mempool layout, the ideal ordering might call for smaller transactions first, last, or anywhere in between). It’s also not a strong ordering as there can be many identically-sized transactions. However, if it were to encourage grinding behavior, incentivizing smaller transactions is probably not a bad thing.
As of #32545, the current behavior is primarily picking randomly, though inconsistently, as some code paths also use internal identifiers and size. #33335 sought to change it to use random (preferring size in a few places), with the downsides listed above.
This PR is an alternative to that, which changes the order to tie-break based on size everywhere possible, and use lowest-txid-first as final fallback. This is fully deterministic: for any given set of mempool transactions, if all linearized optimally, the transaction order exposed by TxGraph is deterministic.
The transactions within a chunk are sorted according to:
PostLinearize(which improves sub-chunk order), using an initial linearization created using the rules 2-5 below.- Topology (parents before children).
- Individual transaction feerate (high to low)
- Individual transaction weight (small to large)
- Txid (low to high txid)
The chunks within a cluster are sorted according to:
- Topology (chunks after their dependencies)
- Chunk feerate (high to low)
- Chunk weight (small to large)
- Max-txid (chunk with lowest maximum-txid first)
The chunks across clusters are sorted according to:
- Feerate (high to low)
- Equal-feerate-chunk-prefix weight (small to large)
- Max-txid (chunk with lowest maximum-txid first)
The equal-feerate-chunk-prefix weight of a chunk C is defined as the sum of the weights of all chunks in the same cluster as C, with the same feerate as C, up to and including C itself, in linearization order (but excluding such chunks that appear after C). This is a well-defined approximation of sorting chunks from small to large across clusters, while remaining consistent with intra-cluster linearization order.