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

feat: zones refactor #280

Merged
merged 13 commits into from
Oct 21, 2022
4 changes: 4 additions & 0 deletions _tasks/dnt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ await Promise.all([
name: "parity-scale-codec",
version: "^0.6.1",
},
"https://deno.land/x/[email protected]/mod.ts": {
name: "zones",
version: "0.1.0-beta.6",
},
"deps/smoldot_phantom.ts": {
name: "@substrate/smoldot-light",
version: "0.6.20",
Expand Down
1 change: 1 addition & 0 deletions deps/zones.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "https://deno.land/x/[email protected]/mod.ts";
34 changes: 34 additions & 0 deletions effect/BlockRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as Z from "../deps/zones.ts";
import * as known from "../known/mod.ts";
import * as U from "../util/mod.ts";
import { $extrinsic } from "./core/$extrinsic.ts";
import { deriveCodec } from "./core/deriveCodec.ts";
import { Metadata } from "./Metadata.ts";
import { RpcCall } from "./RpcCall.ts";

export class BlockRead<Rest extends [blockHash?: Z.$<U.HashHexString | undefined>]> extends Z.Name {
root;

constructor(
config: known.rpc.Config<string, "state_getMetadata" | "chain_getBlock">,
...[blockHash]: [...Rest]
) {
super();
const metadata_ = new Metadata(config, blockHash);
const $extrinsic_ = $extrinsic(deriveCodec(metadata_), metadata_, undefined!);
const call = new RpcCall(config, "chain_getBlock", [blockHash]);
const decoded = Z.call(Z.ls($extrinsic_, call), function mapExtrinsicCall([$extrinsic_, call]) {
const { block: { extrinsics, header }, justifications } = call.result;
return {
justifications,
block: {
header,
extrinsics: extrinsics.map((extrinsic) => {
return $extrinsic_.decode(U.hex.decode(extrinsic));
}),
},
};
});
this.root = Z.wrap(decoded, "block");
}
}
39 changes: 39 additions & 0 deletions effect/BlockWatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as Z from "../deps/zones.ts";
import { Extrinsic } from "../frame_metadata/mod.ts";
import * as known from "../known/mod.ts";
import * as U from "../util/mod.ts";
import { BlockRead } from "./BlockRead.ts";
import { RpcCall } from "./RpcCall.ts";
import { RpcSubscription } from "./RpcSubscription.ts";
import { run } from "./run.ts";

export class BlockWatch extends Z.Name {
root;

constructor(
config: known.rpc.Config<
string,
"state_getMetadata" | "chain_getBlockHash" | "chain_getBlock" | "chain_unsubscribeNewHead",
"chain_subscribeNewHeads"
>,
createWatchHandler: U.CreateWatchHandler<known.types.Block<Extrinsic>>,
) {
super();
this.root = new RpcSubscription(
config,
"chain_subscribeNewHeads",
[],
function subscribeNewHeadsHandler(stop) {
const watchHandler = createWatchHandler(stop);
return async (result) => {
const blockNum = result.params.result.number;
const blockHash = Z.sel(new RpcCall(config, "chain_getBlockHash", [blockNum]), "result");
// TODO: use derived util from Zones
const block = U.throwIfError(await run(new BlockRead(config, blockHash)));
watchHandler(block.block);
};
},
(ok) => new RpcCall(config, "chain_unsubscribeNewHead", [ok.result]),
);
}
}
40 changes: 40 additions & 0 deletions effect/EntryRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as Z from "../deps/zones.ts";
import * as known from "../known/mod.ts";
import * as U from "../util/mod.ts";
import { $storageKey } from "./core/$storageKey.ts";
import { codec } from "./core/codec.ts";
import { decoded } from "./core/decoded.ts";
import { deriveCodec } from "./core/deriveCodec.ts";
import { storageKey } from "./core/storageKey.ts";
import { entryMetadata, Metadata, palletMetadata } from "./Metadata.ts";
import { RpcCall } from "./RpcCall.ts";

export class EntryRead<
PalletName extends Z.$<string>,
EntryName extends Z.$<string>,
Keys extends unknown[],
Rest extends [blockHash?: Z.$<U.HashHexString | undefined>],
> extends Z.Name {
root;

constructor(
config: known.rpc.Config<string, "state_getMetadata" | "state_getStorage">,
palletName: PalletName,
entryName: EntryName,
keys: [...Keys],
...[blockHash]: [...Rest]
) {
super();
const metadata_ = new Metadata(config, blockHash);
const deriveCodec_ = deriveCodec(metadata_);
const palletMetadata_ = palletMetadata(metadata_, palletName);
const entryMetadata_ = entryMetadata(palletMetadata_, entryName);
const $storageKey_ = $storageKey(deriveCodec_, palletMetadata_, entryMetadata_);
const storageKey_ = storageKey($storageKey_, ...keys);
const storageCall = new RpcCall(config, "state_getStorage", [storageKey_, blockHash]);
const entryValueTypeI = Z.sel(entryMetadata_, "value");
const $entry = codec(deriveCodec_, entryValueTypeI);
const resultHex = Z.sel(storageCall, "result");
this.root = decoded($entry, resultHex, "value");
}
}
69 changes: 69 additions & 0 deletions effect/EntryWatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as Z from "../deps/zones.ts";
import * as known from "../known/mod.ts";
import * as rpc from "../rpc/mod.ts";
import * as U from "../util/mod.ts";
import { $storageKey } from "./core/$storageKey.ts";
import { codec } from "./core/codec.ts";
import { deriveCodec } from "./core/deriveCodec.ts";
import { storageKey } from "./core/storageKey.ts";
import { entryMetadata, Metadata, palletMetadata } from "./Metadata.ts";
import { RpcCall } from "./RpcCall.ts";
import { RpcSubscription } from "./RpcSubscription.ts";

export type WatchEntryEvent = [key?: U.HexString, value?: unknown];

type Config = known.rpc.Config<
string,
"state_getMetadata" | "state_unsubscribeStorage",
"state_subscribeStorage"
>;

export class EntryWatch<
PalletName extends Z.$<string>,
EntryName extends Z.$<string>,
Keys extends unknown[],
> extends Z.Name {
root;

constructor(
readonly config: Config,
readonly palletName: PalletName,
readonly entryName: EntryName,
readonly keys: Keys,
readonly createWatchHandler: U.CreateWatchHandler<WatchEntryEvent[]>,
) {
super();
const metadata_ = new Metadata(config);
const deriveCodec_ = deriveCodec(metadata_);
const palletMetadata_ = palletMetadata(metadata_, palletName);
const entryMetadata_ = entryMetadata(palletMetadata_, entryName);
const $storageKey_ = $storageKey(deriveCodec_, palletMetadata_, entryMetadata_);
const entryValueTypeI = Z.sel(entryMetadata_, "value");
const $entry = codec(deriveCodec_, entryValueTypeI);
const storageKeys = Z.call(
storageKey($storageKey_, ...keys.length ? [keys] : []),
function wrapWithList(v) {
return [v];
},
);
const watchInit = Z.call($entry, function entryWatchInit($entry) {
return U.mapCreateWatchHandler(
createWatchHandler,
(message: rpc.NotifMessage<Config, "state_subscribeStorage">) => {
return message.params.result.changes.map(([key, val]) => {
return <WatchEntryEvent> [key, val ? $entry.decode(U.hex.decode(val)) : undefined];
});
},
);
});
this.root = new RpcSubscription(
config,
"state_subscribeStorage",
[storageKeys],
watchInit,
(ok) => {
return new RpcCall(config, "state_unsubscribeStorage", [ok.result]);
},
);
}
}
134 changes: 134 additions & 0 deletions effect/ExtrinsicSentWatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { unimplemented } from "../deps/std/testing/asserts.ts";
import * as Z from "../deps/zones.ts";
import * as M from "../frame_metadata/mod.ts";
import * as known from "../known/mod.ts";
import * as rpc from "../rpc/mod.ts";
import * as ss58 from "../ss58/mod.ts";
import * as U from "../util/mod.ts";
import { $extrinsic } from "./core/$extrinsic.ts";
import { deriveCodec } from "./core/deriveCodec.ts";
import { hexDecode } from "./core/hex.ts";
import { Metadata } from "./Metadata.ts";
import { RpcCall } from "./RpcCall.ts";
import { RpcSubscription } from "./RpcSubscription.ts";

export { type Config as SendAndWatchExtrinsicConfig };
type Config = known.rpc.Config<
string,
| "state_getMetadata"
| "state_getRuntimeVersion"
| "chain_getBlockHash"
| "system_accountNextIndex"
| "system_chain"
| "author_unwatchExtrinsic",
"author_submitAndWatchExtrinsic"
>;

export interface SendAndWatchExtrinsicProps {
sender: M.MultiAddress;
palletName: string;
methodName: string;
args: Record<string, unknown>;
checkpoint?: U.HashHexString;
mortality?: [period: bigint, phase: bigint];
nonce?: string;
tip?: bigint;
sign: M.SignExtrinsic;
createWatchHandler: U.CreateWatchHandler<
rpc.NotifMessage<Config, "author_submitAndWatchExtrinsic">
>;
}

export class ExtrinsicSentWatch<Props extends Z.Rec$<SendAndWatchExtrinsicProps>> extends Z.Name {
root;

constructor(
readonly config: Config,
readonly props_: Props,
) {
super();
const props = props_ as Z.Rec$Access<Props>;
const metadata_ = new Metadata(config);
const deriveCodec_ = deriveCodec(metadata_);
const $extrinsic_ = $extrinsic(deriveCodec_, metadata_, props.sign, config.addressPrefix);
const runtimeVersion = new RpcCall(config, "state_getRuntimeVersion", []);
const senderSs58 = Z.call(props.sender, function senderSs58(sender) {
return ((): string => {
switch (sender.type) {
case "Id": {
return ss58.encode(config.addressPrefix, sender.value);
}
// TODO: other types
default: {
unimplemented();
}
}
})() as U.AccountIdString;
});
const accountNextIndex = new RpcCall(config, "system_accountNextIndex", [senderSs58]);
const genesisHash = hexDecode(Z.sel(new RpcCall(config, "chain_getBlockHash", [0]), "result"));
const checkpointHash = props.checkpoint
? Z.call(props.checkpoint, function checkpointOrUndef(v) {
return v ? U.hex.decode(v) : v;
})
: genesisHash;
const extrinsicHex = Z.call(
Z.ls(
$extrinsic_,
props.sender,
props.methodName,
props.palletName,
runtimeVersion,
accountNextIndex,
genesisHash,
props.args,
checkpointHash,
props.tip,
props.mortality,
),
async function formExtrinsicHex([
$extrinsic,
sender,
methodName,
palletName,
{ result: { specVersion, transactionVersion } },
{ result: nonce },
genesisHash,
args,
checkpoint,
tip,
mortality,
]) {
const extrinsicBytes = await $extrinsic.encodeAsync({
protocolVersion: 4, // TODO: grab this from elsewhere
palletName,
methodName,
args,
signature: {
address: sender,
extra: [
mortality
? {
type: "Mortal",
value: mortality,
}
: { type: "Immortal" },
nonce,
tip || 0,
],
additional: [specVersion, transactionVersion, checkpoint, genesisHash],
},
});
return U.hex.encode(extrinsicBytes) as U.HexString;
},
);
this.root = new RpcSubscription(
config,
"author_submitAndWatchExtrinsic",
[extrinsicHex],
props.createWatchHandler,
// TODO: use effect system for cbs such as this
(ok) => new RpcCall(config, "author_unwatchExtrinsic", [ok.result]),
);
}
}
62 changes: 62 additions & 0 deletions effect/KeyPageRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as Z from "../deps/zones.ts";
import * as known from "../known/mod.ts";
import * as U from "../util/mod.ts";
import { $key } from "./core/$key.ts";
import { $storageKey } from "./core/$storageKey.ts";
import { deriveCodec } from "./core/deriveCodec.ts";
import { storageKey } from "./core/storageKey.ts";
import { entryMetadata, Metadata, palletMetadata } from "./Metadata.ts";
import { RpcCall } from "./RpcCall.ts";

export class KeyPageRead<
PalletName extends Z.$<string>,
EntryName extends Z.$<string>,
Count extends Z.$<number>,
Rest extends [start?: unknown[] | undefined, blockHash?: Z.$<U.HashHexString | undefined>],
> extends Z.Name {
root;

constructor(
config: known.rpc.Config<string, "state_getMetadata" | "state_getKeysPaged">,
palletName: PalletName,
entryName: EntryName,
count: Count,
...[start, blockHash]: [...Rest]
) {
super();
const metadata_ = new Metadata(config, blockHash as Rest[1]);
const deriveCodec_ = deriveCodec(metadata_);
const palletMetadata_ = palletMetadata(metadata_, palletName);
const entryMetadata_ = Z.call(
entryMetadata(palletMetadata_, entryName),
function assertIsMap(entryMetadata) {
if (entryMetadata.type !== "Map") {
return new ReadingKeysOfNonMapError();
}
return entryMetadata;
},
);
const $storageKey_ = $storageKey(deriveCodec_, palletMetadata_, entryMetadata_);
const startKey = start ? storageKey($storageKey_, start) : undefined;
const storageKey_ = storageKey($storageKey_);
const call = new RpcCall(config, "state_getKeysPaged", [
storageKey_,
count,
startKey,
blockHash as Rest[1],
]);
const $key_ = $key(deriveCodec_, palletMetadata_, entryMetadata_);
const keysEncoded = Z.sel(call, "result");
const keysDecoded = Z.call(
Z.ls($key_, keysEncoded),
function keysDecodedImpl([$key, keysEncoded]) {
return keysEncoded.map((keyEncoded) => {
return $key.decode(U.hex.decode(keyEncoded));
});
},
);
this.root = Z.wrap(keysDecoded, "keys");
}
}

export class ReadingKeysOfNonMapError extends U.ErrorCtor("ReadingKeysOfNonMap") {}
Loading