Activity-matched decoy sampler (D5) (fixes #95, stacks on #89) - #96
Closed
liqdmetal wants to merge 2 commits into
Closed
Activity-matched decoy sampler (D5) (fixes #95, stacks on #89)#96liqdmetal wants to merge 2 commits into
liqdmetal wants to merge 2 commits into
Conversation
liqdmetal
force-pushed
the
feature/activity-matched-sampler
branch
from
August 23, 2026 03:26
f697460 to
38c1460
Compare
This was referenced Aug 23, 2026
…dor noise) Daemon GetRandomAddressBatch (<=512 real accounts with encrypted balances, one RPC call, 5-block filter removed); wallet Random_ring_members_batch() with CSPRNG Fisher-Yates draw. Effect: no per-decoy round-trips; daemon posterior collapses to 1/C(B,R).
…h RPC) DecoyModel/DecoyBin quantile model; recency from NonceHeight (plaintext in balance tree); SelectDecoys weighted CSPRNG draw without replacement. Effect: decoy distribution tracks observed mainnet activity (~±5%).
liqdmetal
force-pushed
the
feature/activity-matched-sampler
branch
from
August 25, 2026 01:32
d7baf2f to
b5dd0ed
Compare
Author
|
Superseded by PR #89 — the activity-matched sampler is now folded into the decoy-selection package (batch RPC + sampler as one feature). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
D5 from the decoy-activity-distribution spec: decoy sampling that matches the real participant activity distribution, so an observer cannot distinguish real sender/receiver from decoys by on-chain activity alone — the OSPEAD analog for DERO's account model. Pure client logic, no consensus impact.
The leak it fixes
The current decoy pool is "accounts not seen in any ring for the last 5 blocks" — guaranteed-dormant. Real participants (settlement wallets, AMMs, recurring users) appear in rings far more often. An observer computing per-account ring-appearance history scores each ring member by "how unlike a decoy is this account's activity?" — the posterior skews away from uniform, and effective anonymity is smaller than ring size.
Measured (D2, 3.6k-block scan): participants skew heavily recent (13.6% in 0–5 blocks, 24.7% in 5–10) while the decoy pool is guaranteed-dormant — p/d = ∞ in bin 0–5.
The posterior is uniform iff decoys are drawn from the same distribution as real participants (spec §3.1). This module implements that match.
The implementation
walletapi/decoy_sampler.go:DecoyModel— a compact published bin table (blocks-since-last-appearance× weight), sourced from the D2 direct estimator (participant density p(x) recovered from ringsize-2 members — the only rings where both members are real, so no deconvolution noise; the naive mixture estimator was rejected by D4 validation)candidateRecency— extracts blocks-since-last from the candidate's embedded NonceBalance (uvarint NonceHeight + ElGamal)SelectDecoys— weighted draws without replacement, probability ∝ bin weight; zero-weight candidates never drawnPlus a decode fix in the batch path: the tree value is NonceBalance (varint NonceHeight + 66B ElGamal), not a bare ElGamal; and
NonceBalance.Unmarshalpanics on malformed input, so it must be recover-guarded — a wallet must never panic on a daemon's malformed batch.Tests (
walletapi/decoy_sampler_test.go)TestCandidateRecency— extraction + malformed → 0TestSelectDecoysMatchesModel— sampled distribution matches model weights (recent share ~0.67 vs expected 0.714 on a 10:1 model, within finite-population tolerance); zero-weight candidates excludedModel publication (D6, follow-up)
The model file is a published dataset (quantile/bin table, kilobytes) re-published per epoch from the D2 estimator. Model drift (spec §6.3) is handled by the publication cadence; a stale model re-creates the leak, so this is an operational commitment, not a code change.
Honest limits
Relationship
decoy-activity-distribution.md(D5), companion todecoy-selection-batch-rpc.mdBranch:
feature/activity-matched-samplerin the forkliqdmetal/derohe-improvements-by-liqdmetal(stacked onfeature/decoy-batch-rpc). Carries the build-manifest fix (go.mod/go.sum).