Skip to content

Commit

Permalink
Signer DB Discrepancy Detection (#4334)
Browse files Browse the repository at this point in the history
### Description

We'd like to be alerted when there is a discrepancy with the quota-related state in the databases of the signers. This change leverages the shared interface between the signer and combiner to pass the DB state information (performedQueryCount + totalQuota). 

### Other changes

Move shared constants and utils to the common library.

### Tested

Local unit tests.

### Backwards compatibility

Yes, the combiner shouldn't fail if the signer doesn't pass these new values.
  • Loading branch information
codyborn authored Jul 16, 2020
1 parent fa9755b commit 2e6fefc
Show file tree
Hide file tree
Showing 37 changed files with 505 additions and 188 deletions.
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,6 @@ VOTING_BOT_SCORE_SENSITIVITY=5
VOTING_BOT_CRON_SCHEDULE="*/15 * * * *"

# PGPNP Service Tests
PHONE_NUM_PRIVACY_SERVICE_URL=http://localhost:5000/celo-phone-number-privacy/us-central1
PHONE_NUM_PRIVACY_SIGNER_SERVICE_URL=https://staging-pgpnp-signer0.azurefd.net
PHONE_NUM_PRIVACY_COMBINER_SERVICE_URL=https://us-central1-celo-phone-number-privacy-stg.cloudfunctions.net
BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org
20 changes: 18 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "Debug PhoneNumberPrivacy Tests",
"name": "Debug PhoneNumberPrivacy Combiner Tests",
"type": "node",
"request": "launch",
"runtimeArgs": [
Expand All @@ -11,7 +11,23 @@
"--rootDir",
"${workspaceFolder}/packages/phone-number-privacy",
"--runInBand",
"${workspaceFolder}/packages/phone-number-privacy/test/**",
"${workspaceFolder}/packages/phone-number-privacy/combiner/test/**",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"port": 9229
},
{
"name": "Debug PhoneNumberPrivacy Signer Tests",
"type": "node",
"request": "launch",
"runtimeArgs": [
"--inspect-brk",
"${workspaceRoot}/node_modules/.bin/jest",
"--rootDir",
"${workspaceFolder}/packages/phone-number-privacy/signer",
"--runInBand",
"${workspaceFolder}/packages/phone-number-privacy/signer/test/**",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
Expand Down
2 changes: 2 additions & 0 deletions dependency-graph.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
"location": "packages/phone-number-privacy/combiner",
"dependencies": [
"@celo/contractkit",
"@celo/phone-number-privacy-common",
"@celo/utils"
]
},
Expand All @@ -144,6 +145,7 @@
"location": "packages/phone-number-privacy/signer",
"dependencies": [
"@celo/contractkit",
"@celo/phone-number-privacy-common",
"@celo/utils"
]
}
Expand Down
1 change: 0 additions & 1 deletion dockerfiles/phone-number-privacy/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ FROM node:10
WORKDIR /celo-phone-number-privacy/

COPY packages/phone-number-privacy/signer signer
COPY packages/phone-number-privacy/common common

WORKDIR /celo-phone-number-privacy/signer
COPY yarn.lock ./
Expand Down
1 change: 1 addition & 0 deletions packages/phone-number-privacy/combiner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
},
"dependencies": {
"@celo/contractkit": "0.4.5",
"@celo/phone-number-privacy-common": "1.0.5",
"@celo/utils": "0.1.13",
"blind-threshold-bls": "https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a",
"firebase-admin": "^8.10.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ErrorMessage } from '@celo/phone-number-privacy-common'
import threshold_bls from 'blind-threshold-bls'
import { ErrorMessage } from '../common/error-utils'
import logger from '../common/logger'
import config from '../config'

Expand All @@ -12,38 +12,53 @@ function flattenSigsArray(sigs: Uint8Array[]) {
return Uint8Array.from(sigs.reduce((a, b) => a.concat(Array.from(b)), [] as any))
}
export class BLSCryptographyClient {
/*
* Computes the BLS signature for the blinded phone number.
*/
public static async combinePartialBlindedSignatures(
serviceResponses: ServicePartialSignature[],
private verifiedSignatures: Uint8Array[] = []

public async addSignature(
serviceResponse: ServicePartialSignature,
blindedMessage: string
): Promise<string> {
): Promise<void> {
const polynomial = config.thresholdSignature.polynomial
const sigs: Uint8Array[] = []
for (const serviceResponse of serviceResponses) {
const sigBuffer = Buffer.from(serviceResponse.signature, 'base64')
try {
await threshold_bls.partialVerifyBlindSignature(
Buffer.from(polynomial, 'hex'),
Buffer.from(blindedMessage, 'base64'),
sigBuffer
)
sigs.push(sigBuffer)
} catch (e) {
logger.error(
`${ErrorMessage.VERIFY_PARITAL_SIGNATURE_ERROR}
Failed to verify signature for ${serviceResponse.url}`,
e
)
}
const sigBuffer = Buffer.from(serviceResponse.signature, 'base64')

try {
await threshold_bls.partialVerifyBlindSignature(
Buffer.from(polynomial, 'hex'),
Buffer.from(blindedMessage, 'base64'),
sigBuffer
)
this.verifiedSignatures.push(sigBuffer)
} catch (e) {
logger.error(
`${ErrorMessage.VERIFY_PARITAL_SIGNATURE_ERROR}
Failed to verify signature for ${serviceResponse.url}`,
e
)
}
}

/**
* Returns true if the number of valid signatures is enough to perform a combination
*/
public hasSufficientVerifiedSignatures(): boolean {
const threshold = config.thresholdSignature.threshold
return this.verifiedSignatures.length >= threshold
}

/*
* Computes the BLS signature for the blinded phone number.
*/
public async combinePartialBlindedSignatures(): Promise<string> {
const threshold = config.thresholdSignature.threshold
if (sigs.length < threshold) {
logger.error(`${ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES} ${sigs.length}/${threshold}`)
throw new Error(`${ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES} ${sigs.length}/${threshold}`)
if (!this.hasSufficientVerifiedSignatures()) {
logger.error(
`${ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES} ${this.verifiedSignatures.length}/${threshold}`
)
throw new Error(
`${ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES} ${this.verifiedSignatures.length}/${threshold}`
)
}
const result = threshold_bls.combine(threshold, flattenSigsArray(sigs))
const result = threshold_bls.combine(threshold, flattenSigsArray(this.verifiedSignatures))
return Buffer.from(result).toString('base64')
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const REASONABLE_BODY_CHAR_LIMIT: number = 2000
export const DB_TIMEOUT = 10000
export const MAX_BLOCK_DISCREPANCY_THRESHOLD = 3
23 changes: 1 addition & 22 deletions packages/phone-number-privacy/combiner/src/common/error-utils.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,8 @@
import { ErrorMessage, WarningMessage } from '@celo/phone-number-privacy-common'
import { Response } from 'firebase-functions'
import { VERSION } from '../config'
import logger from './logger'

export enum ErrorMessage {
UNKNOWN_ERROR = 'CELO_PNP_ERR_00 Something went wrong',
DATABASE_UPDATE_FAILURE = 'CELO_PNP_ERR_01 DB_ERR Failed to update database entry',
DATABASE_INSERT_FAILURE = 'CELO_PNP_ERR_02 DB_ERR Failed to insert database entry',
DATABASE_GET_FAILURE = 'CELO_PNP_ERR_03 DB_ERR Failed to get database entry',
KEY_FETCH_ERROR = 'CELO_PNP_ERR_04 INIT_ERR Failed to retrieve key from keystore',
SIGNATURE_COMPUTATION_FAILURE = 'CELO_PNP_ERR_05 SIG_ERR Failed to compute BLS signature',
VERIFY_PARITAL_SIGNATURE_ERROR = 'CELO_PNP_ERR_06 SIG_ERR BLS partial signature verification Failure',
NOT_ENOUGH_PARTIAL_SIGNATURES = 'CELO_PNP_ERR_07 SIG_ERR Not enough partial signatures',
INCONSISTENT_SINGER_RESPONSES = 'CELO_PNP_ERR_08 SIG_ERR Inconsistent responses from signers',
ERROR_REQUESTING_SIGNATURE = 'CELO_PNP_ERR_09 SIG_ERR Failed to request signature from signer',
TIMEOUT_FROM_SIGNER = 'CELO_PNP_ERR_10 SIG_ERR Timeout from signer',
}

export enum WarningMessage {
INVALID_INPUT = 'CELO_PNP_WARN_01 BAD_INPUT Invalid input paramaters',
UNAUTHENTICATED_USER = 'CELO_PNP_WARN_02 BAD_INPUT Missing or invalid authentication header',
EXCEEDED_QUOTA = 'CELO_PNP_WARN_03 QUOTA Requester exceeded salt service query quota',
UNVERIFIED_USER_ATTEMPT_TO_MATCHMAKE = 'CELO_PNP_WARN_04 QUOTA Unverified user attempting to matchmake',
DUPLICATE_REQUEST_TO_MATCHMAKE = 'CELO_PNP_WARN_05 QUOTA Attempt to request >1 matchmaking',
}

export type ErrorType = ErrorMessage | WarningMessage

export function respondWithError(res: Response, statusCode: number, error: ErrorType) {
Expand Down
4 changes: 2 additions & 2 deletions packages/phone-number-privacy/combiner/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { toBool } from '@celo/phone-number-privacy-common'
import * as functions from 'firebase-functions'
import logger from './common/logger'

Expand Down Expand Up @@ -68,8 +69,7 @@ if (DEV_MODE) {
password: functionConfig.db.pass,
database: functionConfig.db.name,
host: `/cloudsql/${functionConfig.db.host}`,
// Cody TODO: replace with combined lib version once it's published
ssl: functionConfig.db.ssl ? functionConfig.db.ssl.toLowerCase() === 'true' : true,
ssl: toBool(functionConfig.db.ssl, true),
},
pgpnpServices: {
signers: functionConfig.pgpnpservices.signers,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ErrorMessage } from '@celo/phone-number-privacy-common'
import { DB_TIMEOUT } from '../../common/constants'
import { ErrorMessage } from '../../common/error-utils'
import logger from '../../common/logger'
import { getDatabase } from '../database'
import { Account, ACCOUNTS_COLUMNS, ACCOUNTS_TABLE } from '../models/account'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ErrorMessage } from '../../common/error-utils'
import { ErrorMessage } from '@celo/phone-number-privacy-common'
import logger from '../../common/logger'
import { getDatabase } from '../database'
import { NUMBER_PAIRS_COLUMN, NUMBER_PAIRS_TABLE, NumberPair } from '../models/numberPair'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ErrorMessage, WarningMessage } from '@celo/phone-number-privacy-common'
import { Request, Response } from 'firebase-functions'
import { ErrorMessage, respondWithError, WarningMessage } from '../common/error-utils'
import { respondWithError } from '../common/error-utils'
import { authenticateUser, isVerified } from '../common/identity'
import {
hasValidAccountParam,
Expand Down
Loading

0 comments on commit 2e6fefc

Please sign in to comment.