Shielded Memo Support

Feature: Inbound Shielded Memo for ZEC Deposits

Background

Currently, MAYAChain requires users to include memo data in Zcash transparent (t-address) transactions using OP_RETURN outputs. This approach has several limitations:

  • OP_RETURN outputs have size constraints (80 bytes standard)
  • Transparent transactions don't utilize Zcash's privacy features
  • Users sending from shielded addresses must first deshield to transparent addresses

Motivation

Zcash's shielded protocols (Sapling and Orchard) support encrypted memo fields (512 bytes) that can carry arbitrary data. By leveraging this capability, we can:

  • Improve wallet compatibility: Most Zcash wallets don't support OP_RETURN out-of-the-box as it's non-standard in Zcash and not widely adopted
  • Avoid OP_RETURN constraints on transparent transactions (80 bytes standard limit)
  • Improve UX for privacy-conscious users by using Zcash's intended memo mechanism
  • Provide atomic memo+value in a single transaction

Proposed Solution

Create a centrally-managed shielded wallet that accepts memo-bearing deposits. Users send a single transaction with two outputs: one transparent (for value) and one shielded (for memo).

Bifrost Processing Flow

  1. Detect transaction with output to vault t-address
  2. Extract value: 1.5 ZEC from transparent output
  3. Trial decrypt shielded outputs using corresponding IVK (sapling/orchard)
  4. Extract memo: "SWAP:MAYA.CACAO:maya1recipient..."
  5. Create inbound observation:
    • TxID: abc123...
    • From: (same as current flow)
    • To: t1VaultAddress...
    • Amount: 1.5 ZEC
    • Memo: "SWAP:MAYA.CACAO:maya1recipient..."

User Flow

Transaction Structure Example

Transaction: abc123...
- Inputs:
  - User's shielded/transparent address
- Transparent Outputs:
- Output 0: 1.5 ZEC # t1VaultAddress... (MAYAChain vault)
- Output 1: 0.01 ZEC # t1Change... (user's change)
- Shielded Outputs (Sapling):
   - Output 0: 0 ZEC → u1ShieldedMemo...
   - Encrypted Memo: "SWAP:MAYA.CACAO:maya1recipient..."

Flow

  1. User queries /inbound_addresses (or /constants) endpoint
  2. Receives:
    • Standard t-address (for value transfer)
    • Unified Address (UA) with Sapling + Orchard components
    • Incoming Viewing Key (IVK) for memo decryption
  3. User sends ONE transaction with minimum TWO outputs to Maya: a. Transparent output to t-address with actual value b. Shielded output (Sapling or Orchard) to UA with value=0 and memo
  4. Bifrost scans the transaction:
    • Reads transparent output for value
    • Decrypts shielded output using IVK to extract memo
    • Processes as single inbound observation

Architecture

Implementation Components

1. Wallet Creation

  • Generate a single HD wallet for shielded memo reception
  • Derive Unified Address containing only:
    • Sapling receiver
    • Orchard receiver
    • No transparent component
  • Store seed/keys in secure configuration (similar to TSS vault approach)

2. API Changes (/inbound_addresses)

Current Response:

{
  "chain": "ZEC",
  "pub_key": "...",
  "address": "t1...",
  "router": null,
  "halted": false,
  "gas_rate": "10"
}

Proposed Enhancement:

{
  "chain": "ZEC",
  "pub_key": "...",
  "address": "t1...",
  "router": null,
  "halted": false,
  "gas_rate": "10",
  "shielded_memo": {
    "unified_address": "u1...",
    "ivk_sapling": "zivk...",
    "ivk_orchard": "...",
    "enabled": true
  }
}

3. Bifrost Scanner Changes

Transaction Scanning:

  • Monitor ZEC chain for transactions containing:
    • Transparent output to vault t-address
    • T-output with OP_RETURN or Shielded output (detectable via IVK trial decryption)
  • For each scanned transaction:
    1. Extract value from transparent output
    2. Attempt to decrypt shielded outputs using provided IVK
    3. If decryption succeeds, extract memo from shielded note
    4. Create single inbound observation with value + memo

1. Key Management

  • IVK exposure allows viewing incoming transactions but NOT spending
  • Seed/spending keys must be secured (HSM, encrypted config, or similar to TSS vault security)
  • Consider key rotation strategy (requires new UA publication)

2. DOS Protection

  • IVK trial decryption is computationally expensive
  • Limit trial decryption attempts per block
  • Cache decryption results to avoid re-processing
  • Consider skipping trial decryption if transaction has valid OP_RETURN memo

4. Privacy Implications

  • IVK publication allows anyone to view incoming shielded transactions to this address
  • This is acceptable as the protocol endpoint is public anyway
  • Users' outgoing addresses remain private (sender privacy preserved)
  • Memo contents are visible to anyone with IVK (same as OP_RETURN)

5. Value in Shielded Output

  • Shielded output should have value=0 (only memo)
  • If value > 0 is sent to shielded address, funds are NOT spendable (no spending key)
  • Document clearly: "Send value to t-address, memo to shielded address"
  • Log warning if shielded output value > 0

Implementation Phases

Phase 1: Infrastructure

  • Generate and securely store shielded wallet keys
  • Implement IVK-based transaction decryption in Bifrost
  • Add shielded memo fields to /inbound_addresses endpoint
  • Update ZEC client library with shielded output trial decryption

Phase 2: Transaction Processing

  • Implement dual-output transaction scanning
  • Add IVK trial decryption to scanner loop
  • Implement memo extraction from decrypted shielded notes
  • Update inbound observer to prioritize OP_RETURN over shielded memo

Phase 3: Testing & Validation

  • Add simulation tests for:
    • Normal dual-output deposit (t-output + shielded memo)
    • Shielded memo-only (no t-output) - should reject
    • Both shielded memo AND OP_RETURN - prefer OP_RETURN (and don't decrypt?)
    • Invalid/malformed memo in shielded output
    • Value > 0 in shielded output - log warning/discord automation?
  • Performance tests: Trial decryption overhead

Phase 4: Rollout

  • Deploy to stagenet
  • Monitor decryption performance and success rate
  • Update documentation and user guides
  • Deploy to mainnet via mimir flag (e.g., ZECShieldedMemoEnabled)

Configuration

Constants/Mimir Values

ZECShieldedMemoEnabled: 0/1          # Feature flag
ZECShieldedMemoTrialDecryption: 1    # Enable trial decryption (expensive)
ZECMaxTrialDecryptionsPerBlock: 100  # Limit decryption attempts

Hard-coded Values

// In querier.go
const (
    ShieldedMemoUA = "u1..." // Unified Address
    SaplingIVK = "zivk..."   // Sapling Incoming Viewing Key
    OrchardIVK = "..."       // Orchard Incoming Viewing Key
)

References

  • ZIP 316 - Unified Addresses
  • ZIP 302 - Standardized Memo Format
  • ZIP 212 - Allow Recipient to Derive Sapling Ephemeral Secret
  • Zcash Documentation: Incoming Viewing Keys
  • librustzcash: Trial Decryption APIs
Assignee Loading
Time tracking Loading