forked from tornadocash/tornado-cli
Added Echo Service
This commit is contained in:
parent
bf6f5f21ae
commit
3f55d5ca99
@ -13,6 +13,7 @@ import {
|
||||
RelayerRegistry__factory,
|
||||
Aggregator__factory,
|
||||
Governance__factory,
|
||||
Echoer__factory,
|
||||
} from '@tornado/contracts';
|
||||
import {
|
||||
JsonRpcProvider,
|
||||
@ -62,6 +63,7 @@ import {
|
||||
TornadoFeeOracle,
|
||||
TokenPriceOracle,
|
||||
calculateSnarkProof,
|
||||
NodeEchoService,
|
||||
NodeEncryptedNotesService,
|
||||
NodeGovernanceService,
|
||||
RelayerClient,
|
||||
@ -1257,10 +1259,11 @@ export function tornadoProgram() {
|
||||
registrySubgraph,
|
||||
tokens,
|
||||
routerContract,
|
||||
echoContract,
|
||||
registryContract,
|
||||
['governance.contract.tornadocash.eth']: governanceContract,
|
||||
deployedBlock,
|
||||
constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, ENCRYPTED_NOTES_BLOCK },
|
||||
constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK },
|
||||
} = config;
|
||||
|
||||
const provider = getProgramProvider(netId, rpc, config, {
|
||||
@ -1301,6 +1304,20 @@ export function tornadoProgram() {
|
||||
await registryService.updateEvents();
|
||||
}
|
||||
|
||||
const echoService = new NodeEchoService({
|
||||
netId,
|
||||
provider,
|
||||
graphApi,
|
||||
subgraphName: tornadoSubgraph,
|
||||
Echoer: Echoer__factory.connect(echoContract, provider),
|
||||
deployedBlock: NOTE_ACCOUNT_BLOCK,
|
||||
fetchDataOptions,
|
||||
cacheDirectory: EVENTS_DIR,
|
||||
userDirectory: SAVED_DIR,
|
||||
});
|
||||
|
||||
await echoService.updateEvents();
|
||||
|
||||
const encryptedNotesService = new NodeEncryptedNotesService({
|
||||
netId,
|
||||
provider,
|
||||
|
@ -1,5 +1,12 @@
|
||||
import { BaseContract, Provider, EventLog, TransactionResponse, getAddress, Block, ContractEventName } from 'ethers';
|
||||
import type { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry } from '@tornado/contracts';
|
||||
import type {
|
||||
Tornado,
|
||||
TornadoRouter,
|
||||
TornadoProxyLight,
|
||||
Governance,
|
||||
RelayerRegistry,
|
||||
Echoer,
|
||||
} from '@tornado/contracts';
|
||||
import * as graph from '../graphql';
|
||||
import {
|
||||
BatchEventsService,
|
||||
@ -21,6 +28,7 @@ import type {
|
||||
GovernanceUndelegatedEvents,
|
||||
RegistersEvents,
|
||||
BaseGraphEvents,
|
||||
EchoEvents,
|
||||
} from './types';
|
||||
|
||||
export const DEPOSIT = 'deposit';
|
||||
@ -454,6 +462,76 @@ export class BaseDepositsService extends BaseEventsService<DepositsEvents | With
|
||||
}
|
||||
}
|
||||
|
||||
export type BaseEchoServiceConstructor = {
|
||||
netId: number | string;
|
||||
provider: Provider;
|
||||
graphApi?: string;
|
||||
subgraphName?: string;
|
||||
Echoer: Echoer;
|
||||
deployedBlock?: number;
|
||||
fetchDataOptions?: fetchDataOptions;
|
||||
};
|
||||
|
||||
export class BaseEchoService extends BaseEventsService<EchoEvents> {
|
||||
constructor({
|
||||
netId,
|
||||
provider,
|
||||
graphApi,
|
||||
subgraphName,
|
||||
Echoer,
|
||||
deployedBlock,
|
||||
fetchDataOptions,
|
||||
}: BaseEchoServiceConstructor) {
|
||||
super({ netId, provider, graphApi, subgraphName, contract: Echoer, deployedBlock, fetchDataOptions });
|
||||
}
|
||||
|
||||
getInstanceName(): string {
|
||||
return `echo_${this.netId}`;
|
||||
}
|
||||
|
||||
getType(): string {
|
||||
return 'Echo';
|
||||
}
|
||||
|
||||
getGraphMethod(): string {
|
||||
return 'getAllGraphEchoEvents';
|
||||
}
|
||||
|
||||
async formatEvents(events: EventLog[]) {
|
||||
return events
|
||||
.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
|
||||
const { who, data } = args;
|
||||
|
||||
if (who && data) {
|
||||
const eventObjects = {
|
||||
blockNumber,
|
||||
logIndex,
|
||||
transactionHash,
|
||||
};
|
||||
|
||||
return {
|
||||
...eventObjects,
|
||||
address: who,
|
||||
encryptedAccount: data,
|
||||
};
|
||||
}
|
||||
})
|
||||
.filter((e) => e) as EchoEvents[];
|
||||
}
|
||||
|
||||
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<EchoEvents>> {
|
||||
// TheGraph doesn't support our batch sync due to missing blockNumber field
|
||||
if (!this.graphApi || this.graphApi.includes('api.thegraph.com')) {
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: fromBlock,
|
||||
};
|
||||
}
|
||||
|
||||
return super.getEventsFromGraph({ fromBlock });
|
||||
}
|
||||
}
|
||||
|
||||
export type BaseEncryptedNotesServiceConstructor = {
|
||||
netId: number | string;
|
||||
provider: Provider;
|
||||
@ -556,7 +634,7 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
||||
}
|
||||
|
||||
getGraphMethod() {
|
||||
return 'governanceEvents';
|
||||
return 'getGovernanceEvents';
|
||||
}
|
||||
|
||||
async formatEvents(events: EventLog[]): Promise<BaseGovernanceEventTypes[]> {
|
||||
|
@ -12,8 +12,17 @@ import {
|
||||
BaseGovernanceServiceConstructor,
|
||||
BaseRegistryServiceConstructor,
|
||||
BaseGovernanceEventTypes,
|
||||
BaseEchoServiceConstructor,
|
||||
BaseEchoService,
|
||||
} from './base';
|
||||
import type { BaseEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, RegistersEvents } from './types';
|
||||
import type {
|
||||
BaseEvents,
|
||||
DepositsEvents,
|
||||
WithdrawalsEvents,
|
||||
EncryptedNotesEvents,
|
||||
RegistersEvents,
|
||||
EchoEvents,
|
||||
} from './types';
|
||||
|
||||
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
||||
cacheDirectory?: string;
|
||||
@ -184,6 +193,151 @@ export class NodeDepositsService extends BaseDepositsService {
|
||||
}
|
||||
}
|
||||
|
||||
export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & {
|
||||
cacheDirectory?: string;
|
||||
userDirectory?: string;
|
||||
};
|
||||
|
||||
export class NodeEchoService extends BaseEchoService {
|
||||
cacheDirectory?: string;
|
||||
userDirectory?: string;
|
||||
|
||||
constructor({
|
||||
netId,
|
||||
provider,
|
||||
graphApi,
|
||||
subgraphName,
|
||||
Echoer,
|
||||
deployedBlock,
|
||||
fetchDataOptions,
|
||||
cacheDirectory,
|
||||
userDirectory,
|
||||
}: NodeEchoServiceConstructor) {
|
||||
super({
|
||||
netId,
|
||||
provider,
|
||||
graphApi,
|
||||
subgraphName,
|
||||
Echoer,
|
||||
deployedBlock,
|
||||
fetchDataOptions,
|
||||
});
|
||||
|
||||
this.cacheDirectory = cacheDirectory;
|
||||
this.userDirectory = userDirectory;
|
||||
}
|
||||
|
||||
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
|
||||
if (toBlock) {
|
||||
console.log(`fromBlock - ${fromBlock}`);
|
||||
console.log(`toBlock - ${toBlock}`);
|
||||
|
||||
if (count) {
|
||||
console.log(`downloaded ${type} events count - ${count}`);
|
||||
console.log('____________________________________________');
|
||||
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
|
||||
if (toBlock) {
|
||||
console.log(`fromBlock - ${fromBlock}`);
|
||||
console.log(`toBlock - ${toBlock}`);
|
||||
|
||||
if (count) {
|
||||
console.log(`downloaded ${type} events from graph node count - ${count}`);
|
||||
console.log('____________________________________________');
|
||||
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getEventsFromDB() {
|
||||
if (!this.userDirectory) {
|
||||
console.log(`Updating events for ${this.netId} chain echo events\n`);
|
||||
console.log(`savedEvents count - ${0}`);
|
||||
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
|
||||
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: this.deployedBlock,
|
||||
};
|
||||
}
|
||||
|
||||
const savedEvents = await loadSavedEvents<EchoEvents>({
|
||||
name: this.getInstanceName(),
|
||||
userDirectory: this.userDirectory,
|
||||
deployedBlock: this.deployedBlock,
|
||||
});
|
||||
|
||||
console.log(`Updating events for ${this.netId} chain echo events\n`);
|
||||
console.log(`savedEvents count - ${savedEvents.events.length}`);
|
||||
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
|
||||
|
||||
return savedEvents;
|
||||
}
|
||||
|
||||
async getEventsFromCache() {
|
||||
if (!this.cacheDirectory) {
|
||||
console.log(`cachedEvents count - ${0}`);
|
||||
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
|
||||
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: this.deployedBlock,
|
||||
};
|
||||
}
|
||||
|
||||
const cachedEvents = await loadCachedEvents<EchoEvents>({
|
||||
name: this.getInstanceName(),
|
||||
cacheDirectory: this.cacheDirectory,
|
||||
deployedBlock: this.deployedBlock,
|
||||
});
|
||||
|
||||
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
|
||||
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
|
||||
|
||||
return cachedEvents;
|
||||
}
|
||||
|
||||
async saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>) {
|
||||
const instanceName = this.getInstanceName();
|
||||
|
||||
console.log('\ntotalEvents count - ', events.length);
|
||||
console.log(
|
||||
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
|
||||
);
|
||||
|
||||
const eventTable = new Table();
|
||||
|
||||
eventTable.push(
|
||||
[{ colSpan: 2, content: 'Echo Accounts', hAlign: 'center' }],
|
||||
['Network', `${this.netId} chain`],
|
||||
['Events', `${events.length} events`],
|
||||
[{ colSpan: 2, content: 'Latest events' }],
|
||||
...events
|
||||
.slice(events.length - 10)
|
||||
.reverse()
|
||||
.map(({ blockNumber }, index) => {
|
||||
const eventIndex = events.length - index;
|
||||
|
||||
return [eventIndex, blockNumber];
|
||||
}),
|
||||
);
|
||||
|
||||
console.log(eventTable.toString() + '\n');
|
||||
|
||||
if (this.userDirectory) {
|
||||
await saveEvents<EchoEvents>({
|
||||
name: instanceName,
|
||||
userDirectory: this.userDirectory,
|
||||
events,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceConstructor & {
|
||||
cacheDirectory?: string;
|
||||
userDirectory?: string;
|
||||
|
@ -64,6 +64,11 @@ export type WithdrawalsEvents = MinimalEvents & {
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
export type EchoEvents = MinimalEvents & {
|
||||
address: string;
|
||||
encryptedAccount: string;
|
||||
};
|
||||
|
||||
export type EncryptedNotesEvents = MinimalEvents & {
|
||||
encryptedNote: string;
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ import type {
|
||||
WithdrawalsEvents,
|
||||
EncryptedNotesEvents,
|
||||
BatchGraphOnProgress,
|
||||
EchoEvents,
|
||||
} from '../events';
|
||||
import {
|
||||
_META,
|
||||
@ -17,6 +18,7 @@ import {
|
||||
GET_WITHDRAWALS,
|
||||
GET_NOTE_ACCOUNTS,
|
||||
GET_ENCRYPTED_NOTES,
|
||||
GET_ECHO_EVENTS,
|
||||
} from './queries';
|
||||
|
||||
export * from './queries';
|
||||
@ -662,6 +664,132 @@ export async function getNoteAccounts({
|
||||
}
|
||||
}
|
||||
|
||||
export interface GraphEchoEvents {
|
||||
noteAccounts: {
|
||||
id: string;
|
||||
blockNumber: string;
|
||||
address: string;
|
||||
encryptedAccount: string;
|
||||
}[];
|
||||
_meta: {
|
||||
block: {
|
||||
number: number;
|
||||
};
|
||||
hasIndexingErrors: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface getGraphEchoEventsParams {
|
||||
graphApi: string;
|
||||
subgraphName: string;
|
||||
fromBlock: number;
|
||||
fetchDataOptions?: fetchDataOptions;
|
||||
onProgress?: BatchGraphOnProgress;
|
||||
}
|
||||
|
||||
export function getGraphEchoEvents({
|
||||
graphApi,
|
||||
subgraphName,
|
||||
fromBlock,
|
||||
fetchDataOptions,
|
||||
}: getGraphEchoEventsParams): Promise<GraphEchoEvents> {
|
||||
return queryGraph<GraphEchoEvents>({
|
||||
graphApi,
|
||||
subgraphName,
|
||||
query: GET_ECHO_EVENTS,
|
||||
variables: {
|
||||
first,
|
||||
fromBlock,
|
||||
},
|
||||
fetchDataOptions,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAllGraphEchoEvents({
|
||||
graphApi,
|
||||
subgraphName,
|
||||
fromBlock,
|
||||
fetchDataOptions,
|
||||
onProgress,
|
||||
}: getGraphEchoEventsParams): Promise<BaseGraphEvents<EchoEvents>> {
|
||||
try {
|
||||
const events = [];
|
||||
let lastSyncBlock = fromBlock;
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
let {
|
||||
noteAccounts: result,
|
||||
_meta: {
|
||||
// eslint-disable-next-line prefer-const
|
||||
block: { number: currentBlock },
|
||||
},
|
||||
} = await getGraphEchoEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions });
|
||||
|
||||
lastSyncBlock = currentBlock;
|
||||
|
||||
if (isEmptyArray(result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const [firstEvent] = result;
|
||||
const [lastEvent] = result.slice(-1);
|
||||
|
||||
if (typeof onProgress === 'function') {
|
||||
onProgress({
|
||||
type: 'EchoEvents',
|
||||
fromBlock: Number(firstEvent.blockNumber),
|
||||
toBlock: Number(lastEvent.blockNumber),
|
||||
count: result.length,
|
||||
});
|
||||
}
|
||||
|
||||
if (result.length < 900) {
|
||||
events.push(...result);
|
||||
break;
|
||||
}
|
||||
|
||||
result = result.filter(({ blockNumber }) => blockNumber !== lastEvent.blockNumber);
|
||||
fromBlock = Number(lastEvent.blockNumber);
|
||||
|
||||
events.push(...result);
|
||||
}
|
||||
|
||||
if (!events.length) {
|
||||
return {
|
||||
events: [],
|
||||
lastSyncBlock,
|
||||
};
|
||||
}
|
||||
|
||||
const result = events.map((e) => {
|
||||
const [transactionHash, logIndex] = e.id.split('-');
|
||||
|
||||
return {
|
||||
blockNumber: Number(e.blockNumber),
|
||||
logIndex: Number(logIndex),
|
||||
transactionHash: transactionHash,
|
||||
address: e.address,
|
||||
encryptedAccount: e.encryptedAccount,
|
||||
};
|
||||
});
|
||||
|
||||
const [lastEvent] = result.slice(-1);
|
||||
|
||||
return {
|
||||
events: result,
|
||||
lastSyncBlock: lastEvent && lastEvent.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock,
|
||||
};
|
||||
} catch (err) {
|
||||
console.log('Error from getAllGraphEchoEvents query');
|
||||
console.log(err);
|
||||
return {
|
||||
events: [],
|
||||
lastSyncBlock: fromBlock,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface GraphEncryptedNotes {
|
||||
encryptedNotes: {
|
||||
blockNumber: string;
|
||||
|
@ -107,6 +107,23 @@ export const GET_NOTE_ACCOUNTS = `
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_ECHO_EVENTS = `
|
||||
query getNoteAccounts($first: Int, $fromBlock: Int) {
|
||||
noteAccounts(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||
id
|
||||
blockNumber
|
||||
address
|
||||
encryptedAccount
|
||||
}
|
||||
_meta {
|
||||
block {
|
||||
number
|
||||
}
|
||||
hasIndexingErrors
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_ENCRYPTED_NOTES = `
|
||||
query getEncryptedNotes($first: Int, $fromBlock: Int) {
|
||||
encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||
|
Loading…
Reference in New Issue
Block a user