Skip to content

[release-v2.1] main: Use backported mixing updates.#3659

Merged
davecgh merged 20 commits intodecred:release-v2.1from
davecgh:rel21_mixing_backports
Apr 6, 2026
Merged

[release-v2.1] main: Use backported mixing updates.#3659
davecgh merged 20 commits intodecred:release-v2.1from
davecgh:rel21_mixing_backports

Conversation

@davecgh
Copy link
Copy Markdown
Member

@davecgh davecgh commented Apr 6, 2026

This updates the 2.1 release branch to use the latest version of the mixing module which includes various optimizations, orphan source tracking, proactive orphan eviction, tighter standardness limits on mixing message sizes, and stricter rejection of malformed mixing pair requests.

In particular, the following updated module version is used:

  • github.com/decred/dcrd/mixing@v0.7.0

Note that it also cherry picks all of the commits included in updates to the mixing module to ensure they are also included in the release branch even though it is not strictly necessary since go.mod has been updated to require the new release and thus will pull in the new code. However, from past experience, not having code backported to modules available in the release branch too leads to headaches for devs building from source in their local workspace with overrides such as those in go.work.

The following PRs are included:

davecgh added 20 commits April 6, 2026 13:19
x25519 was the ECDH used in the older client-server mixing implementation, but
this was replaced with secp256k1 ECDH in the peer-to-peer implementation.
Remove a lingering reference to x25519 that didn't get updated.
This adds benchmarks for signing and validating utxo proofs.
Sum(nil) creates an unnecessary heap allocation appending the digest to the
nil slice.  Replace these calls with direct calls to the
(*blake256.Hasher256).Sum256 method to avoid unnecessary allocations and
copies.

While here, use the hasher's specialized Write* methods in the utxoproof
package to improve readability.
Creating the hash to verify the schnorr signature can be done without causing
any allocations.  This has also been moved out into a new schnorrHash helper
function to reuse across both signing and verification.
This modifies the debug log for evicted mempool orphans to explicitly
call out that it is referring to mempool orphans to avoid any potential
confusion between orphans of other types that exist (for example mixpool
orphans).
This renames the internal rpcserver SubmitMixMessage interface method to
AcceptMixMessage to more accurately reflect what it actually does and
updates the comments accordingly.

The comments also previously incorrectly claimed that the method submits
the message to the network after it processes it, but that is handled by
RelayMixMessages instead.
This makes use of the slices.Contains method to determine when a key
exchange orphan references a pair request that has been added during
orphan reconsideration.
This renames the type used for orphan messages to orphanMsg to avoid
shadowing the name with variables.  The name "orphan" is better for
variables.
The current behavior when adding orphans is slightly different for KEs
and other types of mixing messages.  Specifically, orphan KEs overwrite
any existing entries while other types do not.  The preferred behavior
is to keep existing orphans so that the timestamps reflect the time they
were first added, and, in the future, any other data associated with the
orphan also reflects when it was first seen.

Instead of just adding an additional check for KEs to remedy the
aforementioned, this consolidates the logic for adding mixpool orphans
by introducing a new function named addOrphan and updates the places
that add orphans to use it.  The new function will only add orphans when
they are not already in the pool.

This approach simplifies future modifications to the logic related to
adding orphans, ensures the same logic is consistently applied
regardless of the path leading up to the addition of an orphan, and is
more consistent with the how the long standing and battle tested
existing code in the mempool handles adding orphans.
This consolidates the logic for removing mixpool orphans by introducing
a new function named removeOrphan and updates the places that remove
orphans to use it.

This approach simplifies future modifications to the logic related to
removing orphans, ensures the same logic is consistently applied
regardless of the path leading up to the removal of an orphan, and is
more consistent with the how the long standing and battle tested
existing code in the mempool handles removing orphans.

It is perhaps worth noting that there is a very slight tradeoff in terms
of a few extra map length checks and removals that the existing code was
able to avoid in some circumstances due to having specialized knowledge,
but the difference is entirely negligible and does not warrant the
downsides to splitting the removal logic all over the code.
This modifies the map that tracks orphans by their associated identity
to point to the entire orphan struct instead of only the mixing message
associated with it.

This will allow any additional data associated with the orphan message
to be immediately available without needing another orphans map lookup.
This allows a caller-provided source to be associated with mix messages.
This is useful for things such as keeping track of which peers messages
were first seen from and grouping messages on per-peer source.

This currently only keeps track of the source with orphans to pave the
way for some upcoming changes related to orphans.  It might also be
useful in the future to store it with messages in the main pool too, but
that is not done for now since there are no immediate plans to make use
of it.
In addition to the existing logic which evicts orphans once an epoch has
expired and when the peer that sent the orphans disconnects, this
implements proactive eviction of mixing message orphans once the orphan
pool grows to a maximum limit.

The eviction algorithm works as follows:

- Determine the source peer that sent the most remaining active orphans
- Remove as many orphans sent by that peer as possible until the orphan
  pool size reaches 75% of the maximum allowed amount
- Repeat the previous two steps as many times as necessary to reach the
  target pruned orphan pool size

In short, it prioritizes removing the orphans sent by the peer that sent
the most.

This approach was chosen because it is fairly efficient and, in
practice, orphan messages are quite rare after initial startup when
ongoing mixing sessions are discovered, so any peer sending a lot of
orphans is likely experiencing severe connectivity issues or otherwise
misbehaving. It also has the added benefit of handling a variety of
orphan flooding misbehavior well.

A comprehensive set of tests is included to ensure the behavior works as
expected.
This avoids reusing the same initial seed for multiple tests when testing with -count greater than 1.

The -seed flag now requires providing the initial nonce in the string, so that
the failing test can be reproduced with -count=1.  If a test fails, the flag
to reproduce is logged at the end of the output.
The cleanup function returned by useTestLogger must disable writes to the
backend to prevent client goroutines still running after the test finishes
from writing to the old *testing.T logger and panicking.
Tests run on an increased schedule by artifically ticking the epoch.  This
would occasionally result in a disruption tests hanging due to the two clients
involved being ticked at roughly the same time but continuing with a different
Unix epoch.  There was a roughly 50% chance that when this happened, the test
would hang, depending on which of the two clients contained the misbehaving
peer.

Test reliability has been improved by passing same time.Time value with the
intended epoch to the testTickC channel by the test function.  Client code
also signals to the tests when it is waiting for a test epoch, allowing the
test to wait for all clients before proceeding with the current time as the
epoch.
This commit places stricter limits on message sizes without changes to wire
protocol.  These reduced limits help avoid memory exhaustion when mixing
messages must be saved to the orphan pool.
PRs which duplicated inputs would allow for low cost entry to the mixpool and
could be used to consume excessive memory resources.
This updates the 2.1 release branch to use the latest version of the
mixing module which includes various optimizations, orphan source
tracking, proactive orphan eviction, tighter standardness limits on
mixing message sizes, and stricter rejection of malformed mixing pair
requests.

In particular, the following updated module version is used:

- github.com/decred/dcrd/mixing@v0.7.0
@davecgh davecgh added this to the 2.1.4 milestone Apr 6, 2026
@davecgh davecgh merged commit f27aa60 into decred:release-v2.1 Apr 6, 2026
34 checks passed
@davecgh davecgh deleted the rel21_mixing_backports branch April 6, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants