Skip to content

Commit

Permalink
Add MPC guard service
Browse files Browse the repository at this point in the history
  • Loading branch information
lok52 committed Dec 17, 2023
1 parent b1c0524 commit 86079c0
Show file tree
Hide file tree
Showing 15 changed files with 334 additions and 71 deletions.
12 changes: 12 additions & 0 deletions zp-relayer/guard/guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import express from 'express'
import { logger } from '../services/appLogger'
import { createRouter } from './router'
import { init } from './init'

const app = express()

init().then(({ pool, signer }) => {
app.use(createRouter({ pool, signer }))
const PORT = 8080
app.listen(PORT, () => logger.info(`Started guard on port ${PORT}`))
})
39 changes: 39 additions & 0 deletions zp-relayer/guard/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import relayerConfig from '@/configs/relayerConfig'
import config from '@/configs/relayerConfig'
import { Pool } from '@/pool'
import { EvmBackend, Network, NetworkBackend, TronBackend } from '@/services/network'
import { Wallet } from 'ethers'

export async function init() {
let networkBackend: NetworkBackend<Network>
const baseConfig = {
poolAddress: config.COMMON_POOL_ADDRESS,
tokenAddress: config.RELAYER_TOKEN_ADDRESS,
pk: config.RELAYER_ADDRESS_PRIVATE_KEY,
rpcUrls: config.COMMON_RPC_URL,
requireHTTPS: config.COMMON_REQUIRE_RPC_HTTPS,
}
if (config.RELAYER_NETWORK === Network.Ethereum) {
networkBackend = new EvmBackend({
...baseConfig,
rpcRequestTimeout: config.COMMON_RPC_REQUEST_TIMEOUT,
rpcSyncCheckInterval: config.COMMON_RPC_SYNC_STATE_CHECK_INTERVAL,
jsonRpcErrorCodes: config.COMMON_JSONRPC_ERROR_CODES,
relayerTxRedundancy: config.RELAYER_TX_REDUNDANCY,
})
} else if (config.RELAYER_NETWORK === Network.Tron) {
networkBackend = new TronBackend({
...baseConfig,
})
} else {
throw new Error('Unsupported network backend')
}
await networkBackend.init()

const pool = new Pool(networkBackend)
await pool.init(false)

const signer = new Wallet(relayerConfig.RELAYER_ADDRESS_PRIVATE_KEY) // TODO: config pk

return { pool, signer }
}
62 changes: 62 additions & 0 deletions zp-relayer/guard/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import cors from 'cors'
import express, { NextFunction, Request, Response } from 'express'
import { checkSignMPCSchema, validateBatch } from '../validation/api/validation'
import { logger } from '../services/appLogger'
import { validateTxMPC } from '../validation/tx/validateTx'
import type { Pool } from '@/pool'
import { TxData, buildTxData } from '@/txProcessor'
import { Signer } from 'ethers'
import { truncateHexPrefix } from '@/utils/helpers'
import { VK } from 'libzkbob-rs-node'

function wrapErr(f: (_req: Request, _res: Response, _next: NextFunction) => Promise<void> | void) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
await f(req, res, next)
} catch (e) {
next(e)
}
}
}

export function createRouter({ pool, signer }: { pool: Pool; signer: Signer }) {
const router = express.Router()

router.use(cors())
router.use(express.urlencoded({ extended: true }))
router.use(express.json())
router.use(express.text())

router.use((err: any, _req: Request, res: Response, next: NextFunction) => {
if (err) {
logger.error('Request error:', err)
return res.sendStatus(500)
}
next()
})

router.post(
'/sign',
wrapErr(async (req: Request, res: Response) => {
validateBatch([[checkSignMPCSchema, req.body]])
const message = req.body as TxData

// Validate
const vk: VK = require('../params/tree_verification_key.json') // TODO: config vk
try {
await validateTxMPC(message, pool, vk)
} catch (e) {
console.log('Validation error', e)
throw new Error('Invalid transaction')
}

// Sign
const calldata = truncateHexPrefix(buildTxData(message))
const signature = await signer.signMessage(calldata)

res.json({ signature })
})
)

return router
}
3 changes: 2 additions & 1 deletion zp-relayer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"scripts": {
"initialize": "yarn install --frozen-lockfile",
"build": "tsc --project ./ && tsc-alias",
"start:dev": "DOTENV_CONFIG_PATH=relayer.env ts-node -r dotenv/config index.ts",
"start:dev": "DOTENV_CONFIG_PATH=relayer.env ts-node -r dotenv/config relayer/relayer.ts",
"start:mpc:guard:dev": "DOTENV_CONFIG_PATH=mpc.env ts-node -r dotenv/config guard/guard.ts",
"start:prod": "node index.js",
"start:direct-deposit-watcher:dev": "DOTENV_CONFIG_PATH=watcher.env ts-node -r dotenv/config direct-deposit/watcher.ts",
"start:direct-deposit-watcher:prod": "node direct-deposit/watcher.js",
Expand Down
8 changes: 5 additions & 3 deletions zp-relayer/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export interface LimitsFetch {
}

export class Pool<N extends Network = Network> {
private txVK: VK
public txVK: VK
public state: PoolState
public optimisticState: PoolState
public denominator: BN = toBN(1)
Expand All @@ -95,7 +95,7 @@ export class Pool<N extends Network = Network> {
this.optimisticState = states.optimisticState
}

async init() {
async init(sync: boolean = true) {
if (this.isInitialized) return

this.denominator = toBN(await this.network.pool.call('denominator'))
Expand All @@ -113,7 +113,9 @@ export class Pool<N extends Network = Network> {
throw new Error("Cannot infer pool's permit standard")
}
await this.permitRecover?.initializeDomain()
await this.syncState(config.COMMON_START_BLOCK)
if (sync) {
await this.syncState(config.COMMON_START_BLOCK)
}
this.isInitialized = true
}

Expand Down
13 changes: 9 additions & 4 deletions zp-relayer/queue/poolTxQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@ export enum JobState {
FAILED = 'failed',
}

export interface BaseTxPayload {
txProof: Proof
export interface BasePayload {
txHash: string | null
state: JobState
}

export interface TxPayload extends BaseTxPayload {
export interface Tx {
txProof: Proof
amount: string
txType: TxType
rawMemo: string
depositSignature: string | null
}

export interface TxPayload extends BasePayload, Tx {}

interface ZkAddress {
diversifier: string
pk: string
Expand All @@ -38,12 +40,15 @@ export interface DirectDeposit {
deposit: string
}

export interface DirectDepositTxPayload extends BaseTxPayload {
export interface DirectDepositTx {
txProof: Proof
deposits: DirectDeposit[]
outCommit: string
memo: string
}

export interface DirectDepositTxPayload extends BasePayload, DirectDepositTx {}

export enum WorkerTxType {
Normal = 'normal',
DirectDeposit = 'dd',
Expand Down
12 changes: 6 additions & 6 deletions zp-relayer/endpoints.ts → zp-relayer/relayer/endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Queue } from 'bullmq'
import { Request, Response } from 'express'
import type { LimitsFetch, Pool, PoolTx } from './pool'
import { JobState, PoolTx as Tx, WorkerTxType, poolTxQueue } from './queue/poolTxQueue'
import config from './configs/relayerConfig'
import type { LimitsFetch, Pool, PoolTx } from '../pool'
import { JobState, PoolTx as Tx, WorkerTxType, poolTxQueue } from '../queue/poolTxQueue'
import config from '../configs/relayerConfig'
import {
validateCountryIP,
checkGetLimits,
Expand All @@ -12,9 +12,9 @@ import {
checkSendTransactionsErrors,
checkTraceId,
validateBatch,
} from './validation/api/validation'
import { HEADER_TRACE_ID } from './utils/constants'
import type { FeeManager } from './services/fee'
} from '../validation/api/validation'
import { HEADER_TRACE_ID } from '../utils/constants'
import type { FeeManager } from '../services/fee'

interface PoolInjection {
pool: Pool
Expand Down
24 changes: 12 additions & 12 deletions zp-relayer/init.ts → zp-relayer/relayer/init.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Mutex } from 'async-mutex'
import { Params } from 'libzkbob-rs-node'
import { Pool } from './pool'
import config from './configs/relayerConfig'
import { createPoolTxWorker } from './workers/poolTxWorker'
import { createDirectDepositWorker } from './workers/directDepositWorker'
import { redis } from './services/redisClient'
import { validateTx } from './validation/tx/validateTx'
import { Circuit, IProver, LocalProver, ProverType, RemoteProver } from './prover'
import { FeeManagerType, FeeManager, StaticFeeManager, DynamicFeeManager, OptimismFeeManager } from './services/fee'
import type { IPriceFeed } from './services/price-feed/IPriceFeed'
import type { IWorkerBaseConfig } from './workers/workerTypes'
import { NativePriceFeed, OneInchPriceFeed, PriceFeedType } from './services/price-feed'
import { Network, TronBackend, EvmBackend, NetworkBackend, isEthereum } from './services/network'
import { Pool } from '../pool'
import config from '../configs/relayerConfig'
import { createPoolTxWorker } from '../workers/poolTxWorker'
import { createDirectDepositWorker } from '../workers/directDepositWorker'
import { redis } from '../services/redisClient'
import { validateTx } from '../validation/tx/validateTx'
import { Circuit, IProver, LocalProver, ProverType, RemoteProver } from '../prover'
import { FeeManagerType, FeeManager, StaticFeeManager, DynamicFeeManager, OptimismFeeManager } from '../services/fee'
import type { IPriceFeed } from '../services/price-feed/IPriceFeed'
import type { IWorkerBaseConfig } from '../workers/workerTypes'
import { NativePriceFeed, OneInchPriceFeed, PriceFeedType } from '../services/price-feed'
import { Network, TronBackend, EvmBackend, NetworkBackend, isEthereum } from '../services/network'

function buildProver<T extends Circuit>(circuit: T, type: ProverType, path: string): IProver<T> {
switch (type) {
Expand Down
6 changes: 3 additions & 3 deletions zp-relayer/index.ts → zp-relayer/relayer/relayer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import express from 'express'
import { createRouter } from './router'
import { logger } from './services/appLogger'
import { createConsoleLoggerMiddleware, createPersistentLoggerMiddleware } from './services/loggerMiddleware'
import config from './configs/relayerConfig'
import { logger } from '../services/appLogger'
import { createConsoleLoggerMiddleware, createPersistentLoggerMiddleware } from '../services/loggerMiddleware'
import config from '../configs/relayerConfig'
import { init } from './init'

init().then(({ feeManager, pool }) => {
Expand Down
14 changes: 7 additions & 7 deletions zp-relayer/router.ts → zp-relayer/relayer/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import express, { NextFunction, Request, Response } from 'express'
import cors from 'cors'
import semver from 'semver'
import endpoints, { inject } from './endpoints'
import { logger } from './services/appLogger'
import { ValidationError } from './validation/api/validation'
import config from './configs/relayerConfig'
import { HEADER_LIBJS, HEADER_TRACE_ID, LIBJS_MIN_VERSION } from './utils/constants'
import { getFileHash } from './utils/helpers'
import type { FeeManager } from './services/fee'
import type { Pool } from './pool'
import { logger } from '@/services/appLogger'
import { ValidationError } from '@/validation/api/validation'
import config from '@/configs/relayerConfig'
import { HEADER_LIBJS, HEADER_TRACE_ID, LIBJS_MIN_VERSION } from '../utils/constants'
import { getFileHash } from '@/utils/helpers'
import type { FeeManager } from '@/services/fee'
import type { Pool } from '@/pool'

interface IRouterConfig {
feeManager: FeeManager
Expand Down
33 changes: 19 additions & 14 deletions zp-relayer/txProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Contract from 'web3-eth-contract'
import { AbiItem, toBN } from 'web3-utils'
import type { TxType } from 'zp-memo-parser'
import { DelegatedDepositsData, SnarkProof } from 'libzkbob-rs-node'
import { DelegatedDepositsData, Proof } from 'libzkbob-rs-node'
import type { PoolState } from './state/PoolState'
import PoolAbi from './abi/pool-abi.json'
import { logger } from './services/appLogger'
Expand All @@ -15,28 +15,29 @@ import type { Circuit, IProver } from './prover/IProver'
// Used only to get `transact` method selector
const PoolInstance = new Contract(PoolAbi as AbiItem[])

interface TxData {
txProof: SnarkProof
treeProof: SnarkProof
type Stringified<T> = {
[P in keyof T]: string
}
export interface TxData {
txProof: Proof
treeProof: Proof
nullifier: string
outCommit: string
rootAfter: string
delta: Delta
delta: Stringified<Omit<Delta, 'poolId'>>
txType: TxType
memo: string
depositSignature: string | null
}

function buildTxData(txData: TxData) {
export function buildTxData(txData: TxData) {
const selector: string = PoolInstance.methods.transact().encodeABI()

const transferIndex = numToHex(txData.delta.transferIndex, TRANSFER_INDEX_SIZE)
const energyAmount = numToHex(txData.delta.energyAmount, ENERGY_SIZE)
const tokenAmount = numToHex(txData.delta.tokenAmount, TOKEN_SIZE)
const { transferIndex, energyAmount, tokenAmount } = txData.delta
logger.debug(`DELTA ${transferIndex} ${energyAmount} ${tokenAmount}`)

const txFlatProof = encodeProof(txData.txProof)
const treeFlatProof = encodeProof(txData.treeProof)
const txFlatProof = encodeProof(txData.txProof.proof)
const treeFlatProof = encodeProof(txData.treeProof.proof)

const memoMessage = txData.memo
const memoSize = numToHex(toBN(memoMessage.length).divn(2), 4)
Expand Down Expand Up @@ -119,12 +120,16 @@ export async function buildTx(

const rootAfter = treeProof.inputs[1]
const data = buildTxData({
txProof: txProof.proof,
treeProof: treeProof.proof,
txProof,
treeProof,
nullifier: numToHex(toBN(nullifier)),
outCommit: numToHex(toBN(outCommit)),
rootAfter: numToHex(toBN(rootAfter)),
delta,
delta: {
transferIndex: numToHex(delta.transferIndex, TRANSFER_INDEX_SIZE),
energyAmount: numToHex(delta.energyAmount, ENERGY_SIZE),
tokenAmount: numToHex(delta.tokenAmount, TOKEN_SIZE),
},
txType,
memo: rawMemo,
depositSignature,
Expand Down
Loading

0 comments on commit 86079c0

Please sign in to comment.