Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Commit

Permalink
work on zombienet provider
Browse files Browse the repository at this point in the history
  • Loading branch information
harrysolovay committed Jan 10, 2023
1 parent 27086ec commit 4306269
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 74 deletions.
4 changes: 1 addition & 3 deletions codegen/codecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,7 @@ export function codecs(ctx: CodegenCtx) {
return file

function addCodecDecl(ty: Ty, value: string) {
if (ty.path.length > 1) {
namespaceImports.add(ty.path[0]!)
}
if (ty.path.length > 1) namespaceImports.add(ty.path[0]!)
file.code += `export const $${ty.id}: $.Codec<${ctx.typeVisitor.visit(ty)}> = ${value}\n\n`
return `$${ty.id}`
}
Expand Down
6 changes: 2 additions & 4 deletions examples/xcm_teleport_assets.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// This example requires zombienet-macos/zombienet-linux, polkadot and polkadot-parachain binaries in the PATH

import * as C from "capi/mod.ts"

import { client as relayChainClient } from "http://localhost:8000/zombienet/examples/xcm_teleport_assets.toml#alice/_/client.ts"
import { client as parachainClient } from "http://localhost:8000/zombienet/examples/xcm_teleport_assets.toml#collator01/_/client.ts"
import { client as relayChainClient } from "http://localhost:8000/zombienet/examples/xcm_teleport_assets.toml@v9.36.0#alice/_/client.ts"
import { client as parachainClient } from "http://localhost:8000/zombienet/examples/xcm_teleport_assets.toml@v9.36.0#collator01/_/client.ts"

const teleportAssetsTx = C.extrinsic(relayChainClient)({
sender: C.alice.address,
Expand Down
4 changes: 2 additions & 2 deletions server/provider/PolkadotDev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { parsePolkadotDevPathInfo } from "./PolkadotDev.ts"

Deno.test("Polkadot Dev Path Info Parsing", () => {
assertEquals(parsePolkadotDevPathInfo("polkadot@version/mod.ts"), {
discoveryValue: "polkadot",
chainKey: "polkadot@version",
runtimeName: "polkadot",
version: "version",
filePath: "mod.ts",
key: "polkadot@version",
ext: ".ts",
})
})
34 changes: 19 additions & 15 deletions server/provider/PolkadotDev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@ import { outdent } from "../../deps/outdent.ts"
import { extname } from "../../deps/std/path.ts"
import { Client, proxyProvider } from "../../rpc/mod.ts"
import * as port from "../../util/port.ts"
import { FrameProvider, FrameProviderPathInfo } from "./common/mod.ts"
import { FramePathInfo, FrameProvider } from "./common/mod.ts"

export type PolkadotDevPathInfo = FrameProviderPathInfo<DevRuntimeName>
export interface PolkadotDevPathInfo extends FramePathInfo {
runtimeName: DevRuntimeName
}

export interface PolkadotDevProviderProps {
polkadotPath?: string
additional?: string[]
}

export class PolkadotDevProvider extends FrameProvider<DevRuntimeName> {
#devNets: Partial<Record<DevRuntimeName, number>> = {}
export class PolkadotDevProvider extends FrameProvider {
devNets: Partial<Record<DevRuntimeName, number>> = {}

constructor(readonly props: PolkadotDevProviderProps = {}) {
constructor(readonly props?: PolkadotDevProviderProps) {
super()
}

Expand Down Expand Up @@ -52,8 +54,8 @@ export class PolkadotDevProvider extends FrameProvider<DevRuntimeName> {
return `ws://localhost:${port_}`
}

devNet({ discoveryValue }: PolkadotDevPathInfo) {
let port_ = this.#devNets[discoveryValue]
devNet({ runtimeName }: PolkadotDevPathInfo) {
let port_ = this.devNets[runtimeName]
if (!port_) {
port_ = port.getAvailable()
const polkadotPath_ = this.props?.polkadotPath ?? "polkadot"
Expand All @@ -64,7 +66,8 @@ export class PolkadotDevProvider extends FrameProvider<DevRuntimeName> {
port_.toString(),
...this.props?.additional ?? [],
]
if (discoveryValue !== "polkadot") cmd.push(`--force-${discoveryValue}`)
if (runtimeName !== "polkadot") cmd.push(`--force-${runtimeName}`)
if (this.props?.additional) cmd.push(...this.props.additional)
try {
const process = Deno.run({
cmd,
Expand All @@ -76,7 +79,7 @@ export class PolkadotDevProvider extends FrameProvider<DevRuntimeName> {
await process.status()
process.close()
})
this.#devNets[discoveryValue] = port_
this.devNets[runtimeName] = port_
} catch (_e) {
console.log(POLKADOT_PATH_NOT_FOUND)
Deno.exit(1)
Expand All @@ -86,17 +89,18 @@ export class PolkadotDevProvider extends FrameProvider<DevRuntimeName> {
}
}

// TODO: auto installation prompt?
const POLKADOT_PATH_NOT_FOUND =
"The Polkadot CLI was not found. Please ensure Polkadot is installed and PATH is set for `polkadot`."
+ `For more information, visit the following link: "https://github.com/paritytech/polkadot".`
+ ` For more information, visit the following link: "https://github.com/paritytech/polkadot".`

export function parsePolkadotDevPathInfo(path: string): PolkadotDevPathInfo {
const atI = path.search("@")
if (atI == -1) throw new Error(`Could not find "@" char in path`)
const discoveryValue = path.slice(0, atI)
if (!isDevRuntimeName(discoveryValue)) {
const runtimeName = path.slice(0, atI)
if (!isDevRuntimeName(runtimeName)) {
throw new Error(
`"${discoveryValue}" is not a valid dev runtime name. Please specify one of the following: "${
`"${runtimeName}" is not a valid dev runtime name. Please specify one of the following: "${
DEV_RUNTIME_NAMES.join(`", "`)
}"`,
)
Expand All @@ -105,10 +109,10 @@ export function parsePolkadotDevPathInfo(path: string): PolkadotDevPathInfo {
const slashI0 = atTrailing.search("/")
if (slashI0 == -1) throw new Error("Could not extract chain path")
const version = atTrailing.slice(0, slashI0)
const key = path.slice(0, atI + 1 + slashI0)
const chainKey = path.slice(0, atI + 1 + slashI0)
const filePath = atTrailing.slice(slashI0 + 1)
const ext = extname(filePath) as Ext
return { key, discoveryValue, version, filePath, ext }
return { chainKey, runtimeName, version, filePath, ext }
}

export const DEV_RUNTIME_NAMES = ["polkadot", "kusama", "westend", "rococo"] as const
Expand Down
4 changes: 2 additions & 2 deletions server/provider/Wss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { parseWssPathInfo } from "./Wss.ts"

Deno.test("Wss Path Info Parsing", () => {
assertEquals(parseWssPathInfo("rpc.polkadot.io@version/mod.ts"), {
discoveryValue: "rpc.polkadot.io",
chainKey: "rpc.polkadot.io@version",
protocolTrailing: "rpc.polkadot.io",
version: "version",
filePath: "mod.ts",
key: "rpc.polkadot.io@version",
ext: ".ts",
})
})
43 changes: 27 additions & 16 deletions server/provider/Wss.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
import { Ext, File } from "../../codegen/mod.ts"
import { extname } from "../../deps/std/path.ts"
import { outdent } from "../../deps/outdent.ts"
import * as path from "../../deps/std/path.ts"
import { Client, proxyProvider } from "../../rpc/mod.ts"
import { FrameProvider, FrameProviderPathInfo } from "./common/mod.ts"
import { FramePathInfo, FrameProvider } from "./common/mod.ts"

export type WssPathInfo = FrameProviderPathInfo<string>
export interface WssPathInfo extends FramePathInfo {
protocolTrailing: string
}

export class WssProvider extends FrameProvider<string> {
export class WssProvider extends FrameProvider {
parsePathInfo = parseWssPathInfo

client({ discoveryValue }: WssPathInfo) {
return new Client(proxyProvider, `wss://${discoveryValue}`)
url({ protocolTrailing }: WssPathInfo) {
return `wss://${protocolTrailing}`
}

client(pathInfo: WssPathInfo) {
return new Client(proxyProvider, this.url(pathInfo))
}

clientFile() {
clientFile(pathInfo: WssPathInfo) {
const clientFile = new File()
clientFile.code = `export const client = null!`
clientFile.code = outdent`
import * as C from "../capi.ts"
export const client = new C.Client(C.rpc.proxyProvider, "${this.url(pathInfo)}")
`
return clientFile
}
}

export function parseWssPathInfo(path: string): FrameProviderPathInfo<string> {
const atI = path.search("@")
if (atI == -1) throw new Error(`Expected "@" character and version to appear in URL`)
const discoveryValue = path.slice(0, atI)
const atTrailing = path.slice(atI + 1)
export function parseWssPathInfo(path_: string): WssPathInfo {
const atI = path_.search("@")
if (atI == -1) throw new Error(`Expected "@" character to appear in URL`)
const protocolTrailing = path_.slice(0, atI)
const atTrailing = path_.slice(atI + 1)
const slashI = atTrailing.search("/")
const version = atTrailing.slice(0, slashI)
const filePath = atTrailing.slice(slashI + 1)
const key = path.slice(0, atI + 1 + slashI)
const ext = extname(filePath) as Ext
return { key, discoveryValue, version, filePath, ext }
const chainKey = path_.slice(0, atI + 1 + slashI)
const ext = path.extname(filePath) as Ext
return { chainKey, protocolTrailing, version, filePath, ext }
}
13 changes: 13 additions & 0 deletions server/provider/Zombienet.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { assertEquals } from "../../deps/std/testing/asserts.ts"
import { parseZombienetPathInfo } from "./Zombienet.ts"

Deno.test("Zombienet Path Info Parsing", () => {
assertEquals(parseZombienetPathInfo("examples/xcm_teleport_assets.toml#alice@version/mod.ts"), {
chainKey: "examples/xcm_teleport_assets.toml#alice@version",
configPath: "examples/xcm_teleport_assets.toml",
chainId: "alice",
version: "version",
filePath: "mod.ts",
ext: ".ts",
})
})
107 changes: 86 additions & 21 deletions server/provider/Zombienet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,89 @@
// TODO
export {}
import { Ext } from "../../codegen/mod.ts"
import * as path from "../../deps/std/path.ts"
import { FramePathInfo, FrameProvider } from "./common/mod.ts"

export interface ZombienetPathInfo extends FramePathInfo {
configPath: string
chainId: string
}

export interface ZombienetProviderProps {
zombienetPath?: string
additional?: string[]
}

export class ZombienetProvider extends FrameProvider {
zombienetTmp = Deno.makeTempDirSync({ prefix: "capi_zombienet_" })
zombienets: Record<string, string> = {}

constructor(readonly props?: ZombienetProviderProps) {
super()
}

parsePathInfo = parseZombienetPathInfo

client(pathInfo: ZombienetPathInfo) {}

clientFile(pathInfo: ZombienetPathInfo) {}

url(pathInfo: ZombienetPathInfo) {}

zombienet(pathInfo: ZombienetPathInfo) {
const cmd: string[] = [
this.props?.zombienetPath ?? "zombienet",
"-d",
this.zombienetTmp,
"--provider",
"native",
"--force",
"spawn",
pathInfo.configPath,
]
if (this.props?.additional) cmd.push(...this.props.additional)
try {
const process = Deno.run({
cmd,
stdout: "piped",
stderr: "piped",
})
this.ctx.signal.addEventListener("abort", async () => {
process.kill("SIGINT")
await process.status()
process.close()
Deno.remove(this.zombienetTmp, { recursive: true })
})
} catch (_e) {
console.log(ZOMBIENET_PATH_NOT_FOUND)
Deno.exit(1)
}
}
}

export function parseZombienetPathInfo(path_: string): ZombienetPathInfo {
const atI = path_.search("@")
if (atI == -1) throw new Error(`Expected "@" character to appear in URL`)
const leading = path_.slice(0, atI)
const hashI = leading.search("#")
if (hashI == -1) throw new Error(`Expected "#" character to appear in URL`)
const configPath = leading.slice(0, hashI)
const chainId = leading.slice(hashI + 1)
const trailing = path_.slice(atI + 1)
const slashI = trailing.search("/")
if (slashI == -1) throw new Error()
const version = trailing.slice(0, slashI)
const filePath = trailing.slice(slashI + 1)
const chainKey = path_.slice(0, atI + 1 + slashI)
const ext = path.extname(filePath) as Ext
return { chainId, chainKey, configPath, ext, filePath, version }
}

// TODO: auto installation prompt?
const ZOMBIENET_PATH_NOT_FOUND =
"The Zombienet CLI was not found. Please ensure Zombienet is installed and PATH is set for `zombienet`."
+ ` For more information, visit the following link: "https://github.com/paritytech/zombienet".`

// import { client as relayChainClient } from "http://localhost:8000/zombienet/examples/xcm_teleport_assets.toml#alice/_/client.ts"
// import { client as parachainClient } from "http://localhost:8000/zombienet/examples/xcm_teleport_assets.toml#collator01/_/client.ts"

// import * as Z from "../../deps/zones.ts"
// import * as rpc from "../../rpc/mod.ts"
Expand Down Expand Up @@ -71,22 +155,3 @@ export {}
// }
// return { close, config, clients }
// }

// export class NodeClientEffect extends Z.Effect<rpc.Client<string, Event, Event, Event>, Error> {
// constructor(readonly url: string) {
// super({
// kind: "Client",
// impl: Z
// .call(() => {
// try {
// return new rpc.Client(rpc.proxyProvider, url)
// } catch (e) {
// return e
// }
// })
// .impl,
// items: [url],
// memoize: true,
// })
// }
// }
21 changes: 10 additions & 11 deletions server/provider/common/Frame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@ import { Client } from "../../../rpc/mod.ts"
import * as U from "../../../util/mod.ts"
import { Provider } from "./Base.ts"

export interface FrameProviderPathInfo<DiscoveryValue> {
key: string
discoveryValue: DiscoveryValue
export interface FramePathInfo {
chainKey: string
version: string
filePath: string
ext: Ext
}

export abstract class FrameProvider<DiscoveryValue> extends Provider {
export abstract class FrameProvider extends Provider {
codegenCtxPendings: Record<string, Promise<CodegenCtx>> = {}

abstract parsePathInfo(path: string): FrameProviderPathInfo<DiscoveryValue>
abstract client(pathInfo: FrameProviderPathInfo<DiscoveryValue>): U.PromiseOr<Client>
abstract clientFile(pathInfo: FrameProviderPathInfo<DiscoveryValue>): File
abstract parsePathInfo(path: string): FramePathInfo
abstract client(pathInfo: FramePathInfo): U.PromiseOr<Client>
abstract clientFile(pathInfo: FramePathInfo): File

async run(req: Request, path: string) {
const pathInfo = this.parsePathInfo(path)
Expand All @@ -27,8 +26,8 @@ export abstract class FrameProvider<DiscoveryValue> extends Provider {
return this.ctx.code(req, pathInfo.filePath, file.code)
}

codegenCtx(pathInfo: FrameProviderPathInfo<DiscoveryValue>) {
let codegenCtxPending = this.codegenCtxPendings[pathInfo.key]
codegenCtx(pathInfo: FramePathInfo) {
let codegenCtxPending = this.codegenCtxPendings[pathInfo.chainKey]
if (!codegenCtxPending) {
codegenCtxPending = (async () => {
const client = await this.client(pathInfo)
Expand All @@ -50,10 +49,10 @@ export abstract class FrameProvider<DiscoveryValue> extends Provider {
this.postInitCodegenCtx(codegenCtx, pathInfo)
return codegenCtx
})()
this.codegenCtxPendings[pathInfo.key] = codegenCtxPending
this.codegenCtxPendings[pathInfo.chainKey] = codegenCtxPending
}
return codegenCtxPending
}

postInitCodegenCtx(_codegenCtx: CodegenCtx, _pathInfo: FrameProviderPathInfo<DiscoveryValue>) {}
postInitCodegenCtx(_codegenCtx: CodegenCtx, _pathInfo: FramePathInfo) {}
}

0 comments on commit 4306269

Please sign in to comment.