diff --git a/light-client-db-worker/.gitignore b/light-client-db-worker/.gitignore index 234a7a4..b3b109d 100644 --- a/light-client-db-worker/.gitignore +++ b/light-client-db-worker/.gitignore @@ -3,3 +3,4 @@ /pkg /node_modules /index.d.ts +/package-lock.json diff --git a/light-client-js/.gitignore b/light-client-js/.gitignore index ae36e85..d0abeb6 100644 --- a/light-client-js/.gitignore +++ b/light-client-js/.gitignore @@ -1,3 +1,4 @@ /yarn.lock /node_modules /dist +/package-lock.json diff --git a/light-client-js/package.json b/light-client-js/package.json index 868db62..d6ab2bc 100644 --- a/light-client-js/package.json +++ b/light-client-js/package.json @@ -1,11 +1,9 @@ -{ + { "name": "light-client-js", "version": "1.0.0", "main": "dist/index.js", "license": "MIT", "devDependencies": { - "@ckb-ccc/ccc": "^0.0.16-alpha.7", - "@ckb-ccc/core": "^0.1.0-alpha.5", "ts-loader": "^9.5.1", "typescript": "^5.7.2", "webpack": "^5.96.1", @@ -13,7 +11,9 @@ }, "dependencies": { "light-client-db-worker": "file:../light-client-db-worker", - "light-client-wasm": "file:../light-client-wasm" + "light-client-wasm": "file:../light-client-wasm", + "stream-browserify": "^3.0.0", + "@ckb-ccc/core": "https://gitpkg.vercel.app/Officeyutong/ccc/packages/core?a1b8a02" }, "scripts": { "build-debug": "webpack --mode=development --stats-children", diff --git a/light-client-js/src/index.ts b/light-client-js/src/index.ts index 5a0804e..91f7e8c 100644 --- a/light-client-js/src/index.ts +++ b/light-client-js/src/index.ts @@ -1,8 +1,9 @@ -import { ClientFindCellsResponse, ClientFindTransactionsGroupedResponse, ClientFindTransactionsResponse, ClientIndexerSearchKeyLike, ClientIndexerSearchKeyTransactionLike, ClientTransactionResponse } from "@ckb-ccc/core/src/client"; -import { FetchResponse, transformFetchResponse } from "./types"; -import { JsonRpcTransformers } from "@ckb-ccc/core/src/client/jsonRpc/transformers"; -import { Num, numFrom, NumLike, numToHex } from "@ckb-ccc/core/src/num"; -import { Hex, hexFrom, HexLike, TransactionLike } from "@ckb-ccc/core/src/barrel"; +import { ClientFindCellsResponse, ClientFindTransactionsGroupedResponse, ClientFindTransactionsResponse, ClientIndexerSearchKeyLike, ClientIndexerSearchKeyTransactionLike, ClientTransactionResponse } from "@ckb-ccc/core/client"; +import { FetchResponse, JsonRpcLocalNode, JsonRpcRemoteNode, JsonRpcScriptStatus, LocalNode, localNodeTo, RemoteNode, remoteNodeTo, ScriptStatus, scriptStatusFrom, scriptStatusTo, transformFetchResponse } from "./types"; +import { JsonRpcTransformers } from "@ckb-ccc/core/client/jsonRpc/transformers"; +import { Num, numFrom, NumLike, numToHex } from "@ckb-ccc/core/num"; +import { ClientBlock, ClientBlockHeader, Hex, hexFrom, HexLike, TransactionLike } from "@ckb-ccc/core/barrel"; +import { JsonRpcBlockHeader } from "@ckb-ccc/core/advancedBarrel"; const DEFAULT_BUFFER_SIZE = 50 * (1 << 20); /** * A LightClient instance @@ -28,17 +29,17 @@ class LightClient { /** * Start the light client. */ - async start() { + async start(logLevel: "debug" | "info" = "info") { this.dbWorker.postMessage({ inputBuffer: this.inputBuffer, outputBuffer: this.outputBuffer, - logLevel: "info" + logLevel: logLevel }); this.lightClientWorker.postMessage({ inputBuffer: this.inputBuffer, outputBuffer: this.outputBuffer, netName: "dev", - logLevel: "info" + logLevel: logLevel }); await new Promise((res, rej) => { this.dbWorker.onmessage = () => res(); @@ -71,31 +72,31 @@ class LightClient { * Returns the header with the highest block number in the canonical chain * @returns HeaderView */ - async getTipHeader(): Promise { - return await this.invokeLightClientCommand("get_tip_header"); + async getTipHeader(): Promise { + return JsonRpcTransformers.blockHeaderTo(await this.invokeLightClientCommand("get_tip_header")); } /** * Returns the genesis block * @returns BlockView */ - async getGenesisBlock(): Promise { - return await this.invokeLightClientCommand("get_genesis_block"); + async getGenesisBlock(): Promise { + return JsonRpcTransformers.blockTo(await this.invokeLightClientCommand("get_genesis_block")); } /** * Returns the information about a block header by hash. * @param hash the block hash, equal to Vec in Rust * @returns HeaderView */ - async getHeader(hash: HexLike): Promise { - return await this.invokeLightClientCommand("get_header", [hexFrom(hash)]); + async getHeader(hash: HexLike): Promise { + return JsonRpcTransformers.blockHeaderTo(await this.invokeLightClientCommand("get_header", [hexFrom(hash)])); } /** * Fetch a header from remote node. If return status is not_found will re-sent fetching request immediately. * @param hash the block hash, equal to Vec in Rust * @returns FetchHeaderResponse */ - async fetchHeader(hash: HexLike): Promise> { - return await this.invokeLightClientCommand("fetch_header", [hexFrom(hash)]); + async fetchHeader(hash: HexLike): Promise> { + return transformFetchResponse(await this.invokeLightClientCommand("fetch_header", [hexFrom(hash)]), (arg: JsonRpcBlockHeader) => JsonRpcTransformers.blockHeaderTo(arg)); } /** * See https://github.com/nervosnetwork/ckb/tree/develop/rpc#method-estimate_cycles @@ -109,29 +110,29 @@ class LightClient { * Returns the local node information. * @returns LocalNode */ - async localNodeInfo(): Promise { - return await this.invokeLightClientCommand("local_node_info"); + async localNodeInfo(): Promise { + return localNodeTo(await this.invokeLightClientCommand("local_node_info") as JsonRpcLocalNode); } /** * Returns the connected peers' information. * @returns */ - async getPeers(): Promise { - return await this.invokeLightClientCommand("get_peers"); + async getPeers(): Promise { + return (await this.invokeLightClientCommand("get_peers") as JsonRpcRemoteNode[]).map(x => remoteNodeTo(x)); } /** * Set some scripts to filter * @param scripts Array of script status * @param command An optional enum parameter to control the behavior of set_scripts */ - async setScripts(scripts: any, command: any): Promise { - await this.invokeLightClientCommand("set_scripts", [scripts, command]); + async setScripts(scripts: ScriptStatus[], command?: "all" | "partial" | "delete"): Promise { + await this.invokeLightClientCommand("set_scripts", [scripts.map(x => scriptStatusFrom(x)), command]); } /** * Get filter scripts status */ - async getScripts(): Promise { - return await this.invokeLightClientCommand("get_scripts"); + async getScripts(): Promise { + return (await this.invokeLightClientCommand("get_scripts") as JsonRpcScriptStatus[]).map(x => scriptStatusTo(x)); } /** * See https://github.com/nervosnetwork/ckb-indexer#get_cells diff --git a/light-client-js/src/types.ts b/light-client-js/src/types.ts index fd98b53..5e330d2 100644 --- a/light-client-js/src/types.ts +++ b/light-client-js/src/types.ts @@ -1,3 +1,7 @@ +import { JsonRpcScript, JsonRpcTransformers } from "@ckb-ccc/core/advancedBarrel"; +import { Script } from "@ckb-ccc/core/barrel"; +import { Num } from "@ckb-ccc/core/num"; + interface WorkerInitializeOptions { inputBuffer: SharedArrayBuffer; outputBuffer: SharedArrayBuffer; @@ -28,10 +32,136 @@ export function transformFetchResponse(input: FetchResponse, fn: (arg: return input; } +type JsonRpcScriptType = "lock" | "type"; + +interface JsonRpcScriptStatus { + script: JsonRpcScript; + script_type: JsonRpcScriptType; + block_number: Num; +} + +interface ScriptStatus { + script: Script, + scriptType: JsonRpcScriptType, + blockNumber: Num +} + +export function scriptStatusTo(input: JsonRpcScriptStatus): ScriptStatus { + return ({ + blockNumber: input.block_number, + script: JsonRpcTransformers.scriptTo(input.script), + scriptType: input.script_type + }) +} + +export function scriptStatusFrom(input: ScriptStatus): JsonRpcScriptStatus { + return ({ + block_number: input.blockNumber, + script: JsonRpcTransformers.scriptFrom(input.script), + script_type: input.scriptType + }) +} + +interface NodeAddress { + address: string; + score: Num; +} + +interface RemoteNodeProtocol { + id: Num; + version: string; +} + +interface JsonRpcRemoteNode { + version: string; + node_id: string; + addresses: NodeAddress[]; + connected_duration: Num; + sync_state?: any; + protocols: RemoteNodeProtocol[]; + +} + +interface RemoteNode { + version: string; + nodeId: string; + addresses: NodeAddress[]; + connestedDuration: Num; + syncState?: any; + protocols: RemoteNodeProtocol[]; +} + +export function remoteNodeTo(input: JsonRpcRemoteNode): RemoteNode { + return ({ + addresses: input.addresses, + connestedDuration: input.connected_duration, + nodeId: input.node_id, + protocols: input.protocols, + version: input.version, + syncState: input.sync_state + }) +} + +interface JsonRpcLocalNodeProtocol { + id: Num; + name: string; + support_version: string[]; +} + + +interface LocalNodeProtocol { + id: Num; + name: string; + supportVersion: string[]; +} + +export function localNodeProtocolTo(input: JsonRpcLocalNodeProtocol): LocalNodeProtocol { + return ({ + id: input.id, + name: input.name, + supportVersion: input.support_version + }) +} + +interface JsonRpcLocalNode { + version: string; + node_id: string; + active: boolean; + addresses: NodeAddress[]; + protocols: JsonRpcLocalNodeProtocol[]; + connections: bigint; +} + +interface LocalNode { + version: string; + nodeId: string; + active: boolean; + addresses: NodeAddress[]; + protocols: LocalNodeProtocol[]; + connections: bigint; +} + +export function localNodeTo(input: JsonRpcLocalNode): LocalNode { + return ({ + nodeId: input.node_id, + protocols: input.protocols.map(x => localNodeProtocolTo(x)), + active: input.active, + addresses: input.addresses, + connections: input.connections, + version: input.version + }) +} + export type { LightClientFunctionCall, WorkerInitializeOptions, DbWorkerInitializeOptions, LightClientWorkerInitializeOptions, - FetchResponse + FetchResponse, + JsonRpcScriptStatus, + ScriptStatus, + JsonRpcRemoteNode, + RemoteNode, + LocalNode, + JsonRpcLocalNode } diff --git a/light-client-js/tsconfig.json b/light-client-js/tsconfig.json index e0fca66..c01f681 100644 --- a/light-client-js/tsconfig.json +++ b/light-client-js/tsconfig.json @@ -3,9 +3,8 @@ "outDir": "./dist/", "noImplicitAny": true, "module": "ES2020", - "target": "ES2015", + "target": "ES2020", "allowJs": true, - "moduleResolution": "node", "mapRoot": "dist", "sourceMap": true, "declaration": true, @@ -19,7 +18,8 @@ "lib": [ "ES2017", "WebWorker" - ] + ], + "moduleResolution": "bundler" }, "exclude": [ "webpack.config.js", diff --git a/light-client-js/webpack.config.js b/light-client-js/webpack.config.js index 91c6262..c154bf1 100644 --- a/light-client-js/webpack.config.js +++ b/light-client-js/webpack.config.js @@ -22,6 +22,7 @@ module.exports = { }, resolve: { extensions: ['.ts', '.js'], + fallback: { "stream": require.resolve("stream-browserify") } }, mode: "production", experiments: { diff --git a/light-client-wasm/src/lib.rs b/light-client-wasm/src/lib.rs index ab3fbeb..ad9d446 100644 --- a/light-client-wasm/src/lib.rs +++ b/light-client-wasm/src/lib.rs @@ -58,7 +58,9 @@ static NET_CONTROL: OnceLock = OnceLock::new(); static CONSENSUS: OnceLock> = OnceLock::new(); -static SERIALIZER: Serializer = Serializer::new().serialize_large_number_types_as_bigints(true); +static SERIALIZER: Serializer = Serializer::new() + .serialize_large_number_types_as_bigints(true) + .serialize_maps_as_objects(true); /// 0b0 init /// 0b1 start @@ -218,7 +220,7 @@ pub async fn light_client(net_flag: String, log_level: String) -> Result<(), JsV } #[wasm_bindgen] -pub async fn stop() { +pub fn stop() { broadcast_exit_signals(); STORAGE_WITH_DATA.get().unwrap().storage().shutdown(); change_status(0b10);