Skip to content

Commit

Permalink
Fix guard signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
lok52 committed Dec 19, 2023
1 parent 9d4fc86 commit 13f6848
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 56 deletions.
2 changes: 2 additions & 0 deletions zp-relayer/configs/guardConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const schema = z.object({
GUARD_TOKEN_ADDRESS: z.string(),
COMMON_REQUIRE_RPC_HTTPS: zBooleanString().default('false'),
COMMON_POOL_ADDRESS: z.string(),
GUARD_TX_VK_PATH: z.string().default('../params/transfer_verification_key.json'),
GUARD_TREE_VK_PATH: z.string().default('../params/tree_verification_key.json'),
})

const config = schema.parse(process.env)
Expand Down
25 changes: 17 additions & 8 deletions zp-relayer/guard/router.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// @ts-ignore
import cors from 'cors'
import { Signer } from 'ethers'
import { Signer, keccak256, getBytes } from 'ethers'
import { toBN } from 'web3-utils'
import express, { NextFunction, Request, Response } from 'express'
import { checkSignMPCSchema, validateBatch } from '../validation/api/validation'
import { logger } from '../services/appLogger'
import { TxDataMPC, validateTxMPC } from '../validation/tx/validateTx'
import { TxData, buildTxData } from '@/txProcessor'
import { numToHex, truncateHexPrefix } from '@/utils/helpers'
import { numToHex, packSignature } from '@/utils/helpers'
import { VK } from 'libzkbob-rs-node'
import { getTxProofField, parseDelta } from '@/utils/proofInputs'
import { ENERGY_SIZE, TOKEN_SIZE, TRANSFER_INDEX_SIZE } from '@/utils/constants'
Expand Down Expand Up @@ -52,13 +52,13 @@ export function createRouter({ signer, poolContract }: RouterConfig) {
const message = req.body as TxDataMPC

// Validate
const vk: VK = require('../params/tree_verification_key.json') // TODO: config vk
const txVK: VK = require(config.GUARD_TX_VK_PATH)
const treeVK: VK = require(config.GUARD_TREE_VK_PATH)

const denominator = toBN(await poolContract.call('denominator'))
const poolId = toBN(await poolContract.call('pool_id'))

try {
await validateTxMPC(message, config.GUARD_RELAYER_ADDRESS, poolContract, poolId, denominator, vk, vk)
await validateTxMPC(message, poolId, treeVK, txVK)
} catch (e) {
console.log('Validation error', e)
throw new Error('Invalid transaction')
Expand All @@ -69,6 +69,7 @@ export function createRouter({ signer, poolContract }: RouterConfig) {
const nullifier = getTxProofField(txProof, 'nullifier')
const outCommit = getTxProofField(txProof, 'out_commit')
const delta = parseDelta(getTxProofField(txProof, 'delta'))

const rootAfter = treeProof.inputs[1]

const txData: TxData = {
Expand All @@ -84,11 +85,19 @@ export function createRouter({ signer, poolContract }: RouterConfig) {
},
txType: message.txType,
memo: message.memo,
depositSignature: null,
depositSignature: message.depositSignature,
}
const calldata = truncateHexPrefix(buildTxData(txData))
const signature = await signer.signMessage(calldata)

const transferRoot = numToHex(toBN(getTxProofField(txProof, 'root')))
const currentRoot = numToHex(toBN(treeProof.inputs[0]))

let calldata = buildTxData(txData)
calldata += transferRoot + currentRoot + numToHex(poolId)

const digest = getBytes(keccak256(calldata))
const signature = packSignature(await signer.signMessage(digest))

console.log('Signed', signature)
res.json({ signature })
})
)
Expand Down
13 changes: 12 additions & 1 deletion zp-relayer/txProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { numToHex, flattenProof, truncateHexPrefix, encodeProof, truncateMemoTxP
import { Delta, getTxProofField, parseDelta } from './utils/proofInputs'
import type { DirectDeposit, WorkerTx, WorkerTxType } from './queue/poolTxQueue'
import type { Circuit, IProver } from './prover/IProver'
import { TxDataMPC } from './validation/tx/validateTx'

// @ts-ignore
// Used only to get `transact` method selector
Expand Down Expand Up @@ -146,18 +147,28 @@ export async function buildTx(
let mpc = false
const mpcSignatures = []
if (mpcGuards) {
const txDataMPC: TxDataMPC = {
txProof,
treeProof,
memo: rawMemo,
txType,
depositSignature,
}
logger.debug('Collecting signatures from MPC guards...')
for (const [, guardHttp] of mpcGuards) {
const rawRes = await fetch(guardHttp, {
headers: {
'Content-type': 'application/json',
},
method: 'POST',
body: JSON.stringify(txData),
body: JSON.stringify(txDataMPC),
})
const res = await rawRes.json()
const signature = truncateHexPrefix(res.signature)
mpcSignatures.push(signature)
}
logger.debug('Collected %d signatures', mpcSignatures.length)
console.log(mpcSignatures)
mpc = true
}

Expand Down
20 changes: 20 additions & 0 deletions zp-relayer/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ export function numToHex(num: BN, pad = 64) {
return padLeft(hex, pad)
}

export function packSignature(signature: string): string {
signature = truncateHexPrefix(signature)

if (signature.length > 128) {
let v = signature.slice(128, 130)
if (v == '1c') {
return `${signature.slice(0, 64)}${(parseInt(signature[64], 16) | 8).toString(16)}${signature.slice(65, 128)}`
} else if (v != '1b') {
throw 'invalid signature: v should be 27 or 28'
}

return signature.slice(0, 128)
} else if (signature.length < 128) {
throw 'invalid signature: it should consist at least 64 bytes (128 chars)'
}

return signature
}


export function unpackSignature(packedSign: string) {
if (packedSign.length === 130) {
return '0x' + packedSign
Expand Down
48 changes: 1 addition & 47 deletions zp-relayer/validation/tx/validateTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { checkAssertion, TxValidationError, checkSize, checkScreener, checkCondi
import type { PermitRecover } from '@/utils/permit/types'
import type { FeeManager } from '@/services/fee'
import type { NetworkBackend } from '@/services/network/NetworkBackend'
import type { Network, NetworkContract } from '@/services/network/types'
import type { Network } from '@/services/network/types'

const ZERO = toBN(0)

Expand Down Expand Up @@ -326,10 +326,7 @@ export type TxDataMPC = {

export async function validateTxMPC(
{ memo: rawMemo, txType, txProof, depositSignature, treeProof }: TxDataMPC,
relayerAddress: string,
poolContract: NetworkContract<Network>,
poolId: BN,
denominator: BN,
treeVK: VK,
txVK: VK
) {
Expand All @@ -338,8 +335,6 @@ export async function validateTxMPC(
const buf = Buffer.from(rawMemo, 'hex')
const txData = getTxData(buf, txType)

const root = getTxProofField(txProof, 'root')
const nullifier = getTxProofField(txProof, 'nullifier')
const delta = parseDelta(getTxProofField(txProof, 'delta'))
const fee = toBN(txData.fee)

Expand All @@ -351,62 +346,21 @@ export async function validateTxMPC(
)

await checkAssertion(() => checkPoolId(delta.poolId, poolId))
await checkAssertion(async () => {
const res = await poolContract.callRetry('roots', [delta.transferIndex])
if (res !== root) {
return new TxValidationError(`Incorrect root at index ${delta.transferIndex}: given ${root}, expected ${res}`)
}
return null
})
await checkAssertion(async () => {
const res = await poolContract.callRetry('nullifiers', [nullifier])
if (res !== '0') {
return new TxValidationError(`DoubleSpend detected in contract`)
}
return null
})
// TODO: handle index
// await checkAssertion(() => checkTransferIndex(toBN(pool.optimisticState.getNextIndex()), delta.transferIndex))
await checkAssertion(() => checkProof(txProof, (p, i) => Proof.verify(txVK, p, i)))
await checkAssertion(() => checkProof(treeProof, (p, i) => Proof.verify(treeVK, p, i)))

const tokenAmount = delta.tokenAmount
const tokenAmountWithFee = tokenAmount.add(fee)
const energyAmount = delta.energyAmount

let userAddress: string

if (txType === TxType.WITHDRAWAL) {
checkCondition(tokenAmountWithFee.lte(ZERO) && energyAmount.lte(ZERO), 'Incorrect withdraw amounts')

const { receiver } = txData as TxData<TxType.WITHDRAWAL>
userAddress = bytesToHex(Array.from(receiver))
logger.info('Withdraw address: %s', userAddress)
await checkAssertion(() => checkNonZeroWithdrawAddress(userAddress))
} else if (txType === TxType.DEPOSIT || txType === TxType.PERMITTABLE_DEPOSIT) {
checkCondition(tokenAmount.gt(ZERO) && energyAmount.eq(ZERO), 'Incorrect deposit amounts')
checkCondition(depositSignature !== null, 'Deposit signature is required')

const requiredTokenAmount = applyDenominator(tokenAmountWithFee, denominator)
// userAddress = await getRecoveredAddress(
// txType,
// nullifier,
// txData,
// pool.network,
// requiredTokenAmount,
// depositSignature as string,
// pool.permitRecover
// )
// logger.info('Deposit address: %s', userAddress)
// TODO check for approve in case of deposit
// await checkAssertion(() => checkDepositEnoughBalance(pool.network, userAddress, requiredTokenAmount))
} else if (txType === TxType.TRANSFER) {
userAddress = relayerAddress
checkCondition(tokenAmountWithFee.eq(ZERO) && energyAmount.eq(ZERO), 'Incorrect transfer amounts')
} else {
throw new TxValidationError('Unsupported TxType')
}

// const limits = await pool.getLimitsFor(userAddress)
// await checkAssertion(() => checkLimits(limits, delta.tokenAmount))
}

0 comments on commit 13f6848

Please sign in to comment.