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

chore: beacon-related tweaks #131

Merged
merged 2 commits into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions _tasks/download_frame_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ const outDir = path.join(Deno.cwd(), "frame_metadata", "_downloaded");
await fs.emptyDir(outDir);
await Promise.all(
Object.entries({
acala: known.ACALA_PROXY_WS_URL,
kusama: known.KUSAMA_PROXY_WS_URL,
moonbeam: known.MOONBEAM_PROXY_WS_URL,
polkadot: known.POLKADOT_PROXY_WS_URL,
statemint: known.STATEMINT_PROXY_WS_URL,
subsocial: known.SUBSOCIAL_PROXY_WS_URL,
westend: known.WESTEND_PROXY_WS_URL,
}).map(async ([name, url]) => {
const client = await rpc.client(rpc.beacon([url]));
acala: known.acalaBeacon,
kusama: known.kusamaBeacon,
moonbeam: known.moonbeamBeacon,
polkadot: known.polkadotBeacon,
statemint: known.statemintBeacon,
subsocial: known.subsocialBeacon,
westend: known.westendBeacon,
}).map(async ([name, beacon]) => {
const client = await rpc.client(beacon);
assert(!(client instanceof Error));
try {
const metadata = await client.call("state_getMetadata", []);
Expand Down
2 changes: 1 addition & 1 deletion effect/std/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { rpcCall } from "./rpcCall.ts";

export const metadata = effector(
"metadata",
(rpc: EffectorItem<rpc.AnyClient>, blockHash?: EffectorItem<U.HashHexString>) => {
(rpc: EffectorItem<any>, blockHash?: EffectorItem<U.HashHexString>) => {
Copy link
Contributor Author

@harrysolovay harrysolovay Jun 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effects are in need of a refactor, given the way that RPC types flow through the beacon into any/all calls. This any-ifying is temporary

const rpcCall_ = rpcCall(rpc, "state_getMetadata", blockHash);
const result = select(rpcCall_, "result");
return metadataDecoded(result);
Expand Down
2 changes: 1 addition & 1 deletion effect/std/pallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface Pallet {
export const pallet = effector.sync(
"pallet",
() =>
(rpc: AnyClient, name: string): Pallet => ({
(rpc: any, name: string): Pallet => ({
rpc,
name,
}),
Expand Down
2 changes: 1 addition & 1 deletion examples/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { polkadotBeacon } from "../known/mod.ts";
import * as C from "../mod.ts";
import * as rpc from "../rpc/mod.ts";

const client = await rpc.client(rpc.beacon(polkadotBeacon));
const client = await rpc.client(polkadotBeacon);
assert(!(client instanceof Error));
const ss58 = C.ss58FromText("13SceNt2ELz3ti4rnQbY1snpYH4XE4fLFsW8ph9rpwJd6HFC");
const pubKey = C.pubKeyFromSs58(ss58);
Expand Down
2 changes: 1 addition & 1 deletion examples/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { westendBeacon } from "../known/mod.ts";
import * as C from "../mod.ts";
import * as rpc from "../rpc/mod.ts";

const client = await rpc.client(rpc.beacon(westendBeacon));
const client = await rpc.client(westendBeacon);
assert(!(client instanceof Error));
const $pallet = C.pallet(client, "System");
const $entry = C.entry($pallet, "Events");
Expand Down
3 changes: 2 additions & 1 deletion examples/first_ten_keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { polkadotBeacon } from "../known/mod.ts";
import * as C from "../mod.ts";
import * as rpc from "../rpc/mod.ts";

const client = await rpc.client(rpc.beacon(polkadotBeacon));
const client = await rpc.client(polkadotBeacon);
assert(!(client instanceof Error));
client;
const pallet = C.pallet(client, "System");
const map = C.map(pallet, "Account");
const result = await C.mapKeys(map, 10).run();
Expand Down
2 changes: 1 addition & 1 deletion examples/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { polkadotBeacon } from "../known/mod.ts";
import * as C from "../mod.ts";
import * as rpc from "../rpc/mod.ts";

const client = await rpc.client(rpc.beacon(polkadotBeacon));
const client = await rpc.client(polkadotBeacon);
assert(!(client instanceof Error));
const $metadata = C.metadata(client);
const result = await $metadata.run();
Expand Down
2 changes: 1 addition & 1 deletion examples/rpc/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { assert } from "../../_deps/asserts.ts";
import { polkadotBeacon } from "../../known/mod.ts";
import * as rpc from "../../rpc/mod.ts";

const client = await rpc.client(rpc.beacon(polkadotBeacon));
const client = await rpc.client(polkadotBeacon);
assert(!(client instanceof Error));
const result = await client.call("state_getMetadata", []);
console.log(result);
Expand Down
2 changes: 1 addition & 1 deletion examples/rpc/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { assert } from "../../_deps/asserts.ts";
import { polkadotBeacon } from "../../known/mod.ts";
import * as rpc from "../../rpc/mod.ts";

const client = await rpc.client(rpc.beacon(polkadotBeacon));
const client = await rpc.client(polkadotBeacon);
assert(!(client instanceof Error));
const stop = await client.subscribe("chain_subscribeAllHeads", [], (message) => {
console.log(message.params.result);
Expand Down
2 changes: 1 addition & 1 deletion examples/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import * as rpc from "../rpc/mod.ts";
import * as U from "../util/mod.ts";

const [client, sr25519, hashers] = await Promise.all([
rpc.client(rpc.beacon(westendBeacon)),
rpc.client(westendBeacon),
Sr25519(),
Hashers(),
]);
Expand Down
41 changes: 13 additions & 28 deletions known/beacons.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
import { EnsureLookup } from "../util/types.ts";
import { LOOKUP } from "./generated.ts";

export type EnsureKnownLookup<
T,
L extends { [N in keyof LOOKUP]: T },
> = EnsureLookup<keyof LOOKUP, T, L>;

export const ACALA_PROXY_WS_URL = "wss://acala-polkadot.api.onfinality.io/public-ws";
export const acalaBeacon = [ACALA_PROXY_WS_URL] as const;

export const KUSAMA_PROXY_WS_URL = "wss://kusama-rpc.polkadot.io";
export const kusamaBeacon = [KUSAMA_PROXY_WS_URL] as const;

export const MOONBEAM_PROXY_WS_URL = "wss://wss.api.moonbeam.network";
export const moonbeamBeacon = [MOONBEAM_PROXY_WS_URL] as const;

export const POLKADOT_PROXY_WS_URL = "wss://rpc.polkadot.io";
export const polkadotBeacon = [POLKADOT_PROXY_WS_URL] as const;

export const STATEMINT_PROXY_WS_URL = "wss://statemint-rpc.polkadot.io";
export const statemintBeacon = [STATEMINT_PROXY_WS_URL] as const;

export const SUBSOCIAL_PROXY_WS_URL = "wss://para.subsocial.network";
export const subsocialBeacon = [SUBSOCIAL_PROXY_WS_URL] as const;

export const WESTEND_PROXY_WS_URL = "wss://westend-rpc.polkadot.io";
export const westendBeacon = [WESTEND_PROXY_WS_URL] as const;
import { beacon } from "../rpc/mod.ts";
import { KnownRpcMethods } from "./methods.ts";

// TODO: swap out `KnownRpcMethods` with narrowed lookups
export const acalaBeacon = beacon<KnownRpcMethods>(
"wss://acala-polkadot.api.onfinality.io/public-ws",
);
export const kusamaBeacon = beacon<KnownRpcMethods>("wss://kusama-rpc.polkadot.io");
export const moonbeamBeacon = beacon<KnownRpcMethods>("wss://wss.api.moonbeam.network");
export const polkadotBeacon = beacon<KnownRpcMethods>("wss://rpc.polkadot.io");
export const statemintBeacon = beacon<KnownRpcMethods>("wss://statemint-rpc.polkadot.io");
export const subsocialBeacon = beacon<KnownRpcMethods>("wss://para.subsocial.network");
export const westendBeacon = beacon<KnownRpcMethods>("wss://westend-rpc.polkadot.io");
12 changes: 0 additions & 12 deletions known/mod.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
import { Branded } from "../util/mod.ts";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proper branded types are still a todo.


// TODO: narrowly type the template literal of `ProxyWsUrlBeacon`
declare const _proxyWsUrl: unique symbol;
export type ProxyWsUrl = Branded<`wss://${string}`, typeof _proxyWsUrl>;

// TODO: use branded type to represent validated chain spec string
declare const _chainSpec: unique symbol;
export type ChainSpec = Branded<string, typeof _proxyWsUrl>;

export type Beacon = ProxyWsUrl | ChainSpec;

export * from "./beacons.ts";
export * from "./generated.ts";
export * from "./methods.ts";
16 changes: 13 additions & 3 deletions rpc/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export type Subscription<NotificationResult = any> = { [_N]: NotificationResult

export interface ClientProps<
M extends AnyMethods,
Beacon,
DiscoveryValue,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zeroing in on correct terminology. Beacons contain discovery values (such as URLs and chain specs). Beacons are encoded with the type information necessary to constrain client usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zeroing in on correct terminology. Beacons contain discovery values (such as URLs and chain specs). Beacons are encoded with the type information necessary to constrain client usage.

ParsedError extends Error,
> {
beacon: Beacon;
discoveryValue: DiscoveryValue;
hooks?: {
send?: (message: InitMessage<M>) => void;
receive?: (message: IngressMessage<M>) => void;
Expand Down Expand Up @@ -51,7 +51,17 @@ export abstract class Client<
*
* @param egressMessage the message you wish to send to the RPC server
*/
abstract send: (egressMessage: InitMessage<M>) => void;
send = (egressMessage: InitMessage<M>): void => {
this.props.hooks?.send?.(egressMessage);
this._send(egressMessage);
};

/**
* The provider-specific send implementation
*
* @param egressMessage the message you wish to send to the RPC server
*/
abstract _send: (egressMessage: InitMessage<M>) => void;

/**
* Parse messages returned from the RPC server (this includes RPC server errors)
Expand Down
17 changes: 17 additions & 0 deletions rpc/Beacon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as rpc from "../rpc/mod.ts";

export type DiscoveryValues = [string, ...string[]];

declare const _M: unique symbol;

export class Beacon<M extends rpc.AnyMethods> {
declare [_M]: M;
discoveryValues;

constructor(...discoveryValues: DiscoveryValues) {
this.discoveryValues = discoveryValues;
}
}
export function beacon<M extends rpc.AnyMethods>(...discoveryValues: DiscoveryValues): Beacon<M> {
return new Beacon(...discoveryValues);
}
58 changes: 21 additions & 37 deletions rpc/auto.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
import { ErrorCtor } from "../util/mod.ts";
import { ErrorCtor, isWsUrl } from "../util/mod.ts";
import { AnyMethods } from "./Base.ts";
import { Beacon } from "./Beacon.ts";
import { FailedToAddChainError, FailedToStartSmoldotError, SmoldotClient } from "./smoldot.ts";
import { FailedToOpenConnectionError, ProxyWsUrlClient } from "./ws.ts";

type DiscoveryValues = readonly [string, ...string[]];
// TODO: replace with better branded types
type Beacon<M extends AnyMethods> = DiscoveryValues & { _beacon: { supported: M } };
export function beacon<Supported extends AnyMethods>(
discoveryValues: DiscoveryValues,
): Beacon<Supported> {
return discoveryValues as Beacon<Supported>;
}

// TODO: use branded beacon types instead of string
// TODO: dyn import smoldot and provider if chain spec is provided
// TODO: handle retry
// TODO: narrow to `[string, ...string[]]`
export async function client<M extends AnyMethods>(beacon: Beacon<M>): Promise<
export async function client<M extends AnyMethods>(
beacon: Beacon<M>,
currentDiscoveryValueI = 0,
): Promise<
| SmoldotClient<M>
| ProxyWsUrlClient<M>
| FailedToOpenConnectionError
| FailedToStartSmoldotError
| FailedToAddChainError
| AllBeaconsErroredError
| BeaconFailedError
> {
const [e0, ...rest] = beacon;
const result = await (async () => {
if (isWsUrl(e0)) {
return ProxyWsUrlClient.open({ beacon: e0 });
} else {
return SmoldotClient.open({ beacon: e0 });
}
})();
if (result instanceof Error) {
if (rest.length > 0) {
return await client(rest as unknown as Beacon<M>);
const currentDiscoveryValue = beacon.discoveryValues[currentDiscoveryValueI];
if (currentDiscoveryValue) {
const result = await (async () => {
if (isWsUrl(currentDiscoveryValue)) {
return ProxyWsUrlClient.open<M>({ discoveryValue: currentDiscoveryValue });
} else {
return SmoldotClient.open<M>({ discoveryValue: currentDiscoveryValue });
}
})();
if (result instanceof Error) {
return await client(beacon, currentDiscoveryValueI + 1);
}
return new AllBeaconsErroredError();
return result;
}
// TODO: fix
return result as any;
}

// TODO: validate chain spec as well
// TODO: better validation
function isWsUrl(inQuestion: string): boolean {
return inQuestion.startsWith("wss://");
return new BeaconFailedError();
}

export class AllBeaconsErroredError extends ErrorCtor("AllBeaconsErrored") {}
export class BeaconFailedError extends ErrorCtor("AllBeaconsErrored") {}
1 change: 1 addition & 0 deletions rpc/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type AnyClient = ProxyWsUrlClient<AnyMethods> | SmoldotClient<AnyMethods>

export * from "./auto.ts";
export * from "./Base.ts";
export * from "./Beacon.ts";
export * from "./messages.ts";
export * from "./smoldot.ts";
export * from "./ws.ts";
4 changes: 2 additions & 2 deletions rpc/smoldot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class SmoldotClient<M extends B.AnyMethods>
const client = new SmoldotClient(props);
// TODO: wire up `onError`
client.#chain = await inner.addChain({
chainSpec: props.beacon,
chainSpec: props.discoveryValue,
jsonRpcCallback: client.onMessage,
});
return client;
Expand All @@ -55,7 +55,7 @@ export class SmoldotClient<M extends B.AnyMethods>
}
};

send = (egressMessage: InitMessage<M>): void => {
_send = (egressMessage: InitMessage<M>): void => {
this.#chain?.sendJsonRpc(JSON.stringify(egressMessage));
};

Expand Down
4 changes: 2 additions & 2 deletions rpc/ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class ProxyWsUrlClient<M extends B.AnyMethods>
props: B.ClientProps<M, string, WebSocketInternalError>,
): Promise<ProxyWsUrlClient<M> | FailedToOpenConnectionError> => {
const client = new ProxyWsUrlClient(props);
const ws = new WebSocket(props.beacon);
const ws = new WebSocket(props.discoveryValue);
client.#ws = ws;
ws.addEventListener("error", client.onError);
ws.addEventListener("message", client.onMessage);
Expand Down Expand Up @@ -60,7 +60,7 @@ export class ProxyWsUrlClient<M extends B.AnyMethods>
return pending;
};

send = (egressMessage: InitMessage<M>): void => {
_send = (egressMessage: InitMessage<M>): void => {
this.#ws?.send(JSON.stringify(egressMessage));
};

Expand Down
6 changes: 6 additions & 0 deletions util/discovery_value_validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// TODO: validate chain spec as well
// TODO: better ws validation

export function isWsUrl(inQuestion: string): boolean {
return inQuestion.startsWith("wss://");
}
File renamed without changes.
2 changes: 1 addition & 1 deletion util/mod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from "./branded.ts";
export * from "./discovery_value_validation.ts";
export * from "./ErrorCtor.ts";
export * from "./fn.ts";
export * as hex from "./hex.ts";
export * from "./load_env.ts";
export * from "./types.ts";