diff --git a/.github/actions/setup-binary/action.yml b/.github/actions/setup-binary/action.yml new file mode 100644 index 000000000..55dbea469 --- /dev/null +++ b/.github/actions/setup-binary/action.yml @@ -0,0 +1,28 @@ +name: "Setup Binary" +description: "Setup Binary" +inputs: + binary-name: + description: "Binary name" + required: true + binary-version: + description: "Binary version" + required: true + binary-github: + description: "Binary github repository url" + required: true +runs: + using: "composite" + steps: + - name: Cache Binary + uses: actions/cache@v3 + id: cache-binary + with: + path: | + /usr/local/bin/${{ inputs.binary-name }} + key: binary-${{ inputs.binary-name }}-${{ inputs.binary-version }} + - if: ${{ steps.cache-binary.outputs.cache-hit != 'true' }} + name: Download Binary + run: | + curl -L -o /usr/local/bin/${{ inputs.binary-name }} ${{ inputs.binary-github }}/releases/download/${{ inputs.binary-version }}/${{ inputs.binary-name }} + chmod +x /usr/local/bin/${{ inputs.binary-name }} + shell: bash diff --git a/.github/workflows/example.yml b/.github/workflows/example.yml index 90706efbd..49763fadd 100644 --- a/.github/workflows/example.yml +++ b/.github/workflows/example.yml @@ -30,6 +30,22 @@ jobs: ~/.cache/deno key: ${{ runner.os }}-deno-${{ hashFiles('lock.json', 'deps/**/*.ts') }} - name: Setup Polkadot - uses: ./.github/actions/setup-polkadot + uses: ./.github/actions/setup-binary + with: + binary-name: polkadot + binary-version: v0.9.31 + binary-github: https://github.com/paritytech/polkadot + - name: Setup Cumulus + uses: ./.github/actions/setup-binary + with: + binary-name: polkadot-parachain + binary-version: v0.9.320 + binary-github: https://github.com/paritytech/cumulus + - name: Setup Zombienet + uses: ./.github/actions/setup-binary + with: + binary-name: zombienet-linux + binary-version: v1.3.18 + binary-github: https://github.com/paritytech/zombienet - run: deno task codegen - run: deno task run ${{ matrix.example_path }} diff --git a/dprint.json b/dprint.json index 9dca9e2f7..5f94c58e9 100644 --- a/dprint.json +++ b/dprint.json @@ -7,12 +7,18 @@ "arrowFunction.useParentheses": "force", "semiColons": "asi" }, - "includes": ["**.{dockerfile,json,md,ts}"], - "excludes": ["frame_metadata/raw_erc20_metadata.json", "target"], + "includes": [ + "**.{dockerfile,json,md,toml,ts}" + ], + "excludes": [ + "frame_metadata/raw_erc20_metadata.json", + "target" + ], "plugins": [ "https://plugins.dprint.dev/dockerfile-0.2.3.wasm", "https://plugins.dprint.dev/json-0.15.3.wasm", "https://plugins.dprint.dev/markdown-0.13.3.wasm", - "https://plugins.dprint.dev/typescript-0.69.4.wasm" + "https://plugins.dprint.dev/typescript-0.69.4.wasm", + "https://plugins.dprint.dev/toml-0.5.4.wasm" ] } diff --git a/examples/.ignore b/examples/.ignore index 7ac3786e8..49b58b983 100644 --- a/examples/.ignore +++ b/examples/.ignore @@ -1,2 +1,3 @@ mod.ts -multisig_transfer.ts \ No newline at end of file +multisig_transfer.ts +xcm_teleport_assets.ts diff --git a/examples/mod.ts b/examples/mod.ts index 95d1e3dab..051addd5f 100644 --- a/examples/mod.ts +++ b/examples/mod.ts @@ -23,3 +23,4 @@ export * from "./ticker.ts" export * from "./transfer.ts" export * from "./watch_blocks.ts" export * from "./watch_events.ts" +export * from "./xcm_teleport_assets.ts" diff --git a/examples/xcm_teleport_assets.toml b/examples/xcm_teleport_assets.toml new file mode 100644 index 000000000..7d2622b0c --- /dev/null +++ b/examples/xcm_teleport_assets.toml @@ -0,0 +1,24 @@ +[relaychain] +default_image = "docker.io/paritypr/polkadot-debug:master" +default_command = "polkadot" +default_args = ["-lparachain=debug"] +chain = "rococo-local" + +[[relaychain.nodes]] +name = "alice" +validator = true + +[[relaychain.nodes]] +name = "bob" +validator = true + +[[parachains]] +id = 1000 +cumulus_based = true +chain = "statemine-local" + +[parachains.collator] +name = "collator01" +image = "docker.io/parity/polkadot-parachain:latest" +command = "polkadot-parachain" +args = ["-lparachain=debug"] diff --git a/examples/xcm_teleport_assets.ts b/examples/xcm_teleport_assets.ts new file mode 100644 index 000000000..efdd539af --- /dev/null +++ b/examples/xcm_teleport_assets.ts @@ -0,0 +1,151 @@ +// This example requires zombienet-macos/zombienet-linux, polkadot and polkadot-parachain binaries in the PATH + +import * as path from "http://localhost:5646/@local/deps/std/path.ts" +import * as C from "http://localhost:5646/@local/mod.ts" +import * as T from "http://localhost:5646/@local/test_util/mod.ts" +import * as U from "http://localhost:5646/@local/util/mod.ts" + +const configFile = path.join( + path.dirname(path.fromFileUrl(import.meta.url)), + "xcm_teleport_assets.toml", +) +const zombienet = await T.zombienet.start(configFile) + +Deno.addSignalListener("SIGINT", async () => { + try { + await zombienet.close() + } finally { + Deno.exit() + } +}) + +// see xcm_teleport_asset.toml for node names +const relaychainClient = zombienet.clients.byName["alice"]! +const parachainClient = zombienet.clients.byName["collator01"]! + +const teleportAssetsTx = C.extrinsic(relaychainClient)({ + sender: T.alice.address, + call: { + type: "XcmPallet", + value: { + type: "limitedTeleportAssets", + dest: { + type: "V1", + value: { + parents: 0, + interior: { + type: "X1", + value: { + type: "Parachain", + value: 1000, + }, + }, + }, + }, + beneficiary: { + type: "V1", + value: { + parents: 0, + interior: { + type: "X1", + value: { + type: "AccountId32", + id: T.alice.address.value, + network: { + type: "Any", + }, + }, + }, + }, + }, + assets: { + type: "V1", + value: [{ + id: { + type: "Concrete", + value: { + parents: 0, + interior: { + type: "Here", + }, + }, + }, + fun: { + type: "Fungible", + value: 500_000_000_000_000n, + }, + }], + }, + feeAssetItem: 0, + weightLimit: { + type: "Unlimited", + }, + }, + }, +}) + .signed(T.alice.sign) + .watch(({ end }) => { + return (status) => { + console.log(status) + if (typeof status !== "string" && status.finalized) { + return end(status.finalized) + } else if (C.rpc.known.TransactionStatus.isTerminal(status)) { + return end(new Error()) + } + return + } + }) + +const aliceParachainBalance = () => + C.entryRead(parachainClient)("System", "Account", [T.alice.publicKey]) + .access("value") + .access("data") + .access("free") + +const watchForDownwardMessagesProcessed = C.entryWatch(parachainClient)( + "System", + "Events", + [], + ({ end }) => { + return (entry: any) => { + for (const [_hash, events] of entry) { + if (!events) return + for (const { event } of events) { + if ( + event.type === "ParachainSystem" && event.value.type === "DownwardMessagesProcessed" + ) { + return end(event) + } + } + } + return + } + }, +) + +const watchForDownwardMessagesProcessedPending = watchForDownwardMessagesProcessed.run() + +const watchForParachainBlocksPending = C.blockWatch(parachainClient)( + ({ end }) => { + let i = 0 + return () => { + if (i === 1) { + return end() + } + i++ + return + } + }, +).run() + +console.log("Alice parachain balance before XcmPallet.limitedTeleportAssets") +console.log(U.throwIfError(await aliceParachainBalance().run())) +U.throwIfError(await teleportAssetsTx.run()) +console.log("waiting for parachain ParachainSystem.DownwardMessagesProcessed event") +console.log(U.throwIfError(await watchForDownwardMessagesProcessedPending)) +console.log("waiting for parachain to start generating blocks") +U.throwIfError(await watchForParachainBlocksPending) +console.log("Alice parachain balance after XcmPallet.limitedTeleportAssets") +console.log(U.throwIfError(await aliceParachainBalance().run())) + +await zombienet.close() diff --git a/test_util/mod.ts b/test_util/mod.ts index 080bf6201..5a2da4080 100644 --- a/test_util/mod.ts +++ b/test_util/mod.ts @@ -9,3 +9,4 @@ export { export * as extrinsic from "./extrinsic.ts" export * from "./local.ts" export * from "./pairs.ts" +export * as zombienet from "./zombienet.ts" diff --git a/test_util/zombienet.ts b/test_util/zombienet.ts new file mode 100644 index 000000000..028208670 --- /dev/null +++ b/test_util/zombienet.ts @@ -0,0 +1,88 @@ +import * as Z from "../deps/zones.ts" +import * as C from "../mod.ts" + +export const start = async (configFile: string, env?: Record) => { + const networkFilesPath = await Deno.makeTempDir({ prefix: "capi_zombienet" }) + const process = Deno.run({ + cmd: [ + zombienetBinary(), + "-d", + networkFilesPath, + "--provider", + "native", + "--force", + "spawn", + configFile, + ], + stdout: "piped", + env, + }) + // TODO: improve Network launched detection + // Deno.watchFs on `${networkFilesPath}/zombie.json` could be an alternative + const buffer = new Uint8Array(1024) + while (true) { + await process.stdout?.read(buffer) + const text = new TextDecoder().decode(buffer) + if (text.includes("Network launched")) { + process.stdout.close() + break + } + } + const close = async () => { + process.kill("SIGINT") + await process.status() + process.close() + Deno.remove(networkFilesPath, { recursive: true }) + } + const config = JSON.parse(await Deno.readTextFile(`${networkFilesPath}/zombie.json`)) + const clients = { + relay: config.relay.map((node: any) => new NodeClientEffect(node.wsUri)) as NodeClientEffect[], + paras: Object.entries(config.paras) + .reduce( + (acc, [name, { nodes }]: any) => { + acc[name] = nodes.map((node: any) => new NodeClientEffect(node.wsUri)) + return acc + }, + {} as Record, + ), + byName: Object.entries(config.nodesByName) + .reduce( + (acc, [name, node]: any) => { + acc[name] = new NodeClientEffect(node.wsUri) + return acc + }, + {} as Record, + ), + } + return { close, config, clients } +} + +export class NodeClientEffect extends Z.Effect, Error> { + constructor(readonly url: string) { + super({ + kind: "Client", + impl: Z + .call(() => { + try { + return new C.rpc.Client(C.rpc.proxyProvider, url) + } catch (e) { + return e + } + }) + .impl, + items: [url], + memoize: true, + }) + } +} + +function zombienetBinary() { + switch (Deno.build.os) { + case "darwin": + return "zombienet-macos" + case "linux": + return "zombienet-linux" + default: + throw new Error(`zombienet does not support ${Deno.build.os} OS`) + } +} diff --git a/words.txt b/words.txt index bcc97f921..23623d296 100644 --- a/words.txt +++ b/words.txt @@ -205,4 +205,7 @@ xxhash xxnetwork zio zombienet +relaychain +paritypr +lparachain unioned