UNPKG

shield-bridge-sdk

Version:
66 lines 3.59 kB
/** * Incremental shielded-balance cache (the DECRYPT layer — "v2"). * * v1 (saplingDiffCache) made the FETCH incremental but the viewer still trial-decrypts EVERY * commitment and re-checks EVERY matched note's spent status on each read (O(pool size)). This * layer makes the DECRYPT incremental too: it caches the account's matched notes and only * decrypts commitments added since the last scan, then re-checks spent status using octez.js's * own `isSpent` (no nullifier re-implementation — the audited logic is reused verbatim). * * Safety model (this is a money path, so it is deliberately conservative): * - PER-ACCOUNT + private: the cache holds DECRYPTED notes, so it is keyed by a non-reversible * fingerprint of the viewing key. Evict on account forget (clearForViewingKey). * - Reorg-safe: decrypted notes are persisted ONLY for the finalized prefix (head~2, immutable * under Tenderbake). The unconfirmed tail is decrypted fresh every scan and never persisted; * a spent flag is persisted only when caused by a FINALIZED nullifier. So a reorg of a recent * block self-heals, and a just-received note shows immediately. * - Self-checking: every Nth scan (and on every cold build) the incremental result is compared * against a full stock `getBalance()`. Any mismatch invalidates the cache, returns the stock * value, and warns — so a bug here can never silently surface a wrong balance for long. * - Foolproof: any thrown error falls back to the full stock `getBalance()`. */ import BigNumber from 'bignumber.js'; import { type SaplingDiffStore, type DiffTarget } from './saplingDiffCache.js'; /** A matched, decrypted note in the FINALIZED prefix (the only notes we persist). */ interface CachedNote { position: number; valueStr: string; addressHex: string; rcmHex: string; spent: boolean; } interface CachedAccountBalance { decryptCursor: number; notes: CachedNote[]; } /** The subset of octez.js's SaplingTransactionViewer this layer drives (runtime-accessible). */ export interface BalanceViewer { decryptCiphertextAsReceiver(commitmentAndCiphertext: unknown): Promise<{ value: unknown; paymentAddress: Uint8Array; randomCommitmentTrapdoor: unknown; } | undefined>; isSpent(address: unknown, value: string, randomCommitmentTrapdoor: unknown, position: number, nullifiers: unknown[]): Promise<boolean>; getBalance(): Promise<BigNumber>; } /** Stable fingerprint of a full viewing key — non-reversible, so it never exposes the key. */ export declare function viewingKeyFingerprint(fvkHex: string): string; /** Decrypt a stored envelope, or return null (→ rebuild) on a wrong key, tamper, or bad shape. * Exported for inspection/tests; the SDK package does not re-export it. */ export declare function decryptAccount(stored: unknown, fvkHex: string): CachedAccountBalance | null; /** * Incremental shielded balance (base units). Falls back to a full stock `getBalance()` on any * error. The same `store` backs v1's diff cache (namespaced keys never collide). */ export declare function incrementalBalance(opts: { store: SaplingDiffStore; rpcUrl: string; target: DiffTarget; viewer: BalanceViewer; fvkHex: string; selfCheckEvery?: number; }): Promise<BigNumber>; /** Evict every cached balance for a viewing key (call on account forget — removes decrypted data). */ export declare function clearForViewingKey(store: SaplingDiffStore, fvkHex: string): Promise<void>; export {}; //# sourceMappingURL=saplingBalanceCache.d.ts.map