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.
Files changed (74) hide show
  1. package/README.md +18 -21
  2. package/dist/cjs/base64.d.ts +22 -0
  3. package/dist/cjs/base64.js +104 -0
  4. package/dist/cjs/client.d.ts +287 -0
  5. package/dist/cjs/client.js +175 -0
  6. package/dist/cjs/index-browser.d.ts +11 -0
  7. package/dist/cjs/index-browser.js +465 -0
  8. package/dist/cjs/index-deno.d.ts +10 -0
  9. package/dist/cjs/index-deno.js +263 -0
  10. package/dist/cjs/index-nodejs.d.ts +10 -0
  11. package/dist/cjs/index-nodejs.js +174 -0
  12. package/dist/cjs/instance/autogen/wasm.d.ts +2 -0
  13. package/dist/cjs/instance/autogen/wasm.js +6 -0
  14. package/dist/cjs/instance/autogen/wasm0.d.ts +1 -0
  15. package/dist/cjs/instance/autogen/wasm0.js +4 -0
  16. package/dist/cjs/instance/autogen/wasm1.d.ts +1 -0
  17. package/dist/cjs/instance/autogen/wasm1.js +4 -0
  18. package/dist/cjs/instance/autogen/wasm2.d.ts +1 -0
  19. package/dist/cjs/instance/autogen/wasm2.js +4 -0
  20. package/dist/cjs/instance/bindings-smoldot-light.d.ts +154 -0
  21. package/dist/cjs/instance/bindings-smoldot-light.js +266 -0
  22. package/dist/cjs/instance/bindings-wasi.d.ts +23 -0
  23. package/dist/cjs/instance/bindings-wasi.js +142 -0
  24. package/dist/cjs/instance/bindings.d.ts +31 -0
  25. package/dist/cjs/instance/bindings.js +5 -0
  26. package/dist/cjs/instance/buffer.d.ts +10 -0
  27. package/dist/cjs/instance/buffer.js +53 -0
  28. package/dist/cjs/instance/instance.d.ts +45 -0
  29. package/dist/cjs/instance/instance.js +301 -0
  30. package/dist/cjs/instance/raw-instance.d.ts +65 -0
  31. package/dist/cjs/instance/raw-instance.js +72 -0
  32. package/dist/cjs/package.json +3 -0
  33. package/dist/mjs/base64.d.ts +22 -0
  34. package/dist/mjs/base64.js +97 -0
  35. package/dist/mjs/client.d.ts +287 -0
  36. package/dist/mjs/client.js +165 -0
  37. package/dist/mjs/index-browser.d.ts +11 -0
  38. package/dist/mjs/index-browser.js +455 -0
  39. package/dist/mjs/index-deno.d.ts +10 -0
  40. package/dist/mjs/index-deno.js +253 -0
  41. package/dist/mjs/index-nodejs.d.ts +10 -0
  42. package/dist/mjs/index-nodejs.js +164 -0
  43. package/dist/mjs/instance/autogen/wasm.d.ts +2 -0
  44. package/dist/mjs/instance/autogen/wasm.js +4 -0
  45. package/dist/mjs/instance/autogen/wasm0.d.ts +1 -0
  46. package/dist/mjs/instance/autogen/wasm0.js +1 -0
  47. package/dist/mjs/instance/autogen/wasm1.d.ts +1 -0
  48. package/dist/mjs/instance/autogen/wasm1.js +1 -0
  49. package/dist/mjs/instance/autogen/wasm2.d.ts +1 -0
  50. package/dist/mjs/instance/autogen/wasm2.js +1 -0
  51. package/dist/mjs/instance/bindings-smoldot-light.d.ts +154 -0
  52. package/dist/mjs/instance/bindings-smoldot-light.js +261 -0
  53. package/dist/mjs/instance/bindings-wasi.d.ts +23 -0
  54. package/{src → dist/mjs/instance}/bindings-wasi.js +47 -58
  55. package/dist/mjs/instance/bindings.d.ts +31 -0
  56. package/dist/mjs/instance/bindings.js +4 -0
  57. package/dist/mjs/instance/buffer.d.ts +10 -0
  58. package/dist/mjs/instance/buffer.js +46 -0
  59. package/dist/mjs/instance/instance.d.ts +45 -0
  60. package/dist/mjs/instance/instance.js +293 -0
  61. package/dist/mjs/instance/raw-instance.d.ts +65 -0
  62. package/dist/mjs/instance/raw-instance.js +67 -0
  63. package/dist/mjs/package.json +3 -0
  64. package/package.json +35 -19
  65. package/src/autogen/wasm.js +0 -1
  66. package/src/bindings-smoldot-js.js +0 -223
  67. package/src/compat-browser.js +0 -25
  68. package/src/compat-nodejs.js +0 -30
  69. package/src/health.js +0 -206
  70. package/src/index.d.ts +0 -68
  71. package/src/index.js +0 -192
  72. package/src/index.test-d.ts +0 -36
  73. package/src/tsconfig.json +0 -13
  74. 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.dev/).
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 = await smoldot.start();
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 send a response using the `jsonRpcCallback`
60
- callback that was passed to `addChain`. This callback takes as parameter the string JSON-RPC
61
- response.
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 no `jsonRpcCallback` was passed to `addChain`, then this chain won't be capable of serving
67
- any JSON-RPC request at all. This can be used to save resources.
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. For security reasons, it is important
71
- to not establish a parachain-relay-chain link between two chains that weren't created by the same
72
- user.
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;