UNPKG

@unirep/core

Version:

Client library for protocol related functions which are used in UniRep protocol.

439 lines (438 loc) 15.4 kB
/// <reference types="node" /> import { EventEmitter } from 'events'; import { DB, TransactionDB } from 'anondb'; import { ethers } from 'ethers'; import { IncrementalMerkleTree } from '@unirep/utils'; /** * The type of data to be processed in an event handler. */ type EventHandlerArgs = { event: ethers.Event; decodedData: { [key: string]: any; }; db: TransactionDB; }; /** * Turn either a decimal or hex string to a decimal string. * @param content A `bigint`, `string` or `number` type data. * @returns A decimal string. */ export declare function toDecString(content: bigint | string | number): string; /** * The synchronizer is used to construct the Unirep state. After events are emitted from the Unirep contract, * the synchronizer will verify the events and then save the states. * @example * ```ts * import { Synchronizer } from '@unirep/core' * * const state = new Synchronizer({ * unirepAddress: '0xaabbccaabbccaabbccaabbccaabbccaabbccaaaa', * provider, // an ethers.js provider * }) * * // start the synchronizer deamon * await state.start() * await state.waitForSync() * * // stop the synchronizer deamon * state.stop() * ``` */ export declare class Synchronizer extends EventEmitter { /** * @private * The database object. */ private _db; /** * @private * The provider which is connected in the synchronizer. */ private _provider; /** * @private * The UniRep smart contract object which is connected in the synchronizer. */ private _unirepContract; /** * @private * The array of attester IDs which are synchronized in the synchronizer. */ private _attesterId; /** * @private * The circuits settings. */ private _settings; /** * @private * The settings of attesters. There are `startTimestamp` and `epochLength` of each attester. */ private _attesterSettings; /** * @protected * The default zero [state tree](https://developer.unirep.io/docs/protocol/trees#state-tree) leaf. */ protected defaultStateTreeLeaf: bigint; /** * @protected * The default zero [epoch tree](https://developer.unirep.io/docs/protocol/trees#epoch-tree) leaf. */ protected defaultEpochTreeLeaf: bigint; /** * @private * Indicates if the synchronizer is to sync with all Unirep attesters. */ private _syncAll; /** * @private * The mapping of the event name and its handler */ private _eventHandlers; /** * @private * The mapping of a contract address and its event filters. */ private _eventFilters; /** * @private * The id of the poll event. */ private pollId; /** * How frequently the synchronizer will poll the blockchain for new events (specified in milliseconds). Default: `5000` */ pollRate: number; /** * How many blocks the synchronizer will query on each poll. Default: `100000` */ blockRate: number; /** * Allow passing the genesis block as the starting block for querying events */ private _genesisBlock; /** * @private * Check if a setup is completed or not. */ private setupComplete; /** * @private * If a setup is not completed, there will be a setup promise. */ private setupPromise; /** * @private * Lock on poll event. */ private lock; /** * @private * Load events promises. */ private promises; /** * @private * Unprocessed events. */ private _blocks; /** * @private * The latest completed block number. */ private _blockEnd; constructor(config: { db?: DB; attesterId?: bigint | bigint[]; provider: ethers.providers.Provider; unirepAddress: string; genesisBlock?: number; }); private buildEventHandlers; /** * Read the database object. */ get db(): DB; /** * Read the provider which is connected in the synchronizer. */ get provider(): ethers.providers.Provider; /** * Read the UniRep smart contract object which is connected in the synchronizer. */ get unirepContract(): ethers.Contract; /** * Read the settings of the UniRep smart contract. */ get settings(): any; /** * Read the genesis block of the provider environment. */ get genesisBlock(): number; /** * The default attester ID that is set when constructed. * If there is a list of attester IDs, then the first one will be the default attester ID. * If no attester ID is given during construction, all attesters will be synchronized and the default `attesterId` will be `BigInt(0)`. * * :::caution * The default attester ID should be checked carefully while synchronizing more than one attester. * The default attester ID can be changed with [setAttesterId](#setattesterid). * ::: */ get attesterId(): bigint; get attestersOrClauses(): any[]; /** * Change default [attesterId](#attesterid) to another attester ID. * It will fail if an `attesterId` is not synchronized during construction. * @param attesterId The default attester Id to be set. */ setAttesterId(attesterId: string | bigint): void; /** * Check if attester events are synchronized in this synchronizer. * @param attesterId The queried attester ID. * @returns True if the attester events are synced, false otherwise. */ attesterExist(attesterId: string | bigint): boolean; /** * Check if attester events are synchronized in this synchronizer. It will throw an error if the attester is not synchronized. * @param attesterId The queried attester ID. */ checkAttesterId(attesterId: string | bigint): void; /** * Run setup promises. * @returns Setup promises. */ setup(): Promise<any>; /** * Query settings from smart contract and setup event handlers. */ _setup(): Promise<void>; /** * Find the attester's genesis block in the Unirep smart contract. * Then store the `startTimestamp` and `epochLength` in database and in the memory. */ private _findStartBlock; /** * Start the synchronizer daemon. * Start polling the blockchain for new events. If we're behind the HEAD block we'll poll many times quickly */ start(): Promise<void>; /** * Stop synchronizing with Unirep contract. */ stop(): void; /** * Manually poll for new events. * Returns a boolean indicating whether the synchronizer has synced to the head of the blockchain. */ poll(): Promise<{ complete: boolean; }>; /** * @private * Execute polling events and processing events. */ private _poll; /** * Load more events from the smart contract. * @param n How many blocks will be loaded. */ loadBlocks(n: number): Promise<void>; /** * Load new event from smart contract. * @param fromBlock From which block number. * @param toBlock To which block number. * @returns All events in the block range. */ loadNewEvents(fromBlock: number, toBlock: number): Promise<any[]>; /** * Overwrite this to query events in different attester addresses. * @example * ```ts * export class AppSynchronizer extends Synchronizer { * ... * get contracts(){ * return { * ...super.contracts, * [this.appContract.address]: { * contract: this.appContract, * eventNames: [ * 'Event1', * 'Event2', * 'Event3', * ... * ] * } * } * } * ``` */ get contracts(): { [x: string]: { contract: ethers.Contract; eventNames: string[]; }; }; /** * Process events with each event handler. * @param events The array of events will be proccessed. */ private processEvents; /** * Wait for the synchronizer to sync up to a certain block. * By default this will wait until the current latest known block (according to the provider). * @param blockNumber The block number to be synced to. */ waitForSync(blockNumber?: number): Promise<void>; /** * Get the latest processed epoch from the database. * * :::caution * This value may mismatch the onchain value depending on synchronization status. * ::: * @param attesterId The queried attester Id. * @returns `{number: number, sealed: boolean}` */ readCurrentEpoch(attesterId?: bigint | string): Promise<any>; /** * Calculate the current epoch determining the amount of time since the attester registration timestamp. * This operation is **synchronous** and does not involve any database operations. * @param attesterId The queried attester Id. * @returns The number of current calculated epoch. */ calcCurrentEpoch(attesterId?: bigint | string): number; /** * Calculate the amount of time remaining in the current epoch. This operation is **synchronous** and does not involve any database operations. * @param attesterId The queried attester Id. * @returns Current calculated time to the next epoch. */ calcEpochRemainingTime(attesterId?: bigint | string): number; /** * Load the current epoch number from the blockchain. * * :::tip * Use this function in test environments where the blockchain timestamp may not match the real timestamp (e.g. due to snapshot/revert patterns). * ::: * @param attesterId The queried attester Id. * @returns The number of current epoch on-chain. */ loadCurrentEpoch(attesterId?: bigint | string): Promise<number>; /** * Get the epoch tree root for a certain epoch. * @param epoch The epoch to be queried. * @param attesterId The attester to be queried. * @returns The epoch tree root. */ epochTreeRoot(epoch: number, attesterId?: bigint | string): Promise<any>; /** * Build a merkle inclusion proof for the tree from a certain epoch. * @param epoch The epoch to be queried. * @param leafIndex The leaf index to be queried. * @param attesterId The attester to be queried. * @returns The merkle proof of the epoch tree index. */ epochTreeProof(epoch: number, leafIndex: any, attesterId?: bigint | string): Promise<import("@zk-kit/incremental-merkle-tree").MerkleProof>; /** * Determine if a [nullifier](https://developer.unirep.io/docs/protocol/nullifiers) exists. All nullifiers are stored in a single mapping and expected to be globally unique. * @param nullifier A nullifier to be queried. * @returns True if the nullifier exists on-chain before, false otherwise. */ nullifierExist(nullifier: any): Promise<any>; /** * Build the latest state tree for a certain epoch. * @param epoch The epoch to be queried. * @param attesterId The attester to be queried. * @returns The state tree. */ genStateTree(epoch: number | bigint, attesterId?: bigint | string): Promise<IncrementalMerkleTree>; /** * Build the latest history tree for the current attester. * @param attesterId The attester to be queried. * @returns The history tree. */ genHistoryTree(attesterId?: bigint | string): Promise<IncrementalMerkleTree>; /** * Build the latest epoch tree for a certain epoch. * @param epoch The epoch to be queried. * @param attesterId The attester to be queried. * @returns The epoch tree. */ genEpochTree(epoch: number | bigint, attesterId?: bigint | string): Promise<IncrementalMerkleTree>; /** * Determine if a state root exists in a certain epoch. * @param root The queried global state tree root * @param epoch The queried epoch of the global state tree * @param attesterId The attester to be queried. * @returns True if the global state tree root exists, false otherwise. */ stateTreeRootExists(root: bigint | string, epoch: number, attesterId?: bigint | string): Promise<any>; /** * Check if the epoch tree root is stored in the database. * @note * :::caution * Epoch tree root of current epoch might change frequently and the tree root is not stored everytime. * Try use this function when querying the epoch tree in the **previous epoch**. * ::: * @param root The queried epoch tree root * @param epoch The queried epoch of the epoch tree * @param attesterId The attester to be queried. * @returns True if the epoch tree root is in the database, false otherwise. */ epochTreeRootExists(root: bigint | string, epoch: number, attesterId?: bigint | string): Promise<boolean>; /** * Get the number of state tree leaves in a certain epoch. * @param epoch The queried epoch. * @returns The number of the state tree leaves. */ numStateTreeLeaves(epoch: number, attesterId?: bigint | string): Promise<number>; /** * Handle state tree leaf event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleStateTreeLeaf({ event, db, decodedData }: EventHandlerArgs): Promise<true | undefined>; /** * Handle epoch tree leaf event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleEpochTreeLeaf({ event, db, decodedData }: EventHandlerArgs): Promise<true | undefined>; /** * Handle user signup event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleUserSignedUp({ decodedData, event, db }: EventHandlerArgs): Promise<true | undefined>; /** * Handle attestation event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleAttestation({ decodedData, event, db }: EventHandlerArgs): Promise<true | undefined>; /** * Handle user state transition event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleUserStateTransitioned({ decodedData, event, db, }: EventHandlerArgs): Promise<true | undefined>; /** * Handle epoch ended event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleEpochEnded({ decodedData, event, db }: EventHandlerArgs): Promise<true | undefined>; /** * Handle attester signup event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleAttesterSignedUp({ decodedData, event, db }: EventHandlerArgs): Promise<true | undefined>; /** * Handle history tree leaf event * @param args `EventHandlerArgs` type arguments. * @returns True if succeeds, false otherwise. */ handleHistoryTreeLeaf({ decodedData, event, db }: EventHandlerArgs): Promise<true | undefined>; } export {};