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
TypeScript
/// <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>;
}