Skip to content

Commit

Permalink
support cloudHSM as signing method
Browse files Browse the repository at this point in the history
  • Loading branch information
toolchainx committed May 13, 2024
1 parent 2059123 commit 2d021e6
Show file tree
Hide file tree
Showing 11 changed files with 2,157 additions and 123 deletions.
6 changes: 6 additions & 0 deletions app/mmConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ module.exports = {
*/
WALLET_PRIVATE_KEY: process.env.WALLET_PRIVATE_KEY,

/**
* GoogleCloud KMS keyVersionName, used unless both of SIGNING_URL and WALLET_PRIVATE_KEY are not set.
* Txs or messages are signed by GoogleCloud KMS remotely
*/
WALLET_KEY_VERSION_NAME: process.env.WALLET_KEY_VERSION_NAME,

// AMM
AMMWRAPPER_CONTRACT_ADDRESS: process.env.AMMWRAPPER_CONTRACT_ADDRESS,

Expand Down
2,171 changes: 2,081 additions & 90 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"hardhat/**/@ethereumjs/tx": "3.5.0"
},
"dependencies": {
"@ethersproject/abstract-signer": "5.7.0",
"@toolchainx/ethers-gcp-kms-signer": "1.0.5",
"@0x/contract-addresses": "^4.11.0",
"@0x/subproviders": "6.0.8",
"@babel/runtime": "7.3.1",
Expand Down
5 changes: 3 additions & 2 deletions src/check/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ export const checkMMSK = async (config: ConfigForStart) => {
} else {
quoter = new QuoteDispatcher(config.HTTP_SERVER_ENDPOINT, QuoterProtocol.HTTP)
}
const wallet = getWallet()
await startUpdater(quoter, wallet.address)
const wallet = await getWallet()
const walletAddress = await wallet.getAddress()
await startUpdater(quoter, walletAddress)

for (let i = 0; i < arr.length; i += 1) {
const item = arr[i]
Expand Down
39 changes: 32 additions & 7 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import * as readlineSync from 'readline-sync'
import { ConfigForStart } from './types'
import { Wallet } from 'ethers'
import { ethers, Wallet } from 'ethers'
import { GcpKmsSignerCredentials, GcpKmsSigner } from '@toolchainx/ethers-gcp-kms-signer'
import { TypedDataSigner } from '@ethersproject/abstract-signer'
import path from 'path'

const config = {
EXCHANGE_URL: null,
PROVIDER_URL: null,

WALLET_ADDRESS: null,
WALLET_PRIVATE_KEY: null,
WALLET_KEY_VERSION_NAME: null,
USE_KEYSTORE: true,

WALLET_KEYSTORE: null,
Expand All @@ -30,12 +34,33 @@ const setConfig = (conf: ConfigForStart) => {
return Object.assign(config, conf)
}

const getWallet = () => {
return new Wallet(
config.WALLET_PRIVATE_KEY.startsWith('0x')
? config.WALLET_PRIVATE_KEY
: '0x' + config.WALLET_PRIVATE_KEY
)
const getWallet = async (): Promise<ethers.Signer & TypedDataSigner> => {
if (config.WALLET_PRIVATE_KEY) {
return new Wallet(
config.WALLET_PRIVATE_KEY.startsWith('0x')
? config.WALLET_PRIVATE_KEY
: '0x' + config.WALLET_PRIVATE_KEY
)
} else if (config.WALLET_KEY_VERSION_NAME) {
const signer = new GcpKmsSigner(getKmsCredentials(config.WALLET_KEY_VERSION_NAME))
return signer
} else {
return null
}
}

const getKmsCredentials = (fullVersionedKeyName: string): GcpKmsSignerCredentials => {
if (!fullVersionedKeyName || fullVersionedKeyName.length === 0) {
return null
}
const strSplits = fullVersionedKeyName.split(path.sep)
return {
projectId: strSplits[1],
locationId: strSplits[3],
keyRingId: strSplits[5],
keyId: strSplits[7],
keyVersion: strSplits[9],
}
}

export { config, setConfig, getWallet }
9 changes: 5 additions & 4 deletions src/handler/signOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ export const signOrder = async (ctx) => {
const { chainID, walletType } = ctx
const order = ctx.request.body
const protocol = ctx.request.body.protocol
const signer = getWallet()
const signer = await getWallet()
const signerAddress = await signer.getAddress()
console.log('signer')
console.log(signer)
const config = updaterStack.markerMakerConfigUpdater.cacheResult
Expand All @@ -59,12 +60,12 @@ export const signOrder = async (ctx) => {
switch (protocol) {
case Protocol.PMMV5:
signature =
signer.address.toLowerCase() == order.pmmOrder.makerAddress.toLowerCase()
signerAddress.toLowerCase() == order.pmmOrder.makerAddress.toLowerCase()
? await signPMMV5ByEOA(order.orderSignDigest, signer)
: await signByMMPSigner(order.orderSignDigest, order.userAddr, order.feeFactor, signer)
break
case Protocol.RFQV1:
if (signer.address.toLowerCase() == order.rfqOrder.makerAddr.toLowerCase()) {
if (signerAddress.toLowerCase() == order.rfqOrder.makerAddr.toLowerCase()) {
const signatureTypedData = await signRFQOrder(
chainID,
config.addressBookV5.RFQ,
Expand Down Expand Up @@ -95,7 +96,7 @@ export const signOrder = async (ctx) => {
}
break
case Protocol.RFQV2:
if (signer.address.toLowerCase() == order.rfqOrder.maker.toLowerCase()) {
if (signerAddress.toLowerCase() == order.rfqOrder.maker.toLowerCase()) {
signature = await signOffer(
chainID,
config.addressBookV5.RFQV2,
Expand Down
14 changes: 8 additions & 6 deletions src/signer/pmmv5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import {
EIP712Types,
} from '0x-v2-order-utils'
import * as ethUtils from 'ethereumjs-util'
import { utils, Wallet } from 'ethers'
import { utils } from 'ethers'
import axios from 'axios'
import { BigNumber, orderBNToString } from '../utils'
import { Protocol } from '../types'
import { ExtendedZXOrder, RemoteSigningPMMV5Request } from './types'
import { Order as ZXOrder } from '0x-v2-order-utils'
import { AbstractSigner } from '@toolchainx/ethers-gcp-kms-signer'

export const EIP712_ORDER_SCHEMA = {
name: 'Order',
Expand Down Expand Up @@ -62,7 +63,7 @@ export const generateSaltWithFeeFactor = (feeFactor: number, prefixSalt?: string
// | V | R | S |userAddr |feeFactor|
// +------|---------|---------|---------|---------+
export async function signWithUserAndFee(
signer: Wallet,
signer: AbstractSigner,
orderSignDigest: string,
userAddr: string,
feeFactor: number
Expand Down Expand Up @@ -92,7 +93,7 @@ export async function signWithUserAndFee(
// +------|---------|---------|---------+
// | v | R | S | type(3) |
// +------|---------|---------|---------+
export async function signByEOA(orderSignDigest: string, wallet: Wallet): Promise<string> {
export async function signByEOA(orderSignDigest: string, wallet: AbstractSigner): Promise<string> {
const hashArray = utils.arrayify(orderSignDigest)
let signature = await wallet.signMessage(hashArray)
signature = signature.slice(2)
Expand All @@ -111,7 +112,7 @@ export async function signByMMPSigner(
orderSignDigest: string,
userAddr: string,
feeFactor: number,
wallet: Wallet
wallet: AbstractSigner
): Promise<string> {
const walletSign = await signWithUserAndFee(wallet, orderSignDigest, userAddr, feeFactor)
return signatureUtils.convertToSignatureWithType(walletSign, SignatureType.Wallet)
Expand All @@ -137,7 +138,7 @@ export const forwardUnsignedOrder = async (

// Move fee factor to salt field
export const buildSignedOrder = async (
signer: Wallet | undefined,
signer: AbstractSigner | undefined,
order: ExtendedZXOrder,
userAddr: string,
chainId: number,
Expand Down Expand Up @@ -179,8 +180,9 @@ export const buildSignedOrder = async (
console.log(`orderSignDigest: ${orderSignDigest}`)
let makerWalletSignature
if (!signingUrl) {
const signerAddress = await signer.getAddress()
makerWalletSignature =
signer.address.toLowerCase() == o.makerAddress.toLowerCase()
signerAddress.toLowerCase() == o.makerAddress.toLowerCase()
? await signByEOA(orderSignDigest, signer)
: await signByMMPSigner(orderSignDigest, userAddr, feeFactor, signer)
} else {
Expand Down
12 changes: 7 additions & 5 deletions src/signer/rfqv1.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Wallet, utils } from 'ethers'
import { utils } from 'ethers'
import { orderBNToString, BigNumber } from '../utils'
import { generateSaltWithFeeFactor, signWithUserAndFee } from './pmmv5'
import { getOrderHash, getOrderSignDigest } from './orderHash'
Expand All @@ -7,6 +7,7 @@ import * as ethUtils from 'ethereumjs-util'
import { SignatureType } from './types'
import axios from 'axios'
import { Protocol } from '../types'
import { AbstractSigner } from '@toolchainx/ethers-gcp-kms-signer'

// spec of RFQV1
// - taker address point to userAddr
Expand All @@ -18,7 +19,7 @@ export async function signByMMPSigner(
orderSignDigest: string,
userAddr: string,
feeFactor: number,
wallet: Wallet,
wallet: AbstractSigner,
walletType: WalletType
): Promise<string> {
if (walletType === WalletType.MMP_VERSION_4) {
Expand Down Expand Up @@ -73,7 +74,7 @@ export const signRFQOrder = async (
chainId: number,
rfqAddr: string,
order: RFQOrder,
maker: Wallet,
maker: AbstractSigner,
feeFactor = 30
): Promise<string> => {
const domain = {
Expand Down Expand Up @@ -117,7 +118,7 @@ export const signRFQOrder = async (
}

export const buildSignedOrder = async (
signer: Wallet | undefined,
signer: AbstractSigner | undefined,
order: ExtendedZXOrder,
userAddr: string,
chainId: number,
Expand Down Expand Up @@ -147,7 +148,8 @@ export const buildSignedOrder = async (
console.log(`orderSignDigest: ${orderSignDigest}`)
let makerWalletSignature
if (!signingUrl) {
if (signer.address.toLowerCase() == order.makerAddress.toLowerCase()) {
const signerAddress = await signer.getAddress()
if (signerAddress.toLowerCase() == order.makerAddress.toLowerCase()) {
const signatureTypedData = await signRFQOrder(
chainId,
rfqAddr,
Expand Down
14 changes: 8 additions & 6 deletions src/signer/rfqv2.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { utils, Wallet } from 'ethers'
import { utils } from 'ethers'
import { orderBNToString } from '../utils'
import { getOfferHash, getOfferSignDigest } from './orderHash'
import {
Expand All @@ -15,6 +15,7 @@ import { generatePseudoRandomSalt } from '0x-v2-order-utils'
import { signWithUserAndFee } from './pmmv5'
import { Protocol } from '../types'
import { BigNumber } from '0x-v2-utils'
import { AbstractSigner } from '@toolchainx/ethers-gcp-kms-signer'

// spec of RFQV2
// - taker address point to userAddr
Expand All @@ -26,7 +27,7 @@ import { BigNumber } from '0x-v2-utils'
// +------|---------|---------|-------------------|---------+
// | R | S | V | reserved 32 bytes | type(3) |
// +------|---------|---------|-------------------|---------+
export async function signByEOA(orderSignDigest: string, wallet: Wallet): Promise<string> {
export async function signByEOA(orderSignDigest: string, wallet: AbstractSigner): Promise<string> {
// signature: R+S+V
const hashArray = utils.arrayify(orderSignDigest)
let signature = await wallet.signMessage(hashArray)
Expand All @@ -43,7 +44,7 @@ export async function signByMMPSigner(
orderSignDigest: string,
userAddr: string,
feeFactor: number,
wallet: Wallet,
wallet: AbstractSigner,
walletType: WalletType
): Promise<string> {
if (walletType === WalletType.MMP_VERSION_4) {
Expand Down Expand Up @@ -98,7 +99,7 @@ export const signOffer = async (
chainId: number,
rfqAddr: string,
order: Offer,
maker: Wallet,
maker: AbstractSigner,
signatureType = SignatureType.EIP712
): Promise<string> => {
const domain = {
Expand Down Expand Up @@ -133,7 +134,7 @@ export const signOffer = async (
}

export const buildSignedOrder = async (
signer: Wallet | undefined,
signer: AbstractSigner | undefined,
order: ExtendedZXOrder,
userAddr: string,
chainId: number,
Expand Down Expand Up @@ -166,7 +167,8 @@ export const buildSignedOrder = async (
console.log(`orderSignDigest: ${orderSignDigest}`)
let makerWalletSignature
if (!signingUrl) {
if (signer.address.toLowerCase() == order.makerAddress.toLowerCase()) {
const signerAddress = await signer.getAddress()
if (signerAddress.toLowerCase() == order.makerAddress.toLowerCase()) {
makerWalletSignature = await signOffer(
chainId,
rfqAddr,
Expand Down
7 changes: 4 additions & 3 deletions src/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,13 @@ export const startMMSK = async (config: ConfigForStart) => {
setConfig(config)
try {
console.log(config.SIGNING_URL)
if (!config.SIGNING_URL) {
wallet = getWallet()
if (!config.SIGNING_URL && config.AMM_ONLY !== 'true') {
wallet = await getWallet()
if (!wallet) {
throw new Error(`Please set either WALLET_PRIVATE_KEY or SIGNING_URL`)
}
if (wallet.address.toLowerCase() != config.WALLET_ADDRESS.toLowerCase()) {
const walletAddress = await wallet.getAddress()
if (walletAddress.toLowerCase() != config.WALLET_ADDRESS.toLowerCase()) {
throw `wallet's address${wallet.address} and ${
config.USE_KEYSTORE ? 'keystore' : 'privateKey'
}(${config.WALLET_ADDRESS}) not matched`
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface ConfigForStart {
EXCHANGE_URL: string
PROVIDER_URL: string
SIGNING_URL: string
AMM_ONLY: string
PERMIT_TYPE: string

WALLET_ADDRESS: string
Expand Down

0 comments on commit 2d021e6

Please sign in to comment.