Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Zosh

The trustless privacy bridge for Solana and Zcash.

  • Privacy: Zcash orchard pool integrated.
  • Trustless: No centralized custody, on-chain MPC
  • Permissionless: Any Solana holder can be a validator.

Why Zosh?

BridgePrivacyOpen SourceTrustlessPermissionlessScalability
Zosh🟢🟢🟢🟢🟢
Near Intents🔴🟡🟡🔴🟢
Zenrock🟢🔴🟡🔴🔴

Technical Overview

Zosh is a Solana L2 built for Zcash that focus on privacy and scalability.

Zosh uses a custom consensus algorithm called zoshBFT inspired by Hotstuff and its successors. Both the algorithm and networking stack are optimized from the ground up to support the unique demands of the crossing chain liquidity and privacy.

flowchart LR
    A[Zcash]
    B[Solana]
    C[ZoshBFT <-> State Machine]

    A -.-> |Sync| C
    B -.-> |Sync| C
    C --> |Frost| A
    C --> |Frost| B

    subgraph The Privacy Bridge
        direction LR
        A
        B
    end

zosh state execution is heavily based on external transactions, all confirmed output transactions will be committed on chain and finally can be executed by anyone.

Validators

Validators are the consensus nodes in the Zosh network. They run the zoshBFT consensus algorithm and sign bridge transactions using FROST threshold cryptography.

What Validators Do

  • Participate in zoshBFT consensus (BFT voting, block production)
  • Maintain the zosh blockchain state
  • Sign blocks with Ed25519 signatures
  • Create bridge bundles (batching multiple requests)
  • Perform FROST threshold signing for bridge transactions
  • Commit signed bundles to zosh blocks

Validators must also run collectors to monitor external chains for bridge events.

Requirements

Infrastructure

  • Storage: parity-db for blockchain state
  • Network: Stable connection for P2P consensus
  • Collectors: See Collectors for chain monitoring requirements

Resources

  • Moderate compute for BFT consensus
  • Network I/O for P2P communication
  • Storage for blockchain state

Running a Validator Node

See Node CLI for detailed setup and commands.

Validator Selection

Validators are selected as block leaders using VRF (Verifiable Random Function):

  • Randomized leader selection prevents predictability
  • Fair rotation based on cryptographic randomness
  • Timeout conditions for leader rotation if unresponsive

See ZoshBFT for consensus algorithm details.

Economics

Current (POC):

  • Permissionless - anyone can run a validator
  • No staking requirements
  • No rewards (testnet only)

Future:

  • Stake SOL to become a validator
  • Transaction fees distributed to validators
  • Slashing for malicious behavior:
    • Providing false bridge event data
    • Double-signing
    • Extended downtime

Collectors

Collectors are off-chain services that monitor Solana and Zcash for bridge events. Each validator must run their own collectors to independently verify bridge requests.

Why Collectors Are Required

Validators cannot verify bridge events without off-chain data:

  • Solana burn transactions require RPC access to verify
  • Zcash deposits are in shielded pool (require light wallet scanning)

Critical: Validators MUST run their own collectors. Outsourcing to third parties creates a trust assumption that breaks the security model.

What Collectors Do

Watching Solana

  • Subscribe to Solana program logs via WebSocket
  • Detect BurnEvent emissions (zoZEC → ZEC bridges)
  • Parse event data: sender, amount, Zcash recipient
  • Submit bridge requests to local mempool

Watching Zcash

  • Scan orchard shielded pool using light wallet
  • Detect incoming ZEC deposits to bridge address
  • Decode transaction memos to extract Solana recipient (32-byte pubkey, base58)
  • Blacklist invalid memos
  • Submit bridge requests to local mempool

Requirements

Infrastructure

  • Solana RPC: Self-hosted or third-party (Helius, Quicknode, etc.)
  • Zcash lightwalletd: Connection to Zcash network
  • Storage: Cache for blacklist and processed transactions
  • Bridge keys: Viewing keys for scanning Zcash orchard pool

Resources

  • Network I/O for chain monitoring (primary bottleneck)
  • Storage for cache and blacklist
  • Minimal compute

Running Collectors

Collectors are embedded in the zosh node:

# Collectors start automatically with the node
cargo run -p zosh-node -- dev

Configuration (~/.config/zosh/):

  • Solana RPC endpoint
  • Zcash lightwalletd server
  • Bridge viewing keys

See Node CLI for detailed configuration.

Verification Flow

  1. Collector detects bridge event (Solana burn or Zcash deposit)
  2. Submits to validator’s local mempool
  3. Validator independently verifies using their collector’s data
  4. Validator votes on consensus only if their collector confirms the event
  5. Bridge bundle created only when threshold validators confirm

This ensures no single point of failure - each validator independently verifies external chain state.

Zcash to Solana

Since zcash doesn’t have a VM, we have to maintain a threshold wallet, to make the funds on zcash secure and trustless.

1. User deposits ZEC to the shielded address

User send ZEC to the shielded address of bridge with memo which includes a serialized bridge instruction of Zosh network.

#![allow(unused)]
fn main() {
enum MemoInstruction {
    BridgeToSolana {
        // the recipient address on solana
        recipient: Pubkey,
    }
}
}

The sender will get refunded if the bridge can not decode the memo.

2. Collectors detect and submit bridge requests

Collectors (off-chain services) watch the Zcash orchard pool for incoming deposits:

  1. Scan spendable notes using the bridge’s viewing key
  2. Decode memo to extract Solana recipient address (32-byte pubkey, base58 encoded)
  3. Create bridge request and submit to the zosh mempool
#![allow(unused)]
fn main() {
struct Bridge {
    /// The token of the transaction
    coin: Coin::Zec,

    /// The recipient address (Solana pubkey from memo)
    recipient: Vec<u8>,

    /// The amount of the transaction
    amount: u64,

    /// The zcash transaction id
    txid: Vec<u8>,

    /// The source chain
    source: Chain::Zcash,

    /// The target chain
    target: Chain::Solana,
}
}

Anyone can run a collector - it’s permissionless. Invalid bridge requests (wrong memo format, invalid addresses) are automatically blacklisted.

3. Validators aggregate threshold signatures

Validators collect bridge requests in the mempool and create bundles:

  1. Mempool queuing: Bridge requests enter the mempool
  2. Bundle creation: Validators batch multiple bridge requests together
  3. Threshold signing: Each validator signs the bundle. When threshold (e.g., 2/3) is reached, bundle is ready
  4. Consensus: Bundle is committed to a zosh block
#![allow(unused)]
fn main() {
struct BridgeBundle {
    /// The target chain of the bundle
    target: Chain::Solana,

    /// The bridge transactions
    bridge: Vec<Bridge>,

    /// The data we need for reconstructing the outer transaction
    data: Vec<u8>,

    /// The signatures for the upcoming outer transactions
    signatures: Vec<Vec<u8>>,
}
}

Once enough validators sign, the bundle moves to completed status in the mempool and can be executed on Solana.

4. The recipient receives zoZEC on Solana

After validators sign the bundle, anyone can submit the mint transaction to Solana:

  • The user themselves
  • A collector/relayer service
  • Any third party (permissionless)

The Solana program verifies:

  1. MPC signature: Transaction must be signed by the validator MPC pubkey
  2. Batch processing: Up to 10 recipients can be minted in one transaction
  3. ATA creation: Automatically creates Associated Token Accounts if needed
#![allow(unused)]
fn main() {
// Solana program validates MPC signature
require!(payer.key() == bridge_state.mpc, InvalidMpcSigner);
}

The program mints zoZEC (8 decimals) directly to recipients’ token accounts.

Validation is based on MPC threshold signatures, not nonces. The MPC pubkey represents the collective signing authority of the validator set.

Solana to Zcash

Bridging zoZEC from Solana to Zcash is pretty straightforward here.

1. User burns zoZEC with ZEC recipient specified

#![allow(unused)]
fn main() {
struct BridgeToZcash {
    /// The amount of the transaction
    amount: u64,

    /// The zcash recipient address (orchard unified address, 110 chars)
    recipient: String,
}
}

User calls the burn instruction on the Solana program. This is a public action - anyone can burn their own zoZEC.

The program:

  1. Burns the zoZEC from the user’s token account
  2. Emits a BurnEvent with sender, amount, and Zcash recipient
  3. Collectors watch these events to trigger the bridge process

2. Collectors detect and submit bridge requests

Collectors subscribe to Solana program logs and detect BurnEvent emissions:

  1. Listen to Solana program logs for burn events
  2. Parse the event to extract sender, amount, and Zcash recipient
  3. Submit bridge request to zosh mempool
#![allow(unused)]
fn main() {
struct Bridge {
    coin: Coin::Zec,
    recipient: Vec<u8>,  // Zcash orchard address
    amount: u64,
    txid: Vec<u8>,       // Solana burn transaction signature
    source: Chain::Solana,
    target: Chain::Zcash,
}
}

Anyone can run a collector - it’s permissionless.

3. Validators create and sign Zcash transaction

Validators collect bridge requests in the mempool and create Zcash transactions:

  1. Bridge requests enter the mempool
  2. Validators create a Zcash orchard transaction bundle
  3. FROST threshold signing: Validators perform 2-round signature aggregation to sign the Zcash transaction
  4. The finalized Zcash transaction is committed to a zosh block
#![allow(unused)]
fn main() {
struct BridgeBundle {
    target: Chain::Zcash,
    bridge: Vec<Bridge>,           // Burn requests
    data: Vec<u8>,                 // Zcash transaction data
    signatures: Vec<Vec<u8>>,      // FROST threshold signatures
}
}

The Zcash transaction is created using the bridge’s orchard wallet. Unspent funds (UTXO change) are sent back to the bridge’s orchard address.

4. The recipient receives ZEC on Zcash

After validators sign the Zcash transaction with FROST, anyone can submit it to the Zcash network:

  • The user themselves
  • A collector/relayer service
  • Any third party (permissionless)

The signed Zcash transaction is stored in the zosh block and can be broadcast by anyone with access to a Zcash node.

No on-chain verification on Zcash is needed - the FROST signature already proves validator consensus. Recipients receive ZEC directly to their orchard shielded address.

Block

Blocks are the fundamental units of the Zosh blockchain, containing both consensus metadata and transaction data. Each block extends the chain through cryptographic commitments to previous state and new transactions.

Structure

A block consists of two components:

  • Header: Consensus metadata and cryptographic commitments
  • Extrinsic: Transaction data (bridge bundles and receipts)

The block header contains all consensus-critical metadata:

  • slot: Block height (incrementing sequence number starting from genesis)
  • parent: Hash of the previous block header (32 bytes)
  • state: Merkle root of the parent state (32 bytes)
  • accumulator: Cumulative hash of all transaction IDs up to this block (32 bytes)
  • extrinsic: Merkle root of transactions included in this block (32 bytes)
  • votes: Map of validator public keys to Ed25519 signatures

Block Hash:

The block hash is computed as:

BLAKE3(slot || parent || state || accumulator || extrinsic)

The votes field is excluded from the hash computation to allow validators to sign and aggregate their signatures after the block is proposed.

Extrinsic

The extrinsic contains the actual transaction data:

Transactions are organized into bundles for efficient processing. The extrinsic root is a Merkle tree commitment to all transaction IDs, allowing efficient verification without processing all transactions.

See Transaction for detailed information about bridge requests and receipts.

Production

  1. Leader collects transactions from the mempool
  2. Leader computes the new accumulator from parent accumulator + new transaction IDs
  3. Leader builds the header with all commitments
  4. Leader proposes the block to validators
  5. Validators sign the block hash if valid
  6. Once 2/3 threshold is reached, the block is finalized

Validation

When receiving a block, validators verify:

  1. Parent hash: Block extends the correct parent
  2. State root: Parent state root matches current storage
  3. Accumulator: New accumulator correctly extends previous
  4. Extrinsic root: Merkle root matches included transactions
  5. Signatures: At least 2/3 validators signed the block hash

Failed validation results in block rejection, preventing invalid blocks from entering the chain.

Finality

Blocks achieve finality once they receive 2/3 validator signatures. Unlike probabilistic finality in Nakamoto consensus, zoshBFT provides deterministic finality - finalized blocks cannot be reverted.

The votes field accumulates signatures until the threshold is met. Once finalized, the block is committed to storage and propagated to the network.

Bundle

Bridge transaction bundling is the end-to-end process of aggregating cross-chain transfer requests and executing them with validator consensus. This process involves collectors, validators, the mempool, and network coordination.

Overview

Bundling transforms individual bridge requests into batched, threshold-signed transactions ready for execution on the target chain. The process ensures trustless operation - every validator independently verifies events, and 2/3 consensus is required before execution.

Architecture

flowchart LR
    subgraph Collectors
        A[Monitor Zcash<br/>Orchard Pool]
        B[Monitor Solana<br/>Program Logs]
    end
    
    subgraph Validators
        C[Detect Bridge<br/>Events]
        D[Create Bundles]
        E[Sign Bundles]
    end
    
    subgraph Network
        F[Gossip Bundles]
        G[Aggregate<br/>Signatures]
    end
    
    subgraph Mempool
        H[Queue Bundles]
        I[Track Threshold]
        J[Mark Completed]
    end
    
    A --> C
    B --> C
    C --> D
    D --> F
    F --> H
    H --> E
    E --> G
    G --> I
    I -->|2/3 reached| J
    J --> K[Leader Packs<br/>into Block]

    style H fill:#4a5568,stroke:#718096,stroke-width:2px
    style J fill:#2d3748,stroke:#48bb78,stroke-width:2px
    style K fill:#2c5282,stroke:#63b3ed,stroke-width:2px

Workflow

1. Collection Phase

Components: Collectors

Each validator runs collectors monitoring both chains:

  • Zcash collector: Scans orchard pool for shielded transactions with Zosh memo fields
  • Solana collector: Monitors program logs for zoZEC burn events

Collectors detect bridge events and convert them to bridge requests containing:

  • Source transaction ID
  • Target chain recipient address
  • Transfer amount
  • Chain routing (source/target)

2. Bundle Creation

Components: Validators

Validators aggregate bridge requests into bundles:

  • Solana bundles: Maximum 10 requests per bundle (transaction size limit)
  • Zcash bundles: Limited by available notes in the orchard pool

Each bundle includes:

  • Array of bridge requests
  • Target chain identifier
  • Serialized transaction data
  • Empty signatures array (to be filled)

Bundle hash computed: BLAKE3(bundle_data)

3. Network Propagation

Components: Network

Bundles gossiped to all validators via QUIC:

  • Each validator receives bundle proposal
  • Validators verify bundle validity (valid source txs, correct target chain)
  • Network ensures all validators see consistent bundle set
  • Invalid bundles rejected before mempool

4. Signature Collection

Components: Mempool + Network

Validators independently sign and propagate signatures:

  1. Bundle queued in mempool (in-progress state)
  2. Each validator computes bundle hash and signs with Ed25519 key
  3. Signatures broadcast via network to all validators
  4. Mempool tracks signature count per bundle hash
  5. Network aggregates signatures from multiple validators

5. Threshold Achievement

Components: Mempool

When bundle reaches 2/3 validator signatures:

  • Bundle transitions: in-progress → completed
  • Bundle ready for block inclusion
  • Completed bundles stored in mempool until next block

Example threshold calculation:

  • 4 validators: 3 signatures required
  • 7 validators: 5 signatures required
  • 10 validators: 7 signatures required

6. Block Inclusion

Components: Block Production

Leader packs completed bundles into block:

  1. Leader selected via VRF
  2. Leader calls mempool.pack() to drain completed bundles
  3. Bundles included in block extrinsic
  4. Block propagated for consensus
  5. Once block finalized, bundles executed on target chain

Bundle Execution

After block finalization, bundles execute on the target chain:

  1. Validator constructs target chain transaction with bundle data
  2. MPC signs transaction using threshold signatures (FROST for Zcash)
  3. Transaction submitted to target chain
  4. Target chain processes transfer (Solana: mint zoZEC, Zcash: create shielded output)
  5. Receipt generated linking source tx → target tx

Chain-specific limits:

  • Solana: Batch up to 10 recipients per transaction
  • Zcash: Limited by available notes in orchard pool

Failure Handling

Bundles may fail to complete for several reasons:

Insufficient Signatures

  • Bundle remains in-progress if threshold not reached
  • Persists across blocks until threshold met
  • May be included in future block once signatures collected

Execution Failure

  • Target chain rejects transaction (insufficient funds, invalid recipient, etc.)
  • Bundle marked as unresolved
  • Validators may retry in future block
  • Dispute mechanism (planned) allows challenging failed bundles

Invalid Bundle

  • Detected during verification phase
  • Rejected before mempool queuing
  • Never reaches signature collection
  • Malicious bundler may be slashed (future)

Security Properties

The bundling process ensures:

  1. Trustless verification: Every validator independently verifies source chain events via collectors
  2. Threshold security: 2/3 consensus required, tolerates up to 1/3 Byzantine validators
  3. No single point of failure: Distributed collectors, no centralized oracle
  4. Censorship resistance: Any validator can propose bundles, leader cannot censor
  5. Execution atomicity: All requests in bundle execute together or none execute

Performance Considerations

Batch Efficiency

  • Solana: 10 requests per bundle reduces transaction count 10x
  • Zcash: Variable bundle size based on orchard pool state
  • Larger batches reduce per-request overhead

Latency

Bundle processing time:

  1. Collection: ~1 block confirmation (Zcash: ~75s, Solana: ~400ms)
  2. Bundling + signing: ~1-2 zoshBFT rounds
  3. Block inclusion: Next block slot
  4. Execution: Target chain confirmation time

Total latency typically 2-5 minutes depending on chain confirmations.

Throughput

Limited by:

  • Target chain transaction size (Solana: 10 per bundle)
  • Source chain note availability (Zcash: depends on orchard pool)
  • Validator signature speed (Ed25519 signing: ~50k/sec per validator)
  • Collectors: Off-chain monitoring infrastructure
  • MemPool: Bundle state management and packing
  • Network: QUIC-based bundle propagation
  • Transaction: Bridge request and receipt structure
  • Block: Block production and extrinsic inclusion

Chain State

The Zosh chain state maintains a cryptographic commitment to all on-chain data and consensus state. Each block header contains commitments to both the current state and the historical transaction accumulator.

State Root

The state root is a Merkle root commitment to the current chain state, including:

  • BFT consensus state: Validator set, threshold, and randomness series
  • Present block head: Current slot height and block hash
  • Accumulator: Historical transaction accumulation root

The state root is validated during block import to ensure continuity. Each new block must reference the correct parent state root, preventing forks from diverging state.

Accumulator

The accumulator provides a cumulative cryptographic commitment to all processed transactions since genesis. It enables efficient verification that a transaction was included in the chain history without replaying the entire chain.

Accumulation process:

  1. Start with the previous accumulator hash
  2. Concatenate all transaction IDs from the current block
  3. Hash the combined data to produce the new accumulator

The accumulator hash is included in the block header and validated during import. Invalid accumulator values cause block rejection, ensuring transaction integrity across the chain.

State Validation

When importing a block, validators verify:

  1. State root match: Block’s parent state root equals current storage root
  2. Accumulator continuity: New accumulator correctly extends previous accumulator
  3. Vote threshold: Sufficient validator signatures (2/3 consensus)
  4. Extrinsic root: Merkle root matches included transactions

Failed validation results in block rejection, maintaining chain integrity and preventing invalid state transitions.

Storage

The state is persisted using a key-value storage backend with atomic commits:

  • BFT state: Serialized validator set and consensus parameters
  • Present head: Current block slot and hash
  • Accumulator: Latest transaction accumulation root
  • Blocks: Full block data indexed by hash
  • Transactions: Individual transaction IDs for lookups

State updates are atomic to prevent corruption from crashes or network failures during block import.

MemPool

The mempool manages pending transactions before they are included in blocks. It handles two types of transactions: bridge bundles and receipts, coordinating signature aggregation for bridge operations.

Architecture

The mempool consists of two components:

  • Bridge Pool: Manages bridge bundles with threshold signature aggregation
  • Receipt Queue: Stores receipt transactions awaiting inclusion

Bridge Pool States

Bridge bundles progress through three states in the mempool:

  1. Queued: Bundle submitted to mempool, awaiting validator signatures
  2. In-Progress: Collecting validator signatures until threshold (2/3) is reached
  3. Completed: Threshold met, ready for block inclusion

Process Flow

flowchart LR
    A[Collectors<br/>detect events] --> B[Create<br/>bundles]
    B --> C[Queue in<br/>mempool]
    C --> D[Validators<br/>sign]
    D --> E{Threshold?}
    E -->|No| D
    E -->|Yes| F[Completed]
    F --> G[Leader<br/>packs]
    G --> H[Execute on<br/>target chain]

    style C fill:#4a5568,stroke:#718096,stroke-width:2px
    style F fill:#2d3748,stroke:#48bb78,stroke-width:2px
    style H fill:#2c5282,stroke:#63b3ed,stroke-width:2px

See Bundle for the complete end-to-end bundling workflow involving collectors, validators, network, and mempool.

Signature Aggregation

The bridge pool aggregates threshold signatures before execution:

  1. Bundle submitted with bundle.queue([bundles])
  2. Validators independently verify and sign the bundle hash
  3. Signatures collected via bundle.complete(bundle_hash, signature)
  4. When signatures.len() >= threshold, bundle moves to completed state
  5. Leader includes completed bundles in next block

Threshold: 2/3 of validator signatures required (e.g., 3 of 4 validators, 7 of 10 validators)

Receipt Queue

Receipts are simpler than bridge bundles - they don’t require signature aggregation. When a bridge bundle executes successfully on the target chain, validators generate receipts and add them directly to the queue.

Receipts are drained when the leader packs the mempool into a block extrinsic.

Packing

When a validator becomes the block leader, they pack the mempool:

#![allow(unused)]
fn main() {
extrinsic = mempool.pack()
// Returns:
// - bridge: All completed bundles (cleared from completed map)
// - receipts: All queued receipts (drained from queue)
}

The pack operation is atomic - all completed transactions are included in the block or none are.

Validation

Before adding to mempool, validators verify:

  • Bridge bundles: Valid source chain transaction, correct target chain
  • Receipts: Matching anchor transaction exists, valid target chain confirmation

Invalid transactions are rejected and never enter the mempool.

Network

The Zosh network layer enables P2P communication between validators using QUIC protocol for efficient, secure message exchange. The network facilitates consensus operations, block propagation, and bridge transaction coordination.

Transport

Zosh uses QUIC (Quick UDP Internet Connections) for P2P networking:

  • Low latency: UDP-based with multiplexing and 0-RTT connection establishment
  • Built-in encryption: TLS 1.3 by default, securing all validator communication
  • Connection migration: Maintains connections across network changes
  • Stream multiplexing: Multiple concurrent message streams without head-of-line blocking

QUIC is well-suited for BFT consensus where rapid message exchange is critical for achieving 2/3 validator agreement.

Message Types

The network layer handles several types of messages:

Block Gossip

Validators propagate new blocks to the network:

  1. Leader authors a new block
  2. Leader broadcasts block to all validators
  3. Validators verify and sign the block
  4. Signatures gossiped back to leader and peers
  5. Once 2/3 threshold reached, block is finalized

Block gossip ensures all validators maintain synchronized chain state.

Round Signing

During consensus rounds, validators exchange signatures:

  • Vote messages: Validators sign block hashes and broadcast votes
  • Signature aggregation: Network collects votes until 2/3 threshold
  • QC formation: Quorum certificate formed when threshold met

Round signing enables the zoshBFT consensus to achieve finality.

Bridge Bundle Coordination

Validators coordinate bridge bundle creation and signing across the network. See Bundle for the complete bundling workflow.

State Synchronization

New validators or validators recovering from downtime sync state:

  • Request missing blocks from peers
  • Download chain state snapshots
  • Verify state transitions and accumulator

Peer Discovery

Validators discover and maintain connections to peers:

  • Bootstrap nodes: Initial peer set from configuration
  • Peer exchange: Validators share known peer addresses
  • Connection management: Maintain stable connections to subset of network

Security

Network security mechanisms:

  • TLS 1.3 encryption: All QUIC connections encrypted by default
  • Peer authentication: Validators verify peer identities using Ed25519 keys
  • Message validation: Invalid messages rejected before processing
  • DoS protection: Rate limiting and connection quotas

The network layer ensures validators can securely coordinate consensus and bridge operations without centralized infrastructure.

Transaction

Zosh processes three types of transactions: Bridge requests, Receipts, and Disputes.

Bridge

Bridge transactions represent requests to move assets between Solana and Zcash. Collectors detect these requests when users lock ZEC on Zcash or burn zoZEC on Solana.

Validators aggregate multiple bridge requests into bundles for batch processing. Each bundle requires threshold signatures from 2/3 of validators before execution on the target chain.

Limits:

  • Solana bundles: Maximum 10 requests per bundle
  • Zcash bundles: Limited by available notes in the orchard pool

Receipt

Receipts are confirmation transactions that link source and target chain operations. When validators successfully execute a bridge bundle, they generate receipts proving the cross-chain transfer completed.

Receipts allow users to verify their bridge operations by matching the original transaction ID with the confirmation transaction on the target chain.

Dispute

Status: Not yet implemented

Disputes allow validators to challenge bridge operations that failed or executed incorrectly. When a bridge request doesn’t complete successfully, validators can submit a dispute to trigger re-verification by other validators.

Invalid disputes may result in slashing of the challenger’s staked SOL, preventing spam attacks on the dispute mechanism.

Transaction Flow

  1. Collector detects lock/burn on source chain
  2. Validators aggregate requests in mempool
  3. 2/3 validators sign bundle with threshold signatures
  4. Bundle executed on target chain
  5. Receipt generated and propagated to network
  6. Dispute submitted if execution fails (planned feature)

ZoshBFT

NOTE: The consensus design is still under research & confirmation, could be changed in the future.

ZoshBFT is Zosh’s BFT consensus algorithm combining HotStuff for BFT consensus with Safrole (from Polkadot JAM) for VRF-based leader selection. The consensus achieves deterministic finality through 2/3 validator agreement with epoch-based block production.

Design Principles

  • No fixed block time: Blocks produced when transactions ready or timeout reached
  • VRF leader selection: Randomized, unpredictable leader rotation
  • Threshold signatures: 2/3 validator consensus for finality
  • Timeout-based rotation: Automatic leader rotation on stalls

Architecture

flowchart LR
    A[Leader Selected<br/>via VRF] --> B[Propose Block]
    B --> C[Validators<br/>Verify]
    C --> D{Valid?}
    D -->|Yes| E[Broadcast Vote]
    D -->|No| F[Timeout]
    E --> G{2/3 Threshold?}
    G -->|Not Yet| H[Wait]
    G -->|Reached| I[Finalize Block]
    H --> G
    F --> J[Next Slot]
    I --> J

    style I fill:#2d3748,stroke:#48bb78,stroke-width:2px

Consensus State

The BFT state maintains:

  • Validators: Set of Ed25519 public keys
  • Threshold: Number of signatures required (2/3 of validator count)
  • Series: Randomness series for VRF leader selection

The state evolves through validator set updates (PoS staking/unstaking in future).

Leader Selection

Leaders are selected via VRF (Verifiable Random Function) using an epoch-based mechanism inspired by Safrole:

Epoch System:

  • Leader assignments are fixed for the entire epoch
  • Leaders determined in advance during the previous epoch
  • VRF tickets submitted by validators to compete for leader slots
  • Each slot in the epoch has a predetermined leader

VRF Process:

  1. Validators submit VRF tickets during epoch N for epoch N+1
  2. VRF outputs are evaluated - smallest outputs win leader slots
  3. Leader schedule for epoch N+1 is finalized at end of epoch N
  4. During epoch N+1, assigned leaders produce blocks in their designated slots
  5. VRF output is verifiable by all validators

Security:

  • Leaders predetermined but verifiable prevents targeted DoS
  • VRF ensures randomness and fairness in selection
  • No validator can manipulate future epoch assignments
  • Epoch-based system allows network to prepare for upcoming leaders

Consensus Round

flowchart LR
    A[Slot N<br/>Assigned Leader] --> B{Leader<br/>Online?}
    B -->|Yes| C[Propose Block]
    B -->|No| D[Timeout]
    C --> E[Validators<br/>Sign]
    E --> F{2/3?}
    F -->|Yes| G[Finalize]
    F -->|No| D
    D --> H[Slot N+1]
    G --> H

    style G fill:#2d3748,stroke:#48bb78,stroke-width:2px

Round Steps

  1. Slot begins: Predetermined leader (from epoch VRF schedule) assigned to slot
  2. Block proposal: If leader online, packs mempool and proposes block
  3. Validation: Validators verify block (parent, state root, accumulator, extrinsic)
  4. Voting: Validators broadcast Ed25519 signatures
  5. Finalization: Once 2/3 threshold reached, block finalized
  6. Timeout handling: If leader offline or 2/3 not reached, timeout expires and skip to next slot

Leader Failure Handling:

  • Leader offline: Timeout expires, slot skipped, next slot’s leader produces block
  • Invalid block: Validators reject, timeout expires, move to next slot
  • Network partition: Timeout expires if 2/3 signatures not collected
  • Chain continues with next slot’s predetermined leader from epoch schedule

Finality

ZoshBFT provides deterministic finality through threshold signatures:

  • Blocks are only committed once 2/3 validator signatures are collected
  • Deterministically final - cannot be reverted
  • 2/3 validator signatures prevent rollbacks
  • All state transitions committed atomically

Finality Rule: A block achieves finality immediately upon receiving 2/3 validator signatures.

Timeouts and Liveness

To prevent indefinite stalls, ZoshBFT uses timeout-based leader rotation:

Timeout Conditions:

  • Leader fails to propose block within timeout
  • Proposed block is invalid
  • Network partition prevents signature aggregation

Timeout Behavior:

  1. Validators wait for timeout period
  2. On timeout, validators move to next slot
  3. Next slot’s predetermined leader (from epoch schedule) takes over
  4. New leader proposes block extending current chain

Liveness Guarantee: As long as 2/3 validators are online and network connected, chain progresses by achieving 2/3 consensus on each block.

Safety Properties

ZoshBFT ensures:

  1. Agreement: All honest validators agree on finalized blocks
  2. Validity: Only valid state transitions are finalized
  3. Integrity: Blocks cannot be altered after finalization
  4. Liveness: Chain progresses as long as 2/3 validators online

Byzantine Tolerance: Tolerates up to 1/3 Byzantine (malicious or faulty) validators without compromising safety or liveness.

Performance Characteristics

Block Latency:

  • Proposal: ~100ms (block authoring + propagation)
  • Validation: ~50ms (verification + signature)
  • Signature aggregation: ~500ms - 2s (network-dependent)
  • Total: ~650ms - 2.15s per block

Throughput:

  • Limited by transaction validation speed
  • Bridge bundles: 10 Solana transfers per bundle
  • Block size: Configurable based on network capacity

Future: Proof of Stake

Current Status: PoA (Proof of Authority) with fixed validator set

Planned PoS Migration:

  • Validators stake SOL to participate
  • Slashing for malicious behavior (invalid blocks, double-signing)
  • Rewards from transaction fees
  • Dynamic validator set updates
  • VRF already supports PoS transition

See Validators for economics and staking details.

Comparison to Base Protocols

FeatureHotStuffSafroleZoshBFT
Consensus3-phase BFTBABE-basedHotStuff-inspired
Leader SelectionRound-robinVRF tickets (ring VRF)VRF tickets (epoch-based)
Block TimeDynamic (view-based)Constant (6s in JAM)Dynamic (slot-based)
FinalityAfter 3 phasesGRANDPA (separate)Immediate with 2/3
SignaturesThreshold or individualRing signaturesThreshold (Ed25519)
LivenessView change protocolFork-free by designTimeout-based rotation

Innovations:

  • Combines HotStuff’s linear consensus with Safrole’s epoch-based VRF leader selection
  • Simplified single-phase voting reduces latency compared to multi-phase HotStuff
  • Epoch-based leader scheduling prevents targeted DoS while maintaining verifiability
  • Threshold signatures optimized for bridge operations with MPC integration
  • Block: Block structure and validation
  • Network: Signature propagation and gossip
  • Validators: Validator operations and economics

Deployments

Node CLI

Command-line interface for running the zosh node (zoshd).

Installation

# Build from source
cargo build --release -p zosh-node

# Binary location
./target/release/zoshd

Commands

dev - Development Mode

Run a development node with default configuration.

# Start dev node (default port 1439)
zoshd dev

# Custom address
zoshd dev --address 0.0.0.0:8080

# Short flag
zoshd dev -a 0.0.0.0:8080

Verbosity

Control log output level:

# Info level (default)
zoshd dev

# Debug level
zoshd dev -v

# Trace level (maximum verbosity)
zoshd dev -vv

Verbosity levels:

  • 0 (default): Info
  • 1 (-v): Debug
  • 2 (-vv): Trace

Alternatively, use RUST_LOG environment variable:

RUST_LOG=debug zoshd dev

Configuration

Config Directory

~/.config/zosh/

Configuration files for:

  • Solana RPC endpoint
  • Zcash lightwalletd server
  • Bridge viewing keys
  • Network parameters

Cache Directory

~/.cache/zosh/

Cached data for:

  • Collector blacklist
  • Processed transactions
  • Light wallet state

Solana Keypair

~/.config/solana/id.json

Required for:

  • Signing Solana transactions
  • MPC threshold operations

Subcommands

solana

Solana-specific operations (for advanced users):

zoshd solana <subcommand>

zcash

Zcash-specific operations (for advanced users):

zoshd zcash <subcommand>

Note: Subcommands are for development and testing. Normal operation uses dev mode.

Port Configuration

Default ports:

  • Node P2P: 1439
  • RPC WebSocket: 9944 (default, configurable)

Examples

# Basic development node
zoshd dev

# Custom port with debug logging
zoshd dev -a 0.0.0.0:8080 -v

# Maximum verbosity for troubleshooting
zoshd dev -vv

Version Information

# Show version, branch, commit hash, build time
zoshd --version

RPC API

JSON-RPC WebSocket API for querying zosh node state and subscribing to events.

Connection

Default endpoint: ws://localhost:9944

const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:9944');

Methods

All methods are under the zosh namespace.

zosh_chainInfo

Get current chain state including validators and BFT configuration.

Request:

{
  "jsonrpc": "2.0",
  "method": "zosh_chainInfo",
  "params": [],
  "id": 1
}

Response:

{
  "jsonrpc": "2.0",
  "result": {
    "validators": ["<pubkey1>", "<pubkey2>", ...],
    "threshold": 2,
    "height": 12345,
    "...": "..."
  },
  "id": 1
}

Returns:

  • validators: List of validator Ed25519 public keys
  • threshold: Number of validators required for BFT consensus
  • height: Current block height
  • Additional state data

zosh_subscribeBlock

Subscribe to new block events.

Request:

{
  "jsonrpc": "2.0",
  "method": "zosh_subscribeBlock",
  "params": [],
  "id": 1
}

Response (initial):

{
  "jsonrpc": "2.0",
  "result": "<subscription_id>",
  "id": 1
}

Notifications:

{
  "jsonrpc": "2.0",
  "method": "zosh_subscribeBlock",
  "params": {
    "subscription": "<subscription_id>",
    "result": {
      "block": "<hex_encoded_block_data>"
    }
  }
}

Block data:

  • Encoded in postcard format (binary)
  • Contains: header, extrinsics, BFT votes

zosh_subscribeTransaction

Subscribe to a specific transaction status.

Request:

{
  "jsonrpc": "2.0",
  "method": "zosh_subscribeTransaction",
  "params": ["<txid_hex>"],
  "id": 1
}

Response (initial):

{
  "jsonrpc": "2.0",
  "result": "<subscription_id>",
  "id": 1
}

Notifications:

{
  "jsonrpc": "2.0",
  "method": "zosh_subscribeTransaction",
  "params": {
    "subscription": "<subscription_id>",
    "result": "<tx_status_bytes>"
  }
}

Error Handling

Errors follow JSON-RPC 2.0 specification:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32600,
    "message": "Invalid request"
  },
  "id": 1
}

Usage

The RPC API is primarily used by:

  • UI service for block indexing
  • Collectors for submitting bridge requests
  • External monitoring tools

See the UI service implementation for a complete example.

Program API

Solana program interface for the Zosh bridge (Anchor framework).

Program ID: zosh4npemTuTj18MHgbn7NRihzMkTfgswTyP34LPaVx

Instructions

initialize

Initialize the bridge state and create zoZEC SPL token mint.

Authority required: Program deployer (one-time setup)

Parameters:

  • mpc: Pubkey - MPC public key for validator threshold signing

Accounts:

  • Creates bridge_state PDA
  • Creates zec_mint PDA (8 decimals)

Example:

#![allow(unused)]
fn main() {
initialize(
    mpc: Pubkey
)
}

metadata

Update zoZEC token metadata (name, symbol, URI).

Authority required: Bridge authority only

Parameters:

  • name: String - Token name
  • symbol: String - Token symbol (e.g., “zoZEC”)
  • uri: String - Metadata URI

mint

Mint zoZEC to recipients (threshold action, validator-only).

Authority required: MPC signature (validators)

Parameters:

  • mints: Vec<MintEntry> - Batch of mint operations (max 10)

MintEntry structure:

#![allow(unused)]
fn main() {
struct MintEntry {
    recipient: Pubkey,  // Solana address to receive zoZEC
    amount: u64,        // Amount in lamports (8 decimals)
}
}

Features:

  • Batch minting (up to 10 recipients per transaction)
  • Automatic ATA creation if needed
  • Emits MintEvent for each recipient

Requirements:

  • Transaction must be signed by MPC pubkey
  • Validates all recipient addresses

burn

Burn zoZEC to bridge back to Zcash (public action).

Authority required: None (anyone can burn their own zoZEC)

Parameters:

  • amount: u64 - Amount to burn (8 decimals)
  • zec_recipient: String - Zcash orchard address (110 characters)

Requirements:

  • User must have sufficient zoZEC balance
  • Zcash address must be valid orchard unified address (110 chars)

Emits:

  • BurnEvent with sender, amount, and Zcash recipient
  • Collectors watch for this event to trigger bridge process

Example:

#![allow(unused)]
fn main() {
burn(
    amount: 100_000_000,  // 1 ZEC (8 decimals)
    zec_recipient: "utest1..."  // 110-char address
)
}

update_mpc

Update the MPC public key (threshold action, validator-only).

Authority required: Current MPC signature (validators)

Parameters:

  • mpc: Pubkey - New MPC public key

Use case: Validator set rotation, key refresh

State Accounts

bridge_state

PDA storing bridge configuration.

Seeds: ["bridge-state"]

Fields:

  • authority: Pubkey - Bridge authority (for metadata updates)
  • mpc: Pubkey - MPC public key (validator threshold signing)
  • zec_mint: Pubkey - zoZEC SPL token mint address
  • bump: u8 - PDA bump seed

zec_mint

SPL token mint for zoZEC.

Seeds: ["zec-mint"]

Properties:

  • Decimals: 8 (same as Zcash)
  • Mint authority: bridge_state PDA
  • Freeze authority: None

Events

MintEvent

Emitted when zoZEC is minted.

#![allow(unused)]
fn main() {
struct MintEvent {
    mints: Vec<(Pubkey, u64)>,  // (recipient, amount) pairs
    timestamp: i64,
}
}

BurnEvent

Emitted when zoZEC is burned.

#![allow(unused)]
fn main() {
struct BurnEvent {
    sender: Pubkey,
    amount: u64,
    zec_recipient: String,
    timestamp: i64,
}
}

Error Codes

  • InvalidMpcSigner - Transaction not signed by MPC pubkey
  • InvalidRecipient - Invalid recipient address
  • InvalidAmount - Amount is zero or invalid
  • InvalidMint - Incorrect mint account
  • InvalidBatchSize - Batch size exceeds maximum (10)
  • InvalidZcashAddress - Zcash address invalid (must be 110 chars)

Deployment

Testnet (Devnet):

  • Program: zosh4npemTuTj18MHgbn7NRihzMkTfgswTyP34LPaVx
  • See Addresses for bridge state and mint addresses

Mainnet: Not yet deployed