Skip to content

Viewing Keys

Viewing keys enable selective disclosure of shielded transaction details to authorized parties without compromising overall privacy.

Use CaseDescription
Tax ComplianceShare transaction history with tax authority
AuditAllow auditor to verify specific transactions
InstitutionalMeet regulatory requirements
Dispute ResolutionProve transaction occurred to third party
InheritanceGrant read access to estate executor
flowchart LR
    subgraph Full["Full Privacy (Shielded)"]
        U1["User"] --> T1["Transactions"]
    end

    subgraph Selective["Selective Disclosure"]
        U2["User"] --> T2["Transactions"]
        V2["Authorized Viewer"] --> T2
    end

    subgraph Compliant["Compliant Mode"]
        U3["User"] --> T3["All Transactions"]
        A3["Auditor"] --> T3
    end

    style Full fill:#4c1d95,stroke:#a78bfa,stroke-width:2px
    style Selective fill:#312e81,stroke:#8b5cf6
    style Compliant fill:#1e1b4b,stroke:#8b5cf6
flowchart TB
    SEED["User Seed"] --> MVK["Master Viewing Key (MVK)"]

    MVK --> FVK["Full Viewing Key (FVK)"]
    MVK --> AK["Auditor Key (AK)"]
    MVK --> TVK["Transaction Viewing Keys (TVK)"]

    FVK --> FVK_DESC["Sees ALL transactions"]
    AK --> AK_DESC["Sees ALL txs, time-limited"]
    TVK --> TVK_DESC["Sees SINGLE transaction"]

    style SEED fill:#1e1b4b,stroke:#8b5cf6
    style MVK fill:#4c1d95,stroke:#a78bfa,stroke-width:2px
    style FVK fill:#ef4444,stroke:#f87171
    style AK fill:#f59e0b,stroke:#fbbf24
    style TVK fill:#22c55e,stroke:#86efac

    style FVK_DESC fill:none,stroke:none
    style AK_DESC fill:none,stroke:none
    style TVK_DESC fill:none,stroke:none

Root key derived from user’s wallet seed:

const mvk = generateViewingKey('/m/44/501/0')

Grants access to ALL transactions:

const fvk = deriveViewingKey(mvk, '/full')

Warning: FVK exposure reveals entire transaction history.

Time-limited viewing key for compliance:

const auditKey = deriveViewingKey(mvk, '/audit/2024')

Per-transaction key for selective disclosure:

const tvk = deriveViewingKey(mvk, `/tx/${intentHash}`)

Reveals ONLY the specific transaction.

ComponentAlgorithm
Key derivationHKDF-SHA256
Symmetric encryptionXChaCha20-Poly1305
Nonce24 bytes random
import { encryptForViewing, decryptWithViewing } from '@sip-protocol/sdk'
const txData = {
sender: '0xabc...',
recipient: '0xdef...',
amount: 1000n,
asset: 'ETH'
}
// Encrypt for viewing key holder
const encrypted = encryptForViewing(txData, viewingKey)
// Decrypt with key
const decrypted = decryptWithViewing(encrypted, viewingKey)

Encrypt for multiple authorized viewers:

const encrypted = encryptForViewing(txData, [
auditorKey,
complianceKey,
taxKey
])

Each key holder can decrypt independently.

A ViewingProof demonstrates that decrypted data is authentic without revealing the viewing key.

interface ViewingProof {
intentHash: string
revealedData: TransactionData
commitmentProof: {
inputAmountProof: ZKProof
outputAmountProof: ZKProof
}
viewingKeyHash: string
proofTimestamp: number
}

Verifier checks:

  1. ZK proof is valid
  2. Revealed amounts match on-chain commitments
  3. Viewing key hash matches authorized viewer
interface ViewingKeyGrant {
grantId: string
recipientId: string
keyType: 'full' | 'auditor' | 'transaction'
scope: {
startTime?: number
endTime?: number
intentHashes?: string[]
}
createdAt: number
revokedAt?: number
}

Keys can be revoked but previously-viewed data cannot be “un-revealed”:

await revokeViewingKey(grantId)

Important: Revocation prevents future access. Past disclosures are permanent.

PropertyGuarantee
ConfidentialityOnly key holders can decrypt
IntegrityAEAD prevents tampering
AuthenticityViewingProof proves data is real
Selective DisclosureTVK reveals only one transaction
Forward SecrecyKey compromise doesn’t reveal past txs (with TVK)
  • Decrypt transaction details
  • Prove transaction authenticity
  • Verify amounts match commitments
  • Spend funds
  • Create new transactions
  • Modify transaction history
  • Reveal other users’ transactions
LevelViewing Key Behavior
TRANSPARENTNo encryption, public
SHIELDEDEncrypted, no viewing key shared
COMPLIANTEncrypted, auditor key pre-shared
sequenceDiagram
    participant U as User
    participant SIP as SIP Protocol
    participant A as Auditor

    U->>SIP: Set privacy_level = COMPLIANT
    U->>A: Pre-share auditor_key

    loop For Each Transaction
        U->>SIP: Create shielded intent
        SIP->>SIP: Encrypt tx_data with auditor_key
        SIP->>SIP: Store encrypted blob
    end

    Note over A: Audit Requested
    A->>SIP: Request transaction data
    SIP->>A: Return encrypted blobs
    A->>A: Decrypt with auditor_key
    A->>A: Generate ViewingProofs
    A->>U: Provide audit report
sequenceDiagram
    participant U as User
    participant ARB as Arbiter
    participant SIP as SIP Protocol

    Note over U: Dispute arises
    U->>U: Has shielded transaction
    U->>U: Derive TVK for specific intent
    U->>ARB: Share TVK

    ARB->>SIP: Request encrypted tx
    SIP->>ARB: Return encrypted data
    ARB->>ARB: Decrypt with TVK
    ARB->>ARB: Generate ViewingProof
    ARB->>U: Dispute resolved
import {
generateViewingKey,
deriveViewingKey,
encryptForViewing,
decryptWithViewing
} from '@sip-protocol/sdk'
// Generate master key from path
const masterKey = generateViewingKey('/m/44/501/0')
// Derive purpose-specific keys
const auditKey = deriveViewingKey(masterKey, '/audit/2024')
const taxKey = deriveViewingKey(masterKey, '/tax/quarterly')
// Encrypt transaction for auditor
const txData = {
intentHash: '0x123...',
inputAmount: 1000n,
outputAmount: 950n,
sender: '0xabc...',
recipient: '0xdef...'
}
const encrypted = encryptForViewing(txData, auditKey)
// Auditor decrypts
const revealed = decryptWithViewing(encrypted, auditKey)
console.log(revealed.inputAmount) // 1000n
Key TypeTrust RequiredRisk
FVKHighFull history exposure
AKMediumBounded by time
TVKLowSingle transaction
FeatureZcashSIP
Viewing key conceptYesYes
Time-limited keysNoYes
ViewingProofNoYes
Multi-key encryptionNoYes
Intent integrationNoYes