@unirep/core
Version:
Client library for protocol related functions which are used in UniRep protocol.
439 lines (438 loc) • 15.4 kB
TypeScript
/// <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 {};