smoldot 0.3.6 → 0.7.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -21
- package/dist/cjs/base64.d.ts +22 -0
- package/dist/cjs/base64.js +104 -0
- package/dist/cjs/client.d.ts +287 -0
- package/dist/cjs/client.js +175 -0
- package/dist/cjs/index-browser.d.ts +11 -0
- package/dist/cjs/index-browser.js +465 -0
- package/dist/cjs/index-deno.d.ts +10 -0
- package/dist/cjs/index-deno.js +263 -0
- package/dist/cjs/index-nodejs.d.ts +10 -0
- package/dist/cjs/index-nodejs.js +174 -0
- package/dist/cjs/instance/autogen/wasm.d.ts +2 -0
- package/dist/cjs/instance/autogen/wasm.js +6 -0
- package/dist/cjs/instance/autogen/wasm0.d.ts +1 -0
- package/dist/cjs/instance/autogen/wasm0.js +4 -0
- package/dist/cjs/instance/autogen/wasm1.d.ts +1 -0
- package/dist/cjs/instance/autogen/wasm1.js +4 -0
- package/dist/cjs/instance/autogen/wasm2.d.ts +1 -0
- package/dist/cjs/instance/autogen/wasm2.js +4 -0
- package/dist/cjs/instance/bindings-smoldot-light.d.ts +154 -0
- package/dist/cjs/instance/bindings-smoldot-light.js +266 -0
- package/dist/cjs/instance/bindings-wasi.d.ts +23 -0
- package/dist/cjs/instance/bindings-wasi.js +142 -0
- package/dist/cjs/instance/bindings.d.ts +31 -0
- package/dist/cjs/instance/bindings.js +5 -0
- package/dist/cjs/instance/buffer.d.ts +10 -0
- package/dist/cjs/instance/buffer.js +53 -0
- package/dist/cjs/instance/instance.d.ts +45 -0
- package/dist/cjs/instance/instance.js +301 -0
- package/dist/cjs/instance/raw-instance.d.ts +65 -0
- package/dist/cjs/instance/raw-instance.js +72 -0
- package/dist/cjs/package.json +3 -0
- package/dist/mjs/base64.d.ts +22 -0
- package/dist/mjs/base64.js +97 -0
- package/dist/mjs/client.d.ts +287 -0
- package/dist/mjs/client.js +165 -0
- package/dist/mjs/index-browser.d.ts +11 -0
- package/dist/mjs/index-browser.js +455 -0
- package/dist/mjs/index-deno.d.ts +10 -0
- package/dist/mjs/index-deno.js +253 -0
- package/dist/mjs/index-nodejs.d.ts +10 -0
- package/dist/mjs/index-nodejs.js +164 -0
- package/dist/mjs/instance/autogen/wasm.d.ts +2 -0
- package/dist/mjs/instance/autogen/wasm.js +4 -0
- package/dist/mjs/instance/autogen/wasm0.d.ts +1 -0
- package/dist/mjs/instance/autogen/wasm0.js +1 -0
- package/dist/mjs/instance/autogen/wasm1.d.ts +1 -0
- package/dist/mjs/instance/autogen/wasm1.js +1 -0
- package/dist/mjs/instance/autogen/wasm2.d.ts +1 -0
- package/dist/mjs/instance/autogen/wasm2.js +1 -0
- package/dist/mjs/instance/bindings-smoldot-light.d.ts +154 -0
- package/dist/mjs/instance/bindings-smoldot-light.js +261 -0
- package/dist/mjs/instance/bindings-wasi.d.ts +23 -0
- package/{src → dist/mjs/instance}/bindings-wasi.js +47 -58
- package/dist/mjs/instance/bindings.d.ts +31 -0
- package/dist/mjs/instance/bindings.js +4 -0
- package/dist/mjs/instance/buffer.d.ts +10 -0
- package/dist/mjs/instance/buffer.js +46 -0
- package/dist/mjs/instance/instance.d.ts +45 -0
- package/dist/mjs/instance/instance.js +293 -0
- package/dist/mjs/instance/raw-instance.d.ts +65 -0
- package/dist/mjs/instance/raw-instance.js +67 -0
- package/dist/mjs/package.json +3 -0
- package/package.json +35 -19
- package/src/autogen/wasm.js +0 -1
- package/src/bindings-smoldot-js.js +0 -223
- package/src/compat-browser.js +0 -25
- package/src/compat-nodejs.js +0 -30
- package/src/health.js +0 -206
- package/src/index.d.ts +0 -68
- package/src/index.js +0 -192
- package/src/index.test-d.ts +0 -36
- package/src/tsconfig.json +0 -13
- package/src/worker.js +0 -151
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This JavaScript library provides a light client for
|
|
4
4
|
[the Polkadot blockchain](https://polkadot.network/) and for chains built
|
|
5
|
-
using [the Substrate blockchain framework](https://substrate.
|
|
5
|
+
using [the Substrate blockchain framework](https://substrate.io/).
|
|
6
6
|
|
|
7
7
|
It is an "actual" light client, in the sense that it is byzantine-resilient.
|
|
8
8
|
It does not rely on the presence of an RPC server, but directly connects to
|
|
@@ -17,19 +17,16 @@ import * as smoldot from 'smoldot';
|
|
|
17
17
|
const chainSpec = fs.readFileSync('./westend.json', 'utf8');
|
|
18
18
|
|
|
19
19
|
// A single client can be used to initialize multiple chains.
|
|
20
|
-
const client =
|
|
20
|
+
const client = smoldot.start();
|
|
21
21
|
|
|
22
|
-
const chain = await client.addChain({
|
|
23
|
-
chainSpec,
|
|
24
|
-
jsonRpcCallback: (jsonRpcResponse) => {
|
|
25
|
-
// Called whenever the client emits a response to a JSON-RPC request,
|
|
26
|
-
// or a JSON-RPC pub-sub notification.
|
|
27
|
-
console.log(jsonRpcResponse)
|
|
28
|
-
}
|
|
29
|
-
});
|
|
22
|
+
const chain = await client.addChain({ chainSpec });
|
|
30
23
|
|
|
31
24
|
chain.sendJsonRpc('{"jsonrpc":"2.0","id":1,"method":"system_name","params":[]}');
|
|
32
25
|
|
|
26
|
+
// Wait for a JSON-RPC response to come back. This is typically done in a loop in the background.
|
|
27
|
+
const jsonRpcResponse = await chain.nextJsonRpcResponse();
|
|
28
|
+
console.log(jsonRpcResponse)
|
|
29
|
+
|
|
33
30
|
// Later:
|
|
34
31
|
// chain.remove();
|
|
35
32
|
```
|
|
@@ -55,18 +52,18 @@ After having obtained a chain, use `sendJsonRpc` to send a JSON-RPC request towa
|
|
|
55
52
|
The function accepts as parameter a string request. See
|
|
56
53
|
[the specification of the JSON-RPC protocol](https://www.jsonrpc.org/specification),
|
|
57
54
|
and [the list of requests that smoldot is capable of serving](https://polkadot.js.org/docs/substrate/rpc/).
|
|
55
|
+
Smoldot also has experimental support for an extra (still experimental at the time of writing of
|
|
56
|
+
this comment) set of JSON-RPC functions [found here](https://github.com/paritytech/json-rpc-interface-spec/).
|
|
58
57
|
|
|
59
|
-
If the request is well formatted, the client will
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
If the request is a subscription, the notifications will also be sent back using the same
|
|
64
|
-
`jsonRpcCallback`.
|
|
58
|
+
If the request is well formatted, the client will generate a response. This response can be pulled
|
|
59
|
+
using the `nextJsonRpcResponse` asynchronous function. Calling this function waits until a response
|
|
60
|
+
is available and returns it.
|
|
65
61
|
|
|
66
|
-
If
|
|
67
|
-
|
|
62
|
+
If the request is a subscription, the notifications will also be sent back using the same mechanism
|
|
63
|
+
and can be pulled using `nextJsonRpcResponse`.
|
|
68
64
|
|
|
69
65
|
If the chain specification passed to `addChain` is a parachain, then the list of potential relay
|
|
70
|
-
chains must be passed as parameter to `addChain` as well.
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
chains must be passed as parameter to `addChain` as well. In situations where the chain
|
|
67
|
+
specifications passed to `addChain` are not trusted, it is important for security reasons to not
|
|
68
|
+
establish a parachain-relay-chain link between two chains that aren't part of the same "trust
|
|
69
|
+
sandbox".
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes a multibase-encoded string.
|
|
3
|
+
*
|
|
4
|
+
* Throws an exception if the encoding isn't base64 or one of its variants.
|
|
5
|
+
*/
|
|
6
|
+
export declare function multibaseBase64Decode(input: string): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
|
|
9
|
+
*
|
|
10
|
+
* See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
|
|
11
|
+
*/
|
|
12
|
+
export declare function classicDecode(input: string): Uint8Array;
|
|
13
|
+
/**
|
|
14
|
+
* Decodes a base64-encoded string into bytes using the URL-safe alphabet.
|
|
15
|
+
*
|
|
16
|
+
* See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
|
|
17
|
+
*/
|
|
18
|
+
export declare function urlSafeDecode(input: string): Uint8Array;
|
|
19
|
+
/**
|
|
20
|
+
* Decodes a base64-encoded string into bytes using the given alphabet.
|
|
21
|
+
*/
|
|
22
|
+
export declare function base64Decode(input: string, alphabet: Map<string, number>): Uint8Array;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Smoldot
|
|
3
|
+
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
|
|
4
|
+
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.base64Decode = exports.urlSafeDecode = exports.classicDecode = exports.multibaseBase64Decode = void 0;
|
|
7
|
+
// This program is free software: you can redistribute it and/or modify
|
|
8
|
+
// it under the terms of the GNU General Public License as published by
|
|
9
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
// (at your option) any later version.
|
|
11
|
+
// This program is distributed in the hope that it will be useful,
|
|
12
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
// GNU General Public License for more details.
|
|
15
|
+
// You should have received a copy of the GNU General Public License
|
|
16
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
let rfc4648Alphabet = new Map();
|
|
18
|
+
const rfc4648AlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
19
|
+
for (let i = 0; i < rfc4648AlphabetAsStr.length; ++i) {
|
|
20
|
+
rfc4648Alphabet.set(rfc4648AlphabetAsStr[i], i);
|
|
21
|
+
}
|
|
22
|
+
let urlSafeAlphabet = new Map();
|
|
23
|
+
const urlSafeAlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
24
|
+
for (let i = 0; i < urlSafeAlphabetAsStr.length; ++i) {
|
|
25
|
+
urlSafeAlphabet.set(urlSafeAlphabetAsStr[i], i);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Decodes a multibase-encoded string.
|
|
29
|
+
*
|
|
30
|
+
* Throws an exception if the encoding isn't base64 or one of its variants.
|
|
31
|
+
*/
|
|
32
|
+
function multibaseBase64Decode(input) {
|
|
33
|
+
if (input.length === 0)
|
|
34
|
+
throw new Error("Invalid multibase");
|
|
35
|
+
switch (input[0]) {
|
|
36
|
+
case 'm':
|
|
37
|
+
case 'M':
|
|
38
|
+
return classicDecode(input.slice(1));
|
|
39
|
+
case 'u':
|
|
40
|
+
case 'U':
|
|
41
|
+
return urlSafeDecode(input.slice(1));
|
|
42
|
+
default:
|
|
43
|
+
throw new Error('Unknown multibase prefix: ' + input[0]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.multibaseBase64Decode = multibaseBase64Decode;
|
|
47
|
+
/**
|
|
48
|
+
* Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
|
|
49
|
+
*
|
|
50
|
+
* See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
|
|
51
|
+
*/
|
|
52
|
+
function classicDecode(input) {
|
|
53
|
+
return base64Decode(input, rfc4648Alphabet);
|
|
54
|
+
}
|
|
55
|
+
exports.classicDecode = classicDecode;
|
|
56
|
+
/**
|
|
57
|
+
* Decodes a base64-encoded string into bytes using the URL-safe alphabet.
|
|
58
|
+
*
|
|
59
|
+
* See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
|
|
60
|
+
*/
|
|
61
|
+
function urlSafeDecode(input) {
|
|
62
|
+
return base64Decode(input, urlSafeAlphabet);
|
|
63
|
+
}
|
|
64
|
+
exports.urlSafeDecode = urlSafeDecode;
|
|
65
|
+
/**
|
|
66
|
+
* Decodes a base64-encoded string into bytes using the given alphabet.
|
|
67
|
+
*/
|
|
68
|
+
function base64Decode(input, alphabet) {
|
|
69
|
+
// Remove the padding bytes at the end of the string. We don't check whether the padding is
|
|
70
|
+
// accurate.
|
|
71
|
+
while (input.length !== 0 && input[input.length - 1] === '=')
|
|
72
|
+
input = input.slice(0, -1);
|
|
73
|
+
// Contains the output data.
|
|
74
|
+
const out = new Uint8Array(Math.floor(input.length * 6 / 8));
|
|
75
|
+
// Position within `out` of the next byte to write.
|
|
76
|
+
let outPos = 0;
|
|
77
|
+
// The bits decoded from the input are added to the right of this value.
|
|
78
|
+
let currentByte = 0;
|
|
79
|
+
// The left-most `validBitsInCurrentByte` bits of `currentByte` must be written out.
|
|
80
|
+
let validBitsInCurrentByte = 0;
|
|
81
|
+
for (let i = 0; i < input.length; ++i) {
|
|
82
|
+
const inputChr = input[i];
|
|
83
|
+
const bitsToAppend = alphabet.get(inputChr);
|
|
84
|
+
if (bitsToAppend === undefined)
|
|
85
|
+
throw new Error('Invalid base64 character: ' + inputChr);
|
|
86
|
+
console.assert(bitsToAppend < (1 << 6));
|
|
87
|
+
currentByte = (currentByte << 6) | bitsToAppend;
|
|
88
|
+
validBitsInCurrentByte += 6;
|
|
89
|
+
if (validBitsInCurrentByte >= 8) {
|
|
90
|
+
let outByte = currentByte >> (validBitsInCurrentByte - 8);
|
|
91
|
+
out[outPos] = outByte;
|
|
92
|
+
outPos += 1;
|
|
93
|
+
validBitsInCurrentByte -= 8;
|
|
94
|
+
}
|
|
95
|
+
console.assert(validBitsInCurrentByte < 8);
|
|
96
|
+
currentByte &= 0xff;
|
|
97
|
+
}
|
|
98
|
+
if ((currentByte & ((1 << validBitsInCurrentByte) - 1)) !== 0)
|
|
99
|
+
throw new Error("Unexpected EOF");
|
|
100
|
+
if (validBitsInCurrentByte >= 6)
|
|
101
|
+
throw new Error("Unexpected EOF");
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
exports.base64Decode = base64Decode;
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { PlatformBindings } from './instance/instance.js';
|
|
2
|
+
export { MalformedJsonRpcError, QueueFullError, CrashError } from './instance/instance.js';
|
|
3
|
+
/**
|
|
4
|
+
* Thrown in case of a problem when initializing the chain.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AddChainError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Thrown in case the API user tries to use a chain or client that has already been destroyed.
|
|
11
|
+
*/
|
|
12
|
+
export declare class AlreadyDestroyedError extends Error {
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Thrown when trying to send a JSON-RPC message to a chain whose JSON-RPC system hasn't been
|
|
17
|
+
* enabled.
|
|
18
|
+
*/
|
|
19
|
+
export declare class JsonRpcDisabledError extends Error {
|
|
20
|
+
constructor();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Client with zero or more active connections to blockchains.
|
|
24
|
+
*/
|
|
25
|
+
export interface Client {
|
|
26
|
+
/**
|
|
27
|
+
* Connects to a chain.
|
|
28
|
+
*
|
|
29
|
+
* Throws an exception if the chain specification isn't valid, or if the chain specification
|
|
30
|
+
* concerns a parachain but no corresponding relay chain can be found.
|
|
31
|
+
*
|
|
32
|
+
* Smoldot will automatically de-duplicate chains if multiple identical chains are added, in
|
|
33
|
+
* order to save resources. In other words, it is not a problem to call `addChain` multiple
|
|
34
|
+
* times with the same chain specifications and obtain multiple `Chain`.
|
|
35
|
+
* When the same client is used for multiple different purposes, you are in fact strongly
|
|
36
|
+
* encouraged to trust smoldot and not attempt to de-duplicate chains yourself, as determining
|
|
37
|
+
* whether two chains are identical is complicated and might have security implications.
|
|
38
|
+
*
|
|
39
|
+
* Smoldot tries to distribute CPU resources equally between all active `Chain` objects.
|
|
40
|
+
*
|
|
41
|
+
* @param options Configuration of the chain to add.
|
|
42
|
+
*
|
|
43
|
+
* @throws {@link AddChainError} If the chain can't be added.
|
|
44
|
+
* @throws {@link AlreadyDestroyedError} If the client has been terminated earlier.
|
|
45
|
+
* @throws {@link CrashError} If the background client has crashed.
|
|
46
|
+
*/
|
|
47
|
+
addChain(options: AddChainOptions): Promise<Chain>;
|
|
48
|
+
/**
|
|
49
|
+
* Terminates the client.
|
|
50
|
+
*
|
|
51
|
+
* Afterwards, trying to use the client or any of its chains again will lead to an exception
|
|
52
|
+
* being thrown.
|
|
53
|
+
*
|
|
54
|
+
* @throws {@link AlreadyDestroyedError} If the client has already been terminated earlier.
|
|
55
|
+
* @throws {@link CrashError} If the background client has crashed.
|
|
56
|
+
*/
|
|
57
|
+
terminate(): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Active connection to a blockchain.
|
|
61
|
+
*/
|
|
62
|
+
export interface Chain {
|
|
63
|
+
/**
|
|
64
|
+
* Enqueues a JSON-RPC request that the client will process as soon as possible.
|
|
65
|
+
*
|
|
66
|
+
* The response will be sent back using the callback passed when adding the chain.
|
|
67
|
+
*
|
|
68
|
+
* See <https://www.jsonrpc.org/specification> for a specification of the JSON-RPC format. Only
|
|
69
|
+
* version 2 is supported.
|
|
70
|
+
* Be aware that some requests will cause notifications to be sent back using the same callback
|
|
71
|
+
* as the responses.
|
|
72
|
+
*
|
|
73
|
+
* A {@link MalformedJsonRpcError} is thrown if the request isn't a valid JSON-RPC request or
|
|
74
|
+
* if the request is unreasonably large (64 MiB at the time of writing of this comment).
|
|
75
|
+
* If, however, the request is a valid JSON-RPC request but that concerns an unknown method, a
|
|
76
|
+
* error response is properly generated.
|
|
77
|
+
*
|
|
78
|
+
* Two JSON-RPC APIs are supported by smoldot:
|
|
79
|
+
*
|
|
80
|
+
* - The "legacy" one, documented here: <https://polkadot.js.org/docs/substrate/rpc>
|
|
81
|
+
* - The more recent one: <https://github.com/paritytech/json-rpc-interface-spec>
|
|
82
|
+
*
|
|
83
|
+
* @param rpc JSON-encoded RPC request.
|
|
84
|
+
*
|
|
85
|
+
* @throws {@link MalformedJsonRpcError} If the payload isn't valid JSON-RPC.
|
|
86
|
+
* @throws {@link QueueFullError} If the queue of JSON-RPC requests of the chain is full.
|
|
87
|
+
* @throws {@link AlreadyDestroyedError} If the chain has been removed or the client has been terminated.
|
|
88
|
+
* @throws {@link JsonRpcDisabledError} If the JSON-RPC system was disabled in the options of the chain.
|
|
89
|
+
* @throws {@link CrashError} If the background client has crashed.
|
|
90
|
+
*/
|
|
91
|
+
sendJsonRpc(rpc: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Waits for a JSON-RPC response or notification to be generated.
|
|
94
|
+
*
|
|
95
|
+
* If this function is called multiple times "simultaneously" (generating multiple different
|
|
96
|
+
* `Promise`s), each `Promise` will return a different JSON-RPC response or notification.
|
|
97
|
+
*
|
|
98
|
+
* Each chain contains a buffer of the responses waiting to be sent out. Calling this function
|
|
99
|
+
* pulls one element from the buffer. If this function is called at a slower rate than responses
|
|
100
|
+
* are generated, then buffer will eventually become full, at which point calling
|
|
101
|
+
* {@link Chain.sendJsonRpc} will throw an exception.
|
|
102
|
+
*
|
|
103
|
+
* @throws {@link AlreadyDestroyedError} If the chain has been removed or the client has been terminated.
|
|
104
|
+
* @throws {@link JsonRpcDisabledError} If the JSON-RPC system was disabled in the options of the chain.
|
|
105
|
+
* @throws {@link CrashError} If the background client has crashed.
|
|
106
|
+
*/
|
|
107
|
+
nextJsonRpcResponse(): Promise<string>;
|
|
108
|
+
/**
|
|
109
|
+
* Disconnects from the blockchain.
|
|
110
|
+
*
|
|
111
|
+
* The JSON-RPC callback will no longer be called.
|
|
112
|
+
*
|
|
113
|
+
* Trying to use the chain again will lead to an exception being thrown.
|
|
114
|
+
*
|
|
115
|
+
* If this chain is a relay chain, then all parachains that use it will continue to work. Smoldot
|
|
116
|
+
* automatically keeps alive all relay chains that have an active parachains. There is no need
|
|
117
|
+
* to track parachains and relaychains, or to destroy them in the correct order, as this is
|
|
118
|
+
* handled automatically.
|
|
119
|
+
*
|
|
120
|
+
* @throws {@link AlreadyDestroyedError} If the chain has already been removed or the client has been terminated.
|
|
121
|
+
* @throws {@link CrashError} If the background client has crashed.
|
|
122
|
+
*/
|
|
123
|
+
remove(): void;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @param level How important this message is. 1 = Error, 2 = Warn, 3 = Info, 4 = Debug, 5 = Trace
|
|
127
|
+
* @param target Name of the sub-system that the message concerns.
|
|
128
|
+
* @param message Human-readable message that developers can use to figure out what is happening.
|
|
129
|
+
*/
|
|
130
|
+
export type LogCallback = (level: number, target: string, message: string) => void;
|
|
131
|
+
/**
|
|
132
|
+
* Configuration of a client.
|
|
133
|
+
*/
|
|
134
|
+
export interface ClientOptions {
|
|
135
|
+
/**
|
|
136
|
+
* Callback that the client will invoke in order to report a log event.
|
|
137
|
+
*
|
|
138
|
+
* By default, prints the log on the `console`. If you want to disable logging altogether,
|
|
139
|
+
* please pass an empty callback function.
|
|
140
|
+
*/
|
|
141
|
+
logCallback?: LogCallback;
|
|
142
|
+
/**
|
|
143
|
+
* The client will never call the callback with a value of `level` superior to this value.
|
|
144
|
+
* Defaults to 3.
|
|
145
|
+
*
|
|
146
|
+
* While this filtering could be done directly by the `logCallback`, passing a maximum log level
|
|
147
|
+
* leads to better performances as the client doesn't even need to generate a `message` when it
|
|
148
|
+
* knows that this message isn't interesting.
|
|
149
|
+
*/
|
|
150
|
+
maxLogLevel?: number;
|
|
151
|
+
/**
|
|
152
|
+
* Maximum amount of CPU that the client should consume on average.
|
|
153
|
+
*
|
|
154
|
+
* This must be a number between `0.0` and `1.0`. For example, passing `0.25` bounds the client
|
|
155
|
+
* to 25% of CPU power.
|
|
156
|
+
* Defaults to `1.0` if no value is provided.
|
|
157
|
+
*
|
|
158
|
+
* Note that this is implemented by sleeping for certain amounts of time in order for the average
|
|
159
|
+
* CPU consumption to not go beyond the given limit. It is therefore still possible for the
|
|
160
|
+
* client to use high amounts of CPU for short amounts of time.
|
|
161
|
+
*/
|
|
162
|
+
cpuRateLimit?: number;
|
|
163
|
+
/**
|
|
164
|
+
* If `true`, then the client will never open any TCP connection.
|
|
165
|
+
* Defaults to `false`.
|
|
166
|
+
*
|
|
167
|
+
* This option can be used in order to mimic an environment where the TCP protocol isn't
|
|
168
|
+
* supported (e.g. browsers) from an environment where TCP is supported (e.g. NodeJS).
|
|
169
|
+
*
|
|
170
|
+
* This option has no effect in environments where the TCP protocol isn't supported anyway.
|
|
171
|
+
*/
|
|
172
|
+
forbidTcp?: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* If `true`, then the client will never open any non-secure WebSocket connection.
|
|
175
|
+
* Defaults to `false`.
|
|
176
|
+
*
|
|
177
|
+
* This option can be used in order to mimic an environment where non-secure WebSocket
|
|
178
|
+
* connections aren't supported (e.g. web pages) from an environment where they are supported
|
|
179
|
+
* (e.g. NodeJS).
|
|
180
|
+
*
|
|
181
|
+
* This option has no effect in environments where non-secure WebSocket connections aren't
|
|
182
|
+
* supported anyway.
|
|
183
|
+
*/
|
|
184
|
+
forbidWs?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* If `true`, then the client will never open any non-secure WebSocket connection to addresses
|
|
187
|
+
* other than `localhost` or `127.0.0.1`.
|
|
188
|
+
* Defaults to `false`.
|
|
189
|
+
*
|
|
190
|
+
* This option is similar to `forbidWs`, except that connections to `localhost` and `127.0.0.1`
|
|
191
|
+
* do not take the value of this option into account.
|
|
192
|
+
*
|
|
193
|
+
* This option can be used in order to mimic an environment where non-secure WebSocket
|
|
194
|
+
* connections aren't supported (e.g. web pages) from an environment where they are supported
|
|
195
|
+
* (e.g. NodeJS).
|
|
196
|
+
*
|
|
197
|
+
* This option has no effect in environments where non-secure WebSocket connections aren't
|
|
198
|
+
* supported anyway.
|
|
199
|
+
*/
|
|
200
|
+
forbidNonLocalWs?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* If `true`, then the client will never open any secure WebSocket connection.
|
|
203
|
+
* Defaults to `false`.
|
|
204
|
+
*
|
|
205
|
+
* This option exists of the sake of completeness. All environments support secure WebSocket
|
|
206
|
+
* connections.
|
|
207
|
+
*/
|
|
208
|
+
forbidWss?: boolean;
|
|
209
|
+
/**
|
|
210
|
+
* If `true`, then the client will never open any WebRTC connection.
|
|
211
|
+
* Defaults to `false`.
|
|
212
|
+
*
|
|
213
|
+
* This option has no effect in environments where non-secure WebSocket connections aren't
|
|
214
|
+
* supported anyway.
|
|
215
|
+
*/
|
|
216
|
+
forbidWebRtc?: boolean;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Configuration of a blockchain.
|
|
220
|
+
*/
|
|
221
|
+
export interface AddChainOptions {
|
|
222
|
+
/**
|
|
223
|
+
* JSON-encoded specification of the chain.
|
|
224
|
+
*
|
|
225
|
+
* The specification of the chain can be generated from a Substrate node by calling
|
|
226
|
+
* `<client> build-spec --raw > spec.json`. Only "raw" chain specifications are supported by
|
|
227
|
+
* smoldot at the moment.
|
|
228
|
+
*
|
|
229
|
+
* If the chain specification contains a `relay_chain` field, then smoldot will try to match
|
|
230
|
+
* the value in `relay_chain` with the value in `id` of the chains in `potentialRelayChains`.
|
|
231
|
+
*/
|
|
232
|
+
chainSpec: string;
|
|
233
|
+
/**
|
|
234
|
+
* Content of the database of this chain. Can be obtained by using the
|
|
235
|
+
* `chainHead_unstable_finalizedDatabase` JSON-RPC function.
|
|
236
|
+
*
|
|
237
|
+
* Smoldot reserves the right to change its database format, making previous databases
|
|
238
|
+
* incompatible. For this reason, no error is generated if the content of the database is invalid
|
|
239
|
+
* and/or can't be decoded.
|
|
240
|
+
*
|
|
241
|
+
* Important: please note that using a malicious database content can lead to a security
|
|
242
|
+
* vulnerability. This database content is considered by smoldot as trusted input. It is the
|
|
243
|
+
* responsibility of the API user to make sure that the value passed in this field comes from
|
|
244
|
+
* the same source of trust as the chain specification that was used when retrieving this
|
|
245
|
+
* database content.
|
|
246
|
+
*/
|
|
247
|
+
databaseContent?: string;
|
|
248
|
+
/**
|
|
249
|
+
* If `chainSpec` concerns a parachain, contains the list of chains whose `id` smoldot will try
|
|
250
|
+
* to match with the parachain's `relay_chain`.
|
|
251
|
+
* Defaults to `[]`.
|
|
252
|
+
*
|
|
253
|
+
* Must contain exactly the objects that were returned by previous calls to `addChain`. The
|
|
254
|
+
* library uses a `WeakMap` in its implementation in order to identify chains.
|
|
255
|
+
*
|
|
256
|
+
* The primary way smoldot determines which relay chain is associated to a parachain is by
|
|
257
|
+
* inspecting the chain specification of that parachain (i.e. the `chainSpec` field).
|
|
258
|
+
*
|
|
259
|
+
* This poses a problem in situations where the same client is shared between multiple different
|
|
260
|
+
* applications: multiple applications could add mutiple different chains with the same `id`,
|
|
261
|
+
* creating an ambiguity, or an application could register malicious chains with small variations
|
|
262
|
+
* of a popular chain's `id` and try to benefit from a typo in a legitimate application's
|
|
263
|
+
* `relay_chain`.
|
|
264
|
+
*
|
|
265
|
+
* These problems can be solved by using this parameter to segregate multiple different uses of
|
|
266
|
+
* the same client. To use it, pass the list of all chains that the same application has
|
|
267
|
+
* previously added to the client. By doing so, you are guaranteed that the chains of multiple
|
|
268
|
+
* different applications can't interact in any way (good or bad), while still benefiting from
|
|
269
|
+
* the de-duplication of resources that smoldot performs in `addChain`.
|
|
270
|
+
*
|
|
271
|
+
* When multiple different parachains use the same relay chain, it is important to be sure that
|
|
272
|
+
* they are indeed using the same relay chain, and not accidentally using different ones. For
|
|
273
|
+
* this reason, this parameter is a list of potential relay chains in which only one chain
|
|
274
|
+
* should match, rather than a single `Chain` corresponding to the relay chain.
|
|
275
|
+
*/
|
|
276
|
+
potentialRelayChains?: Chain[];
|
|
277
|
+
/**
|
|
278
|
+
* Disables the JSON-RPC system of the chain.
|
|
279
|
+
*
|
|
280
|
+
* This option can be used in order to save up some resources.
|
|
281
|
+
*
|
|
282
|
+
* It will be illegal to call {@link Chain.sendJsonRpc} and {@link Chain.nextJsonRpcResponse} on
|
|
283
|
+
* this chain.
|
|
284
|
+
*/
|
|
285
|
+
disableJsonRpc?: boolean;
|
|
286
|
+
}
|
|
287
|
+
export declare function start(options: ClientOptions, platformBindings: PlatformBindings): Client;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Smoldot
|
|
3
|
+
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
|
|
4
|
+
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
5
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.start = exports.JsonRpcDisabledError = exports.AlreadyDestroyedError = exports.AddChainError = exports.CrashError = exports.QueueFullError = exports.MalformedJsonRpcError = void 0;
|
|
16
|
+
// This program is free software: you can redistribute it and/or modify
|
|
17
|
+
// it under the terms of the GNU General Public License as published by
|
|
18
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
19
|
+
// (at your option) any later version.
|
|
20
|
+
// This program is distributed in the hope that it will be useful,
|
|
21
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
22
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
23
|
+
// GNU General Public License for more details.
|
|
24
|
+
// You should have received a copy of the GNU General Public License
|
|
25
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
26
|
+
const instance_js_1 = require("./instance/instance.js");
|
|
27
|
+
var instance_js_2 = require("./instance/instance.js");
|
|
28
|
+
Object.defineProperty(exports, "MalformedJsonRpcError", { enumerable: true, get: function () { return instance_js_2.MalformedJsonRpcError; } });
|
|
29
|
+
Object.defineProperty(exports, "QueueFullError", { enumerable: true, get: function () { return instance_js_2.QueueFullError; } });
|
|
30
|
+
Object.defineProperty(exports, "CrashError", { enumerable: true, get: function () { return instance_js_2.CrashError; } });
|
|
31
|
+
/**
|
|
32
|
+
* Thrown in case of a problem when initializing the chain.
|
|
33
|
+
*/
|
|
34
|
+
class AddChainError extends Error {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "AddChainError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.AddChainError = AddChainError;
|
|
41
|
+
/**
|
|
42
|
+
* Thrown in case the API user tries to use a chain or client that has already been destroyed.
|
|
43
|
+
*/
|
|
44
|
+
class AlreadyDestroyedError extends Error {
|
|
45
|
+
constructor() {
|
|
46
|
+
super();
|
|
47
|
+
this.name = "AlreadyDestroyedError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.AlreadyDestroyedError = AlreadyDestroyedError;
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when trying to send a JSON-RPC message to a chain whose JSON-RPC system hasn't been
|
|
53
|
+
* enabled.
|
|
54
|
+
*/
|
|
55
|
+
class JsonRpcDisabledError extends Error {
|
|
56
|
+
constructor() {
|
|
57
|
+
super();
|
|
58
|
+
this.name = "JsonRpcDisabledError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.JsonRpcDisabledError = JsonRpcDisabledError;
|
|
62
|
+
// This function is similar to the `start` function found in `index.ts`, except with an extra
|
|
63
|
+
// parameter containing the platform-specific bindings.
|
|
64
|
+
// Contrary to the one within `index.js`, this function is not supposed to be directly used.
|
|
65
|
+
function start(options, platformBindings) {
|
|
66
|
+
const logCallback = options.logCallback || ((level, target, message) => {
|
|
67
|
+
// The first parameter of the methods of `console` has some printf-like substitution
|
|
68
|
+
// capabilities. We don't really need to use this, but not using it means that the logs might
|
|
69
|
+
// not get printed correctly if they contain `%`.
|
|
70
|
+
if (level <= 1) {
|
|
71
|
+
console.error("[%s] %s", target, message);
|
|
72
|
+
}
|
|
73
|
+
else if (level == 2) {
|
|
74
|
+
console.warn("[%s] %s", target, message);
|
|
75
|
+
}
|
|
76
|
+
else if (level == 3) {
|
|
77
|
+
console.info("[%s] %s", target, message);
|
|
78
|
+
}
|
|
79
|
+
else if (level == 4) {
|
|
80
|
+
console.debug("[%s] %s", target, message);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.trace("[%s] %s", target, message);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// For each chain object returned by `addChain`, the associated internal chain id.
|
|
87
|
+
//
|
|
88
|
+
// Immediately cleared when `remove()` is called on a chain.
|
|
89
|
+
let chainIds = new WeakMap();
|
|
90
|
+
// If `Client.terminate()̀ is called, this error is set to a value.
|
|
91
|
+
// All the functions of the public API check if this contains a value.
|
|
92
|
+
let alreadyDestroyedError = null;
|
|
93
|
+
const instance = (0, instance_js_1.start)({
|
|
94
|
+
// Maximum level of log entries sent by the client.
|
|
95
|
+
// 0 = Logging disabled, 1 = Error, 2 = Warn, 3 = Info, 4 = Debug, 5 = Trace
|
|
96
|
+
maxLogLevel: options.maxLogLevel || 3,
|
|
97
|
+
logCallback,
|
|
98
|
+
// `enableCurrentTask` adds a small performance hit, but adds some additional information to
|
|
99
|
+
// crash reports. Whether this should be enabled is very opiniated and not that important. At
|
|
100
|
+
// the moment, we enable it all the time, except if the user has logging disabled altogether.
|
|
101
|
+
enableCurrentTask: options.maxLogLevel ? options.maxLogLevel >= 1 : true,
|
|
102
|
+
cpuRateLimit: options.cpuRateLimit || 1.0,
|
|
103
|
+
}, platformBindings);
|
|
104
|
+
return {
|
|
105
|
+
addChain: (options) => __awaiter(this, void 0, void 0, function* () {
|
|
106
|
+
if (alreadyDestroyedError)
|
|
107
|
+
throw alreadyDestroyedError;
|
|
108
|
+
// Passing a JSON object for the chain spec is an easy mistake, so we provide a more
|
|
109
|
+
// readable error.
|
|
110
|
+
if (!(typeof options.chainSpec === 'string'))
|
|
111
|
+
throw new Error("Chain specification must be a string");
|
|
112
|
+
let potentialRelayChainsIds = [];
|
|
113
|
+
if (!!options.potentialRelayChains) {
|
|
114
|
+
for (const chain of options.potentialRelayChains) {
|
|
115
|
+
// The content of `options.potentialRelayChains` are supposed to be chains earlier
|
|
116
|
+
// returned by `addChain`.
|
|
117
|
+
const id = chainIds.get(chain);
|
|
118
|
+
if (id === undefined) // It is possible for `id` to be missing if it has earlier been removed.
|
|
119
|
+
continue;
|
|
120
|
+
potentialRelayChainsIds.push(id);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const outcome = yield instance.addChain(options.chainSpec, typeof options.databaseContent === 'string' ? options.databaseContent : "", potentialRelayChainsIds, !!options.disableJsonRpc);
|
|
124
|
+
if (!outcome.success)
|
|
125
|
+
throw new AddChainError(outcome.error);
|
|
126
|
+
const chainId = outcome.chainId;
|
|
127
|
+
const wasDestroyed = { destroyed: false };
|
|
128
|
+
// `expected` was pushed by the `addChain` method.
|
|
129
|
+
// Resolve the promise that `addChain` returned to the user.
|
|
130
|
+
const newChain = {
|
|
131
|
+
sendJsonRpc: (request) => {
|
|
132
|
+
if (alreadyDestroyedError)
|
|
133
|
+
throw alreadyDestroyedError;
|
|
134
|
+
if (wasDestroyed.destroyed)
|
|
135
|
+
throw new AlreadyDestroyedError();
|
|
136
|
+
if (options.disableJsonRpc)
|
|
137
|
+
throw new JsonRpcDisabledError();
|
|
138
|
+
if (request.length >= 64 * 1024 * 1024) {
|
|
139
|
+
throw new instance_js_1.MalformedJsonRpcError();
|
|
140
|
+
}
|
|
141
|
+
;
|
|
142
|
+
instance.request(request, chainId);
|
|
143
|
+
},
|
|
144
|
+
nextJsonRpcResponse: () => {
|
|
145
|
+
if (alreadyDestroyedError)
|
|
146
|
+
return Promise.reject(alreadyDestroyedError);
|
|
147
|
+
if (wasDestroyed.destroyed)
|
|
148
|
+
return Promise.reject(new AlreadyDestroyedError());
|
|
149
|
+
if (options.disableJsonRpc)
|
|
150
|
+
return Promise.reject(new JsonRpcDisabledError());
|
|
151
|
+
return instance.nextJsonRpcResponse(chainId);
|
|
152
|
+
},
|
|
153
|
+
remove: () => {
|
|
154
|
+
if (alreadyDestroyedError)
|
|
155
|
+
throw alreadyDestroyedError;
|
|
156
|
+
if (wasDestroyed.destroyed)
|
|
157
|
+
throw new AlreadyDestroyedError();
|
|
158
|
+
wasDestroyed.destroyed = true;
|
|
159
|
+
console.assert(chainIds.has(newChain));
|
|
160
|
+
chainIds.delete(newChain);
|
|
161
|
+
instance.removeChain(chainId);
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
chainIds.set(newChain, chainId);
|
|
165
|
+
return newChain;
|
|
166
|
+
}),
|
|
167
|
+
terminate: () => __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
if (alreadyDestroyedError)
|
|
169
|
+
throw alreadyDestroyedError;
|
|
170
|
+
alreadyDestroyedError = new AlreadyDestroyedError();
|
|
171
|
+
instance.startShutdown();
|
|
172
|
+
})
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
exports.start = start;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
import { Client, ClientOptions } from './client.js';
|
|
3
|
+
export { AddChainError, AddChainOptions, AlreadyDestroyedError, Chain, Client, ClientOptions, CrashError, JsonRpcDisabledError, MalformedJsonRpcError, QueueFullError, LogCallback } from './client.js';
|
|
4
|
+
/**
|
|
5
|
+
* Initializes a new client. This is a pre-requisite to connecting to a blockchain.
|
|
6
|
+
*
|
|
7
|
+
* Can never fail.
|
|
8
|
+
*
|
|
9
|
+
* @param options Configuration of the client. Defaults to `{}`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function start(options?: ClientOptions): Client;
|