Skip to content

Instantly share code, notes, and snippets.

@aruokhai
Last active March 16, 2026 05:46
Show Gist options
  • Select an option

  • Save aruokhai/b91c1393334772981690aa95c0d81a4c to your computer and use it in GitHub Desktop.

Select an option

Save aruokhai/b91c1393334772981690aa95c0d81a4c to your computer and use it in GitHub Desktop.
arkade-auth.md

Pseudonymous Authentication for Arkade Services Using Ark Batches as Anonymity Sets


The Problem

Arkade is an ecosystem of Bitcoin services built on the Ark Protocol, where users hold Virtual Transaction Outputs (VTXOs) that are periodically refreshed through coordinated rounds managed by an Ark Service Provider (ASP). Each round produces a VTXO tree whose root is committed on-chain in a Bitcoin transaction, establishing a publicly verifiable, Bitcoin-anchored record of all participants in that round.

As Arkade grows and services like LendASat emerge, a fundamental authentication gap surfaces. The protocol coordinates VTXO ownership and refresh cycles efficiently, but it provides no mechanism for Arkade services to answer three questions simultaneously: is this user a legitimate Ark participant with a real VTXO in a committed round batch, has this user already registered with this service under a different identity from the same VTXO, and can this user authenticate on return visits without re-proving round membership every time?

The naive approach — identifying users by their VTXO public key — answers the first two questions but destroys privacy entirely. A VTXO public key is linkable across services. Any two Arkade services that compare their user lists can immediately determine they are dealing with the same participant. The ASP, who constructs the VTXO tree and posts the commitment transaction, can correlate service registrations with on-chain identity trivially. This is structurally equivalent to requiring users to authenticate with their Bitcoin address — functional, but collapsing the pseudonymity that motivates building on Ark.

The core tension is that Arkade services need two cryptographic guarantees that appear contradictory. They need Sybil resistance — a binding between a VTXO and exactly one service identity, preventing a single participant from opening multiple accounts using the same VTXO to drain lending liquidity, abuse service terms, or manipulate service state. And they need cross-service unlinkability — service-specific identities that are computationally independent of each other, so no coalition of Arkade services can collaborate with the ASP to reconstruct a participant's activity profile across the ecosystem.


The Construction

We apply the CRS-ASC construction introduced by Alupotha et al. in Anonymous Self-Credentials and their Application to Single-Sign-On directly to the Ark round architecture.

Each user derives a master credential from their VTXO secret key vtxo_sk. Specifically, they derive L service-specific nullifier scalars using HKDF — one per registered Arkade service — and commit to all of them simultaneously in a single multi-value Pedersen commitment:

s_l  = HKDF(vtxo_sk, service_id_l)    // nullifier scalar for service l
Φ    = k·G + s_1·H_1 + s_2·H_2 + ... + s_L·H_L

This commitment Φ is the master identity. It is a single 33-byte elliptic curve point that cryptographically locks in one nullifier per Arkade service, all derived deterministically from the VTXO secret key, without revealing any of them.

The collection of master identities from all participants in an Ark round forms the anonymity set:

Λ = [Φ_1, Φ_2, ..., Φ_N]

A Merkle tree is built over this list and its root R = MerkleRoot(Φ_1, ..., Φ_N) is committed in the Ark round's commitment transaction on Bitcoin. This gives the anonymity set a Bitcoin-anchored, immutable, publicly verifiable on-chain anchor at zero marginal cost — the commitment transaction was going to be posted anyway as part of normal Ark operation. Any Arkade service can fetch R from Bitcoin and be certain of the exact set of master identities that constitute the anonymity set for that round.

Membership proofs over Λ use the Bootle et al. one-out-of-many proof — operating over the flat list of Pedersen commitments directly. The prover binary-decomposes their index j into n = log N bits, commits to each bit, and uses a polynomial identity to prove the linear combination of all N shifted commitments collapses to a commitment to zero at a random challenge point. This produces an O(log N) proof — approximately 4KB for N=1024 — with no trusted setup, under the DDH assumption over SECP256K1.

Service-specific authentication credentials are derived independently from a separate root:

csk_S  = HKDF(vtxo_rnd, service_id_S)   // child secret key
nym_S  = csk_S · G                       // pseudonym

The separation of vtxo_sk (used for nullifiers) from vtxo_rnd (used for child credentials) ensures that a compromised service-specific authentication key does not leak the VTXO secret key or any other service's nullifier.


Concrete Example — LendASat

LendASat is a Bitcoin lending service on Arkade. Its threat model requires that a single participant cannot open multiple borrowing accounts from the same VTXO to extract more liquidity than their stake entitles them to, while its privacy model requires that it learns nothing linkable about a borrower beyond their loan history under a pseudonym.

Suppose an Ark round closes with 128 participants. Each participant has already derived their master identity commitment Φ_i from their VTXO secret key and contributed it to the batch. The ASP builds the Merkle tree over Λ = [Φ_1, ..., Φ_128], posts the commitment transaction with root R to Bitcoin, and the round is settled.

A user whose master identity is Φ_j at index j in the batch derives their LendASat credentials:

s_LendASat   = HKDF(vtxo_sk_j, "LendASat")
nul_LendASat = s_LendASat · G
csk_LendASat = HKDF(vtxo_rnd_j, "LendASat")
nym_LendASat = csk_LendASat · G

They apply the commitment to zero reduction — computing D_i = Φ_i - s_LendASat · H_LendASat for all 128 commitments — which shifts D_j to have a zero component at the LendASat position while leaving all other D_i non-zero. They then run the Bootle et al. prover over the shifted list, binary-decomposing j into 7 bits, committing to each bit and mask, and producing the polynomial identity proof. The Fiat-Shamir challenge incorporates nym_LendASat to bind the proof to a specific pseudonym. The resulting proof π is approximately 4KB.

The user sends (nym_LendASat, nul_LendASat, π, R) to LendASat. LendASat fetches Λ using R as the anchor — either from its local cache or by querying the Bitcoin commitment transaction — recomputes the shifted commitments, evaluates the Bootle et al. verification equations, checks nul_LendASat is not in its nullifier registry, and registers nym_LendASat as a new borrowing account. The loan is opened.

On every return visit, LendASat sends a random challenge W. The user responds with σ = Schnorr.Sign(csk_LendASat, W). LendASat verifies against nym_LendASat. No ZK proof, no Ark interaction, no Bitcoin query. The expensive proof generation happened exactly once at registration.

If the user attempts to open a second account from the same VTXO, the Bootle et al. proof will produce the same nul_LendASat — the nullifier is deterministic from vtxo_sk_j and the service identifier. LendASat finds it in its registry and rejects the attempt. A second account requires a new VTXO from a new Ark round, at the normal cost of Ark participation.

If LendASat shares its data with another Arkade service and the ASP to attempt cross-service linking, they find two nullifiers nul_LendASat and nul_Other that are computationally indistinguishable from independent uniform random scalars by the HKDF unlinkability property, two independent pseudonyms with no algebraic relationship, and ZK proofs that reveal nothing about the leaf index j. There is no linking information to extract.


Registration and Authentication Flow

ARK ROUND CLOSES
  ASP builds Merkle tree over Λ = [Φ_1, ..., Φ_N]
  Posts commitment transaction with root R to Bitcoin
         |
         v
USER DERIVES LENDASAT CREDENTIALS (local, offline)
  s_LendASat   = HKDF(vtxo_sk, "LendASat")
  nul_LendASat = s_LendASat · G
  csk_LendASat = HKDF(vtxo_rnd, "LendASat")
  nym_LendASat = csk_LendASat · G
         |
         v
USER GENERATES BOOTLE et al. PROOF (local, offline)
  Compute D_i = Φ_i - s_LendASat · H_LendASat  for all i
  Binary-decompose index j into n = log N bits
  Commit to bits and masks
  Produce polynomial identity proof
  Bind to nym_LendASat via Fiat-Shamir
         |
         v
USER SENDS (nym_LendASat, nul_LendASat, π, R) TO LENDASAT
         |
         v
LENDASAT VERIFIES
  Fetch Λ from cache or Bitcoin using R
  Recompute shifted commitments D_i
  Verify Bootle et al. proof π
  Check nul_LendASat not in nullifier registry
  Register nym_LendASat → open loan account
         |
         v
RETURN VISITS — SIGNATURE ONLY
  LendASat sends challenge W
  User signs: σ = Schnorr.Sign(csk_LendASat, W)
  LendASat verifies: Schnorr.Verify(nym_LendASat, W, σ)

What Each Party Learns

LendASat Learns LendASat Does NOT Learn
Identity A pseudonym nym_LendASat for future authentication Which of the N VTXOs in the batch belongs to this user
Participation User holds a master identity in round batch at root R The user's vtxo_pk or any on-chain footprint
Sybil This VTXO has not registered before (via nullifier) Whether this user uses Flash, Blink, or any other Arkade service
History Loan history under the pseudonym Anything linkable across Arkade services

Properties

Sybil resistance — The binding property of the Pedersen commitment means Φ_j can only be opened to one nullifier scalar at position l. Knowledge soundness of the Bootle et al. proof ensures a valid proof can only be generated by someone who knows the opening of some commitment in Λ. Together these guarantee one valid nullifier per VTXO per service.

Cross-service unlinkability — The HKDF unlinkability property ensures HKDF(vtxo_sk, S) and HKDF(vtxo_sk, S') are computationally indistinguishable from independent uniform random values for any two distinct service identifiers S and S'. The hiding property of the Pedersen commitment and the zero-knowledge property of the Bootle et al. proof ensure no information about any other nullifier or the leaf index leaks from a registration.

Bitcoin-anchored anonymity set — The Merkle root of the master identity list is committed in the Ark round's commitment transaction, giving the anonymity set Bitcoin-level finality and immutability at zero additional on-chain cost.

Lightweight return authentication — Registration requires one Bootle et al. proof generation of approximately 4KB. All subsequent sessions require only a Schnorr signature verification, keeping ongoing authentication overhead negligible.

Natural anonymity set growth — Anonymity set size scales directly with Ark adoption. Larger rounds mean larger batches and stronger anonymity, without any changes to the authentication construction.


Considerations For Ark Transactions

Identity gating through batch participation. A VTXO in set F carries no corresponding master identity Φ in any anonymity set. The construction requires Φ to be a leaf in a Merkle tree whose root R is committed in a confirmed Bitcoin transaction — which only happens at batch close. A user who receives funds via an Ark transaction and is not currently a participant in a confirmed batch cannot register with any Arkade service. They must explicitly request a batch swap from the ASP, participate in the round signing session, and wait for the resulting commitment transaction to confirm on Bitcoin. Only then does their fresh Φ — derived from the new VTXO secret key issued in the batch swap — enter a closed anonymity set and become usable for service registration. This ensures every identity in the anonymity set is anchored to a real, confirmed Bitcoin transaction, not merely to an offchain virtual output that may never settle.

Amount-identity decoupling within a batch. A service provider has no way of identifying a real user that still has enough funds, because the amount may have been totally spent in a series of Ark transactions after the batch was confirmed. What the batch proves is that there existed, at the time the batch was created, a user who was in this batch — nothing more. The credential attests to past participation, not current economic state.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment