mirror of
https://github.com/tornadocash/tornado-pool-relayer
synced 2024-02-02 15:04:09 +01:00
fix: update relayer
-add unique job id -handle contract errors -fix queue management
This commit is contained in:
parent
bb9d6de05f
commit
bd8f40881c
@ -33,3 +33,14 @@ const FIELD_SIZE = BigNumber.from('218882428718392752222464057452572750885483644
|
|||||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
|
|
||||||
export { numbers, NETWORKS_INFO, FIELD_SIZE, BG_ZERO, ZERO_ADDRESS };
|
export { numbers, NETWORKS_INFO, FIELD_SIZE, BG_ZERO, ZERO_ADDRESS };
|
||||||
|
|
||||||
|
export const CONTRACT_ERRORS = [
|
||||||
|
'Invalid merkle root',
|
||||||
|
'Input is already spent',
|
||||||
|
'Incorrect external data hash',
|
||||||
|
'Invalid fee',
|
||||||
|
'Invalid ext amount',
|
||||||
|
'Invalid public amount',
|
||||||
|
'Invalid transaction proof',
|
||||||
|
"Can't withdraw to zero address",
|
||||||
|
];
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { Queue, Job } from 'bull';
|
import { Queue } from 'bull';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
import { InjectQueue } from '@nestjs/bull';
|
import { InjectQueue } from '@nestjs/bull';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { ProviderService } from '@/services';
|
import { ProviderService } from '@/services';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
import { Transaction } from '@/types';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class ApiService {
|
class ApiService {
|
||||||
constructor(
|
constructor(
|
||||||
@ -32,13 +34,24 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async transaction(data: any): Promise<string> {
|
async transaction(data: any): Promise<string> {
|
||||||
const job = await this.transactionQueue.add(data);
|
const jobId = uuid();
|
||||||
|
|
||||||
return String(job.id);
|
await this.transactionQueue.add({ ...data, status: 'QUEUED' }, { jobId });
|
||||||
|
|
||||||
|
return jobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJob(id: string): Promise<Job | null> {
|
async getJob(id: string): Promise<Transaction | null> {
|
||||||
return await this.transactionQueue.getJob(id);
|
const job = await this.transactionQueue.getJob(id);
|
||||||
|
|
||||||
|
if (!job) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...job.data,
|
||||||
|
failedReason: job.failedReason,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async healthCheck(): Promise<Health> {
|
private async healthCheck(): Promise<Health> {
|
||||||
|
@ -1,48 +1,18 @@
|
|||||||
import { Job, Queue } from 'bull';
|
import { BigNumber } from 'ethers';
|
||||||
import { BigNumber, BigNumberish } from 'ethers';
|
|
||||||
import { BytesLike } from '@ethersproject/bytes';
|
|
||||||
import { TxManager } from 'tx-manager';
|
import { TxManager } from 'tx-manager';
|
||||||
|
import { Job, Queue, DoneCallback } from 'bull';
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { InjectQueue, Process, Processor } from '@nestjs/bull';
|
import { InjectQueue, Process, Processor, OnQueueActive, OnQueueCompleted, OnQueueFailed } from '@nestjs/bull';
|
||||||
|
|
||||||
import { numbers } from '@/constants';
|
import { numbers, CONTRACT_ERRORS } from '@/constants';
|
||||||
import { toWei, getToIntegerMultiplier } from '@/utilities';
|
import { toWei, getToIntegerMultiplier } from '@/utilities';
|
||||||
import { GasPriceService, ProviderService } from '@/services';
|
import { GasPriceService, ProviderService } from '@/services';
|
||||||
import txMangerConfig from '@/config/txManager.config';
|
import txMangerConfig from '@/config/txManager.config';
|
||||||
|
|
||||||
import { BaseProcessor } from './base.processor';
|
import { BaseProcessor } from './base.processor';
|
||||||
import { ChainId } from '@/types';
|
import { ChainId, Transaction } from '@/types';
|
||||||
|
|
||||||
export type ExtData = {
|
|
||||||
recipient: string;
|
|
||||||
relayer: string;
|
|
||||||
fee: BigNumberish;
|
|
||||||
extAmount: BigNumberish;
|
|
||||||
encryptedOutput1: BytesLike;
|
|
||||||
encryptedOutput2: BytesLike;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ArgsProof = {
|
|
||||||
proof: BytesLike;
|
|
||||||
root: BytesLike;
|
|
||||||
newRoot: BytesLike;
|
|
||||||
inputNullifiers: string[];
|
|
||||||
outputCommitments: BytesLike[];
|
|
||||||
outPathIndices: string;
|
|
||||||
publicAmount: string;
|
|
||||||
extDataHash: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface Transaction {
|
|
||||||
extData: ExtData;
|
|
||||||
args: ArgsProof;
|
|
||||||
txHash: string;
|
|
||||||
status: string;
|
|
||||||
confirmations: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@Processor('transaction')
|
@Processor('transaction')
|
||||||
export class TransactionProcessor extends BaseProcessor<Transaction> {
|
export class TransactionProcessor extends BaseProcessor<Transaction> {
|
||||||
@ -58,19 +28,40 @@ export class TransactionProcessor extends BaseProcessor<Transaction> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Process()
|
@Process()
|
||||||
async processTransactions(job: Job<Transaction>) {
|
async processTransactions(job: Job<Transaction>, cb: DoneCallback) {
|
||||||
try {
|
try {
|
||||||
await job.isActive();
|
|
||||||
|
|
||||||
const { extData } = job.data;
|
const { extData } = job.data;
|
||||||
|
|
||||||
await this.checkFee({ fee: extData.fee, externalAmount: extData.extAmount });
|
await this.checkFee({ fee: extData.fee, externalAmount: extData.extAmount });
|
||||||
await this.submitTx(job);
|
await this.submitTx(job);
|
||||||
|
|
||||||
|
cb(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await job.moveToFailed(err, true);
|
cb(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnQueueActive()
|
||||||
|
async onActive(job: Job) {
|
||||||
|
job.data.status = 'ACCEPTED';
|
||||||
|
|
||||||
|
await job.update(job.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnQueueCompleted()
|
||||||
|
async onCompleted(job: Job) {
|
||||||
|
job.data.status = 'CONFIRMED';
|
||||||
|
|
||||||
|
await job.update(job.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnQueueFailed()
|
||||||
|
async onFailed(job: Job) {
|
||||||
|
job.data.status = 'FAILED';
|
||||||
|
|
||||||
|
await job.update(job.data);
|
||||||
|
}
|
||||||
|
|
||||||
async submitTx(job: Job<Transaction>) {
|
async submitTx(job: Job<Transaction>) {
|
||||||
try {
|
try {
|
||||||
const txManager = new TxManager(txMangerConfig());
|
const txManager = new TxManager(txMangerConfig());
|
||||||
@ -97,17 +88,11 @@ export class TransactionProcessor extends BaseProcessor<Transaction> {
|
|||||||
await job.update(job.data);
|
await job.update(job.data);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (receipt.status === 1) {
|
if (receipt.status !== 1) {
|
||||||
await job.isCompleted();
|
|
||||||
|
|
||||||
job.data.status = 'SENT';
|
|
||||||
|
|
||||||
await job.update(job.data);
|
|
||||||
} else {
|
|
||||||
throw new Error('Submitted transaction failed');
|
throw new Error('Submitted transaction failed');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
throw new Error(`Revert by smart contract ${e.message}`);
|
return this.handleError(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,4 +155,21 @@ export class TransactionProcessor extends BaseProcessor<Transaction> {
|
|||||||
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.');
|
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleError(e) {
|
||||||
|
// Sometimes ethers wraps known errors, unwrap it in this case
|
||||||
|
if (e?.error?.error) {
|
||||||
|
e = e.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = e?.error ? e.error.message : e.message;
|
||||||
|
|
||||||
|
const error = CONTRACT_ERRORS.find((e) => (typeof e === 'string' ? e === message : message.match(e)));
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw new Error(`Revert by smart contract: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Relayer did not send your transaction. Please choose a different relayer.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,39 @@
|
|||||||
const MAINNET_CHAIN_ID = 1
|
import { BigNumberish } from 'ethers';
|
||||||
const GOERLI_CHAIN_ID = 5
|
import { BytesLike } from '@ethersproject/bytes';
|
||||||
const OPTIMISM_CHAIN_ID = 69
|
|
||||||
|
const MAINNET_CHAIN_ID = 1;
|
||||||
|
const GOERLI_CHAIN_ID = 5;
|
||||||
|
const OPTIMISM_CHAIN_ID = 69;
|
||||||
|
|
||||||
export enum ChainId {
|
export enum ChainId {
|
||||||
MAINNET = MAINNET_CHAIN_ID,
|
MAINNET = MAINNET_CHAIN_ID,
|
||||||
GOERLI = GOERLI_CHAIN_ID,
|
GOERLI = GOERLI_CHAIN_ID,
|
||||||
OPTIMISM = OPTIMISM_CHAIN_ID,
|
OPTIMISM = OPTIMISM_CHAIN_ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ExtData = {
|
||||||
|
recipient: string;
|
||||||
|
relayer: string;
|
||||||
|
fee: BigNumberish;
|
||||||
|
extAmount: BigNumberish;
|
||||||
|
encryptedOutput1: BytesLike;
|
||||||
|
encryptedOutput2: BytesLike;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ArgsProof = {
|
||||||
|
proof: BytesLike;
|
||||||
|
root: BytesLike;
|
||||||
|
inputNullifiers: string[];
|
||||||
|
outputCommitments: BytesLike[];
|
||||||
|
publicAmount: string;
|
||||||
|
extDataHash: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Transaction {
|
||||||
|
extData: ExtData;
|
||||||
|
args: ArgsProof;
|
||||||
|
status: string;
|
||||||
|
txHash?: string;
|
||||||
|
confirmations?: number;
|
||||||
|
failedReason?: string;
|
||||||
|
}
|
||||||
|
@ -5871,11 +5871,16 @@ utils-merge@1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||||
|
|
||||||
uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2:
|
uuid@8.3.2, uuid@^8.3.0:
|
||||||
version "8.3.2"
|
version "8.3.2"
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
|
uuid@^8.3.2:
|
||||||
|
version "8.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
v8-compile-cache@^2.0.3:
|
v8-compile-cache@^2.0.3:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||||
|
Loading…
Reference in New Issue
Block a user