blindjoin

GitHub Repo | Updated: April 2026

A standalone CoinJoin coordinator and client for Bitcoin signet. Uses RSA blind signatures (RFC 9474) so the coordinator cryptographically cannot link transaction inputs to outputs. MIT licensed. No fees. No company. No terms of service.

Tech Stack: - Rust - RSA blind signatures (RFC 9474, blind-rsa-signatures crate) - BIP-322 (ownership proofs) - PKARR (coordinator discovery via Mainline DHT) - arti-client (Tor hidden service and per-phase circuit isolation) - Docker Compose (full stack deployment)

Why I Built This

CoinJoin is a well-understood technique for improving Bitcoin transaction privacy: multiple participants combine inputs and outputs into a single transaction, breaking the chain-analysis assumption that inputs and outputs belong to the same owner. The challenge is the coordinator — the entity that assembles the transaction can see which input registered which output, and that's a surveillance bottleneck.

RSA blind signatures (RFC 9474) solve this cryptographically. Participants submit their output addresses in blinded form: the coordinator signs the blinded token without seeing the underlying address, and the participant unblinds the signature to prove they have coordinator authorization when registering the output. The coordinator issues the credential and later verifies it, but the two events are mathematically unlinkable. It cannot determine which input produced which output even if it tries.

All production traffic runs over Tor hidden services using arti-client. Coordinator discovery happens through the PKARR DHT — no hardcoded addresses, no central registry. The round uses per-phase Tor circuit isolation: input registration goes through one circuit, output registration through a separate one, so the coordinator cannot correlate phases by Tor circuit either.

How It Works

A blindjoin round follows a five-step protocol. The coordinator announces a fixed-denomination round (default: 0.01 BTC on signet). Participants register their inputs with BIP-322 ownership proofs and receive blind-signed tokens. They then switch to a fresh Tor circuit and register their output addresses using the unblinded tokens. The coordinator builds a PSBT, participants verify and sign their own inputs. Finally, the coordinator broadcasts the completed CoinJoin transaction.

Round Protocol:

Participants → register inputs (BIP-322 proof) → coordinator issues blind-signed tokens
                                                               ↓
                        fresh Tor circuit (alice → bob)
                                                               ↓
Participants → register outputs (unblinded token) → coordinator builds PSBT
        ↓
Participants verify + sign their own inputs
        ↓
Coordinator broadcasts CoinJoin transaction
        ↓
Ephemeral RSA keys destroyed; all round state zeroed from memory

The security model makes explicit what the coordinator can and cannot do. It cannot link inputs to outputs (RSA blind signatures, RFC 9474). It cannot steal funds — participants sign their own inputs. It cannot reconstruct round data after completion — all state is zeroed from memory via the zeroize crate after broadcast. It cannot correlate input and output registration by Tor circuit because the client uses isolated circuits for each phase.

What the coordinator can do: refuse to complete rounds, register sybil inputs (diluted by the minimum participant count), and see which UTXOs registered (which is observable on-chain anyway). Non-signers are detected, banned, and the round restarts with remaining participants.

What I Learned

The theoretical hardness of blind signatures is one thing; getting the implementation right under concurrency pressure is another. Async RPC calls to Bitcoin Core needed to run before acquiring the write lock so a slow node could not serialize all participants waiting on a single lock. RSA keys are parsed once per round rather than per request. Blinded tokens are size-bounded to the RSA modulus to prevent padding oracle attacks.

The PKARR DHT for coordinator discovery turned out to be a natural fit for a tool that otherwise has no central infrastructure. A coordinator publishes its Tor .onion address as a PKARR SignedPacket; clients resolve by coordinator public key. The same discovery mechanism cipherpost and cclink use for key-material transport works here for service advertisement.

Building to signet rather than mainnet was the right call for a v1.0 tool at this stage. The fixed-denomination round (0.01 BTC) and minimum participant count (three) are appropriate constraints for a protocol that requires careful review before handling real funds. The full Docker Compose stack — bitcoind signet, coordinator, liquidity bot — makes it easy to run a complete round locally for development and testing.


The coordinator cannot link what it was never allowed to see.