smoldot 0.3.6 → 0.7.11

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
@@ -0,0 +1,53 @@
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.writeUInt32LE = exports.writeUInt8 = exports.readUInt32LE = exports.utf8BytesToString = 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
+ function utf8BytesToString(buffer, offset, length) {
18
+ checkRange(buffer, offset, length);
19
+ // The `TextDecoder` API is supported by all major browsers and by NodeJS.
20
+ // <https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder>
21
+ return new TextDecoder().decode(buffer.slice(offset, offset + length));
22
+ }
23
+ exports.utf8BytesToString = utf8BytesToString;
24
+ function readUInt32LE(buffer, offset) {
25
+ checkRange(buffer, offset, 4);
26
+ return (buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16)) + (buffer[offset + 3] * 0x1000000);
27
+ }
28
+ exports.readUInt32LE = readUInt32LE;
29
+ /**
30
+ * Sets the value of a given byte in the buffer.
31
+ *
32
+ * This function is equivalent to `buffer[offset] = value`, except that an exception is thrown
33
+ * if `offset` is out of range.
34
+ */
35
+ function writeUInt8(buffer, offset, value) {
36
+ checkRange(buffer, offset, 1);
37
+ buffer[offset] = value & 0xff;
38
+ }
39
+ exports.writeUInt8 = writeUInt8;
40
+ function writeUInt32LE(buffer, offset, value) {
41
+ checkRange(buffer, offset, 4);
42
+ buffer[offset + 3] = (value >>> 24) & 0xff;
43
+ buffer[offset + 2] = (value >>> 16) & 0xff;
44
+ buffer[offset + 1] = (value >>> 8) & 0xff;
45
+ buffer[offset] = value & 0xff;
46
+ }
47
+ exports.writeUInt32LE = writeUInt32LE;
48
+ function checkRange(buffer, offset, length) {
49
+ if (!Number.isInteger(offset) || offset < 0)
50
+ throw new RangeError();
51
+ if (offset + length > buffer.length)
52
+ throw new RangeError();
53
+ }
@@ -0,0 +1,45 @@
1
+ import * as instance from './raw-instance.js';
2
+ export { PlatformBindings, ConnectionError, ConnectionConfig, Connection } from './raw-instance.js';
3
+ /**
4
+ * Thrown in case the underlying client encounters an unexpected crash.
5
+ *
6
+ * This is always an internal bug in smoldot and is never supposed to happen.
7
+ */
8
+ export declare class CrashError extends Error {
9
+ constructor(message: string);
10
+ }
11
+ /**
12
+ * Thrown in case a malformed JSON-RPC request is sent.
13
+ */
14
+ export declare class MalformedJsonRpcError extends Error {
15
+ constructor();
16
+ }
17
+ /**
18
+ * Thrown in case the buffer of JSON-RPC requests is full and cannot accept any more request.
19
+ */
20
+ export declare class QueueFullError extends Error {
21
+ constructor();
22
+ }
23
+ /**
24
+ * Contains the configuration of the instance.
25
+ */
26
+ export interface Config {
27
+ logCallback: (level: number, target: string, message: string) => void;
28
+ maxLogLevel: number;
29
+ enableCurrentTask: boolean;
30
+ cpuRateLimit: number;
31
+ }
32
+ export interface Instance {
33
+ request: (request: string, chainId: number) => void;
34
+ nextJsonRpcResponse: (chainId: number) => Promise<string>;
35
+ addChain: (chainSpec: string, databaseContent: string, potentialRelayChains: number[], disableJsonRpc: boolean) => Promise<{
36
+ success: true;
37
+ chainId: number;
38
+ } | {
39
+ success: false;
40
+ error: string;
41
+ }>;
42
+ removeChain: (chainId: number) => void;
43
+ startShutdown: () => void;
44
+ }
45
+ export declare function start(configMessage: Config, platformBindings: instance.PlatformBindings): Instance;
@@ -0,0 +1,301 @@
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.QueueFullError = exports.MalformedJsonRpcError = exports.CrashError = exports.ConnectionError = 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 buffer = require("./buffer.js");
27
+ const instance = require("./raw-instance.js");
28
+ const client_js_1 = require("../client.js");
29
+ var raw_instance_js_1 = require("./raw-instance.js");
30
+ Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return raw_instance_js_1.ConnectionError; } });
31
+ /**
32
+ * Thrown in case the underlying client encounters an unexpected crash.
33
+ *
34
+ * This is always an internal bug in smoldot and is never supposed to happen.
35
+ */
36
+ class CrashError extends Error {
37
+ constructor(message) {
38
+ super(message);
39
+ }
40
+ }
41
+ exports.CrashError = CrashError;
42
+ /**
43
+ * Thrown in case a malformed JSON-RPC request is sent.
44
+ */
45
+ class MalformedJsonRpcError extends Error {
46
+ constructor() {
47
+ super("JSON-RPC request is malformed");
48
+ }
49
+ }
50
+ exports.MalformedJsonRpcError = MalformedJsonRpcError;
51
+ /**
52
+ * Thrown in case the buffer of JSON-RPC requests is full and cannot accept any more request.
53
+ */
54
+ class QueueFullError extends Error {
55
+ constructor() {
56
+ super("JSON-RPC requests queue is full");
57
+ }
58
+ }
59
+ exports.QueueFullError = QueueFullError;
60
+ function start(configMessage, platformBindings) {
61
+ // This variable represents the state of the instance, and serves two different purposes:
62
+ //
63
+ // - At initialization, it is a Promise containing the Wasm VM is still initializing.
64
+ // - After the Wasm VM has finished initialization, contains the `WebAssembly.Instance` object.
65
+ //
66
+ let state;
67
+ const crashError = {};
68
+ const currentTask = { name: null };
69
+ const printError = { printError: true };
70
+ // Contains the information of each chain that is currently alive.
71
+ let chains = new Map();
72
+ // Start initialization of the Wasm VM.
73
+ const config = {
74
+ onWasmPanic: (message) => {
75
+ // TODO: consider obtaining a backtrace here
76
+ crashError.error = new CrashError(message);
77
+ if (!printError.printError)
78
+ return;
79
+ console.error("Smoldot has panicked" +
80
+ (currentTask.name ? (" while executing task `" + currentTask.name + "`") : "") +
81
+ ". This is a bug in smoldot. Please open an issue at " +
82
+ "https://github.com/smol-dot/smoldot/issues with the following message:\n" +
83
+ message);
84
+ for (const chain of Array.from(chains.values())) {
85
+ for (const promise of chain.jsonRpcResponsesPromises) {
86
+ promise.reject(crashError.error);
87
+ }
88
+ chain.jsonRpcResponsesPromises = [];
89
+ }
90
+ },
91
+ logCallback: (level, target, message) => {
92
+ configMessage.logCallback(level, target, message);
93
+ },
94
+ jsonRpcResponsesNonEmptyCallback: (chainId) => {
95
+ // Notify every single promise found in `jsonRpcResponsesPromises`.
96
+ const promises = chains.get(chainId).jsonRpcResponsesPromises;
97
+ while (promises.length !== 0) {
98
+ promises.shift().resolve();
99
+ }
100
+ },
101
+ currentTaskCallback: (taskName) => {
102
+ currentTask.name = taskName;
103
+ },
104
+ cpuRateLimit: configMessage.cpuRateLimit,
105
+ };
106
+ state = {
107
+ initialized: false, promise: instance.startInstance(config, platformBindings).then((instance) => {
108
+ // `config.cpuRateLimit` is a floating point that should be between 0 and 1, while the value
109
+ // to pass as parameter must be between `0` and `2^32-1`.
110
+ // The few lines of code below should handle all possible values of `number`, including
111
+ // infinites and NaN.
112
+ let cpuRateLimit = Math.round(config.cpuRateLimit * 4294967295); // `2^32 - 1`
113
+ if (cpuRateLimit < 0)
114
+ cpuRateLimit = 0;
115
+ if (cpuRateLimit > 4294967295)
116
+ cpuRateLimit = 4294967295;
117
+ if (!Number.isFinite(cpuRateLimit))
118
+ cpuRateLimit = 4294967295; // User might have passed NaN
119
+ // Smoldot requires an initial call to the `init` function in order to do its internal
120
+ // configuration.
121
+ const [periodicallyYield, unregisterCallback] = platformBindings.registerShouldPeriodicallyYield((newValue) => {
122
+ if (state.initialized && !crashError.error) {
123
+ try {
124
+ state.instance.exports.set_periodically_yield(newValue ? 1 : 0);
125
+ }
126
+ catch (_error) { }
127
+ }
128
+ });
129
+ instance.exports.init(configMessage.maxLogLevel, configMessage.enableCurrentTask ? 1 : 0, cpuRateLimit, periodicallyYield ? 1 : 0);
130
+ state = { initialized: true, instance, unregisterCallback };
131
+ return instance;
132
+ })
133
+ };
134
+ function queueOperation(operation) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ // What to do depends on the type of `state`.
137
+ // See the documentation of the `state` variable for information.
138
+ if (!state.initialized) {
139
+ // A message has been received while the Wasm VM is still initializing. Queue it for when
140
+ // initialization is over.
141
+ return state.promise.then((instance) => operation(instance));
142
+ }
143
+ else {
144
+ // Everything is already initialized. Process the message synchronously.
145
+ return operation(state.instance);
146
+ }
147
+ });
148
+ }
149
+ return {
150
+ request: (request, chainId) => {
151
+ // Because `request` is passed as parameter an identifier returned by `addChain`, it is
152
+ // always the case that the Wasm instance is already initialized. The only possibility for
153
+ // it to not be the case is if the user completely invented the `chainId`.
154
+ if (!state.initialized)
155
+ throw new Error("Internal error");
156
+ if (crashError.error)
157
+ throw crashError.error;
158
+ let retVal;
159
+ try {
160
+ const encoded = new TextEncoder().encode(request);
161
+ const ptr = state.instance.exports.alloc(encoded.length) >>> 0;
162
+ new Uint8Array(state.instance.exports.memory.buffer).set(encoded, ptr);
163
+ retVal = state.instance.exports.json_rpc_send(ptr, encoded.length, chainId) >>> 0;
164
+ }
165
+ catch (_error) {
166
+ console.assert(crashError.error);
167
+ throw crashError.error;
168
+ }
169
+ switch (retVal) {
170
+ case 0: break;
171
+ case 1: throw new MalformedJsonRpcError();
172
+ case 2: throw new QueueFullError();
173
+ default: throw new Error("Internal error: unknown json_rpc_send error code: " + retVal);
174
+ }
175
+ },
176
+ nextJsonRpcResponse: (chainId) => __awaiter(this, void 0, void 0, function* () {
177
+ // Because `nextJsonRpcResponse` is passed as parameter an identifier returned by `addChain`,
178
+ // it is always the case that the Wasm instance is already initialized. The only possibility
179
+ // for it to not be the case is if the user completely invented the `chainId`.
180
+ if (!state.initialized)
181
+ throw new Error("Internal error");
182
+ while (true) {
183
+ if (crashError.error)
184
+ throw crashError.error;
185
+ // Try to pop a message from the queue.
186
+ try {
187
+ const mem = new Uint8Array(state.instance.exports.memory.buffer);
188
+ const responseInfo = state.instance.exports.json_rpc_responses_peek(chainId) >>> 0;
189
+ const ptr = buffer.readUInt32LE(mem, responseInfo) >>> 0;
190
+ const len = buffer.readUInt32LE(mem, responseInfo + 4) >>> 0;
191
+ // `len === 0` means "queue is empty" according to the API.
192
+ // In that situation, queue the resolve/reject.
193
+ if (len !== 0) {
194
+ const message = buffer.utf8BytesToString(mem, ptr, len);
195
+ state.instance.exports.json_rpc_responses_pop(chainId);
196
+ return message;
197
+ }
198
+ }
199
+ catch (_error) {
200
+ console.assert(crashError.error);
201
+ throw crashError.error;
202
+ }
203
+ // If no message is available, wait for one to be.
204
+ yield new Promise((resolve, reject) => {
205
+ chains.get(chainId).jsonRpcResponsesPromises.push({ resolve: () => resolve(undefined), reject });
206
+ });
207
+ }
208
+ }),
209
+ addChain: (chainSpec, databaseContent, potentialRelayChains, disableJsonRpc) => {
210
+ return queueOperation((instance) => {
211
+ if (crashError.error)
212
+ throw crashError.error;
213
+ try {
214
+ // Write the chain specification into memory.
215
+ const chainSpecEncoded = new TextEncoder().encode(chainSpec);
216
+ const chainSpecPtr = instance.exports.alloc(chainSpecEncoded.length) >>> 0;
217
+ new Uint8Array(instance.exports.memory.buffer).set(chainSpecEncoded, chainSpecPtr);
218
+ // Write the database content into memory.
219
+ const databaseContentEncoded = new TextEncoder().encode(databaseContent);
220
+ const databaseContentPtr = instance.exports.alloc(databaseContentEncoded.length) >>> 0;
221
+ new Uint8Array(instance.exports.memory.buffer).set(databaseContentEncoded, databaseContentPtr);
222
+ // Write the potential relay chains into memory.
223
+ const potentialRelayChainsLen = potentialRelayChains.length;
224
+ const potentialRelayChainsPtr = instance.exports.alloc(potentialRelayChainsLen * 4) >>> 0;
225
+ for (let idx = 0; idx < potentialRelayChains.length; ++idx) {
226
+ buffer.writeUInt32LE(new Uint8Array(instance.exports.memory.buffer), potentialRelayChainsPtr + idx * 4, potentialRelayChains[idx]);
227
+ }
228
+ // `add_chain` unconditionally allocates a chain id. If an error occurs, however, this chain
229
+ // id will refer to an *erroneous* chain. `chain_is_ok` is used below to determine whether it
230
+ // has succeeeded or not.
231
+ // Note that `add_chain` properly de-allocates buffers even if it failed.
232
+ const chainId = instance.exports.add_chain(chainSpecPtr, chainSpecEncoded.length, databaseContentPtr, databaseContentEncoded.length, disableJsonRpc ? 0 : 1, potentialRelayChainsPtr, potentialRelayChainsLen);
233
+ if (instance.exports.chain_is_ok(chainId) != 0) {
234
+ console.assert(!chains.has(chainId));
235
+ chains.set(chainId, {
236
+ jsonRpcResponsesPromises: new Array()
237
+ });
238
+ return { success: true, chainId };
239
+ }
240
+ else {
241
+ const errorMsgLen = instance.exports.chain_error_len(chainId) >>> 0;
242
+ const errorMsgPtr = instance.exports.chain_error_ptr(chainId) >>> 0;
243
+ const errorMsg = buffer.utf8BytesToString(new Uint8Array(instance.exports.memory.buffer), errorMsgPtr, errorMsgLen);
244
+ instance.exports.remove_chain(chainId);
245
+ return { success: false, error: errorMsg };
246
+ }
247
+ }
248
+ catch (_error) {
249
+ console.assert(crashError.error);
250
+ throw crashError.error;
251
+ }
252
+ });
253
+ },
254
+ removeChain: (chainId) => {
255
+ // Because `removeChain` is passed as parameter an identifier returned by `addChain`, it is
256
+ // always the case that the Wasm instance is already initialized. The only possibility for
257
+ // it to not be the case is if the user completely invented the `chainId`.
258
+ if (!state.initialized)
259
+ throw new Error("Internal error");
260
+ if (crashError.error)
261
+ throw crashError.error;
262
+ // Removing the chain synchronously avoids having to deal with race conditions such as a
263
+ // JSON-RPC response corresponding to a chain that is going to be deleted but hasn't been yet.
264
+ // These kind of race conditions are already delt with within smoldot.
265
+ console.assert(chains.has(chainId));
266
+ for (const { reject } of chains.get(chainId).jsonRpcResponsesPromises) {
267
+ reject(new client_js_1.AlreadyDestroyedError());
268
+ }
269
+ chains.delete(chainId);
270
+ try {
271
+ state.instance.exports.remove_chain(chainId);
272
+ }
273
+ catch (_error) {
274
+ console.assert(crashError.error);
275
+ throw crashError.error;
276
+ }
277
+ },
278
+ startShutdown: () => {
279
+ return queueOperation((instance) => {
280
+ // `startShutdown` is a bit special in its handling of crashes.
281
+ // Shutting down will lead to `onWasmPanic` being called at some point, possibly during
282
+ // the call to `start_shutdown` itself. As such, we move into "don't print errors anymore"
283
+ // mode even before calling `start_shutdown`.
284
+ //
285
+ // Furthermore, if a crash happened in the past, there is no point in throwing an
286
+ // exception when the user wants the shutdown to happen.
287
+ if (crashError.error)
288
+ return;
289
+ if (state.initialized)
290
+ state.unregisterCallback();
291
+ try {
292
+ printError.printError = false;
293
+ instance.exports.start_shutdown();
294
+ }
295
+ catch (_error) {
296
+ }
297
+ });
298
+ }
299
+ };
300
+ }
301
+ exports.start = start;
@@ -0,0 +1,65 @@
1
+ import { ConnectionConfig, Connection } from './bindings-smoldot-light.js';
2
+ import { SmoldotWasmInstance } from './bindings.js';
3
+ export { ConnectionConfig, ConnectionError, Connection } from './bindings-smoldot-light.js';
4
+ export interface Config {
5
+ /**
6
+ * Closure to call when the Wasm instance panics.
7
+ *
8
+ * This callback will always be invoked from within a binding called the Wasm instance.
9
+ *
10
+ * After this callback has been called, it is forbidden to invoke any function from the Wasm
11
+ * VM.
12
+ *
13
+ * If this callback is called while invoking a function from the Wasm VM, this function will
14
+ * throw a dummy exception.
15
+ */
16
+ onWasmPanic: (message: string) => void;
17
+ logCallback: (level: number, target: string, message: string) => void;
18
+ jsonRpcResponsesNonEmptyCallback: (chainId: number) => void;
19
+ currentTaskCallback?: (taskName: string | null) => void;
20
+ cpuRateLimit: number;
21
+ }
22
+ /**
23
+ * Contains functions that the client will use when it needs to leverage the platform.
24
+ */
25
+ export interface PlatformBindings {
26
+ /**
27
+ * Base64-decode the given buffer then decompress its content using the inflate algorithm
28
+ * with zlib header.
29
+ *
30
+ * The input is considered trusted. In other words, the implementation doesn't have to
31
+ * resist malicious input.
32
+ *
33
+ * This function is asynchronous because implementations might use the compression streams
34
+ * Web API, which for whatever reason is asynchronous.
35
+ */
36
+ trustedBase64DecodeAndZlibInflate: (input: string) => Promise<Uint8Array>;
37
+ /**
38
+ * Returns the number of milliseconds since an arbitrary epoch.
39
+ */
40
+ performanceNow: () => number;
41
+ /**
42
+ * Fills the given buffer with randomly-generated bytes.
43
+ */
44
+ getRandomValues: (buffer: Uint8Array) => void;
45
+ /**
46
+ * The "should periodically yield" setting indicates whether the execution should periodically
47
+ * yield back control by using `setTimeout(..., 0)`.
48
+ *
49
+ * On platforms such as browsers where `setTimeout(..., 0)` takes way more than 0 milliseconds,
50
+ * this setting should be set to `false`.
51
+ *
52
+ * This function registers a callback that is called when the setting should be updated with
53
+ * a new value. It returns the initial value of the setting and a function used to unregister
54
+ * the callback.
55
+ */
56
+ registerShouldPeriodicallyYield: (callback: (newValue: boolean) => void) => [boolean, () => void];
57
+ /**
58
+ * Tries to open a new connection using the given configuration.
59
+ *
60
+ * @see Connection
61
+ * @throws {@link ConnectionError} If the multiaddress couldn't be parsed or contains an invalid protocol.
62
+ */
63
+ connect(config: ConnectionConfig): Connection;
64
+ }
65
+ export declare function startInstance(config: Config, platformBindings: PlatformBindings): Promise<SmoldotWasmInstance>;
@@ -0,0 +1,72 @@
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.startInstance = exports.ConnectionError = 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 bindings_smoldot_light_js_1 = require("./bindings-smoldot-light.js");
27
+ const bindings_wasi_js_1 = require("./bindings-wasi.js");
28
+ const wasm_js_1 = require("./autogen/wasm.js");
29
+ var bindings_smoldot_light_js_2 = require("./bindings-smoldot-light.js");
30
+ Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return bindings_smoldot_light_js_2.ConnectionError; } });
31
+ function startInstance(config, platformBindings) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ // The actual Wasm bytecode is base64-decoded then deflate-decoded from a constant found in a
34
+ // different file.
35
+ // This is suboptimal compared to using `instantiateStreaming`, but it is the most
36
+ // cross-platform cross-bundler approach.
37
+ const wasmBytecode = yield platformBindings.trustedBase64DecodeAndZlibInflate(wasm_js_1.default);
38
+ let killAll;
39
+ // Used to bind with the smoldot-light bindings. See the `bindings-smoldot-light.js` file.
40
+ const smoldotJsConfig = Object.assign({ performanceNow: platformBindings.performanceNow, connect: platformBindings.connect, onPanic: (message) => {
41
+ killAll();
42
+ config.onWasmPanic(message);
43
+ throw new Error();
44
+ } }, config);
45
+ // Used to bind with the Wasi bindings. See the `bindings-wasi.js` file.
46
+ const wasiConfig = {
47
+ envVars: [],
48
+ getRandomValues: platformBindings.getRandomValues,
49
+ onProcExit: (retCode) => {
50
+ killAll();
51
+ config.onWasmPanic(`proc_exit called: ${retCode}`);
52
+ throw new Error();
53
+ }
54
+ };
55
+ const { imports: smoldotBindings, killAll: smoldotBindingsKillAll } = (0, bindings_smoldot_light_js_1.default)(smoldotJsConfig);
56
+ killAll = smoldotBindingsKillAll;
57
+ // Start the Wasm virtual machine.
58
+ // The Rust code defines a list of imports that must be fulfilled by the environment. The second
59
+ // parameter provides their implementations.
60
+ const result = yield WebAssembly.instantiate(wasmBytecode, {
61
+ // The functions with the "smoldot" prefix are specific to smoldot.
62
+ "smoldot": smoldotBindings,
63
+ // As the Rust code is compiled for wasi, some more wasi-specific imports exist.
64
+ "wasi_snapshot_preview1": (0, bindings_wasi_js_1.default)(wasiConfig),
65
+ });
66
+ const instance = result.instance;
67
+ smoldotJsConfig.instance = instance;
68
+ wasiConfig.instance = instance;
69
+ return instance;
70
+ });
71
+ }
72
+ exports.startInstance = startInstance;
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -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,97 @@
1
+ // Smoldot
2
+ // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
3
+ // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
+ // This program is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ // This program is distributed in the hope that it will be useful,
9
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ // GNU General Public License for more details.
12
+ // You should have received a copy of the GNU General Public License
13
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
14
+ let rfc4648Alphabet = new Map();
15
+ const rfc4648AlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16
+ for (let i = 0; i < rfc4648AlphabetAsStr.length; ++i) {
17
+ rfc4648Alphabet.set(rfc4648AlphabetAsStr[i], i);
18
+ }
19
+ let urlSafeAlphabet = new Map();
20
+ const urlSafeAlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
21
+ for (let i = 0; i < urlSafeAlphabetAsStr.length; ++i) {
22
+ urlSafeAlphabet.set(urlSafeAlphabetAsStr[i], i);
23
+ }
24
+ /**
25
+ * Decodes a multibase-encoded string.
26
+ *
27
+ * Throws an exception if the encoding isn't base64 or one of its variants.
28
+ */
29
+ export function multibaseBase64Decode(input) {
30
+ if (input.length === 0)
31
+ throw new Error("Invalid multibase");
32
+ switch (input[0]) {
33
+ case 'm':
34
+ case 'M':
35
+ return classicDecode(input.slice(1));
36
+ case 'u':
37
+ case 'U':
38
+ return urlSafeDecode(input.slice(1));
39
+ default:
40
+ throw new Error('Unknown multibase prefix: ' + input[0]);
41
+ }
42
+ }
43
+ /**
44
+ * Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
45
+ *
46
+ * See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
47
+ */
48
+ export function classicDecode(input) {
49
+ return base64Decode(input, rfc4648Alphabet);
50
+ }
51
+ /**
52
+ * Decodes a base64-encoded string into bytes using the URL-safe alphabet.
53
+ *
54
+ * See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
55
+ */
56
+ export function urlSafeDecode(input) {
57
+ return base64Decode(input, urlSafeAlphabet);
58
+ }
59
+ /**
60
+ * Decodes a base64-encoded string into bytes using the given alphabet.
61
+ */
62
+ export function base64Decode(input, alphabet) {
63
+ // Remove the padding bytes at the end of the string. We don't check whether the padding is
64
+ // accurate.
65
+ while (input.length !== 0 && input[input.length - 1] === '=')
66
+ input = input.slice(0, -1);
67
+ // Contains the output data.
68
+ const out = new Uint8Array(Math.floor(input.length * 6 / 8));
69
+ // Position within `out` of the next byte to write.
70
+ let outPos = 0;
71
+ // The bits decoded from the input are added to the right of this value.
72
+ let currentByte = 0;
73
+ // The left-most `validBitsInCurrentByte` bits of `currentByte` must be written out.
74
+ let validBitsInCurrentByte = 0;
75
+ for (let i = 0; i < input.length; ++i) {
76
+ const inputChr = input[i];
77
+ const bitsToAppend = alphabet.get(inputChr);
78
+ if (bitsToAppend === undefined)
79
+ throw new Error('Invalid base64 character: ' + inputChr);
80
+ console.assert(bitsToAppend < (1 << 6));
81
+ currentByte = (currentByte << 6) | bitsToAppend;
82
+ validBitsInCurrentByte += 6;
83
+ if (validBitsInCurrentByte >= 8) {
84
+ let outByte = currentByte >> (validBitsInCurrentByte - 8);
85
+ out[outPos] = outByte;
86
+ outPos += 1;
87
+ validBitsInCurrentByte -= 8;
88
+ }
89
+ console.assert(validBitsInCurrentByte < 8);
90
+ currentByte &= 0xff;
91
+ }
92
+ if ((currentByte & ((1 << validBitsInCurrentByte) - 1)) !== 0)
93
+ throw new Error("Unexpected EOF");
94
+ if (validBitsInCurrentByte >= 6)
95
+ throw new Error("Unexpected EOF");
96
+ return out;
97
+ }