From 257706d3c4f630303538a11cfe07ba3ba5a453a1 Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Mon, 27 Feb 2023 17:27:57 -0300 Subject: [PATCH 1/6] feat: revisit smoldot integration --- rpc/Connection.ts | 1 + rpc/smoldot.test.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 rpc/smoldot.test.ts diff --git a/rpc/Connection.ts b/rpc/Connection.ts index 18a846ca2..d74fd59f9 100644 --- a/rpc/Connection.ts +++ b/rpc/Connection.ts @@ -81,6 +81,7 @@ export abstract class Connection { } handle(message: RpcIngressMessage) { + console.log("handle", { message }) if (typeof message.id === "number") { this.callResultPendings[message.id]?.resolve(message) delete this.callResultPendings[message.id] diff --git a/rpc/smoldot.test.ts b/rpc/smoldot.test.ts new file mode 100644 index 000000000..56e29c199 --- /dev/null +++ b/rpc/smoldot.test.ts @@ -0,0 +1,43 @@ +import { deferred } from "../deps/std/async.ts" +import { SmoldotConnection } from "./smoldot.ts" + +Deno.test({ + name: "Smoldot", + sanitizeOps: false, + sanitizeResources: false, + async fn() { + const relayChainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/polkadot.json", + ) + + const connection = new SmoldotConnection({ + relayChainSpec, + }) + + await connection.ready() + + const controller = new AbortController() + + const pending = deferred() + + connection.subscription( + "chainHead_unstable_follow", + "chainHead_unstable_unfollow", + [false], + (message) => { + console.log({ message }) + pending.resolve() + }, + controller.signal, + ) + + await pending + controller.abort() + + // await delay(60 * 1000) + }, +}) + +async function fetchText(url: string) { + return (await fetch(url)).text() +} From 4f73d9706e1a4ee97dd0150ef9adb338beb86a1c Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Tue, 28 Feb 2023 10:22:04 -0300 Subject: [PATCH 2/6] fix: subscription handler setup --- rpc/Connection.ts | 41 +++++++++++++++++++++++++++++------------ rpc/smoldot.test.ts | 20 +++++++------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/rpc/Connection.ts b/rpc/Connection.ts index d74fd59f9..e811f90cd 100644 --- a/rpc/Connection.ts +++ b/rpc/Connection.ts @@ -51,10 +51,14 @@ export abstract class Connection { protected abstract close(): void - callResultPendings: Record> = {} async call(method: string, params: unknown[]) { - await this.ready() const id = this.nextId++ + return this.#call(id, method, params) + } + + callResultPendings: Record> = {} + async #call(id: number, method: string, params: unknown[]) { + await this.ready() const pending = deferred() this.callResultPendings[id] = pending this.send(id, method, params) @@ -62,6 +66,7 @@ export abstract class Connection { } subscriptionHandlers: Record = {} + subscriptionPendingInits: Record void> = {} async subscription( subscribe: string, unsubscribe: string, @@ -69,22 +74,34 @@ export abstract class Connection { handler: RpcSubscriptionHandler, signal: AbortSignal, ) { - const message = await this.call(subscribe, params) - if (signal.aborted) return - if (message.error) return handler(message) - const subscriptionId = message.result as string - this.subscriptionHandlers[subscriptionId] = handler - signal.addEventListener("abort", () => { - delete this.subscriptionHandlers[subscriptionId] - this.send(this.nextId++, unsubscribe, [subscriptionId]) - }) + const id = this.nextId++ + this.subscriptionPendingInits[id] = (subscriptionId) => { + delete this.subscriptionPendingInits[id] + if (signal.aborted) return + signal.addEventListener("abort", () => { + delete this.subscriptionHandlers[subscriptionId] + this.send(this.nextId++, unsubscribe, [subscriptionId]) + }) + this.subscriptionHandlers[subscriptionId] = handler + } + const message = await this.#call(id, subscribe, params) + if (signal.aborted) { + delete this.subscriptionPendingInits[id] + return + } + if (message.error) { + delete this.subscriptionPendingInits[id] + return handler(message) + } } handle(message: RpcIngressMessage) { - console.log("handle", { message }) if (typeof message.id === "number") { this.callResultPendings[message.id]?.resolve(message) delete this.callResultPendings[message.id] + if (!message.error && this.subscriptionPendingInits[message.id]) { + this.subscriptionPendingInits[message.id]!(message.result as string) + } } else if (message.params) { this.subscriptionHandlers[message.params.subscription]?.(message) } else { diff --git a/rpc/smoldot.test.ts b/rpc/smoldot.test.ts index 56e29c199..ed4ed558a 100644 --- a/rpc/smoldot.test.ts +++ b/rpc/smoldot.test.ts @@ -1,4 +1,6 @@ import { deferred } from "../deps/std/async.ts" +import { assertEquals } from "../deps/std/testing/asserts.ts" +import { RpcSubscriptionMessage } from "./rpc_messages.ts" import { SmoldotConnection } from "./smoldot.ts" Deno.test({ @@ -9,32 +11,24 @@ Deno.test({ const relayChainSpec = await fetchText( "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/polkadot.json", ) - const connection = new SmoldotConnection({ relayChainSpec, }) - await connection.ready() - const controller = new AbortController() - - const pending = deferred() - + const pendingMessage = deferred() connection.subscription( "chainHead_unstable_follow", "chainHead_unstable_unfollow", [false], (message) => { - console.log({ message }) - pending.resolve() + controller.abort() + pendingMessage.resolve(message) }, controller.signal, ) - - await pending - controller.abort() - - // await delay(60 * 1000) + const message = await pendingMessage + assertEquals((await message.params?.result as any).event, "initialized") }, }) From eed09b480cf05e85a58855495629942756f45956 Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Tue, 28 Feb 2023 11:07:20 -0300 Subject: [PATCH 3/6] feat: add smoldot unit tests --- rpc/smoldot.test.ts | 92 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/rpc/smoldot.test.ts b/rpc/smoldot.test.ts index ed4ed558a..b0a354c6c 100644 --- a/rpc/smoldot.test.ts +++ b/rpc/smoldot.test.ts @@ -1,5 +1,6 @@ +import { AddChainError } from "../deps/smoldot.ts" import { deferred } from "../deps/std/async.ts" -import { assertEquals } from "../deps/std/testing/asserts.ts" +import { assertEquals, assertRejects } from "../deps/std/testing/asserts.ts" import { RpcSubscriptionMessage } from "./rpc_messages.ts" import { SmoldotConnection } from "./smoldot.ts" @@ -7,28 +8,77 @@ Deno.test({ name: "Smoldot", sanitizeOps: false, sanitizeResources: false, - async fn() { - const relayChainSpec = await fetchText( - "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/polkadot.json", - ) - const connection = new SmoldotConnection({ - relayChainSpec, + async fn(t) { + await t.step({ + name: "relay chain connection", + async fn() { + const relayChainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/polkadot.json", + ) + const connection = new SmoldotConnection({ + relayChainSpec, + }) + await connection.ready() + const controller = new AbortController() + const pendingMessage = deferred() + connection.subscription( + "chainHead_unstable_follow", + "chainHead_unstable_unfollow", + [false], + (message) => { + controller.abort() + pendingMessage.resolve(message) + }, + controller.signal, + ) + const message = await pendingMessage + assertEquals((await message.params?.result as any).event, "initialized") + }, + }) + + await t.step({ + name: "parachain connection", + async fn() { + const relayChainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/westend2.json", + ) + const parachainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/projects/demo/src/assets/westend-westmint.json", + ) + const connection = new SmoldotConnection({ + relayChainSpec, + parachainSpec, + }) + await connection.ready() + const controller = new AbortController() + const pendingMessage = deferred() + connection.subscription( + "chainHead_unstable_follow", + "chainHead_unstable_unfollow", + [false], + (message) => { + controller.abort() + pendingMessage.resolve(message) + }, + controller.signal, + ) + const message = await pendingMessage + assertEquals((await message.params?.result as any).event, "initialized") + }, }) - await connection.ready() - const controller = new AbortController() - const pendingMessage = deferred() - connection.subscription( - "chainHead_unstable_follow", - "chainHead_unstable_unfollow", - [false], - (message) => { - controller.abort() - pendingMessage.resolve(message) + + await t.step({ + name: "invalid chain spec", + fn() { + assertRejects( + async () => + new SmoldotConnection({ + relayChainSpec: "", + }), + AddChainError, + ) }, - controller.signal, - ) - const message = await pendingMessage - assertEquals((await message.params?.result as any).event, "initialized") + }) }, }) From 8d5d619eb818e23acc07375c763c21a1da8268e3 Mon Sep 17 00:00:00 2001 From: Harry Solovay Date: Tue, 28 Feb 2023 10:59:51 -0500 Subject: [PATCH 4/6] nit cleanup --- rpc/smoldot.test.ts | 113 +++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 65 deletions(-) diff --git a/rpc/smoldot.test.ts b/rpc/smoldot.test.ts index b0a354c6c..ee54c8f7a 100644 --- a/rpc/smoldot.test.ts +++ b/rpc/smoldot.test.ts @@ -9,75 +9,58 @@ Deno.test({ sanitizeOps: false, sanitizeResources: false, async fn(t) { - await t.step({ - name: "relay chain connection", - async fn() { - const relayChainSpec = await fetchText( - "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/polkadot.json", - ) - const connection = new SmoldotConnection({ - relayChainSpec, - }) - await connection.ready() - const controller = new AbortController() - const pendingMessage = deferred() - connection.subscription( - "chainHead_unstable_follow", - "chainHead_unstable_unfollow", - [false], - (message) => { - controller.abort() - pendingMessage.resolve(message) - }, - controller.signal, - ) - const message = await pendingMessage - assertEquals((await message.params?.result as any).event, "initialized") - }, + await t.step("relay chain connection", async () => { + const relayChainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/polkadot.json", + ) + const connection = new SmoldotConnection({ relayChainSpec }) + await connection.ready() + const controller = new AbortController() + const pendingMessage = deferred() + connection.subscription( + "chainHead_unstable_follow", + "chainHead_unstable_unfollow", + [false], + (message) => { + controller.abort() + pendingMessage.resolve(message) + }, + controller.signal, + ) + const message = await pendingMessage + assertEquals((await message.params?.result as any).event, "initialized") }) - await t.step({ - name: "parachain connection", - async fn() { - const relayChainSpec = await fetchText( - "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/westend2.json", - ) - const parachainSpec = await fetchText( - "https://raw.githubusercontent.com/paritytech/substrate-connect/main/projects/demo/src/assets/westend-westmint.json", - ) - const connection = new SmoldotConnection({ - relayChainSpec, - parachainSpec, - }) - await connection.ready() - const controller = new AbortController() - const pendingMessage = deferred() - connection.subscription( - "chainHead_unstable_follow", - "chainHead_unstable_unfollow", - [false], - (message) => { - controller.abort() - pendingMessage.resolve(message) - }, - controller.signal, - ) - const message = await pendingMessage - assertEquals((await message.params?.result as any).event, "initialized") - }, + await t.step("parachain connection", async () => { + const relayChainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/packages/connect/src/connector/specs/westend2.json", + ) + const parachainSpec = await fetchText( + "https://raw.githubusercontent.com/paritytech/substrate-connect/main/projects/demo/src/assets/westend-westmint.json", + ) + const connection = new SmoldotConnection({ + relayChainSpec, + parachainSpec, + }) + await connection.ready() + const controller = new AbortController() + const pendingMessage = deferred() + connection.subscription( + "chainHead_unstable_follow", + "chainHead_unstable_unfollow", + [false], + (message) => { + controller.abort() + pendingMessage.resolve(message) + }, + controller.signal, + ) + const message = await pendingMessage + assertEquals((await message.params?.result as any).event, "initialized") }) - await t.step({ - name: "invalid chain spec", - fn() { - assertRejects( - async () => - new SmoldotConnection({ - relayChainSpec: "", - }), - AddChainError, - ) - }, + await t.step("invalid chain spec", async () => { + await assertRejects(async () => new SmoldotConnection({ relayChainSpec: "" }), AddChainError) }) }, }) From e2fecdf59b7cb8ba0857f414b4f0928140f98722 Mon Sep 17 00:00:00 2001 From: Harry Solovay Date: Tue, 28 Feb 2023 11:08:06 -0500 Subject: [PATCH 5/6] nit cleanup --- rpc/Connection.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rpc/Connection.ts b/rpc/Connection.ts index e811f90cd..181c8db82 100644 --- a/rpc/Connection.ts +++ b/rpc/Connection.ts @@ -99,9 +99,8 @@ export abstract class Connection { if (typeof message.id === "number") { this.callResultPendings[message.id]?.resolve(message) delete this.callResultPendings[message.id] - if (!message.error && this.subscriptionPendingInits[message.id]) { - this.subscriptionPendingInits[message.id]!(message.result as string) - } + const init = this.subscriptionPendingInits[message.id] + if (!message.error && init) init(message.result as string) } else if (message.params) { this.subscriptionHandlers[message.params.subscription]?.(message) } else { From 21e5e6d38017d74f8712a3bdb2a86f4df5dfb76c Mon Sep 17 00:00:00 2001 From: Matias Volpe Date: Tue, 28 Feb 2023 13:41:19 -0300 Subject: [PATCH 6/6] fix: invalid chain spec smoldot test --- rpc/smoldot.test.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/rpc/smoldot.test.ts b/rpc/smoldot.test.ts index ee54c8f7a..0f18b061a 100644 --- a/rpc/smoldot.test.ts +++ b/rpc/smoldot.test.ts @@ -59,9 +59,18 @@ Deno.test({ assertEquals((await message.params?.result as any).event, "initialized") }) - await t.step("invalid chain spec", async () => { - await assertRejects(async () => new SmoldotConnection({ relayChainSpec: "" }), AddChainError) - }) + await t.step( + "invalid chain spec", + async () => { + await assertRejects( + async () => { + const connection = new SmoldotConnection({ relayChainSpec: "" }) + return connection.smoldotChainPending + }, + AddChainError, + ) + }, + ) }, })