Add NodeMultiTornadoService

This commit is contained in:
Tornado Contrib 2024-11-19 10:22:27 +00:00
parent d95635c4f8
commit 0b5adaa9c3
Signed by: tornadocontrib
GPG Key ID: 60B4DF1A076C64B1
25 changed files with 876 additions and 285 deletions

2
lib/config.js vendored

@ -70,6 +70,6 @@ function getRelayerConfig() {
cacheDir: path_1.default.join(STATIC_DIR, './events'), cacheDir: path_1.default.join(STATIC_DIR, './events'),
userEventsDir: path_1.default.join(USER_DIR, './events'), userEventsDir: path_1.default.join(USER_DIR, './events'),
userTreeDir: path_1.default.join(USER_DIR, './trees'), userTreeDir: path_1.default.join(USER_DIR, './trees'),
syncInterval: Number(process_1.default.env.SYNC_INTERVAL || 120), syncInterval: Number(process_1.default.env.SYNC_INTERVAL || 180),
}; };
} }

@ -1,4 +1,4 @@
import { BaseTornadoService, BaseEncryptedNotesService, BaseGovernanceService, BaseRegistryService, BaseTornadoServiceConstructor, BaseEncryptedNotesServiceConstructor, BaseGovernanceServiceConstructor, BaseRegistryServiceConstructor, BaseEchoServiceConstructor, BaseEchoService, CachedRelayers, BatchEventsService, BaseEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, EchoEvents, BatchEventServiceConstructor, BatchEventOnProgress, NetIdType, AllRelayerRegistryEvents, BaseRevenueService, BaseRevenueServiceConstructor, StakeBurnedEvents } from '@tornado/core'; import { BaseTornadoService, BaseMultiTornadoService, BaseEncryptedNotesService, BaseGovernanceService, BaseRegistryService, BaseTornadoServiceConstructor, BaseMultiTornadoServiceConstructor, BaseEncryptedNotesServiceConstructor, BaseGovernanceServiceConstructor, BaseRegistryServiceConstructor, BaseEchoServiceConstructor, BaseEchoService, CachedRelayers, BatchEventsService, BaseEvents, DepositsEvents, WithdrawalsEvents, MultiDepositsEvents, MultiWithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, EchoEvents, BatchEventServiceConstructor, BatchEventOnProgress, NetIdType, AllRelayerRegistryEvents, BaseRevenueService, BaseRevenueServiceConstructor, StakeBurnedEvents, BatchBlockOnProgress, BatchBlockServiceConstructor, BatchBlockService, BatchTransactionService } from '@tornado/core';
import type { Logger } from 'winston'; import type { Logger } from 'winston';
import { TreeCache } from './treeCache'; import { TreeCache } from './treeCache';
export interface NodeEventsConstructor extends BatchEventServiceConstructor { export interface NodeEventsConstructor extends BatchEventServiceConstructor {
@ -12,6 +12,23 @@ export declare class NodeEventsService extends BatchEventsService {
getInstanceName: () => string; getInstanceName: () => string;
constructor(serviceConstructor: NodeEventsConstructor); constructor(serviceConstructor: NodeEventsConstructor);
} }
export interface NodeBlocksConstructor extends BatchBlockServiceConstructor {
netId: NetIdType;
logger: Logger;
getInstanceName: () => string;
}
export declare class NodeBlocksService extends BatchBlockService {
netId: NetIdType;
logger: Logger;
getInstanceName: () => string;
constructor(serviceConstructor: NodeBlocksConstructor);
}
export declare class NodeTransactionsService extends BatchTransactionService {
netId: NetIdType;
logger: Logger;
getInstanceName: () => string;
constructor(serviceConstructor: NodeBlocksConstructor);
}
export interface NodeTornadoServiceConstructor extends BaseTornadoServiceConstructor { export interface NodeTornadoServiceConstructor extends BaseTornadoServiceConstructor {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
@ -27,10 +44,12 @@ export declare class NodeTornadoService extends BaseTornadoService {
treeCache?: TreeCache; treeCache?: TreeCache;
constructor(serviceConstructor: NodeTornadoServiceConstructor); constructor(serviceConstructor: NodeTornadoServiceConstructor);
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>; getEventsFromDB(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<DepositsEvents | WithdrawalsEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<DepositsEvents | WithdrawalsEvents>>;
validateEvents<S>({ events, lastBlock, hasNewEvents, }: BaseEvents<DepositsEvents | WithdrawalsEvents> & { validateEvents<S>({ events, newEvents, lastBlock, }: BaseEvents<DepositsEvents | WithdrawalsEvents> & {
hasNewEvents?: boolean; newEvents: (DepositsEvents | WithdrawalsEvents)[];
}): Promise<S>; }): Promise<S>;
saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>;
updateEvents<S>(): Promise<{ updateEvents<S>(): Promise<{
@ -39,6 +58,37 @@ export declare class NodeTornadoService extends BaseTornadoService {
validateResult: Awaited<S>; validateResult: Awaited<S>;
}>; }>;
} }
export interface NodeMultiTornadoServiceConstructor extends BaseMultiTornadoServiceConstructor {
cacheDirectory: string;
userDirectory: string;
nativeCurrency: string;
logger: Logger;
treeCache?: TreeCache;
}
export declare class NodeMultiTornadoService extends BaseMultiTornadoService {
cacheDirectory: string;
userDirectory: string;
nativeCurrency: string;
logger: Logger;
treeCache?: TreeCache;
constructor(serviceConstructor: NodeMultiTornadoServiceConstructor);
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<MultiDepositsEvents | MultiWithdrawalsEvents>>;
validateEvents<S>({ events, newEvents, lastBlock, }: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}): Promise<S>;
saveEvents({ events, newEvents, lastBlock, }: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}): Promise<void>;
updateEvents<S>(): Promise<{
events: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
lastBlock: number;
validateResult: Awaited<S>;
}>;
}
export interface NodeEchoServiceConstructor extends BaseEchoServiceConstructor { export interface NodeEchoServiceConstructor extends BaseEchoServiceConstructor {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
@ -90,6 +140,7 @@ export declare class NodeGovernanceService extends BaseGovernanceService {
logger: Logger; logger: Logger;
constructor(serviceConstructor: NodeGovernanceServiceConstructor); constructor(serviceConstructor: NodeGovernanceServiceConstructor);
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<AllGovernanceEvents>>; getEventsFromDB(): Promise<BaseEvents<AllGovernanceEvents>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<AllGovernanceEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<AllGovernanceEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>): Promise<void>;
@ -133,6 +184,8 @@ export declare class NodeRevenueService extends BaseRevenueService {
logger: Logger; logger: Logger;
constructor(serviceConstructor: NodeRevenueServiceConstructor); constructor(serviceConstructor: NodeRevenueServiceConstructor);
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<StakeBurnedEvents>>; getEventsFromDB(): Promise<BaseEvents<StakeBurnedEvents>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<StakeBurnedEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<StakeBurnedEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<StakeBurnedEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<StakeBurnedEvents>): Promise<void>;

279
lib/services/events.js vendored

@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeRevenueService = exports.NodeRegistryService = exports.NodeGovernanceService = exports.NodeEncryptedNotesService = exports.NodeEchoService = exports.NodeTornadoService = exports.NodeEventsService = void 0; exports.NodeRevenueService = exports.NodeRegistryService = exports.NodeGovernanceService = exports.NodeEncryptedNotesService = exports.NodeEchoService = exports.NodeMultiTornadoService = exports.NodeTornadoService = exports.NodeTransactionsService = exports.NodeBlocksService = exports.NodeEventsService = void 0;
const path_1 = __importDefault(require("path")); const path_1 = __importDefault(require("path"));
const promises_1 = require("fs/promises"); const promises_1 = require("fs/promises");
const core_1 = require("@tornado/core"); const core_1 = require("@tornado/core");
const contracts_1 = require("@tornado/contracts");
const data_1 = require("./data"); const data_1 = require("./data");
class NodeEventsService extends core_1.BatchEventsService { class NodeEventsService extends core_1.BatchEventsService {
netId; netId;
@ -20,6 +21,30 @@ class NodeEventsService extends core_1.BatchEventsService {
} }
} }
exports.NodeEventsService = NodeEventsService; exports.NodeEventsService = NodeEventsService;
class NodeBlocksService extends core_1.BatchBlockService {
netId;
logger;
getInstanceName;
constructor(serviceConstructor) {
super(serviceConstructor);
this.netId = serviceConstructor.netId;
this.logger = serviceConstructor.logger;
this.getInstanceName = serviceConstructor.getInstanceName;
}
}
exports.NodeBlocksService = NodeBlocksService;
class NodeTransactionsService extends core_1.BatchTransactionService {
netId;
logger;
getInstanceName;
constructor(serviceConstructor) {
super(serviceConstructor);
this.netId = serviceConstructor.netId;
this.logger = serviceConstructor.logger;
this.getInstanceName = serviceConstructor.getInstanceName;
}
}
exports.NodeTransactionsService = NodeTransactionsService;
class NodeTornadoService extends core_1.BaseTornadoService { class NodeTornadoService extends core_1.BaseTornadoService {
cacheDirectory; cacheDirectory;
userDirectory; userDirectory;
@ -41,6 +66,20 @@ class NodeTornadoService extends core_1.BaseTornadoService {
logger, logger,
getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`, getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`,
}); });
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`,
});
this.batchBlockService = new NodeBlocksService({
netId,
provider,
onProgress: this.updateBlockProgress,
logger,
getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`,
});
this.treeCache = treeCache; this.treeCache = treeCache;
} }
updateEventProgress({ fromBlock, toBlock, count }) { updateEventProgress({ fromBlock, toBlock, count }) {
@ -48,6 +87,16 @@ class NodeTornadoService extends core_1.BaseTornadoService {
this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`); this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`);
} }
} }
updateTransactionProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} deposit txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} withdrawal blocks of ${totalIndex}`);
}
}
async getEventsFromDB() { async getEventsFromDB() {
return await (0, data_1.loadSavedEvents)({ return await (0, data_1.loadSavedEvents)({
name: this.getInstanceName(), name: this.getInstanceName(),
@ -61,15 +110,21 @@ class NodeTornadoService extends core_1.BaseTornadoService {
deployedBlock: this.deployedBlock, deployedBlock: this.deployedBlock,
}); });
} }
async validateEvents({ events, lastBlock, hasNewEvents, }) { async validateEvents({ events, newEvents, lastBlock, }) {
const tree = await super.validateEvents({ const tree = await super.validateEvents({
events, events,
newEvents,
lastBlock, lastBlock,
hasNewEvents,
}); });
if (tree && this.currency === this.nativeCurrency && this.treeCache) { if (tree && this.currency === this.nativeCurrency && this.treeCache) {
const merkleTree = tree; const merkleTree = tree;
await this.treeCache.createTree(events, merkleTree); await this.treeCache.createTree({
netId: this.netId,
amount: this.amount,
currency: this.currency,
events: events,
tree: tree,
});
console.log(`${this.getInstanceName()}: Updated tree cache with root ${(0, core_1.toFixedHex)(BigInt(merkleTree.root))}\n`); console.log(`${this.getInstanceName()}: Updated tree cache with root ${(0, core_1.toFixedHex)(BigInt(merkleTree.root))}\n`);
} }
return tree; return tree;
@ -97,6 +152,186 @@ class NodeTornadoService extends core_1.BaseTornadoService {
} }
} }
exports.NodeTornadoService = NodeTornadoService; exports.NodeTornadoService = NodeTornadoService;
class NodeMultiTornadoService extends core_1.BaseMultiTornadoService {
cacheDirectory;
userDirectory;
nativeCurrency;
logger;
treeCache;
constructor(serviceConstructor) {
super(serviceConstructor);
const { netId, provider, cacheDirectory, userDirectory, nativeCurrency, logger, treeCache } = serviceConstructor;
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
this.nativeCurrency = nativeCurrency;
this.logger = logger;
this.batchEventsService = new NodeEventsService({
netId,
provider,
contract: this.contract,
address: Object.keys(this.instances),
onProgress: this.updateEventProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.batchBlockService = new NodeBlocksService({
netId,
provider,
onProgress: this.updateBlockProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.treeCache = treeCache;
}
updateEventProgress({ fromBlock, toBlock, count }) {
if (toBlock) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`);
}
}
updateTransactionProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} deposit txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} withdrawal blocks of ${totalIndex}`);
}
}
async getEventsFromDB() {
return await (0, data_1.loadSavedEvents)({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
});
}
async getEventsFromCache() {
return await (0, data_1.loadCachedEvents)({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
}
async validateEvents({ events, newEvents, lastBlock, }) {
const tree = await super.validateEvents({
events,
newEvents,
lastBlock,
});
if (this.merkleTreeService && this.treeCache) {
const instancesWithNewEvents = [
...new Set(newEvents.filter(({ event }) => event === 'Deposit').map(({ instanceAddress }) => instanceAddress)),
];
for (const instance of instancesWithNewEvents) {
const { amount, currency } = this.instances[instance];
// Only create cached tree for native currencies
if (currency !== this.nativeCurrency) {
continue;
}
const depositEvents = events.filter(({ instanceAddress, event }) => instanceAddress === instance && event === 'Deposit');
// Create tree cache with existing tree
if (tree && this.merkleTreeService.Tornado.target === instance) {
await this.treeCache.createTree({
netId: this.netId,
amount,
currency,
events: depositEvents,
tree: tree,
});
// Create new tree cache
}
else {
const merkleTreeService = new core_1.MerkleTreeService({
netId: this.netId,
amount,
currency,
Tornado: contracts_1.Tornado__factory.connect(instance, this.provider),
merkleWorkerPath: this.merkleTreeService.merkleWorkerPath,
});
const tree = await merkleTreeService.verifyTree(depositEvents);
await this.treeCache.createTree({
netId: this.netId,
amount,
currency,
events: depositEvents,
tree,
});
}
}
}
return tree;
}
async saveEvents({ events, newEvents, lastBlock, }) {
const instancesWithNewEvents = [...new Set(newEvents.map(({ instanceAddress }) => instanceAddress))];
for (const instance of instancesWithNewEvents) {
const { currency, amount } = this.instances[instance];
const { depositEvents, withdrawalEvents } = events.reduce((acc, curr) => {
if (curr.instanceAddress === instance) {
if (curr.event === 'Deposit') {
acc.depositEvents.push({
...curr,
event: undefined,
instanceAddress: undefined,
});
}
else if (curr.event === 'Withdrawal') {
acc.withdrawalEvents.push({
...curr,
event: undefined,
instanceAddress: undefined,
relayerAddress: undefined,
});
}
}
return acc;
}, {
depositEvents: [],
withdrawalEvents: [],
});
await (0, promises_1.writeFile)(path_1.default.join(this.userDirectory, `recent_deposits_${this.netId}_${currency}_${amount}.json`), JSON.stringify(depositEvents.slice(depositEvents.length - 10).reverse(), null, 2) + '\n');
await (0, data_1.saveUserFile)({
fileName: `deposits_${this.netId}_${currency}_${amount}.json`,
userDirectory: this.userDirectory,
dataString: JSON.stringify(depositEvents, null, 2) + '\n',
lastBlock,
});
await (0, data_1.saveUserFile)({
fileName: `withdrawals_${this.netId}_${currency}_${amount}.json`,
userDirectory: this.userDirectory,
dataString: JSON.stringify(withdrawalEvents, null, 2) + '\n',
lastBlock,
});
console.log(`\nSaved ${this.netId}_${currency}_${amount}.json event: ` +
`${depositEvents.length} deposits ${withdrawalEvents.length} withdrawals\n`);
}
await (0, data_1.saveUserFile)({
fileName: this.getInstanceName() + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
lastBlock,
});
}
async updateEvents() {
const { events, lastBlock, validateResult } = await super.updateEvents();
await (0, data_1.saveLastBlock)({
fileName: this.getInstanceName(),
userDirectory: this.userDirectory,
lastBlock,
});
return {
events,
lastBlock,
validateResult,
};
}
}
exports.NodeMultiTornadoService = NodeMultiTornadoService;
class NodeEchoService extends core_1.BaseEchoService { class NodeEchoService extends core_1.BaseEchoService {
cacheDirectory; cacheDirectory;
userDirectory; userDirectory;
@ -237,12 +472,24 @@ class NodeGovernanceService extends core_1.BaseGovernanceService {
logger, logger,
getInstanceName: this.getInstanceName, getInstanceName: this.getInstanceName,
}); });
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: this.getInstanceName,
});
} }
updateEventProgress({ fromBlock, toBlock, count }) { updateEventProgress({ fromBlock, toBlock, count }) {
if (toBlock) { if (toBlock) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`); this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`);
} }
} }
updateTransactionProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} governance txs of ${totalIndex}`);
}
}
async getEventsFromDB() { async getEventsFromDB() {
return await (0, data_1.loadSavedEvents)({ return await (0, data_1.loadSavedEvents)({
name: this.getInstanceName(), name: this.getInstanceName(),
@ -423,12 +670,36 @@ class NodeRevenueService extends core_1.BaseRevenueService {
logger, logger,
getInstanceName: this.getInstanceName, getInstanceName: this.getInstanceName,
}); });
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.batchBlockService = new NodeBlocksService({
netId,
provider,
onProgress: this.updateBlockProgress,
logger,
getInstanceName: this.getInstanceName,
});
} }
updateEventProgress({ fromBlock, toBlock, count }) { updateEventProgress({ fromBlock, toBlock, count }) {
if (toBlock) { if (toBlock) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`); this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`);
} }
} }
updateTransactionProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} blocks of ${totalIndex}`);
}
}
async getEventsFromDB() { async getEventsFromDB() {
return await (0, data_1.loadSavedEvents)({ return await (0, data_1.loadSavedEvents)({
name: this.getInstanceName(), name: this.getInstanceName(),

@ -116,28 +116,39 @@ async function handleGetJob(router, req, reply) {
async function handleEvents(router, netId, req, reply) { async function handleEvents(router, netId, req, reply) {
const { relayerConfig: { userEventsDir: userDirectory }, } = router; const { relayerConfig: { userEventsDir: userDirectory }, } = router;
const { type, currency, amount, fromBlock, recent } = req.body; const { type, currency, amount, fromBlock, recent } = req.body;
const name = [core_1.DEPOSIT, core_1.WITHDRAWAL].includes(type) ? `${type}s_${netId}_${currency}_${amount}` : `${type}_${netId}`; const name = ['deposit', 'withdrawal'].includes(type)
// Can return 0 events but we just return error codes here ? `${type}s_${netId}_${currency}_${amount}`
: `${type}_${netId}`;
// Return 0 length events if not exist (likely 0 events, can be checked by lastSyncBlock === fromBlock)
if (!(await (0, data_1.existsAsync)(path_1.default.join(userDirectory, `${name}.json`)))) { if (!(await (0, data_1.existsAsync)(path_1.default.join(userDirectory, `${name}.json`)))) {
reply.code(404).send(`Events ${name} not found!`); reply.send({
events: [],
lastSyncBlock: fromBlock,
});
return; return;
} }
const { syncManagerStatus } = await (0, routerMsg_1.sendMessage)(router, { type: 'status' }); const { syncManagerStatus } = await (0, routerMsg_1.sendMessage)(router, { type: 'status' });
const lastSyncBlock = Number([core_1.DEPOSIT, core_1.WITHDRAWAL].includes(type) const lastSyncBlock = Number(['deposit', 'withdrawal'].includes(type)
? syncManagerStatus.cachedEvents[netId]?.instances?.[String(currency)]?.[String(amount)]?.[`${type}s`]?.lastBlock ? syncManagerStatus.cachedEvents[netId]?.tornado?.lastBlock
: syncManagerStatus.cachedEvents[netId]?.[String(type)]?.lastBlock); : syncManagerStatus.cachedEvents[netId]?.[String(type)]?.lastBlock);
if (type === 'deposit' && recent) {
const { events } = await (0, data_1.loadSavedEvents)({
name: 'recent_' + name,
userDirectory,
});
reply.send({
events,
lastSyncBlock,
});
return;
}
const { events } = await (0, data_1.loadSavedEvents)({ const { events } = await (0, data_1.loadSavedEvents)({
name, name,
userDirectory, userDirectory,
}); });
if (recent) { if (recent) {
reply.send({ reply.send({
events: events.slice(-10).sort((a, b) => { events: events.slice(events.length - 10).reverse(),
if (a.blockNumber === b.blockNumber) {
return b.logIndex - a.logIndex;
}
return b.blockNumber - a.blockNumber;
}),
lastSyncBlock, lastSyncBlock,
}); });
return; return;

@ -139,7 +139,7 @@ function getEventsKeyword(netId) {
validate: (schema, data) => { validate: (schema, data) => {
try { try {
const { type, currency, amount } = data; const { type, currency, amount } = data;
if ([core_1.DEPOSIT, core_1.WITHDRAWAL].includes(type)) { if (['deposit', 'withdrawal'].includes(type)) {
const instanceAddress = config.tokens[String(currency)]?.instanceAddress?.[String(amount)]; const instanceAddress = config.tokens[String(currency)]?.instanceAddress?.[String(amount)];
if (!instanceAddress) { if (!instanceAddress) {
return false; return false;
@ -159,7 +159,7 @@ function getEventsKeyword(netId) {
} }
return true; return true;
} }
return ['echo', 'encrypted_notes'].includes(type); return ['tornado', 'echo', 'encrypted_notes'].includes(type);
} }
catch { catch {
return false; return false;

@ -2,18 +2,8 @@ import type { Provider } from 'ethers';
import type { Logger } from 'winston'; import type { Logger } from 'winston';
import { NetIdType, TokenPriceOracle, TornadoFeeOracle, TovarishEventsStatus, TovarishSyncStatus } from '@tornado/core'; import { NetIdType, TokenPriceOracle, TornadoFeeOracle, TovarishEventsStatus, TovarishSyncStatus } from '@tornado/core';
import { RelayerConfig } from '../config'; import { RelayerConfig } from '../config';
import { NodeEchoService, NodeEncryptedNotesService, NodeGovernanceService, NodeRegistryService, NodeRevenueService, NodeTornadoService } from './events'; import { NodeEchoService, NodeEncryptedNotesService, NodeGovernanceService, NodeRegistryService, NodeRevenueService, NodeMultiTornadoService } from './events';
import { ErrorTypes, ErrorMessages } from './error'; import { ErrorTypes, ErrorMessages } from './error';
export interface AmountsServices {
depositsService: NodeTornadoService;
withdrawalsService: NodeTornadoService;
}
export interface CurrencyServices {
[index: string]: AmountsServices;
}
export interface TornadoServices {
[index: string]: CurrencyServices;
}
export interface Services { export interface Services {
provider: Provider; provider: Provider;
tokenPriceOracle: TokenPriceOracle; tokenPriceOracle: TokenPriceOracle;
@ -23,7 +13,7 @@ export interface Services {
revenueService?: NodeRevenueService; revenueService?: NodeRevenueService;
echoService: NodeEchoService; echoService: NodeEchoService;
encryptedNotesService: NodeEncryptedNotesService; encryptedNotesService: NodeEncryptedNotesService;
tornadoServices: TornadoServices; tornadoServices: NodeMultiTornadoService;
} }
export interface CachedServices { export interface CachedServices {
[index: NetIdType]: Services; [index: NetIdType]: Services;

76
lib/services/sync.js vendored

@ -18,7 +18,7 @@ function setupServices(syncManager) {
const config = (0, core_1.getConfig)(netId); const config = (0, core_1.getConfig)(netId);
const rpcUrl = relayerConfig.rpcUrls[netId]; const rpcUrl = relayerConfig.rpcUrls[netId];
const provider = (0, core_1.getProviderWithNetId)(netId, rpcUrl, config); const provider = (0, core_1.getProviderWithNetId)(netId, rpcUrl, config);
const { tokens, nativeCurrency, routerContract, echoContract, registryContract, aggregatorContract, reverseRecordsContract, governanceContract, multicallContract, offchainOracleContract, ovmGasPriceOracleContract, deployedBlock, constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK }, } = config; const { nativeCurrency, routerContract, echoContract, registryContract, aggregatorContract, reverseRecordsContract, governanceContract, multicallContract, offchainOracleContract, ovmGasPriceOracleContract, deployedBlock, constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK }, } = config;
if (!syncStatus[netId]) { if (!syncStatus[netId]) {
syncStatus[netId] = { syncStatus[netId] = {
events: false, events: false,
@ -85,49 +85,29 @@ function setupServices(syncManager) {
userDirectory, userDirectory,
logger, logger,
}); });
services.tornadoServices = {}; const instances = (0, core_1.getMultiInstances)(netId, config);
for (const currency of (0, core_1.getActiveTokens)(config)) { const [firstInstance, { amount, currency }] = Object.entries(instances)[0];
const currencyConfig = tokens[currency]; services.tornadoServices = new events_1.NodeMultiTornadoService({
const currencyService = (services.tornadoServices[currency] = {});
for (const [amount, instanceAddress] of Object.entries(currencyConfig.instanceAddress)) {
const Tornado = contracts_1.Tornado__factory.connect(instanceAddress, provider);
const amountService = (currencyService[amount] = {});
const TornadoServiceConstructor = {
netId, netId,
provider, provider,
Tornado, instances,
amount,
currency,
deployedBlock, deployedBlock,
cacheDirectory,
userDirectory,
nativeCurrency,
logger,
};
amountService.depositsService = new events_1.NodeTornadoService({
...TornadoServiceConstructor,
merkleTreeService: new core_1.MerkleTreeService({ merkleTreeService: new core_1.MerkleTreeService({
netId, netId,
amount, amount,
currency, currency,
Tornado, Tornado: contracts_1.Tornado__factory.connect(firstInstance, provider),
merkleWorkerPath, merkleWorkerPath,
}), }),
treeCache: new treeCache_1.TreeCache({ treeCache: new treeCache_1.TreeCache({
netId,
amount,
currency,
userDirectory: userTreeDir, userDirectory: userTreeDir,
}), }),
optionalTree: true, optionalTree: true,
type: 'Deposit', nativeCurrency,
cacheDirectory,
userDirectory,
logger,
}); });
amountService.withdrawalsService = new events_1.NodeTornadoService({
...TornadoServiceConstructor,
type: 'Withdrawal',
});
}
}
} }
syncManager.cachedServices = cachedServices; syncManager.cachedServices = cachedServices;
} }
@ -215,12 +195,11 @@ async function syncNetworkEvents(syncManager, netId) {
logger.info(`${netId}: Syncing events from block ${await provider.getBlockNumber()}`); logger.info(`${netId}: Syncing events from block ${await provider.getBlockNumber()}`);
const eventsStatus = { const eventsStatus = {
governance: governanceService ? {} : undefined, governance: governanceService ? {} : undefined,
registered: registryService ? {} : undefined,
registry: registryService ? {} : undefined, registry: registryService ? {} : undefined,
revenue: revenueService ? {} : undefined, revenue: revenueService ? {} : undefined,
echo: {}, echo: {},
encrypted_notes: {}, encrypted_notes: {},
instances: {}, tornado: {},
}; };
if (governanceService) { if (governanceService) {
const { events, lastBlock } = await governanceService.updateEvents(); const { events, lastBlock } = await governanceService.updateEvents();
@ -241,11 +220,6 @@ async function syncNetworkEvents(syncManager, netId) {
} }
{ {
const { lastBlock, timestamp, relayers } = await registryService.updateRelayers(); const { lastBlock, timestamp, relayers } = await registryService.updateRelayers();
eventsStatus.registered = {
lastBlock,
timestamp,
relayers: relayers.length,
};
logger.info(`${netId}: Updated registry relayers (total: ${relayers.length}, block: ${lastBlock}, timestamp: ${timestamp})`); logger.info(`${netId}: Updated registry relayers (total: ${relayers.length}, block: ${lastBlock}, timestamp: ${timestamp})`);
} }
} }
@ -269,30 +243,12 @@ async function syncNetworkEvents(syncManager, netId) {
lastBlock: encryptedNotesEvents.lastBlock, lastBlock: encryptedNotesEvents.lastBlock,
}; };
logger.info(`${netId}: Updated encrypted notes events (total: ${encryptedNotesEvents.events.length}, block: ${encryptedNotesEvents.lastBlock})`); logger.info(`${netId}: Updated encrypted notes events (total: ${encryptedNotesEvents.events.length}, block: ${encryptedNotesEvents.lastBlock})`);
const currencies = Object.keys(tornadoServices); const tornadoEvents = await tornadoServices.updateEvents();
for (const currency of currencies) { eventsStatus.tornado = {
const currencyStatus = (eventsStatus.instances[currency] = {}); events: tornadoEvents.events.length,
const amounts = Object.keys(tornadoServices[currency]); lastBlock: tornadoEvents.lastBlock,
for (const amount of amounts) {
const instanceStatus = (currencyStatus[amount] = {
deposits: {},
withdrawals: {},
});
const { depositsService, withdrawalsService } = tornadoServices[currency][amount];
const depositEvents = await depositsService.updateEvents();
instanceStatus.deposits = {
events: depositEvents.events.length,
lastBlock: depositEvents.lastBlock,
}; };
logger.info(`${netId}: Updated ${currency} ${amount} Tornado deposit events (total: ${depositEvents.events.length}, block: ${depositEvents.lastBlock})`); logger.info(`${netId}: Updated tornado events (total: ${tornadoEvents.events.length}, block: ${tornadoEvents.lastBlock})`);
const withdrawalEvents = await withdrawalsService.updateEvents();
instanceStatus.withdrawals = {
events: withdrawalEvents.events.length,
lastBlock: withdrawalEvents.lastBlock,
};
logger.info(`${netId}: Updated ${currency} ${amount} Tornado withdrawal events (total: ${withdrawalEvents.events.length}, block: ${withdrawalEvents.lastBlock})`);
}
}
cachedEvents[netId] = eventsStatus; cachedEvents[netId] = eventsStatus;
syncStatus[netId].events = true; syncStatus[netId].events = true;
logger.info(`${netId}: Synced all events`); logger.info(`${netId}: Synced all events`);

@ -7,9 +7,6 @@ import { MerkleTree } from '@tornado/fixed-merkle-tree';
import { DepositsEvents } from '@tornado/core'; import { DepositsEvents } from '@tornado/core';
import type { NetIdType } from '@tornado/core'; import type { NetIdType } from '@tornado/core';
export interface TreeCacheConstructor { export interface TreeCacheConstructor {
netId: NetIdType;
amount: string;
currency: string;
userDirectory: string; userDirectory: string;
PARTS_COUNT?: number; PARTS_COUNT?: number;
LEAVES?: number; LEAVES?: number;
@ -24,12 +21,19 @@ export interface treeMetadata {
leafIndex: number; leafIndex: number;
} }
export declare class TreeCache { export declare class TreeCache {
userDirectory: string;
PARTS_COUNT: number;
constructor({ userDirectory, PARTS_COUNT }: TreeCacheConstructor);
static getInstanceName({ netId, amount, currency, }: {
netId: NetIdType; netId: NetIdType;
amount: string; amount: string;
currency: string; currency: string;
userDirectory: string; }): string;
PARTS_COUNT: number; createTree({ netId, amount, currency, events, tree, }: {
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT }: TreeCacheConstructor); netId: NetIdType;
getInstanceName(): string; amount: string;
createTree(events: DepositsEvents[], tree: MerkleTree): Promise<void>; currency: string;
events: DepositsEvents[];
tree: MerkleTree;
}): Promise<void>;
} }

@ -7,24 +7,19 @@ exports.TreeCache = void 0;
const bloomfilter_js_1 = __importDefault(require("bloomfilter.js")); const bloomfilter_js_1 = __importDefault(require("bloomfilter.js"));
const data_1 = require("./data"); const data_1 = require("./data");
class TreeCache { class TreeCache {
netId;
amount;
currency;
userDirectory; userDirectory;
PARTS_COUNT; PARTS_COUNT;
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT = 4 }) { constructor({ userDirectory, PARTS_COUNT = 4 }) {
this.netId = netId;
this.amount = amount;
this.currency = currency;
this.userDirectory = userDirectory; this.userDirectory = userDirectory;
this.PARTS_COUNT = PARTS_COUNT; this.PARTS_COUNT = PARTS_COUNT;
} }
getInstanceName() { static getInstanceName({ netId, amount, currency, }) {
return `deposits_${this.netId}_${this.currency}_${this.amount}`; return `deposits_${netId}_${currency}_${amount}`;
} }
async createTree(events, tree) { async createTree({ netId, amount, currency, events, tree, }) {
const bloom = new bloomfilter_js_1.default(events.length); const bloom = new bloomfilter_js_1.default(events.length);
console.log(`Creating cached tree for ${this.getInstanceName()}\n`); const instance = TreeCache.getInstanceName({ netId, amount, currency });
console.log(`Creating cached tree for ${instance}\n`);
// events indexed by commitment // events indexed by commitment
const eventsData = events.reduce((acc, { leafIndex, commitment, ...rest }, i) => { const eventsData = events.reduce((acc, { leafIndex, commitment, ...rest }, i) => {
if (leafIndex !== i) { if (leafIndex !== i) {
@ -46,7 +41,7 @@ class TreeCache {
...slice, ...slice,
metadata, metadata,
}, null, 2) + '\n'; }, null, 2) + '\n';
const fileName = `${this.getInstanceName()}_slice${index + 1}.json`; const fileName = `${instance}_slice${index + 1}.json`;
await (0, data_1.saveUserFile)({ await (0, data_1.saveUserFile)({
fileName, fileName,
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
@ -54,7 +49,7 @@ class TreeCache {
}); });
})); }));
const dataString = bloom.serialize() + '\n'; const dataString = bloom.serialize() + '\n';
const fileName = `${this.getInstanceName()}_bloom.json`; const fileName = `${instance}_bloom.json`;
await (0, data_1.saveUserFile)({ await (0, data_1.saveUserFile)({
fileName, fileName,
userDirectory: this.userDirectory, userDirectory: this.userDirectory,

@ -14,7 +14,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/cors": "^10.0.1", "@fastify/cors": "^10.0.1",
"@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#94a62e6193c99457a8dfae0d8684bee299cb1097", "@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#68fcf07c2a344a1f7f74895301f362aa059b53e2",
"bloomfilter.js": "^1.0.2", "bloomfilter.js": "^1.0.2",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"fastify": "^5.0.0", "fastify": "^5.0.0",

@ -98,6 +98,6 @@ export function getRelayerConfig(): RelayerConfig {
cacheDir: path.join(STATIC_DIR, './events'), cacheDir: path.join(STATIC_DIR, './events'),
userEventsDir: path.join(USER_DIR, './events'), userEventsDir: path.join(USER_DIR, './events'),
userTreeDir: path.join(USER_DIR, './trees'), userTreeDir: path.join(USER_DIR, './trees'),
syncInterval: Number(process.env.SYNC_INTERVAL || 120), syncInterval: Number(process.env.SYNC_INTERVAL || 180),
}; };
} }

@ -1,11 +1,13 @@
import path from 'path'; import path from 'path';
import { readFile } from 'fs/promises'; import { readFile, writeFile } from 'fs/promises';
import { import {
BaseTornadoService, BaseTornadoService,
BaseMultiTornadoService,
BaseEncryptedNotesService, BaseEncryptedNotesService,
BaseGovernanceService, BaseGovernanceService,
BaseRegistryService, BaseRegistryService,
BaseTornadoServiceConstructor, BaseTornadoServiceConstructor,
BaseMultiTornadoServiceConstructor,
BaseEncryptedNotesServiceConstructor, BaseEncryptedNotesServiceConstructor,
BaseGovernanceServiceConstructor, BaseGovernanceServiceConstructor,
BaseRegistryServiceConstructor, BaseRegistryServiceConstructor,
@ -17,6 +19,9 @@ import {
BaseEvents, BaseEvents,
DepositsEvents, DepositsEvents,
WithdrawalsEvents, WithdrawalsEvents,
MultiDepositsEvents,
MultiWithdrawalsEvents,
MerkleTreeService,
EncryptedNotesEvents, EncryptedNotesEvents,
AllGovernanceEvents, AllGovernanceEvents,
EchoEvents, EchoEvents,
@ -27,7 +32,12 @@ import {
BaseRevenueService, BaseRevenueService,
BaseRevenueServiceConstructor, BaseRevenueServiceConstructor,
StakeBurnedEvents, StakeBurnedEvents,
BatchBlockOnProgress,
BatchBlockServiceConstructor,
BatchBlockService,
BatchTransactionService,
} from '@tornado/core'; } from '@tornado/core';
import { Tornado__factory } from '@tornado/contracts';
import type { MerkleTree } from '@tornado/fixed-merkle-tree'; import type { MerkleTree } from '@tornado/fixed-merkle-tree';
import type { Logger } from 'winston'; import type { Logger } from 'winston';
import { saveUserFile, loadSavedEvents, loadCachedEvents, existsAsync, saveLastBlock } from './data'; import { saveUserFile, loadSavedEvents, loadCachedEvents, existsAsync, saveLastBlock } from './data';
@ -53,6 +63,40 @@ export class NodeEventsService extends BatchEventsService {
} }
} }
export interface NodeBlocksConstructor extends BatchBlockServiceConstructor {
netId: NetIdType;
logger: Logger;
getInstanceName: () => string;
}
export class NodeBlocksService extends BatchBlockService {
netId: NetIdType;
logger: Logger;
getInstanceName: () => string;
constructor(serviceConstructor: NodeBlocksConstructor) {
super(serviceConstructor);
this.netId = serviceConstructor.netId;
this.logger = serviceConstructor.logger;
this.getInstanceName = serviceConstructor.getInstanceName;
}
}
export class NodeTransactionsService extends BatchTransactionService {
netId: NetIdType;
logger: Logger;
getInstanceName: () => string;
constructor(serviceConstructor: NodeBlocksConstructor) {
super(serviceConstructor);
this.netId = serviceConstructor.netId;
this.logger = serviceConstructor.logger;
this.getInstanceName = serviceConstructor.getInstanceName;
}
}
export interface NodeTornadoServiceConstructor extends BaseTornadoServiceConstructor { export interface NodeTornadoServiceConstructor extends BaseTornadoServiceConstructor {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
@ -102,6 +146,22 @@ export class NodeTornadoService extends BaseTornadoService {
getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`, getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`,
}); });
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`,
});
this.batchBlockService = new NodeBlocksService({
netId,
provider,
onProgress: this.updateBlockProgress,
logger,
getInstanceName: () => `${type.toLowerCase()}s_${netId}_${currency}_${amount}`,
});
this.treeCache = treeCache; this.treeCache = treeCache;
} }
@ -111,6 +171,18 @@ export class NodeTornadoService extends BaseTornadoService {
} }
} }
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} deposit txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} withdrawal blocks of ${totalIndex}`);
}
}
async getEventsFromDB() { async getEventsFromDB() {
return await loadSavedEvents<DepositsEvents | WithdrawalsEvents>({ return await loadSavedEvents<DepositsEvents | WithdrawalsEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
@ -128,21 +200,27 @@ export class NodeTornadoService extends BaseTornadoService {
async validateEvents<S>({ async validateEvents<S>({
events, events,
newEvents,
lastBlock, lastBlock,
hasNewEvents,
}: BaseEvents<DepositsEvents | WithdrawalsEvents> & { }: BaseEvents<DepositsEvents | WithdrawalsEvents> & {
hasNewEvents?: boolean; newEvents: (DepositsEvents | WithdrawalsEvents)[];
}): Promise<S> { }): Promise<S> {
const tree = await super.validateEvents<S>({ const tree = await super.validateEvents<S>({
events, events,
newEvents,
lastBlock, lastBlock,
hasNewEvents,
}); });
if (tree && this.currency === this.nativeCurrency && this.treeCache) { if (tree && this.currency === this.nativeCurrency && this.treeCache) {
const merkleTree = tree as unknown as MerkleTree; const merkleTree = tree as unknown as MerkleTree;
await this.treeCache.createTree(events as DepositsEvents[], merkleTree); await this.treeCache.createTree({
netId: this.netId,
amount: this.amount,
currency: this.currency,
events: events as DepositsEvents[],
tree: tree as unknown as MerkleTree,
});
console.log( console.log(
`${this.getInstanceName()}: Updated tree cache with root ${toFixedHex(BigInt(merkleTree.root))}\n`, `${this.getInstanceName()}: Updated tree cache with root ${toFixedHex(BigInt(merkleTree.root))}\n`,
@ -178,6 +256,253 @@ export class NodeTornadoService extends BaseTornadoService {
} }
} }
export interface NodeMultiTornadoServiceConstructor extends BaseMultiTornadoServiceConstructor {
cacheDirectory: string;
userDirectory: string;
nativeCurrency: string;
logger: Logger;
treeCache?: TreeCache;
}
export class NodeMultiTornadoService extends BaseMultiTornadoService {
cacheDirectory: string;
userDirectory: string;
nativeCurrency: string;
logger: Logger;
treeCache?: TreeCache;
constructor(serviceConstructor: NodeMultiTornadoServiceConstructor) {
super(serviceConstructor);
const { netId, provider, cacheDirectory, userDirectory, nativeCurrency, logger, treeCache } =
serviceConstructor;
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
this.nativeCurrency = nativeCurrency;
this.logger = logger;
this.batchEventsService = new NodeEventsService({
netId,
provider,
contract: this.contract,
address: Object.keys(this.instances),
onProgress: this.updateEventProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.batchBlockService = new NodeBlocksService({
netId,
provider,
onProgress: this.updateBlockProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.treeCache = treeCache;
}
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${count} events from ${fromBlock} to ${toBlock}`);
}
}
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} deposit txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} withdrawal blocks of ${totalIndex}`);
}
}
async getEventsFromDB() {
return await loadSavedEvents<MultiDepositsEvents | MultiWithdrawalsEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
});
}
async getEventsFromCache() {
return await loadCachedEvents<MultiDepositsEvents | MultiWithdrawalsEvents>({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
}
async validateEvents<S>({
events,
newEvents,
lastBlock,
}: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}): Promise<S> {
const tree = await super.validateEvents<S>({
events,
newEvents,
lastBlock,
});
if (this.merkleTreeService && this.treeCache) {
const instancesWithNewEvents = [
...new Set(
newEvents.filter(({ event }) => event === 'Deposit').map(({ instanceAddress }) => instanceAddress),
),
];
for (const instance of instancesWithNewEvents) {
const { amount, currency } = this.instances[instance];
// Only create cached tree for native currencies
if (currency !== this.nativeCurrency) {
continue;
}
const depositEvents = events.filter(
({ instanceAddress, event }) => instanceAddress === instance && event === 'Deposit',
) as MultiDepositsEvents[];
// Create tree cache with existing tree
if (tree && this.merkleTreeService.Tornado.target === instance) {
await this.treeCache.createTree({
netId: this.netId,
amount,
currency,
events: depositEvents,
tree: tree as unknown as MerkleTree,
});
// Create new tree cache
} else {
const merkleTreeService = new MerkleTreeService({
netId: this.netId,
amount,
currency,
Tornado: Tornado__factory.connect(instance, this.provider),
merkleWorkerPath: this.merkleTreeService.merkleWorkerPath,
});
const tree = await merkleTreeService.verifyTree(depositEvents);
await this.treeCache.createTree({
netId: this.netId,
amount,
currency,
events: depositEvents,
tree,
});
}
}
}
return tree;
}
async saveEvents({
events,
newEvents,
lastBlock,
}: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}) {
const instancesWithNewEvents = [...new Set(newEvents.map(({ instanceAddress }) => instanceAddress))];
for (const instance of instancesWithNewEvents) {
const { currency, amount } = this.instances[instance];
const { depositEvents, withdrawalEvents } = events.reduce(
(acc, curr) => {
if (curr.instanceAddress === instance) {
if (curr.event === 'Deposit') {
acc.depositEvents.push({
...(curr as MultiDepositsEvents),
event: undefined,
instanceAddress: undefined,
} as DepositsEvents);
} else if (curr.event === 'Withdrawal') {
acc.withdrawalEvents.push({
...(curr as MultiWithdrawalsEvents),
event: undefined,
instanceAddress: undefined,
relayerAddress: undefined,
} as WithdrawalsEvents);
}
}
return acc;
},
{
depositEvents: [] as DepositsEvents[],
withdrawalEvents: [] as WithdrawalsEvents[],
},
);
await writeFile(
path.join(this.userDirectory, `recent_deposits_${this.netId}_${currency}_${amount}.json`),
JSON.stringify(depositEvents.slice(depositEvents.length - 10).reverse(), null, 2) + '\n',
);
await saveUserFile({
fileName: `deposits_${this.netId}_${currency}_${amount}.json`,
userDirectory: this.userDirectory,
dataString: JSON.stringify(depositEvents, null, 2) + '\n',
lastBlock,
});
await saveUserFile({
fileName: `withdrawals_${this.netId}_${currency}_${amount}.json`,
userDirectory: this.userDirectory,
dataString: JSON.stringify(withdrawalEvents, null, 2) + '\n',
lastBlock,
});
console.log(
`\nSaved ${this.netId}_${currency}_${amount}.json event: ` +
`${depositEvents.length} deposits ${withdrawalEvents.length} withdrawals\n`,
);
}
await saveUserFile({
fileName: this.getInstanceName() + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
lastBlock,
});
}
async updateEvents<S>() {
const { events, lastBlock, validateResult } = await super.updateEvents<S>();
await saveLastBlock({
fileName: this.getInstanceName(),
userDirectory: this.userDirectory,
lastBlock,
});
return {
events,
lastBlock,
validateResult,
};
}
}
export interface NodeEchoServiceConstructor extends BaseEchoServiceConstructor { export interface NodeEchoServiceConstructor extends BaseEchoServiceConstructor {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
@ -368,6 +693,14 @@ export class NodeGovernanceService extends BaseGovernanceService {
logger, logger,
getInstanceName: this.getInstanceName, getInstanceName: this.getInstanceName,
}); });
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: this.getInstanceName,
});
} }
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) { updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
@ -376,6 +709,12 @@ export class NodeGovernanceService extends BaseGovernanceService {
} }
} }
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} governance txs of ${totalIndex}`);
}
}
async getEventsFromDB() { async getEventsFromDB() {
return await loadSavedEvents<AllGovernanceEvents>({ return await loadSavedEvents<AllGovernanceEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
@ -599,6 +938,22 @@ export class NodeRevenueService extends BaseRevenueService {
logger, logger,
getInstanceName: this.getInstanceName, getInstanceName: this.getInstanceName,
}); });
this.batchTransactionService = new NodeTransactionsService({
netId,
provider,
onProgress: this.updateTransactionProgress,
logger,
getInstanceName: this.getInstanceName,
});
this.batchBlockService = new NodeBlocksService({
netId,
provider,
onProgress: this.updateBlockProgress,
logger,
getInstanceName: this.getInstanceName,
});
} }
updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) { updateEventProgress({ fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
@ -607,6 +962,18 @@ export class NodeRevenueService extends BaseRevenueService {
} }
} }
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
this.logger.debug(`${this.getInstanceName()}: Fetched ${currentIndex} blocks of ${totalIndex}`);
}
}
async getEventsFromDB() { async getEventsFromDB() {
return await loadSavedEvents<StakeBurnedEvents>({ return await loadSavedEvents<StakeBurnedEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),

@ -7,8 +7,6 @@ import { fastifyCors } from '@fastify/cors';
import { import {
NetIdType, NetIdType,
getConfig, getConfig,
DEPOSIT,
WITHDRAWAL,
DepositsEvents, DepositsEvents,
WithdrawalsEvents, WithdrawalsEvents,
EchoEvents, EchoEvents,
@ -195,11 +193,16 @@ export async function handleEvents(router: Router, netId: NetIdType, req: Fastif
} = router; } = router;
const { type, currency, amount, fromBlock, recent } = req.body as unknown as TovarishEventsQuery; const { type, currency, amount, fromBlock, recent } = req.body as unknown as TovarishEventsQuery;
const name = [DEPOSIT, WITHDRAWAL].includes(type) ? `${type}s_${netId}_${currency}_${amount}` : `${type}_${netId}`; const name = ['deposit', 'withdrawal'].includes(type)
? `${type}s_${netId}_${currency}_${amount}`
: `${type}_${netId}`;
// Can return 0 events but we just return error codes here // Return 0 length events if not exist (likely 0 events, can be checked by lastSyncBlock === fromBlock)
if (!(await existsAsync(path.join(userDirectory, `${name}.json`)))) { if (!(await existsAsync(path.join(userDirectory, `${name}.json`)))) {
reply.code(404).send(`Events ${name} not found!`); reply.send({
events: [],
lastSyncBlock: fromBlock,
} as BaseTovarishEvents<AllTovarishEvents>);
return; return;
} }
@ -208,13 +211,24 @@ export async function handleEvents(router: Router, netId: NetIdType, req: Fastif
}>(router, { type: 'status' }); }>(router, { type: 'status' });
const lastSyncBlock = Number( const lastSyncBlock = Number(
[DEPOSIT, WITHDRAWAL].includes(type) ['deposit', 'withdrawal'].includes(type)
? syncManagerStatus.cachedEvents[netId]?.instances?.[String(currency)]?.[String(amount)]?.[ ? syncManagerStatus.cachedEvents[netId]?.tornado?.lastBlock
`${type}s` as 'deposits' | 'withdrawals'
]?.lastBlock
: syncManagerStatus.cachedEvents[netId]?.[String(type) as keyof TovarishEventsStatus]?.lastBlock, : syncManagerStatus.cachedEvents[netId]?.[String(type) as keyof TovarishEventsStatus]?.lastBlock,
); );
if (type === 'deposit' && recent) {
const { events } = await loadSavedEvents<AllTovarishEvents>({
name: 'recent_' + name,
userDirectory,
});
reply.send({
events,
lastSyncBlock,
} as BaseTovarishEvents<AllTovarishEvents>);
return;
}
const { events } = await loadSavedEvents<AllTovarishEvents>({ const { events } = await loadSavedEvents<AllTovarishEvents>({
name, name,
userDirectory, userDirectory,
@ -222,12 +236,7 @@ export async function handleEvents(router: Router, netId: NetIdType, req: Fastif
if (recent) { if (recent) {
reply.send({ reply.send({
events: events.slice(-10).sort((a, b) => { events: events.slice(events.length - 10).reverse(),
if (a.blockNumber === b.blockNumber) {
return b.logIndex - a.logIndex;
}
return b.blockNumber - a.blockNumber;
}),
lastSyncBlock, lastSyncBlock,
} as BaseTovarishEvents<AllTovarishEvents>); } as BaseTovarishEvents<AllTovarishEvents>);
return; return;

@ -7,8 +7,6 @@ import {
getInstanceByAddress, getInstanceByAddress,
enabledChains, enabledChains,
TovarishEventsQuery, TovarishEventsQuery,
WITHDRAWAL,
DEPOSIT,
addressSchemaType, addressSchemaType,
proofSchemaType, proofSchemaType,
bytes32SchemaType, bytes32SchemaType,
@ -182,7 +180,7 @@ export function getEventsKeyword(netId: NetIdType) {
try { try {
const { type, currency, amount } = data; const { type, currency, amount } = data;
if ([DEPOSIT, WITHDRAWAL].includes(type)) { if (['deposit', 'withdrawal'].includes(type)) {
const instanceAddress = config.tokens[String(currency)]?.instanceAddress?.[String(amount)]; const instanceAddress = config.tokens[String(currency)]?.instanceAddress?.[String(amount)];
if (!instanceAddress) { if (!instanceAddress) {
@ -207,7 +205,7 @@ export function getEventsKeyword(netId: NetIdType) {
return true; return true;
} }
return ['echo', 'encrypted_notes'].includes(type); return ['tornado', 'echo', 'encrypted_notes'].includes(type);
} catch { } catch {
return false; return false;
} }

@ -21,12 +21,10 @@ import {
OffchainOracle__factory, OffchainOracle__factory,
TornadoFeeOracle, TornadoFeeOracle,
OvmGasPriceOracle__factory, OvmGasPriceOracle__factory,
getActiveTokens,
TovarishEventsStatus, TovarishEventsStatus,
InstanceEventsStatus,
TovarishSyncStatus, TovarishSyncStatus,
EventsStatus,
ReverseRecords__factory, ReverseRecords__factory,
getMultiInstances,
} from '@tornado/core'; } from '@tornado/core';
import { RelayerConfig } from '../config'; import { RelayerConfig } from '../config';
@ -38,23 +36,10 @@ import {
NodeGovernanceService, NodeGovernanceService,
NodeRegistryService, NodeRegistryService,
NodeRevenueService, NodeRevenueService,
NodeTornadoService, NodeMultiTornadoService,
} from './events'; } from './events';
import { ErrorTypes, ErrorMessages, newError } from './error'; import { ErrorTypes, ErrorMessages, newError } from './error';
export interface AmountsServices {
depositsService: NodeTornadoService;
withdrawalsService: NodeTornadoService;
}
export interface CurrencyServices {
[index: string]: AmountsServices;
}
export interface TornadoServices {
[index: string]: CurrencyServices;
}
export interface Services { export interface Services {
provider: Provider; provider: Provider;
tokenPriceOracle: TokenPriceOracle; tokenPriceOracle: TokenPriceOracle;
@ -64,7 +49,7 @@ export interface Services {
revenueService?: NodeRevenueService; revenueService?: NodeRevenueService;
echoService: NodeEchoService; echoService: NodeEchoService;
encryptedNotesService: NodeEncryptedNotesService; encryptedNotesService: NodeEncryptedNotesService;
tornadoServices: TornadoServices; tornadoServices: NodeMultiTornadoService;
} }
export interface CachedServices { export interface CachedServices {
@ -131,7 +116,6 @@ function setupServices(syncManager: SyncManager) {
const provider = getProviderWithNetId(netId, rpcUrl, config); const provider = getProviderWithNetId(netId, rpcUrl, config);
const { const {
tokens,
nativeCurrency, nativeCurrency,
routerContract, routerContract,
echoContract, echoContract,
@ -229,56 +213,31 @@ function setupServices(syncManager: SyncManager) {
logger, logger,
}); });
services.tornadoServices = {} as TornadoServices; const instances = getMultiInstances(netId, config);
for (const currency of getActiveTokens(config)) { const [firstInstance, { amount, currency }] = Object.entries(instances)[0];
const currencyConfig = tokens[currency];
const currencyService = (services.tornadoServices[currency] = {} as CurrencyServices); services.tornadoServices = new NodeMultiTornadoService({
for (const [amount, instanceAddress] of Object.entries(currencyConfig.instanceAddress)) {
const Tornado = Tornado__factory.connect(instanceAddress, provider);
const amountService = (currencyService[amount] = {} as AmountsServices);
const TornadoServiceConstructor = {
netId, netId,
provider, provider,
Tornado, instances,
amount,
currency,
deployedBlock, deployedBlock,
cacheDirectory,
userDirectory,
nativeCurrency,
logger,
};
amountService.depositsService = new NodeTornadoService({
...TornadoServiceConstructor,
merkleTreeService: new MerkleTreeService({ merkleTreeService: new MerkleTreeService({
netId, netId,
amount, amount,
currency, currency,
Tornado, Tornado: Tornado__factory.connect(firstInstance, provider),
merkleWorkerPath, merkleWorkerPath,
}), }),
treeCache: new TreeCache({ treeCache: new TreeCache({
netId,
amount,
currency,
userDirectory: userTreeDir, userDirectory: userTreeDir,
}), }),
optionalTree: true, optionalTree: true,
type: 'Deposit', nativeCurrency,
cacheDirectory,
userDirectory,
logger,
}); });
amountService.withdrawalsService = new NodeTornadoService({
...TornadoServiceConstructor,
type: 'Withdrawal',
});
}
}
} }
syncManager.cachedServices = cachedServices; syncManager.cachedServices = cachedServices;
@ -413,12 +372,11 @@ export async function syncNetworkEvents(syncManager: SyncManager, netId: NetIdTy
const eventsStatus = { const eventsStatus = {
governance: governanceService ? {} : undefined, governance: governanceService ? {} : undefined,
registered: registryService ? {} : undefined,
registry: registryService ? {} : undefined, registry: registryService ? {} : undefined,
revenue: revenueService ? {} : undefined, revenue: revenueService ? {} : undefined,
echo: {}, echo: {},
encrypted_notes: {}, encrypted_notes: {},
instances: {}, tornado: {},
} as TovarishEventsStatus; } as TovarishEventsStatus;
if (governanceService) { if (governanceService) {
@ -447,12 +405,6 @@ export async function syncNetworkEvents(syncManager: SyncManager, netId: NetIdTy
{ {
const { lastBlock, timestamp, relayers } = await registryService.updateRelayers(); const { lastBlock, timestamp, relayers } = await registryService.updateRelayers();
eventsStatus.registered = {
lastBlock,
timestamp,
relayers: relayers.length,
};
logger.info( logger.info(
`${netId}: Updated registry relayers (total: ${relayers.length}, block: ${lastBlock}, timestamp: ${timestamp})`, `${netId}: Updated registry relayers (total: ${relayers.length}, block: ${lastBlock}, timestamp: ${timestamp})`,
); );
@ -492,45 +444,17 @@ export async function syncNetworkEvents(syncManager: SyncManager, netId: NetIdTy
`${netId}: Updated encrypted notes events (total: ${encryptedNotesEvents.events.length}, block: ${encryptedNotesEvents.lastBlock})`, `${netId}: Updated encrypted notes events (total: ${encryptedNotesEvents.events.length}, block: ${encryptedNotesEvents.lastBlock})`,
); );
const currencies = Object.keys(tornadoServices); const tornadoEvents = await tornadoServices.updateEvents();
for (const currency of currencies) { eventsStatus.tornado = {
const currencyStatus = (eventsStatus.instances[currency] = {} as InstanceEventsStatus); events: tornadoEvents.events.length,
lastBlock: tornadoEvents.lastBlock,
const amounts = Object.keys(tornadoServices[currency]);
for (const amount of amounts) {
const instanceStatus = (currencyStatus[amount] = {
deposits: {} as EventsStatus,
withdrawals: {} as EventsStatus,
});
const { depositsService, withdrawalsService } = tornadoServices[currency][amount];
const depositEvents = await depositsService.updateEvents();
instanceStatus.deposits = {
events: depositEvents.events.length,
lastBlock: depositEvents.lastBlock,
}; };
logger.info( logger.info(
`${netId}: Updated ${currency} ${amount} Tornado deposit events (total: ${depositEvents.events.length}, block: ${depositEvents.lastBlock})`, `${netId}: Updated tornado events (total: ${tornadoEvents.events.length}, block: ${tornadoEvents.lastBlock})`,
); );
const withdrawalEvents = await withdrawalsService.updateEvents();
instanceStatus.withdrawals = {
events: withdrawalEvents.events.length,
lastBlock: withdrawalEvents.lastBlock,
};
logger.info(
`${netId}: Updated ${currency} ${amount} Tornado withdrawal events (total: ${withdrawalEvents.events.length}, block: ${withdrawalEvents.lastBlock})`,
);
}
}
cachedEvents[netId] = eventsStatus; cachedEvents[netId] = eventsStatus;
syncStatus[netId].events = true; syncStatus[netId].events = true;

@ -10,9 +10,6 @@ import type { NetIdType } from '@tornado/core';
import { saveUserFile } from './data'; import { saveUserFile } from './data';
export interface TreeCacheConstructor { export interface TreeCacheConstructor {
netId: NetIdType;
amount: string;
currency: string;
userDirectory: string; userDirectory: string;
PARTS_COUNT?: number; PARTS_COUNT?: number;
LEAVES?: number; LEAVES?: number;
@ -29,30 +26,46 @@ export interface treeMetadata {
} }
export class TreeCache { export class TreeCache {
netId: NetIdType;
amount: string;
currency: string;
userDirectory: string; userDirectory: string;
PARTS_COUNT: number; PARTS_COUNT: number;
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT = 4 }: TreeCacheConstructor) { constructor({ userDirectory, PARTS_COUNT = 4 }: TreeCacheConstructor) {
this.netId = netId;
this.amount = amount;
this.currency = currency;
this.userDirectory = userDirectory; this.userDirectory = userDirectory;
this.PARTS_COUNT = PARTS_COUNT; this.PARTS_COUNT = PARTS_COUNT;
} }
getInstanceName(): string { static getInstanceName({
return `deposits_${this.netId}_${this.currency}_${this.amount}`; netId,
amount,
currency,
}: {
netId: NetIdType;
amount: string;
currency: string;
}): string {
return `deposits_${netId}_${currency}_${amount}`;
} }
async createTree(events: DepositsEvents[], tree: MerkleTree) { async createTree({
netId,
amount,
currency,
events,
tree,
}: {
netId: NetIdType;
amount: string;
currency: string;
events: DepositsEvents[];
tree: MerkleTree;
}) {
const bloom = new BloomFilter(events.length); const bloom = new BloomFilter(events.length);
console.log(`Creating cached tree for ${this.getInstanceName()}\n`); const instance = TreeCache.getInstanceName({ netId, amount, currency });
console.log(`Creating cached tree for ${instance}\n`);
// events indexed by commitment // events indexed by commitment
const eventsData = events.reduce( const eventsData = events.reduce(
@ -90,7 +103,7 @@ export class TreeCache {
2, 2,
) + '\n'; ) + '\n';
const fileName = `${this.getInstanceName()}_slice${index + 1}.json`; const fileName = `${instance}_slice${index + 1}.json`;
await saveUserFile({ await saveUserFile({
fileName, fileName,
@ -102,7 +115,7 @@ export class TreeCache {
const dataString = bloom.serialize() + '\n'; const dataString = bloom.serialize() + '\n';
const fileName = `${this.getInstanceName()}_bloom.json`; const fileName = `${instance}_bloom.json`;
await saveUserFile({ await saveUserFile({
fileName, fileName,

BIN
static/events/tornado_1.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_10.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_100.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_11155111.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_137.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_42161.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_43114.json.zip vendored Normal file

Binary file not shown.

BIN
static/events/tornado_56.json.zip vendored Normal file

Binary file not shown.

@ -690,9 +690,9 @@
"@openzeppelin/contracts-v3" "npm:@openzeppelin/contracts@3.2.0-rc.0" "@openzeppelin/contracts-v3" "npm:@openzeppelin/contracts@3.2.0-rc.0"
ethers "^6.13.4" ethers "^6.13.4"
"@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#94a62e6193c99457a8dfae0d8684bee299cb1097": "@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#68fcf07c2a344a1f7f74895301f362aa059b53e2":
version "1.0.19" version "1.0.19"
resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#94a62e6193c99457a8dfae0d8684bee299cb1097" resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#68fcf07c2a344a1f7f74895301f362aa059b53e2"
dependencies: dependencies:
"@ensdomains/content-hash" "2.5.7" "@ensdomains/content-hash" "2.5.7"
"@metamask/eth-sig-util" "^8.0.0" "@metamask/eth-sig-util" "^8.0.0"