MENU
IDENTITY
Identity ▼
ACCOUNT
Account ▼
STATE & PROOFS
State & Proofs ▼
CRYPTOGRAPHY
Cryptography ▼
RESOURCES

Light Client

A Sahyadri client that verifies state without downloading the chain — the practical face of "the present proves the past."

Overview

A light client is a Sahyadri participant that holds only:

It does not store the chain. It does not replay history. It does not download block bodies or the SMT. It verifies state against a root, using proofs, in O(log n) cryptographic operations per account.

Core idea: trust the root, verify everything else. The root itself is trusted only to the extent that it is embedded in a valid block header — one whose PoW, DAG structure, and chain-of-parents the client has checked. Every balance, every credential, every DWN authorization is then verified cryptographically, without needing an RPC node, an explorer, or any other third party to be honest.

Full Node vs Light Client

PropertyFull NodeLight Client
StorageGBs, grows with chain lengthMBs, bounded by header window + queried accounts
BandwidthFull blocks, full DAGHeaders + state proofs on demand
Sync timeHours to daysSeconds to minutes
VerifiesEverythingOnly what it queries
TrustsNothing but PoW + ghostDAG rulesA recent header + continued PoW on top of it
Runs onServer, desktopMobile, browser, IoT

Trust Model

The light client's trust is bounded and explicit. Nothing is trusted implicitly.

Trusted: One Recent Header
The client trusts the header it has chosen as its anchor. That anchor is either supplied out-of-band (a checkpoint bundled with the app) or obtained by verifying a chain of headers back to a known-good ancestor. This is a one-time trust assumption.
Continued: PoW on Top
Each new header the client accepts must extend the anchor with valid PoW and valid DAG structure. The deeper the confirmed chain on top of the anchor, the more secure the anchor becomes. This is probabilistic finality, not an absolute event.
Not Trusted: RPC Nodes
The node serving state proofs has no trust weight. If it lies, verification fails. Clients can rotate between nodes freely, or cross-check multiple peers.
Not Trusted: Explorers or DNS
An explorer displays data; it does not attest to state. DNS can be hijacked to redirect to a malicious peer — but a malicious peer can still only serve data that fails verification. Trust in DNS is trust in availability, not correctness.

Header Chain Verification

Before a header can serve as a trust anchor, the client must check that it is a genuine extension of the chain it already trusts. The steps are:

  1. Locate parent. The new header must declare a parent that already exists in the client's trusted header window. A header whose parent is unknown is rejected.
  2. Check PoW. The header's proof-of-work must meet the target derived from its parent's difficulty window, following the same rules a full node applies.
  3. Check blue work. GHOSTDAG's blue work metric must strictly increase along the chain. A header that rolls back work is rejected.
  4. Check timestamp. The header's timestamp must be consistent with its parent's and within the client's tolerance.

These four checks are the full extent of what a light client trusts the network for. Everything below them — every balance, every account state — is checked against the header's account_commitment using SMT proofs, which are self-authenticating.

Note on finality: Sahyadri is a PoW + ghostDAG chain, so "final" is a probabilistic statement. A header becomes more expensive to reverse the more PoW accumulates on top of it. Light clients typically pick a confirmation depth based on the value at stake — a smaller depth for a balance display, a larger one for a high-value transfer.

Sync Flow

1

Obtain an Anchor Header

Either a bundled checkpoint or a header chain verified back to a known ancestor. The client stores this header and its account_commitment as the initial trusted root.

2

Extend the Window

As new headers arrive, the client verifies each against its parent, checking PoW, blue work, and timestamp. Newly accepted headers are appended to a rolling window; the oldest are dropped once enough confirmations accumulate.

3

Pick a Target Header

For each account query, the client picks a header from its window — usually the deepest confirmed one — and treats that header's account_commitment as the target root.

4

Request State Proof

For each account it cares about, the client requests a proof from any peer via getStateProof(account, block_hash).

5

Verify Locally

The client recomputes the root from the proof. If it matches the header's account_commitment, the account state is authentic. Otherwise, the client drops the peer and tries another.

6

Cache and Reuse

Verified state is cached until the client advances its anchor. Subsequent queries against the same root reuse the same verification. Storage grows with the number of distinct accounts queried, not with chain length.

Storage Requirements

DataSizeNotes
Header ring buffer~1 MBLast ~10,000 headers, pruned as the anchor advances
Per account cached~200 bytesSPK, balance, flash count
Per account proof~1 KB transientNot persisted after verification
Peer list + reputation~50 KBReplaced periodically
100 accounts, 1 year< 5 MBIndependent of chain length
10,000 accounts, 5 years~50 MBStill bounded

For comparison, a full node that has processed the same period of activity would store orders of magnitude more data. This is the "history debt" that light clients eliminate.

Code Example — Header Verification

The critical piece is accepting a new header only if it correctly extends a trusted one:

pub fn verify_header_chain(
    cached: &HeaderRingBuffer,
    new_header: &Header,
) -> Result<(), HeaderError> {
    // 1. Parent must already be in the trusted window
    let parent = cached
        .get(&new_header.selected_parent)
        .ok_or(HeaderError::UnknownParent)?;

    // 2. PoW must meet the target derived from the parent's window
    if !check_pow(new_header, &parent) {
        return Err(HeaderError::InsufficientPow);
    }

    // 3. Blue work must strictly increase
    if new_header.blue_work <= parent.blue_work {
        return Err(HeaderError::BlueWorkNotMonotonic);
    }

    // 4. Timestamp must be sane relative to parent
    if new_header.time < parent.time {
        return Err(HeaderError::TimeRegression);
    }

    Ok(())
}

Code Example — State Verification

Once the header chain is anchored, state is verified against the header's account_commitment:

use sahyadri_smt::{verify_inclusion, verify_exclusion, Proof, Terminal};
use sahyadri_hashes::Hash;

pub struct LightClient {
    /// Most recent trusted header, verified as extending the anchor.
    trusted_header: Hash,
    /// account_commitment field of that header.
    trusted_root:   [u8; 32],
}

impl LightClient {
    pub fn verify_account(
        &self,
        account_key: [u8; 32],
        proof: &Proof,
    ) -> Option<AccountState> {
        match &proof.terminal {
            Terminal::Empty => {
                if verify_exclusion(&self.trusted_root, &account_key, proof) {
                    Some(AccountState::default())    // absent = zero balance
                } else {
                    None
                }
            }
            Terminal::Leaf { value, .. } => {
                if verify_inclusion(&self.trusted_root, &account_key, value, proof) {
                    // value is the content_hash of the account state.
                    // Fetch the full state from the state store via a
                    // second query, or accept the hash-only attestation
                    // if the client does not need the balance value.
                    None
                } else {
                    None
                }
            }
        }
    }
}

For browsers, the same logic compiles to WebAssembly via the Sahyadri WASM SDK. For embedded devices, the Rust core runs with no_std + alloc for a footprint under 100 KB.

Use Cases

Mobile Wallets
Verify balances and transaction history without trusting a backend. Every displayed number comes from a proof the phone checked itself.
Browser Extensions
A wallet or DID manager runs entirely in the browser tab, verifying state as needed. No background node, no daemon.
IoT Devices
A sensor verifies that a firmware update was signed by the DID it trusts — one anchor, one proof, no server.
DWN Clients
Decentralized Web Nodes verify DID ownership and permissions via state proofs instead of trusting a centralized identity server.
Cross-Chain Relayers
A relayer between Sahyadri and another chain holds one anchor header at a time plus proofs for messages it forwards. Storage stays bounded regardless of Sahyadri's activity.

Roadmap

PhaseDeliverable
NowSMT proofs (inclusion + exclusion) — implemented and tested
Phase 5bgetStateProof RPC endpoint
Phase 5cHeader-only sync protocol — rolling header stream
Phase 5dWASM light client SDK (browser + mobile)
Phase 5eReference CLI light client
Phase 5fRust no_std light client for embedded devices
Phase 5gBatch proof API — one RPC, N accounts

Pruning & Storage

A Sahyadri node does not keep every historical state forever. Once a block falls outside the retention window, its SMT nodes and account state snapshots are permanently deleted from disk. This is what keeps a full node lightweight: storage grows with the current account set, not with the entire history of the chain.

Retention parameters (mainnet):

ParameterValueMeaning
Finality depth43,200 blocks (~12 hours)After this depth, a block cannot be reorged.
Pruning depth108,000 blocks (~30 hours)After this depth, block data and SMT state are permanently pruned.

Two kinds of nodes exist:

State proofs are always available for blocks within the retention window. For blocks older than that, an archive node is the only source.

Garbage collection runs automatically once the pruning point advances — roughly every 30 hours on mainnet at the current block rate. A mark-and-sweep pass over the content-addressed SMT identifies every node and state reachable from a live root and deletes the rest. The pass takes well under a second even on a mature chain, and does not block block production.

Known Limitations

Summary

A light client is what makes Sahyadri usable on the devices most people actually carry. It turns the protocol's most abstract property — a pure-function, content-addressed commitment to all account state — into a concrete benefit: verify anything, anywhere, without storing or trusting the world.

This is the practical meaning of "the present proves the past." No node has to carry history to know that today's state is correct. The state itself carries its own proof.