Skip to content

Commit

Permalink
Fix up verification functions in server package (#1132)
Browse files Browse the repository at this point in the history
* Fix up verification functions in server package

* TODO question / comment

* Make blockNumber mandatory and store storage under single key

* Update comments

* Tests for storage

* Shorten setter

* add jsdom to test environment

* Add some common block functions and default block time for when we're running in instant seal mode

* dont require block number when sending dev events

* Move test configs to individual packages

* build:typechain

* package-lock.json && ignore next-env.d.ts when adding licenses

* Add licenses

* Copy test env file to root of repo for GHA

* change expected dir for .env file

* Fix some more package directory stuff

* add missing env files

* Add missing env file

* Use zod to parse local storage!

* remove --if-present so that we error on missing test command

* add some more missing test commands

* add some more missing test commands

* Remove error from message

* add two more missing test commands

* Add test command for procaptcha

* remove duplicate command

* Package lock

* fix type

* fix storage

* Fix test config

* remove old test command
  • Loading branch information
forgetso authored Apr 8, 2024
1 parent b8b0614 commit ff5c294
Show file tree
Hide file tree
Showing 24 changed files with 296 additions and 224 deletions.
2 changes: 1 addition & 1 deletion demos/provider-mock/src/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ async function startApi() {
apiApp.use(express.json())
apiApp.use(i18nMiddleware({}))
apiApp.use(prosopoRouter())

apiApp.use(handleErrors)

apiApp.listen(apiPort, () => {
logger.info(`Prosopo app listening at http://localhost:${apiPort}`)
})
Expand Down
66 changes: 0 additions & 66 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion packages/api/src/api/ProviderApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,17 @@ export default class ProviderApi extends HttpClientBase implements ProviderApi {
public verifyDappUser(
dapp: AccountId,
userAccount: AccountId,
blockNumber: number,
commitmentId?: string,
maxVerifiedTime?: number
): Promise<ImageVerificationResponse> {
const payload: {
[ApiParams.dapp]: AccountId
[ApiParams.user]: AccountId
[ApiParams.blockNumber]: number
[ApiParams.commitmentId]?: string
[ApiParams.maxVerifiedTime]?: number
} = { dapp: dapp, user: userAccount }
} = { dapp: dapp, user: userAccount, blockNumber }
if (commitmentId) {
payload['commitmentId'] = commitmentId
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const handleErrors = (
try {
message = JSON.parse(err.message)
} catch {
console.debug('Invalid JSON error message')
console.error(err)
}
return response.status(code).json({
message,
Expand Down
31 changes: 31 additions & 0 deletions packages/contract/src/contract/block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2021-2024 Prosopo (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { ApiPromise } from '@polkadot/api/promise/Api'
import { BN } from '@polkadot/util/bn'

/**
* Get the current block time in milliseconds
*/
export const getBlockTimeMs = (api: ApiPromise): number => {
const babe = api.consts.babe
const blockTime = babe ? babe.expectedBlockTime : new BN(6000)
return blockTime.toNumber()
}

/**
* Get the current block number
*/
export const getCurrentBlockNumber = async (api: ApiPromise): Promise<number> => {
return (await api.rpc.chain.getBlock()).block.header.number.toNumber()
}
20 changes: 1 addition & 19 deletions packages/contract/src/contract/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,11 @@
// limitations under the License.
import { Abi } from '@polkadot/api-contract/Abi'
import { AbiMessage, ContractCallOutcome, ContractOptions, DecodedEvent } from '@polkadot/api-contract/types'
import {
AccountId,
BlockNumber,
DispatchError,
Event,
EventRecord,
StorageDeposit,
WeightV2,
} from '@polkadot/types/interfaces'
import { AccountId, DispatchError, Event, EventRecord, StorageDeposit, WeightV2 } from '@polkadot/types/interfaces'
import { AnyJson } from '@polkadot/types/types/codec'
import { ApiBase } from '@polkadot/api/types'
import { ApiPromise } from '@polkadot/api/promise/Api'
import { BN, BN_ONE, BN_ZERO, bnFromHex } from '@polkadot/util/bn'
import { Bytes } from '@polkadot/types-codec/extended'
import { Compact } from '@polkadot/types-codec/base'
import { ContractSubmittableResult } from '@polkadot/api-contract/base/Contract'
import { Logger, ProsopoContractError, capitaliseFirstLetter } from '@prosopo/common'
import { Registry } from '@polkadot/types-codec/types/registry'
Expand Down Expand Up @@ -255,11 +245,3 @@ export function formatEvent(event: Event): string {
'docs' in event ? (Array.isArray(event.docs) ? `(${event.docs.join('')})` : event.docs || '') : ''
}`
}

export function getExpectedBlockTime(api: ApiPromise): BN {
return new BN(api.consts.babe?.expectedBlockTime || 6000)
}

export async function getBlockNumber(api: ApiPromise): Promise<Compact<BlockNumber>> {
return (await api.rpc.chain.getBlock()).block.header.number
}
1 change: 1 addition & 0 deletions packages/contract/src/contract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
export * from './block.js'
export * from './interface.js'
export * from './helpers.js'
export * from './useWeight.js'
Expand Down
5 changes: 3 additions & 2 deletions packages/contract/src/contract/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ import { default as Methods } from '@prosopo/captcha-contract/mixed-methods'
import { default as Query } from '@prosopo/captcha-contract/query'
import { QueryReturnType, Result } from '@prosopo/typechain-types'
import { SubmittableExtrinsic } from '@polkadot/api/promise/types'
import { encodeStringArgs, getExpectedBlockTime, getOptions, handleContractCallOutcomeErrors } from './helpers.js'
import { encodeStringArgs, getOptions, handleContractCallOutcomeErrors } from './helpers.js'
import { firstValueFrom } from 'rxjs'
import { getBlockTimeMs } from './block.js'
import {
getPrimitiveStorageFields,
getPrimitiveStorageValue,
Expand Down Expand Up @@ -157,7 +158,7 @@ export class ProsopoCaptchaContract extends Contract implements IProsopoCaptchaC
// Always query first as errors are passed back from a dry run but not from a transaction
const message = this.abi.findMessage(contractMethodName)
const encodedArgs: Uint8Array[] = encodeStringArgs(this.abi, message, args)
const expectedBlockTime = getExpectedBlockTime(this.api)
const expectedBlockTime = new BN(getBlockTimeMs(this.api))
const weight = await useWeightImpl(this.api as ApiPromise, expectedBlockTime, new BN(1))
const gasLimit = weight.isWeightV2 ? weight.weightV2 : weight.isEmpty ? -1 : weight.weight
this.logger.debug('Sending address: ', this.pair.address)
Expand Down
4 changes: 2 additions & 2 deletions packages/procaptcha/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"npm": ">=9"
},
"scripts": {
"test": "echo \"No test specified\"",
"clean": "tsc --build --clean",
"build": "tsc --build --verbose tsconfig.json",
"build:cjs": "npx vite --config vite.cjs.config.ts build",
Expand All @@ -20,7 +19,8 @@
"prettier": "npx prettier . --check --no-error-on-unmatched-pattern --ignore-path ../../.eslintignore",
"prettier:fix": "npm run prettier -- --write",
"lint": "npm run eslint && npm run prettier",
"lint:fix": "npm run eslint:fix && npm run prettier:fix"
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
"test": "NODE_ENV=test vitest --run --config vite.test.config.ts"
},
"exports": {
".": {
Expand Down
31 changes: 13 additions & 18 deletions packages/procaptcha/src/modules/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,12 @@ import {
import { ProsopoCaptchaContract, wrapQuery } from '@prosopo/contract'
import { ProviderApi } from '@prosopo/api'
import { RandomProvider } from '@prosopo/captcha-contract/types-returns'
import { SignerPayloadRaw } from '@polkadot/types/types'
import { WsProvider } from '@polkadot/rpc-provider/ws'
import { ContractAbi as abiJson } from '@prosopo/captcha-contract/contract-info'
import { at, hashToHex } from '@prosopo/util'
import { buildUpdateState, getDefaultEvents } from '@prosopo/procaptcha-common'
import { randomAsHex } from '@polkadot/util-crypto/random'
import { sleep } from '../utils/utils.js'
import { stringToU8a } from '@polkadot/util/string'
import ProsopoCaptchaApi from './ProsopoCaptchaApi.js'
import storage from './storage.js'

Expand Down Expand Up @@ -187,24 +185,25 @@ export function Manager(
}

// Check if there is a provider in local storage or get a random one from the contract
const providerUrlFromStorage = storage.getProviderUrl()
const procaptchaStorage = storage.getProcaptchaStorage()
let providerApi: ProviderApi
if (providerUrlFromStorage) {
providerApi = await loadProviderApi(providerUrlFromStorage)
if (procaptchaStorage.providerUrl && procaptchaStorage.blockNumber) {
providerApi = await loadProviderApi(procaptchaStorage.providerUrl)

// if the provider was already in storage, the user may have already solved some captchas but they have not been put on chain yet
// so contact the provider to check if this is the case
try {
const verifyDappUserResponse = await providerApi.verifyDappUser(
getDappAccount(),
account.account.address,
procaptchaStorage.blockNumber,
undefined,
configOptional.challengeValidLength
)
if (verifyDappUserResponse.verified) {
updateState({ isHuman: true, loading: false })
const output: ProcaptchaOutput = {
[ApiParams.providerUrl]: providerUrlFromStorage,
[ApiParams.providerUrl]: procaptchaStorage.providerUrl,
[ApiParams.user]: account.account.address,
[ApiParams.dapp]: getDappAccount(),
[ApiParams.commitmentId]: hashToHex(verifyDappUserResponse.commitmentId),
Expand All @@ -216,16 +215,10 @@ export function Manager(
}
} catch (err) {
// if the provider is down, we should continue with the process of selecting a random provider
console.error('Error contacting provider from storage', providerUrlFromStorage)
console.error('Error contacting provider from storage', procaptchaStorage.providerUrl)
// continue as if the provider was not in storage
}
}
const payload = {
address: account.account.address,
data: stringToU8a('message'),
type: 'bytes',
}
const signed = await account.extension!.signer!.signRaw!(payload as unknown as SignerPayloadRaw)

// get a random provider
const getRandomProviderResponse: RandomProvider = await wrapQuery(
Expand Down Expand Up @@ -336,11 +329,11 @@ export function Manager(
loading: false,
})
if (state.isHuman) {
const trimmedUrl = trimProviderUrl(captchaApi.provider.provider.url.toString())
const providerUrl = trimProviderUrl(captchaApi.provider.provider.url.toString())
// cache this provider for future use
storage.setProviderUrl(trimmedUrl)
storage.setProcaptchaStorage({ ...storage.getProcaptchaStorage(), providerUrl, blockNumber })
events.onHuman({
providerUrl: trimmedUrl,
providerUrl,
user: account.account.address,
dapp: getDappAccount(),
commitmentId: hashToHex(submission[1]),
Expand Down Expand Up @@ -549,7 +542,8 @@ export function Manager(
}

const exportData = async (events: StoredEvents) => {
const providerUrlFromStorage = storage.getProviderUrl()
const procaptchaStorage = storage.getProcaptchaStorage()
const providerUrlFromStorage = procaptchaStorage.providerUrl
let providerApi: ProviderApi

if (providerUrlFromStorage) {
Expand All @@ -564,7 +558,8 @@ export function Manager(
providerApi = await loadProviderApi(providerUrl)
}

const providerUrl = storage.getProviderUrl() || state.captchaApi?.provider.provider.url.toString()
const providerUrl =
storage.getProcaptchaStorage().providerUrl || state.captchaApi?.provider.provider.url.toString()
if (!providerUrl) {
return
}
Expand Down
1 change: 0 additions & 1 deletion packages/procaptcha/src/modules/ProsopoCaptchaApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ export class ProsopoCaptchaApi implements ProsopoCaptchaApiInterface {
tree.build(captchasHashed)
const commitmentId = tree.root!.hash

console.log('solveCaptchaChallenge commitmentId', commitmentId)
const tx: ContractSubmittableResult | undefined = undefined

let signature: string | undefined = undefined
Expand Down
Loading

0 comments on commit ff5c294

Please sign in to comment.