UNPKG

minauth-merkle-membership-plugin

Version:

This package contains an implementation of a simple MinAuth plugin that extends the concept of password authentication into authenticating within sets that provide a level of anonymity. The proofs are built and verified with MINA's `o1js` library & proof

161 lines (160 loc) 7.04 kB
/// <reference types="node" /> import { Either } from 'fp-ts/lib/Either.js'; import * as O from 'fp-ts/lib/Option.js'; import * as TE from 'fp-ts/lib/TaskEither.js'; import { TaskEither } from 'fp-ts/lib/TaskEither.js'; import fs from 'fs/promises'; import { Field, MerkleTree, PrivateKey } from 'o1js'; import z from 'zod'; import * as ZkProgram from './merklemembershipsprogram.js'; import { TreeRootStorageContract } from './treerootstoragecontract.js'; /** * An interface for o1js Merkle trees store holding sets of members. * * It uses fp-ts TaskEither to allow failible async actions executed * during the tree operations. */ export interface TreeStorage { /** Get the root of the Merkle tree. */ getRoot: () => TaskEither<string, Field>; /** Get a witness for given leaf index */ getWitness: (leafIndex: bigint) => TaskEither<string, O.Option<ZkProgram.TreeWitness>>; /** Check if there's a leaf under the given index */ hasLeaf: (leafIndex: bigint) => TaskEither<string, boolean>; /** Set a Field value under the given index */ setLeaf: (leafIndex: bigint, leaf: Field) => TaskEither<string, void>; /** Get the set of leaves as an array of optional Fields */ getLeaves(): TaskEither<string, Array<O.Option<Field>>>; } /** * An implementation of the tree storage using in-memory data structures. */ export declare class InMemoryStorage implements TreeStorage { /** Set of indexes of occupied leaves */ occupied: Set<bigint>; /** The underlying Merkle tree */ merkleTree: MerkleTree; /** The Merkle tree root */ getRoot(): TaskEither<string, Field>; /** Get a witness for given leaf index */ getWitness(leafIndex: bigint): TE.TaskEither<never, O.None | O.Some<ZkProgram.TreeWitness>>; /** Check if there's a leaf under the given index */ hasLeaf(leafIndex: bigint): TE.TaskEither<never, boolean>; /** Set a Field value under the given index */ setLeaf(leafIndex: bigint, leaf: Field): TE.TaskEither<never, void>; /** Get the set of leaves as an array of optional Fields */ getLeaves(): () => Promise<Either<string, Array<O.Option<Field>>>>; } /** * An implementation of the tree storage using a file system handle. */ export declare class PersistentInMemoryStorage extends InMemoryStorage { readonly file: fs.FileHandle; /** * Write current state of the storage to the file. */ persist(): TaskEither<string, void>; private constructor(); /** * Initialize the storage from a file. * If the file is empty, initialize the storage with the given leaves. */ static initialize(path: string, initialLeaves?: Record<string, string>): TaskEither<string, PersistentInMemoryStorage>; } /** * A tree storage implementation with additional method `updateTreeRootOnChainIfNecessary` * that updates the root stored on chain if the off-chain root differs from the on-chain one. */ export declare class GenericMinaBlockchainTreeStorage<T extends TreeStorage> implements TreeStorage { private underlyingStorage; private contract; private mkTx; constructor(storage: T, contract: TreeRootStorageContract, mkTx: (txFn: () => void) => TaskEither<string, void>); private fetchOnChainRoot; /** * Fetch the root stored on chain, compare to the off-chain counterpart * and update the on-chain root if necessary. */ updateTreeRootOnChainIfNecessary(): TaskEither<string, void>; /** * Get the off-chain Merkle tree root. */ getRoot(): TE.TaskEither<string, import("o1js/dist/node/lib/field.js").Field>; /** Get a witness for given leaf index */ getWitness(leafIdx: bigint): TE.TaskEither<string, O.Option<ZkProgram.TreeWitness>>; /** Check if there's a leaf under the given index */ hasLeaf(leafIdx: bigint): TE.TaskEither<string, boolean>; /** Set a Field value under the given index * * NOTE. This function does not update the on-chain root. */ setLeaf(leafIndex: bigint, leaf: Field): TE.TaskEither<string, void>; /** Get the set of leaves as an array of optional Fields */ getLeaves(): TE.TaskEither<string, O.Option<import("o1js/dist/node/lib/field.js").Field>[]>; } /** * A Merkle tree storage that keeps the tree root on Mina blockchain * guarded by a contract controlled by a private key. * The tree itself is stored in the off-chain file storage. */ export declare class MinaBlockchainTreeStorage extends GenericMinaBlockchainTreeStorage<PersistentInMemoryStorage> { static initialize(path: string, contractPrivateKey: PrivateKey, feePayerPrivateKey: PrivateKey, initialLeaves?: Record<string, string>): TaskEither<string, MinaBlockchainTreeStorage>; } /** * An interface of an entity that provides access to a set of Merkle trees. */ export interface TreesProvider { getTree(treeRoot: Field): TaskEither<string, O.Option<TreeStorage>>; getTreeRoots(): TaskEither<string, Array<Field>>; } /** * Schema for the configuration of a Mina trees provider. * If for given tree `feePayerPrivateKey` and `contractPrivateKey` * are not simultanously present the tree root will NOT be commited to * the MINA blockchain. */ export declare const minaTreesProviderConfigurationSchema: z.ZodObject<{ feePayerPrivateKey: z.ZodOptional<z.ZodString>; trees: z.ZodArray<z.ZodObject<{ contractPrivateKey: z.ZodOptional<z.ZodString>; offchainStoragePath: z.ZodString; initialLeaves: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; }, "strip", z.ZodTypeAny, { offchainStoragePath: string; contractPrivateKey?: string | undefined; initialLeaves?: Record<string, string> | undefined; }, { offchainStoragePath: string; contractPrivateKey?: string | undefined; initialLeaves?: Record<string, string> | undefined; }>, "many">; }, "strip", z.ZodTypeAny, { trees: { offchainStoragePath: string; contractPrivateKey?: string | undefined; initialLeaves?: Record<string, string> | undefined; }[]; feePayerPrivateKey?: string | undefined; }, { trees: { offchainStoragePath: string; contractPrivateKey?: string | undefined; initialLeaves?: Record<string, string> | undefined; }[]; feePayerPrivateKey?: string | undefined; }>; /** * Type of the configuration of the Mina trees provider. */ export type MinaTreesProviderConfiguration = z.infer<typeof minaTreesProviderConfigurationSchema>; /** * Implements a trees provider that uses Mina blockchain to store the roots of the trees. * The trees are stored in the off-chain storage. */ export declare class MinaTreesProvider implements TreesProvider { readonly treeStorages: Array<TreeStorage>; getTree(treeRoot: Field): TaskEither<string, O.Option<TreeStorage>>; getTreeRoots(): TaskEither<string, Array<Field>>; constructor(treeStorages: TreeStorage[]); static initialize(cfg: MinaTreesProviderConfiguration): TaskEither<string, MinaTreesProvider>; }