From b00c1134e674028ae824a1fd7d639328abd675ef Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Sun, 12 Nov 2023 03:11:14 -0800 Subject: [PATCH] add utils package (#536) * add utils package * fix export --- .github/workflows/publish.yml | 5 + packages/testing/package.json | 2 +- packages/testing/src/index.ts | 201 +------------------- packages/testing/tsconfig.json | 2 +- packages/utils/package.json | 46 +++++ packages/utils/src/index.ts | 201 ++++++++++++++++++++ packages/{testing => utils}/src/signFake.ts | 0 packages/utils/tsconfig.json | 10 + tsconfig.base.json | 4 +- yarn.lock | 13 +- 10 files changed, 281 insertions(+), 203 deletions(-) create mode 100644 packages/utils/package.json create mode 100644 packages/utils/src/index.ts rename packages/{testing => utils}/src/signFake.ts (100%) create mode 100644 packages/utils/tsconfig.json diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ae1c597a..91908f70 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -71,5 +71,10 @@ jobs: env: NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} + - name: Publish @acala-network/chopsticks-utils + run: yarn workspace @acala-network/chopsticks-utils npm publish --tolerate-republish --access public ${{ env.NPM_TAG }} + env: + NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} + - run: | git checkout ./.yarnrc.yml diff --git a/packages/testing/package.json b/packages/testing/package.json index cc563fde..b5cfd1c8 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -12,7 +12,7 @@ "build:types": "tsc -p tsconfig.json --emitDeclarationOnly --outDir dist/types" }, "dependencies": { - "@acala-network/chopsticks": "workspace:*" + "@acala-network/chopsticks-utils": "workspace:*" }, "peerDependencies": { "vitest": "^0.34.0" diff --git a/packages/testing/src/index.ts b/packages/testing/src/index.ts index 55b4797c..1b327a6c 100644 --- a/packages/testing/src/index.ts +++ b/packages/testing/src/index.ts @@ -1,157 +1,9 @@ -import { ApiPromise, WsProvider } from '@polkadot/api' -import { - BuildBlockMode, - StorageValues, - connectParachains, - connectVertical, - fetchConfig, - setupWithServer, -} from '@acala-network/chopsticks' +import { ApiPromise } from '@polkadot/api' import { Codec } from '@polkadot/types/types' -import { Config } from '@acala-network/chopsticks/schema/index.js' -import { HexString } from '@polkadot/util/types' -import { Keyring, createTestKeyring } from '@polkadot/keyring' -import { SubmittableExtrinsic } from '@polkadot/api-base/types' import { expect } from 'vitest' export * from './check.js' -export * from './signFake.js' - -export type SetupOption = { - endpoint: string - blockNumber?: number - blockHash?: HexString - wasmOverride?: string - db?: string - timeout?: number - port?: number - maxMemoryBlockCount?: number - resume?: boolean | HexString | number - runtimeLogLevel?: number -} - -export type SetupConfig = Config & { - timeout?: number -} - -export const createConfig = ({ - endpoint, - blockNumber, - blockHash, - wasmOverride, - db, - timeout, - port, - maxMemoryBlockCount, - resume, - runtimeLogLevel, -}: SetupOption): SetupConfig => { - // random port if not specified - port = port ?? Math.floor(Math.random() * 10000) + 10000 - const config = { - endpoint, - port, - block: blockNumber || blockHash, - 'mock-signature-host': true, - 'build-block-mode': BuildBlockMode.Manual, - 'max-memory-block-count': maxMemoryBlockCount ?? 100, - 'runtime-log-level': runtimeLogLevel, - db, - 'wasm-override': wasmOverride, - timeout, - resume: resume ?? false, - } - return config -} - -export const setupContext = async (option: SetupOption) => { - return setupContextWithConfig(createConfig(option)) -} - -export const setupContextWithConfig = async ({ timeout, ...config }: SetupConfig) => { - const { chain, listenPort, close } = await setupWithServer(config) - - const url = `ws://localhost:${listenPort}` - const ws = new WsProvider(url, 3_000, undefined, timeout) - const api = await ApiPromise.create({ - provider: ws, - noInitWarn: true, - }) - - return { - url, - chain, - ws, - api, - dev: { - newBlock: (param?: { count?: number; to?: number; unsafeBlockHeight?: number }): Promise => { - return ws.send('dev_newBlock', [param]) - }, - setStorage: (values: StorageValues, blockHash?: string) => { - return ws.send('dev_setStorage', [values, blockHash]) - }, - timeTravel: (date: string | number) => { - return ws.send('dev_timeTravel', [date]) - }, - setHead: (hashOrNumber: string | number) => { - return ws.send('dev_setHead', [hashOrNumber]) - }, - }, - async teardown() { - await api.disconnect() - await close() - }, - async pause() { - await ws.send('dev_setBlockBuildMode', [BuildBlockMode.Instant]) - - // log a bit later to ensure the message is visible - setTimeout(() => console.log(`Test paused. Polkadot.js apps URL: https://polkadot.js.org/apps/?rpc=${url}`), 100) - - return new Promise((_resolve) => {}) // wait forever - }, - } -} - -export type NetworkContext = Awaited> - -export const setupNetworks = async (networkOptions: Partial>) => { - const ret = {} as Record - - let wasmOverriden = false - - for (const [name, options] of Object.entries(networkOptions) as [string, Config | string | undefined][]) { - const config = typeof options === 'string' ? await fetchConfig(options) : options ?? (await fetchConfig(name)) - ret[name] = await setupContextWithConfig(config) - wasmOverriden ||= config['wasm-override'] != null - } - - const relaychainName = Object.keys(ret).filter((x) => ['polkadot', 'kusama'].includes(x.toLocaleLowerCase()))[0] - const { [relaychainName]: relaychain, ...parachains } = ret - - if (relaychain) { - for (const parachain of Object.values(parachains)) { - await connectVertical(relaychain.chain, parachain.chain) - } - } - - const parachainList = Object.values(parachains).map((i) => i.chain) - if (parachainList.length > 0) { - await connectParachains(parachainList) - } - - if (wasmOverriden) { - // trigger runtime upgrade if needed (due to wasm override) - for (const chain of Object.values(ret)) { - await chain.dev.newBlock() - } - // handle xcm version message if needed (due to wasm override triggered xcm version upgrade) - for (const chain of Object.values(ret)) { - await chain.dev.newBlock() - } - } - - return ret -} +export * from '@acala-network/chopsticks-utils' type CodecOrArray = Codec | Codec[] @@ -236,52 +88,3 @@ export const redact = async (data: any | Promise) => { return process(json) } - -export function defer() { - const deferred = {} as { resolve: (value: any) => void; reject: (reason: any) => void; promise: Promise } - deferred.promise = new Promise((resolve, reject) => { - deferred.resolve = resolve - deferred.reject = reject - }) - return deferred -} - -export const sendTransaction = async (tx: Promise>) => { - const signed = await tx - const deferred = defer() - await signed.send((status) => { - console.log('tranaction status: ', status.status.toHuman()) - if (status.isInBlock || status.isFinalized) { - deferred.resolve(status.events) - } - if (status.isError) { - deferred.reject(status.status) - } - }) - - return { - events: deferred.promise, - } -} - -export const testingPairs = (keyringType: 'ed25519' | 'sr25519' = 'ed25519', ss58Format?: number) => { - const keyringEth = createTestKeyring({ type: 'ethereum' }) - // default to ed25519 because sr25519 signature is non-deterministic - const keyring = new Keyring({ type: keyringType, ss58Format }) - return { - alice: keyring.addFromUri('//Alice'), - bob: keyring.addFromUri('//Bob'), - charlie: keyring.addFromUri('//Charlie'), - dave: keyring.addFromUri('//Dave'), - eve: keyring.addFromUri('//Eve'), - - alith: keyringEth.getPair('0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac'), - baltathar: keyringEth.getPair('0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0'), - charleth: keyringEth.getPair('0x798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc'), - dorothy: keyringEth.getPair('0x773539d4Ac0e786233D90A233654ccEE26a613D9'), - ethan: keyringEth.getPair('0xFf64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB'), - - keyring, - keyringEth, - } -} diff --git a/packages/testing/tsconfig.json b/packages/testing/tsconfig.json index 02a0d0f9..091bc76b 100644 --- a/packages/testing/tsconfig.json +++ b/packages/testing/tsconfig.json @@ -6,5 +6,5 @@ }, "include": ["src/**/*"], "exclude": ["src/**/*.test.ts"], - "references": [{ "path": "../chopsticks/tsconfig.json" }] + "references": [{ "path": "../utils/tsconfig.json" }] } diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 00000000..b27c035b --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,46 @@ +{ + "name": "@acala-network/chopsticks-utils", + "version": "0.9.2-2", + "author": "Acala Developers ", + "license": "Apache-2.0", + "type": "module", + "scripts": { + "clean": "rm -rf dist", + "build": "yarn clean && yarn build:cjs && yarn build:esm && yarn build:types", + "build:cjs": "swc ./src --config-file ../../.cjsswcrc -d dist/cjs && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json", + "build:esm": "swc ./src --config-file ../../.esmswcrc -d dist/esm", + "build:types": "tsc -p tsconfig.json --emitDeclarationOnly --outDir dist/types" + }, + "dependencies": { + "@acala-network/chopsticks": "workspace:*" + }, + "devDependencies": { + "@swc/cli": "0.1.62", + "@swc/core": "^1.3.96", + "typescript": "^5.2.2" + }, + "files": [ + "dist/esm/**", + "dist/cjs/**", + "dist/types/**" + ], + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "require": "./dist/cjs/index.js", + "import": "./dist/esm/index.js", + "default": "./dist/esm/index.js" + }, + "./*": { + "types": "./dist/types/*.d.ts", + "require": "./dist/cjs/*.js", + "import": "./dist/esm/*.js", + "default": "./dist/esm/*.js" + }, + "./package.json": "./package.json", + "./package.cjs.json": "./dist/cjs/package.json" + } +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 00000000..940b5650 --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,201 @@ +import { ApiPromise, WsProvider } from '@polkadot/api' +import { + BuildBlockMode, + StorageValues, + connectParachains, + connectVertical, + fetchConfig, + setupWithServer, +} from '@acala-network/chopsticks' +import { Codec } from '@polkadot/types/types' +import { Config } from '@acala-network/chopsticks/schema/index.js' +import { HexString } from '@polkadot/util/types' +import { Keyring, createTestKeyring } from '@polkadot/keyring' +import { SubmittableExtrinsic } from '@polkadot/api-base/types' + +export * from './signFake.js' + +export type SetupOption = { + endpoint: string + blockNumber?: number + blockHash?: HexString + wasmOverride?: string + db?: string + timeout?: number + port?: number + maxMemoryBlockCount?: number + resume?: boolean | HexString | number + runtimeLogLevel?: number +} + +export type SetupConfig = Config & { + timeout?: number +} + +export const createConfig = ({ + endpoint, + blockNumber, + blockHash, + wasmOverride, + db, + timeout, + port, + maxMemoryBlockCount, + resume, + runtimeLogLevel, +}: SetupOption): SetupConfig => { + // random port if not specified + port = port ?? Math.floor(Math.random() * 10000) + 10000 + const config = { + endpoint, + port, + block: blockNumber || blockHash, + 'mock-signature-host': true, + 'build-block-mode': BuildBlockMode.Manual, + 'max-memory-block-count': maxMemoryBlockCount ?? 100, + 'runtime-log-level': runtimeLogLevel, + db, + 'wasm-override': wasmOverride, + timeout, + resume: resume ?? false, + } + return config +} + +export const setupContext = async (option: SetupOption) => { + return setupContextWithConfig(createConfig(option)) +} + +export const setupContextWithConfig = async ({ timeout, ...config }: SetupConfig) => { + const { chain, listenPort, close } = await setupWithServer(config) + + const url = `ws://localhost:${listenPort}` + const ws = new WsProvider(url, 3_000, undefined, timeout) + const api = await ApiPromise.create({ + provider: ws, + noInitWarn: true, + }) + + return { + url, + chain, + ws, + api, + dev: { + newBlock: (param?: { count?: number; to?: number; unsafeBlockHeight?: number }): Promise => { + return ws.send('dev_newBlock', [param]) + }, + setStorage: (values: StorageValues, blockHash?: string) => { + return ws.send('dev_setStorage', [values, blockHash]) + }, + timeTravel: (date: string | number) => { + return ws.send('dev_timeTravel', [date]) + }, + setHead: (hashOrNumber: string | number) => { + return ws.send('dev_setHead', [hashOrNumber]) + }, + }, + async teardown() { + await api.disconnect() + await close() + }, + async pause() { + await ws.send('dev_setBlockBuildMode', [BuildBlockMode.Instant]) + + // log a bit later to ensure the message is visible + setTimeout(() => console.log(`Test paused. Polkadot.js apps URL: https://polkadot.js.org/apps/?rpc=${url}`), 100) + + return new Promise((_resolve) => {}) // wait forever + }, + } +} + +export type NetworkContext = Awaited> + +export const setupNetworks = async (networkOptions: Partial>) => { + const ret = {} as Record + + let wasmOverriden = false + + for (const [name, options] of Object.entries(networkOptions) as [string, Config | string | undefined][]) { + const config = typeof options === 'string' ? await fetchConfig(options) : options ?? (await fetchConfig(name)) + ret[name] = await setupContextWithConfig(config) + wasmOverriden ||= config['wasm-override'] != null + } + + const relaychainName = Object.keys(ret).filter((x) => ['polkadot', 'kusama'].includes(x.toLocaleLowerCase()))[0] + const { [relaychainName]: relaychain, ...parachains } = ret + + if (relaychain) { + for (const parachain of Object.values(parachains)) { + await connectVertical(relaychain.chain, parachain.chain) + } + } + + const parachainList = Object.values(parachains).map((i) => i.chain) + if (parachainList.length > 0) { + await connectParachains(parachainList) + } + + if (wasmOverriden) { + // trigger runtime upgrade if needed (due to wasm override) + for (const chain of Object.values(ret)) { + await chain.dev.newBlock() + } + // handle xcm version message if needed (due to wasm override triggered xcm version upgrade) + for (const chain of Object.values(ret)) { + await chain.dev.newBlock() + } + } + + return ret +} + +export function defer() { + const deferred = {} as { resolve: (value: any) => void; reject: (reason: any) => void; promise: Promise } + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve + deferred.reject = reject + }) + return deferred +} + +export const sendTransaction = async (tx: Promise>) => { + const signed = await tx + const deferred = defer() + await signed.send((status) => { + console.log('tranaction status: ', status.status.toHuman()) + if (status.isInBlock || status.isFinalized) { + deferred.resolve(status.events) + } + if (status.isError) { + deferred.reject(status.status) + } + }) + + return { + events: deferred.promise, + } +} + +export const testingPairs = (keyringType: 'ed25519' | 'sr25519' = 'ed25519', ss58Format?: number) => { + const keyringEth = createTestKeyring({ type: 'ethereum' }) + // default to ed25519 because sr25519 signature is non-deterministic + const keyring = new Keyring({ type: keyringType, ss58Format }) + return { + alice: keyring.addFromUri('//Alice'), + bob: keyring.addFromUri('//Bob'), + charlie: keyring.addFromUri('//Charlie'), + dave: keyring.addFromUri('//Dave'), + eve: keyring.addFromUri('//Eve'), + + alith: keyringEth.getPair('0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac'), + baltathar: keyringEth.getPair('0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0'), + charleth: keyringEth.getPair('0x798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc'), + dorothy: keyringEth.getPair('0x773539d4Ac0e786233D90A233654ccEE26a613D9'), + ethan: keyringEth.getPair('0xFf64d3F6efE2317EE2807d223a0Bdc4c0c49dfDB'), + + keyring, + keyringEth, + } +} diff --git a/packages/testing/src/signFake.ts b/packages/utils/src/signFake.ts similarity index 100% rename from packages/testing/src/signFake.ts rename to packages/utils/src/signFake.ts diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 00000000..02a0d0f9 --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "declarationDir": "dist/types" + }, + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"], + "references": [{ "path": "../chopsticks/tsconfig.json" }] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index de66b96e..fab260ae 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -25,7 +25,9 @@ "@acala-network/chopsticks-db": ["packages/db/src/index.ts"], "@acala-network/chopsticks-db/*": ["packages/db/src/*"], "@acala-network/chopsticks-testing": ["packages/testing/src/index.ts"], - "@acala-network/chopsticks-testing/*": ["packages/testing/src/*"] + "@acala-network/chopsticks-testing/*": ["packages/testing/src/*"], + "@acala-network/chopsticks-utils": ["packages/utils/src/index.ts"], + "@acala-network/chopsticks-utils/*": ["packages/utils/src/*"] } } } diff --git a/yarn.lock b/yarn.lock index 7f9c3792..44fc7e4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -73,7 +73,7 @@ __metadata: version: 0.0.0-use.local resolution: "@acala-network/chopsticks-testing@workspace:packages/testing" dependencies: - "@acala-network/chopsticks": "workspace:*" + "@acala-network/chopsticks-utils": "workspace:*" "@swc/cli": 0.1.62 "@swc/core": ^1.3.96 "@types/chai": ^4.3.10 @@ -83,6 +83,17 @@ __metadata: languageName: unknown linkType: soft +"@acala-network/chopsticks-utils@workspace:*, @acala-network/chopsticks-utils@workspace:packages/utils": + version: 0.0.0-use.local + resolution: "@acala-network/chopsticks-utils@workspace:packages/utils" + dependencies: + "@acala-network/chopsticks": "workspace:*" + "@swc/cli": 0.1.62 + "@swc/core": ^1.3.96 + typescript: ^5.2.2 + languageName: unknown + linkType: soft + "@acala-network/chopsticks@workspace:*, @acala-network/chopsticks@workspace:packages/chopsticks": version: 0.0.0-use.local resolution: "@acala-network/chopsticks@workspace:packages/chopsticks"