Skip to main content

Stealth Addresses

Specter implements an ERC-5564 compatible stealth address system for private, one-time recipient addresses. Stealth addresses allow a sender to create a fresh, unlinkable address for every transfer — ensuring that on-chain observers cannot determine who received a payment, even when the sender and recipient are known entities.

This system complements the Ghost Protocol. Where the Ghost Protocol provides privacy through commit/reveal unlinkability (vanish and summon are unconnectable), stealth addresses provide privacy at the transfer layer — hiding the recipient of a direct on-chain token transfer.

How Stealth Addresses Work

The stealth address scheme uses Elliptic Curve Diffie-Hellman (ECDH) key agreement on the secp256k1 curve. The recipient publishes a meta-address (a pair of public keys) once, and senders derive unique one-time addresses from it without any interaction.

Key Components

ComponentDescription
Spending key (p_spend)Recipient's long-term private key for claiming funds
Viewing key (p_view)Recipient's private key for scanning and identifying incoming transfers
Meta-addressPublic keys (P_spend, P_view) published once by the recipient
Ephemeral keypairOne-time keypair (r, R) generated by the sender for each transfer
Stealth addressOne-time address derived from ECDH shared secret
View tagSingle-byte hint for efficient scanning

Protocol Flow

Step-by-Step Breakdown

1. Sender generates an ephemeral keypair

The sender creates a random scalar r and computes the corresponding public point R = r * G, where G is the secp256k1 generator. This keypair is used only once and discarded after the transfer.

2. Sender computes the one-time stealth address via ECDH

Using the recipient's published meta-address (P_spend, P_view), the sender computes a shared secret:

s = r * P_view

The stealth public key is derived as:

P_stealth = P_spend + hash(s) * G

The stealth address is the Ethereum-style address of P_stealth. This address has never appeared on-chain before and is unlinkable to the recipient's known addresses.

3. Sender calls GhostStealthAnnouncer

The sender invokes the GhostStealthAnnouncer contract, which:

  • Transfers tokens to the computed stealth address.
  • Records the transfer metadata — the ephemeral public key R and a view tag (first byte of hash(s)) — in an on-chain storage array.

4. Recipient scans the on-chain transfers array

The recipient periodically reads the transfers array from the GhostStealthAnnouncer contract. For each entry:

  • View tag pre-filtering: The recipient computes hash(p_view * R_entry) and checks if the first byte matches the stored view tag. This is a cheap check that filters out ~99.6% of non-matching transfers without performing the full ECDH computation.
  • Full ECDH verification: For entries that pass the view tag filter, the recipient performs the complete shared secret derivation and verifies the stealth address matches.

5. Recipient derives the stealth private key and claims funds

Once a matching transfer is identified, the recipient derives the stealth private key:

p_stealth = p_spend + hash(s)

This private key controls the stealth address. The recipient signs a transaction with it to claim the funds.

On-Chain Array Storage

A standard ERC-5564 implementation on Ethereum uses event logs for the announcement mechanism — the sender emits an event containing the ephemeral public key and view tag, and the recipient scans event logs using eth_getLogs.

Specter uses on-chain array storage instead of event logs. This is a deliberate design choice driven by a limitation of the Cosmos EVM environment:

ApproachEthereumSpecter (Cosmos EVM)
Announcement mechanismEvent logsStorage array
Scanning methodeth_getLogs RPCDirect storage reads
WhyEfficient log indexing availableeth_getLogs not reliably supported in Cosmos EVM
Trade-offLower storage cost, index-dependentHigher storage cost, no indexer dependency

The GhostStealthAnnouncer contract maintains a transfers array in contract storage. Each entry contains:

struct StealthTransfer {
address ephemeralPubKeyAddr; // Compressed ephemeral public key
uint8 viewTag; // First byte of ECDH shared secret hash
address stealthAddress; // Derived one-time address
uint256 amount; // Transfer amount
}

Recipients scan this array directly via eth_call (read-only RPC) without needing a log indexer.

View Tag Optimization

Scanning all on-chain transfers requires an ECDH computation per entry, which becomes expensive as the transfer count grows. The view tag provides a 1-byte probabilistic filter:

The view tag eliminates 255/256 (~99.6%) of non-matching entries from requiring full ECDH computation, reducing scanning cost by approximately two orders of magnitude.

Integration with the Ghost Protocol

Stealth addresses and the Ghost Protocol provide complementary privacy guarantees. They can be composed for layered privacy:

Flow: A sender transfers tokens to a stealth address (hiding the recipient). The recipient claims the stealth funds and immediately vanishes them into the Ghost Protocol (hiding the amount and breaking the transaction graph). Later, the recipient summons the value to any address (hiding the link between claim and spend).

This composition provides:

  1. Recipient privacy — the stealth address hides who received the initial transfer.
  2. Amount privacy — the vanish operation commits the value without revealing it.
  3. Graph privacy — the summon operation is unlinkable to the vanish, breaking the transaction trail.

Privacy Properties

PropertyGuarantee
Recipient unlinkabilityEach transfer uses a unique one-time address; no on-chain link to recipient's known addresses
Sender privacySender's address is visible on-chain (stealth addresses hide recipients, not senders)
Transfer amountVisible on-chain in the current implementation
Ephemeral key reuseEach transfer MUST use a fresh ephemeral keypair; reuse breaks recipient privacy
Meta-address publicationPublished once; does not reveal spending key or viewing key
Scanning privacyOnly the viewing key holder can identify their transfers; requires reading on-chain data

Security Considerations

  • Ephemeral key uniqueness: If a sender reuses an ephemeral keypair (r, R) for two transfers to different recipients, both recipients can compute the same shared secret. This does not directly compromise funds but leaks the fact that both transfers came from the same sender with the same ephemeral randomness. Clients MUST generate a fresh ephemeral keypair per transfer.
  • View tag collisions: The 1-byte view tag produces false positives at a rate of ~1/256. This is a performance optimization, not a security mechanism. False positives are resolved by the full ECDH check.
  • Scanning cost: As the transfers array grows, scanning cost increases linearly. Future optimizations may include sharded announcement contracts or off-chain indexing with Merkle proofs of inclusion.