Skip to main content

Privacy Architecture

RP1 provides optional privacy through zkSNARK proofs, enabling shielded transactions while maintaining auditability.

Privacy Model

┌─────────────────────────────────────────────────────────────┐
│ Public Layer │
│ • Transparent balances │
│ • Auditable transactions │
│ • Standard Cosmos SDK accounts │
└─────────────────────────────────────────────────────────────┘

┌─────┴─────┐
│ Shield │
│ Unshield │
└─────┬─────┘

┌─────────────────────────────────────────────────────────────┐
│ Shielded Layer │
│ • Hidden balances (Pedersen commitments) │
│ • Anonymous transfers (ring signatures) │
│ • Viewing key disclosure (selective transparency) │
└─────────────────────────────────────────────────────────────┘

Core Components

1. Shielded Pool

All private balances are stored as Pedersen commitments:

Commitment = g^amount · h^blinding_factor
  • Hides the actual balance amount
  • Cryptographically binding (can't change amount later)
  • Additively homomorphic (can verify sums without revealing values)

2. ZK Proof Engine

Uses gnark with Groth16 proving system on BN254 curve:

// Circuit constraints
type ShieldCircuit struct {
Amount frontend.Variable `gnark:"amount,public"`
Blinding frontend.Variable `gnark:",secret"`
OldRoot frontend.Variable `gnark:"oldRoot,public"`
NewRoot frontend.Variable `gnark:"newRoot,public"`
Nullifier frontend.Variable `gnark:"nullifier,public"`
}

func (c *ShieldCircuit) Define(api frontend.API) error {
// Verify commitment
commitment := api.Add(
api.Mul(c.Amount, G),
api.Mul(c.Blinding, H),
)
// Verify Merkle proof
// ... additional constraints
return nil
}

3. Ring Signatures (Decoy Mixing)

Provides sender anonymity by mixing with decoy outputs:

Real Input: A → output
Decoy 1: X → (unused)
Decoy 2: Y → (unused)
Decoy 3: Z → (unused)

Ring signature proves: "One of [A,X,Y,Z] authorized this"
(but not which one!)

Configuration:

  • Default ring size: 11 (1 real + 10 decoys)
  • Decoys selected by age-weighted random sampling
  • Prevents timing analysis

4. Viewing Keys

Enables selective disclosure for compliance:

// Viewing key types
type ViewingKeyType =
| "full" // See all transactions and balances
| "incoming" // See only incoming transactions
| "balance" // See current balance only
| "outgoing"; // See only outgoing transactions

// Grant access to auditor
await privacy.grantViewingKey({
grantee: "rp1auditor...",
type: "full",
expiry: "2025-12-31",
});

Transaction Types

Shield (Public → Private)

rp1d tx privacy shield 1000000urp1 --from wallet
  1. Burns public tokens
  2. Creates commitment in shielded pool
  3. Generates ZK proof of correctness
  4. Updates Merkle tree root

Private Transfer

rp1d tx privacy transfer --to rp1recipient... --amount 500000
  1. Proves ownership via nullifier (prevents double-spend)
  2. Creates new commitment for recipient
  3. Ring signature hides sender
  4. ZK proof verifies amount conservation

Unshield (Private → Public)

rp1d tx privacy unshield 500000urp1
  1. Reveals nullifier (marks note as spent)
  2. ZK proof shows valid note ownership
  3. Mints public tokens
  4. Updates Merkle tree

Cryptographic Primitives

PrimitiveImplementationPurpose
HashMiMCZK-friendly hashing
CommitmentPedersenHide amounts
Proving SystemGroth16Succinct proofs
CurveBN254Efficient pairings
SignaturesRing/MLSAGSender anonymity

Proof Verification

Proof verification is O(1) regardless of circuit complexity:

// Verification cost is constant
func VerifyProof(vk VerifyingKey, proof Proof, publicInputs []fr.Element) bool {
// ~2ms verification time
return groth16.Verify(proof, vk, publicInputs)
}
OperationProof SizeVerify Time
Shield256 bytes~2ms
Transfer256 bytes~2ms
Unshield256 bytes~2ms

Privacy Guarantees

PropertyGuaranteedNotes
Amount hidingCommitments hide values
Sender anonymityRing signatures
Recipient privacyStealth addresses
Transaction unlinkabilityOne-time keys
Timing analysis resistance⚠️Depends on network activity

Compliance Features

Viewing Key Audit Trail

type ViewingKeyGrant struct {
Grantee string // Who received access
GrantType string // What they can see
GrantedAt time.Time // When granted
ExpiresAt time.Time // When access expires
RevokedAt time.Time // If revoked early
}

Threshold Disclosure

Require M-of-N authorities to decrypt:

// Configured per account
ThresholdConfig{
Threshold: 3, // 3 of 5 required
Authorities: []string{
"rp1authority1...",
"rp1authority2...",
"rp1authority3...",
"rp1authority4...",
"rp1authority5...",
},
}

Best Practices

  1. Wait for decoys: Let your transaction age before spending for better anonymity
  2. Use standard amounts: Round numbers provide better mixing
  3. Regular shielding: Don't shield immediately before spending
  4. Viewing key rotation: Periodically rotate keys for auditors

Next Steps