getting started

Key Registration

Deriving and registering protocol keys

Key Hierarchy#

Keys are derived deterministically from a wallet signature. This means keys are portable — you can recover them on any device by signing the same message with your Solana wallet.

Key Loss

If you lose access to your Solana wallet, you lose access to all funds in the anonymity pool. There is no recovery mechanism. Store your seed phrase securely.

Derivation#

The derivation uses a single signMessage call:

signature = wallet.signMessage("Mini Veil master keys")
masterSeed = SHA-256(signature)

spendingPriv = SHA-256("mini-veil-spending" || masterSeed)  → 32 bytes
viewingPriv  = SHA-256("mini-veil-viewing"  || masterSeed)  → 32 bytes

spendingPub  = Ed25519.getPublicKey(spendingPriv)  → 32 bytes
viewingPub   = X25519.getPublicKey(viewingPriv)    → 32 bytes

Extended Key Hierarchy#

The core SDK (@mini-veil/corenpm package coming soon) supports an extended hierarchy via generateVeilKeys(seed?):

L1 Ed25519 keypair (Solana key)
  ├── X25519 keypair
  ├── spendingKey (field element)
  │    └── spendingBlinding (field element)
  ├── viewingKey (field element)
  │    └── viewingBlinding (field element)
  └── userCommitment = Poseidon(
        Poseidon(l1Low, l1High),
        Poseidon(
          Poseidon(viewingKey, viewingBlinding),
          Poseidon(spendingKey, spendingBlinding)
        )
      )

On-chain Registration#

Keys are registered on-chain via the register_keys instruction:

register_keys(
  x25519_public_key: [u8; 32],  // viewing public key
  user_commitment: [u8; 32],     // spending public key
)

This creates a KeyRegistry PDA at seed ["key-registry", owner] storing:

| Field | Size | Description | |---|---|---| | owner | 32 bytes | Wallet address | | x25519_public_key | 32 bytes | X25519 viewing public key | | user_commitment | 32 bytes | Ed25519 spending public key | | bump | 1 byte | PDA bump seed |

Key Caching#

Once derived, keys are cached in localStorage under mini-veil-keys-{wallet_address} with the following fields:

  • spendingPrivKey
  • viewingPrivKey
  • spendingPubKey
  • viewingPubKey
  • mixerSpendingKey (field element used for nullifier derivation)

Proactive Registration Check#

The dashboard performs a proactive key registration check with a 500ms debounced useEffect:

  1. Derive keys from wallet signature
  2. Check if KeyRegistry PDA exists on-chain
  3. Compare derived userCommitment against on-chain value
  4. If mismatched or missing, prompt user to register
  5. Registration cost: ~0.005 SOL (5,000,000 lamports)

Key Usage#

| Key | Used For | |---|---| | Spending key | Nullifier derivation, ZK proof generation | | Viewing key (private) | Scanning stealth announcements, decrypting payments | | Viewing key (public) | Sender encrypts ephemeral key for recipient | | Spending public key | Recipient binds stealth address to this key |