Skip to content

Commit

Permalink
fix!: make smoldot discovery interface conform to smoldot interface (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanleecode authored Oct 23, 2024
1 parent d183f3d commit e2a5cef
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 202 deletions.
33 changes: 33 additions & 0 deletions .changeset/friendly-toys-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
"@substrate/connect": major
"@substrate/smoldot-discovery": major
"@substrate/smoldot-discovery-connector": minor
---

## Breaking Changes

- Modified `addChain` and `addWellKnownChain` methods:
- Now accept a single `options` object parameter instead of separate `jsonRpcCallback` and `databaseContent` parameters
- The `jsonRpcCallback` is now passed as `options.jsonRpcCallback`
- The `databaseContent` is now passed as `options.databaseContent`

- Removed `JsonRpcCallback` type export. Use the callback type from the `options` parameter of `addChain` and `addWellKnownChain` instead.

- Updated peer dependency for `@substrate/smoldot-discovery` to "^3"

## New Features

- Added new methods to the Chain interface to conform with smoldot's interface:
- `nextJsonRpcResponse`: Returns a promise that resolves with the next JSON-RPC response
- `jsonRpcResponses`: Returns an async iterable of JSON-RPC responses

## Other Changes

- Updated internal implementation to use Effect for streaming JSON RPC responses in a Queue.
- Updated error handling to include `QueueFullError`.

## Migration Guide

Users of this package will need to update their code to use the new method signatures for `addChain` and `addWellKnownChain`, and adapt to the removed `JsonRpcCallback` type export. Please refer to the updated documentation for the new usage patterns.

When upgrading, ensure you're using version 3 or higher of `@substrate/smoldot-discovery` as a peer dependency.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
devDependencies: ["**/*.test.ts", "**/*.spec.ts", "**/*.bench.ts"],
},
],
"@typescript-eslint/no-redeclare": "off",
},
env: {
browser: true,
Expand Down
16 changes: 4 additions & 12 deletions packages/connect/src/connector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,11 @@ export const createScClient = (config?: Config): ScClient => {
: smoldotScClient(config?.embeddedNodeConfig)

return {
async addChain(chainSpec, jsonRpcCallback, databaseContent) {
return (await client).addChain(
chainSpec,
jsonRpcCallback,
databaseContent,
)
async addChain(chainSpec, options) {
return (await client).addChain(chainSpec, options)
},
async addWellKnownChain(id, jsonRpcCallback, databaseContent) {
return (await client).addWellKnownChain(
id,
jsonRpcCallback,
databaseContent,
)
async addWellKnownChain(id, options) {
return (await client).addWellKnownChain(id, options)
},
}
}
Expand Down
96 changes: 23 additions & 73 deletions packages/connect/src/connector/smoldot-light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ import {
AlreadyDestroyedError,
CrashError,
JsonRpcDisabledError,
type JsonRpcCallback,
type AddChain,
WellKnownChain,
QueueFullError,
} from "./types.js"
import type { AddChainOptions } from "@substrate/smoldot-discovery/types"

const isBrowser = ![typeof window, typeof document].includes("undefined")

let QueueFullError = class {}
let _QueueFullError = class {}

let startPromise: Promise<(options: ClientOptions) => Client> | null = null
const getStart = () => {
if (startPromise) return startPromise
startPromise = import("smoldot").then((sm) => {
QueueFullError = sm.QueueFullError
_QueueFullError = sm.QueueFullError
return sm.start
})
return startPromise
Expand Down Expand Up @@ -151,6 +152,7 @@ const transformErrors = (thunk: () => void) => {
if (error?.name === "CrashError") throw new CrashError(error.message)
if (error?.name === "AlreadyDestroyedError")
throw new AlreadyDestroyedError()
if (error instanceof _QueueFullError) throw new QueueFullError()
throw new CrashError(
e instanceof Error ? e.message : `Unexpected error ${e}`,
)
Expand Down Expand Up @@ -197,70 +199,26 @@ export const createScClient = (config?: Config): ScClient => {

const internalAddChain = async (
chainSpec: string,
jsonRpcCallback?: (msg: string) => void,
databaseContent?: string,
relayChain?: SChain,
options?: AddChainOptions & { relayChain?: SChain },
): Promise<Chain> => {
const client = await getClientAndIncRef(configOrDefault)

try {
const internalChain = await client.addChain({
chainSpec,
potentialRelayChains: relayChain ? [relayChain] : undefined,
disableJsonRpc: jsonRpcCallback === undefined,
databaseContent,
potentialRelayChains: options?.relayChain
? [options.relayChain]
: undefined,
disableJsonRpc: options?.disableJsonRpc,
databaseContent: options?.databaseContent,
})

;(async () => {
while (true) {
let jsonRpcResponse
try {
jsonRpcResponse = await internalChain.nextJsonRpcResponse()
} catch (_) {
break
}

// `nextJsonRpcResponse` throws an exception if we pass `disableJsonRpc: true` in the
// config. We pass `disableJsonRpc: true` if `jsonRpcCallback` is undefined. Therefore,
// this code is never reachable if `jsonRpcCallback` is undefined.
try {
jsonRpcCallback!(jsonRpcResponse)
} catch (error) {
console.error("JSON-RPC callback has thrown an exception:", error)
}
}
})()

return {
sendJsonRpc: (rpc) => {
transformErrors(() => {
try {
internalChain.sendJsonRpc(rpc)
} catch (error) {
if (error instanceof QueueFullError) {
// If the queue is full, we immediately send back a JSON-RPC response indicating
// the error.
try {
const parsedRq = JSON.parse(rpc)
jsonRpcCallback!(
JSON.stringify({
jsonrpc: "v2",
id: parsedRq.id,
error: {
code: -32000,
message: "JSON-RPC server is too busy",
},
}),
)
} catch (_error) {
// An error here counts as a malformed JSON-RPC request, which are ignored.
}
} else {
throw error
}
}
})
transformErrors(() => internalChain.sendJsonRpc(rpc))
},
nextJsonRpcResponse: () => internalChain.nextJsonRpcResponse(),
jsonRpcResponses: internalChain.jsonRpcResponses,
remove: () => {
try {
transformErrors(() => {
Expand All @@ -272,15 +230,12 @@ export const createScClient = (config?: Config): ScClient => {
},
addChain: (
chainSpec: string,
jsonRpcCallback?: JsonRpcCallback | undefined,
databaseContent?: string | undefined,
options?: AddChainOptions,
): Promise<Chain> => {
return internalAddChain(
chainSpec,
jsonRpcCallback,
databaseContent,
internalChain,
)
return internalAddChain(chainSpec, {
...options,
relayChain: internalChain,
})
},
}
} catch (error) {
Expand All @@ -289,25 +244,20 @@ export const createScClient = (config?: Config): ScClient => {
}
}

const addChain: AddChain = (chainSpec, jsonRpcCallback, databaseContent) =>
internalAddChain(chainSpec, jsonRpcCallback, databaseContent)
const addChain: AddChain = (chainSpec, options) =>
internalAddChain(chainSpec, options)

const addWellKnownChain: AddWellKnownChain = async (
supposedChain: WellKnownChain,
jsonRpcCallback?: (msg: string) => void,
databaseContent?: string,
options,
): Promise<Chain> => {
// the following line ensures that the http request for the dynamic import
// of smoldot and the request for the dynamic import of the spec
// happen in parallel
getClientAndIncRef(configOrDefault)

try {
return await internalAddChain(
await getSpec(supposedChain),
jsonRpcCallback,
databaseContent,
)
return await internalAddChain(await getSpec(supposedChain), options)
} finally {
decRef(configOrDefault)
}
Expand Down
11 changes: 5 additions & 6 deletions packages/connect/src/connector/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ export {
AlreadyDestroyedError,
CrashError,
JsonRpcDisabledError,
} from "@substrate/smoldot-discovery/types"
export type {
Chain,
JsonRpcCallback,
AddChain,
AddWellKnownChain,
QueueFullError,
type Chain,
type AddChain,
type AddWellKnownChain,
type AddChainOptions,
} from "@substrate/smoldot-discovery/types"
7 changes: 0 additions & 7 deletions packages/connect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,4 @@
* @packageDocumentation
*/

export { WellKnownChain } from "@substrate/smoldot-discovery/types"
export * from "./connector/index.js"
export type {
JsonRpcCallback,
Chain,
AddChain,
AddWellKnownChain,
} from "./connector/index.js"
7 changes: 4 additions & 3 deletions packages/smoldot-discovery-connector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@
"trailingComma": "all"
},
"dependencies": {
"@substrate/light-client-extension-helpers": "workspace:^"
"@substrate/light-client-extension-helpers": "workspace:^",
"effect": "^3.10.0"
},
"devDependencies": {
"typescript": "catalog:",
"@substrate/smoldot-discovery": "workspace:^",
"typescript": "catalog:",
"vitest": "^2.0.5"
},
"peerDependencies": {
"@substrate/smoldot-discovery": ">=1 && <2"
"@substrate/smoldot-discovery": "^3"
}
}
Loading

0 comments on commit e2a5cef

Please sign in to comment.