smoldot 1.0.2 → 1.0.4
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/dist/cjs/client.d.ts +1 -1
- package/dist/cjs/client.js +15 -18
- package/dist/cjs/index-browser.js +17 -5
- package/dist/cjs/index-deno.js +38 -28
- package/dist/cjs/index-nodejs.js +7 -4
- package/dist/cjs/instance/autogen/wasm0.js +1 -1
- package/dist/cjs/instance/autogen/wasm1.js +1 -1
- package/dist/cjs/instance/autogen/wasm2.js +1 -1
- package/dist/cjs/instance/bindings-smoldot-light.d.ts +7 -6
- package/dist/cjs/instance/bindings-smoldot-light.js +6 -7
- package/dist/cjs/instance/bindings-wasi.d.ts +4 -0
- package/dist/cjs/instance/bindings-wasi.js +30 -0
- package/dist/cjs/instance/bindings.d.ts +3 -3
- package/dist/cjs/instance/buffer.d.ts +1 -0
- package/dist/cjs/instance/buffer.js +13 -1
- package/dist/cjs/instance/instance.d.ts +1 -1
- package/dist/cjs/instance/instance.js +41 -59
- package/dist/cjs/instance/raw-instance.d.ts +2 -11
- package/dist/cjs/instance/raw-instance.js +65 -9
- package/dist/mjs/client.d.ts +1 -1
- package/dist/mjs/client.js +15 -18
- package/dist/mjs/index-browser.js +17 -5
- package/dist/mjs/index-deno.js +38 -28
- package/dist/mjs/index-nodejs.js +7 -4
- package/dist/mjs/instance/autogen/wasm0.js +1 -1
- package/dist/mjs/instance/autogen/wasm1.js +1 -1
- package/dist/mjs/instance/autogen/wasm2.js +1 -1
- package/dist/mjs/instance/bindings-smoldot-light.d.ts +7 -6
- package/dist/mjs/instance/bindings-smoldot-light.js +6 -7
- package/dist/mjs/instance/bindings-wasi.d.ts +4 -0
- package/dist/mjs/instance/bindings-wasi.js +30 -0
- package/dist/mjs/instance/bindings.d.ts +3 -3
- package/dist/mjs/instance/buffer.d.ts +1 -0
- package/dist/mjs/instance/buffer.js +11 -0
- package/dist/mjs/instance/instance.d.ts +1 -1
- package/dist/mjs/instance/instance.js +41 -59
- package/dist/mjs/instance/raw-instance.d.ts +2 -11
- package/dist/mjs/instance/raw-instance.js +65 -9
- package/package.json +2 -2
|
@@ -7,10 +7,6 @@ export interface Config {
|
|
|
7
7
|
* When `buffer_size` or `buffer_index` are called, the buffer is found here.
|
|
8
8
|
*/
|
|
9
9
|
bufferIndices: Array<Uint8Array>;
|
|
10
|
-
/**
|
|
11
|
-
* Returns the number of milliseconds since an arbitrary epoch.
|
|
12
|
-
*/
|
|
13
|
-
performanceNow: () => number;
|
|
14
10
|
/**
|
|
15
11
|
* Tries to open a new connection using the given configuration.
|
|
16
12
|
*
|
|
@@ -26,6 +22,7 @@ export interface Config {
|
|
|
26
22
|
onPanic: (message: string) => never;
|
|
27
23
|
logCallback: (level: number, target: string, message: string) => void;
|
|
28
24
|
jsonRpcResponsesNonEmptyCallback: (chainId: number) => void;
|
|
25
|
+
advanceExecutionReadyCallback: () => void;
|
|
29
26
|
currentTaskCallback?: (taskName: string | null) => void;
|
|
30
27
|
}
|
|
31
28
|
/**
|
|
@@ -71,7 +68,7 @@ export interface Connection {
|
|
|
71
68
|
*
|
|
72
69
|
* The connection and stream must currently be in the `Open` state.
|
|
73
70
|
*
|
|
74
|
-
* The number of bytes
|
|
71
|
+
* The number of bytes must never exceed the number of "writable bytes" of the stream.
|
|
75
72
|
* `onWritableBytes` can be used in order to notify that more writable bytes are available.
|
|
76
73
|
*
|
|
77
74
|
* The `streamId` must be provided if and only if the connection is of type "multi-stream".
|
|
@@ -160,7 +157,8 @@ export interface ConnectionConfig {
|
|
|
160
157
|
*/
|
|
161
158
|
onStreamReset: (streamId: number) => void;
|
|
162
159
|
/**
|
|
163
|
-
* Callback called when
|
|
160
|
+
* Callback called when some data sent using {@link Connection.send} has effectively been
|
|
161
|
+
* written on the stream, meaning that some buffer space is now free.
|
|
164
162
|
*
|
|
165
163
|
* Can only happen while the connection is in the `Open` state.
|
|
166
164
|
*
|
|
@@ -168,6 +166,9 @@ export interface ConnectionConfig {
|
|
|
168
166
|
*
|
|
169
167
|
* The `streamId` parameter must be provided if and only if the connection is of type
|
|
170
168
|
* "multi-stream".
|
|
169
|
+
*
|
|
170
|
+
* Only a number of bytes equal to the size of the data provided to {@link Connection.send}
|
|
171
|
+
* must be reported. In other words, the `initialWritableBytes` must never be exceeded.
|
|
171
172
|
*/
|
|
172
173
|
onWritableBytes: (numExtra: number, streamId?: number) => void;
|
|
173
174
|
/**
|
|
@@ -61,6 +61,9 @@ export default function (config) {
|
|
|
61
61
|
const buf = config.bufferIndices[bufferIndex];
|
|
62
62
|
new Uint8Array(instance.exports.memory.buffer).set(buf, targetPtr);
|
|
63
63
|
},
|
|
64
|
+
advance_execution_ready: () => {
|
|
65
|
+
config.advanceExecutionReadyCallback();
|
|
66
|
+
},
|
|
64
67
|
// Used by the Rust side to notify that a JSON-RPC response or subscription notification
|
|
65
68
|
// is available in the queue of JSON-RPC responses.
|
|
66
69
|
json_rpc_responses_non_empty: (chainId) => {
|
|
@@ -85,12 +88,8 @@ export default function (config) {
|
|
|
85
88
|
config.logCallback(level, target, message);
|
|
86
89
|
}
|
|
87
90
|
},
|
|
88
|
-
// Must return the UNIX time in milliseconds.
|
|
89
|
-
unix_time_ms: () => Date.now(),
|
|
90
|
-
// Must return the value of a monotonic clock in milliseconds.
|
|
91
|
-
monotonic_clock_ms: () => config.performanceNow(),
|
|
92
91
|
// Must call `timer_finished` after the given number of milliseconds has elapsed.
|
|
93
|
-
start_timer: (
|
|
92
|
+
start_timer: (ms) => {
|
|
94
93
|
if (killedTracked.killed)
|
|
95
94
|
return;
|
|
96
95
|
const instance = config.instance;
|
|
@@ -108,7 +107,7 @@ export default function (config) {
|
|
|
108
107
|
if (killedTracked.killed)
|
|
109
108
|
return;
|
|
110
109
|
try {
|
|
111
|
-
instance.exports.timer_finished(
|
|
110
|
+
instance.exports.timer_finished();
|
|
112
111
|
}
|
|
113
112
|
catch (_error) { }
|
|
114
113
|
});
|
|
@@ -118,7 +117,7 @@ export default function (config) {
|
|
|
118
117
|
if (killedTracked.killed)
|
|
119
118
|
return;
|
|
120
119
|
try {
|
|
121
|
-
instance.exports.timer_finished(
|
|
120
|
+
instance.exports.timer_finished();
|
|
122
121
|
}
|
|
123
122
|
catch (_error) { }
|
|
124
123
|
}, ms);
|
|
@@ -5,6 +5,10 @@ export interface Config {
|
|
|
5
5
|
* Fills the given buffer with randomly-generated bytes.
|
|
6
6
|
*/
|
|
7
7
|
getRandomValues: (buffer: Uint8Array) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the number of milliseconds since an arbitrary epoch.
|
|
10
|
+
*/
|
|
11
|
+
performanceNow: () => number;
|
|
8
12
|
/**
|
|
9
13
|
* List of environment variables to feed to the Rust program. An array of strings.
|
|
10
14
|
* Example: `["RUST_BACKTRACE=1", "RUST_LOG=foo"];`
|
|
@@ -39,6 +39,36 @@ export default (config) => {
|
|
|
39
39
|
}
|
|
40
40
|
return 0;
|
|
41
41
|
},
|
|
42
|
+
clock_time_get: (clockId, _precision, outPtr) => {
|
|
43
|
+
// See <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/wasi/time.rs>
|
|
44
|
+
// and <docs.rs/wasi/> for help.
|
|
45
|
+
const instance = config.instance;
|
|
46
|
+
const mem = new Uint8Array(instance.exports.memory.buffer);
|
|
47
|
+
outPtr >>>= 0;
|
|
48
|
+
// We ignore the precision, as it can't be implemented anyway.
|
|
49
|
+
switch (clockId) {
|
|
50
|
+
case 0: {
|
|
51
|
+
// Realtime clock.
|
|
52
|
+
const now = BigInt(Math.floor(Date.now())) * BigInt(1000000);
|
|
53
|
+
buffer.writeUInt64LE(mem, outPtr, now);
|
|
54
|
+
// Success.
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
case 1: {
|
|
58
|
+
// Monotonic clock.
|
|
59
|
+
const nowMs = config.performanceNow();
|
|
60
|
+
const nowMsInt = Math.floor(nowMs);
|
|
61
|
+
const now = BigInt(nowMsInt) * BigInt(1000000) +
|
|
62
|
+
BigInt(Math.floor(((nowMs - nowMsInt) * 1000000)));
|
|
63
|
+
buffer.writeUInt64LE(mem, outPtr, now);
|
|
64
|
+
// Success.
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
// Return an `EINVAL` error.
|
|
69
|
+
return 28;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
42
72
|
// Writing to a file descriptor is used in order to write to stdout/stderr.
|
|
43
73
|
fd_write: (fd, addr, num, outPtr) => {
|
|
44
74
|
const instance = config.instance;
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export interface SmoldotWasmExports extends WebAssembly.Exports {
|
|
8
8
|
memory: WebAssembly.Memory;
|
|
9
|
-
init: (maxLogLevel: number
|
|
10
|
-
|
|
9
|
+
init: (maxLogLevel: number) => void;
|
|
10
|
+
advance_execution: () => number;
|
|
11
11
|
start_shutdown: () => void;
|
|
12
12
|
add_chain: (chainSpecBufferIndex: number, databaseContentBufferIndex: number, jsonRpcRunning: number, potentialRelayChainsBufferIndex: number) => number;
|
|
13
13
|
remove_chain: (chainId: number) => void;
|
|
@@ -17,7 +17,7 @@ export interface SmoldotWasmExports extends WebAssembly.Exports {
|
|
|
17
17
|
json_rpc_send: (textBufferIndex: number, chainId: number) => number;
|
|
18
18
|
json_rpc_responses_peek: (chainId: number) => number;
|
|
19
19
|
json_rpc_responses_pop: (chainId: number) => void;
|
|
20
|
-
timer_finished: (
|
|
20
|
+
timer_finished: () => void;
|
|
21
21
|
connection_open_single_stream: (connectionId: number, handshakeTy: number, initialWritableBytes: number, writeClosable: number) => void;
|
|
22
22
|
connection_open_multi_stream: (connectionId: number, handshakeTyBufferIndex: number) => void;
|
|
23
23
|
stream_writable_bytes: (connectionId: number, streamId: number, numBytes: number) => void;
|
|
@@ -8,3 +8,4 @@ export declare function readUInt32LE(buffer: Uint8Array, offset: number): number
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function writeUInt8(buffer: Uint8Array, offset: number, value: number): void;
|
|
10
10
|
export declare function writeUInt32LE(buffer: Uint8Array, offset: number, value: number): void;
|
|
11
|
+
export declare function writeUInt64LE(buffer: Uint8Array, offset: number, value: bigint): void;
|
|
@@ -38,6 +38,17 @@ export function writeUInt32LE(buffer, offset, value) {
|
|
|
38
38
|
buffer[offset + 1] = (value >>> 8) & 0xff;
|
|
39
39
|
buffer[offset] = value & 0xff;
|
|
40
40
|
}
|
|
41
|
+
export function writeUInt64LE(buffer, offset, value) {
|
|
42
|
+
checkRange(buffer, offset, 8);
|
|
43
|
+
buffer[offset + 7] = Number((value >> BigInt(56)) & BigInt(0xff));
|
|
44
|
+
buffer[offset + 6] = Number((value >> BigInt(48)) & BigInt(0xff));
|
|
45
|
+
buffer[offset + 5] = Number((value >> BigInt(40)) & BigInt(0xff));
|
|
46
|
+
buffer[offset + 4] = Number((value >> BigInt(32)) & BigInt(0xff));
|
|
47
|
+
buffer[offset + 3] = Number((value >> BigInt(24)) & BigInt(0xff));
|
|
48
|
+
buffer[offset + 2] = Number((value >> BigInt(16)) & BigInt(0xff));
|
|
49
|
+
buffer[offset + 1] = Number((value >> BigInt(8)) & BigInt(0xff));
|
|
50
|
+
buffer[offset] = Number(value & BigInt(0xff));
|
|
51
|
+
}
|
|
41
52
|
function checkRange(buffer, offset, length) {
|
|
42
53
|
if (!Number.isInteger(offset) || offset < 0)
|
|
43
54
|
throw new RangeError();
|
|
@@ -24,9 +24,9 @@ export declare class QueueFullError extends Error {
|
|
|
24
24
|
* Contains the configuration of the instance.
|
|
25
25
|
*/
|
|
26
26
|
export interface Config {
|
|
27
|
+
wasmModule: Promise<WebAssembly.Module>;
|
|
27
28
|
logCallback: (level: number, target: string, message: string) => void;
|
|
28
29
|
maxLogLevel: number;
|
|
29
|
-
enableCurrentTask: boolean;
|
|
30
30
|
cpuRateLimit: number;
|
|
31
31
|
}
|
|
32
32
|
export interface Instance {
|
|
@@ -62,65 +62,49 @@ export function start(configMessage, platformBindings) {
|
|
|
62
62
|
const printError = { printError: true };
|
|
63
63
|
// Contains the information of each chain that is currently alive.
|
|
64
64
|
let chains = new Map();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
logCallback: (level, target, message) => {
|
|
85
|
-
configMessage.logCallback(level, target, message);
|
|
86
|
-
},
|
|
87
|
-
jsonRpcResponsesNonEmptyCallback: (chainId) => {
|
|
88
|
-
// Notify every single promise found in `jsonRpcResponsesPromises`.
|
|
89
|
-
const promises = chains.get(chainId).jsonRpcResponsesPromises;
|
|
90
|
-
while (promises.length !== 0) {
|
|
91
|
-
promises.shift().resolve();
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
currentTaskCallback: (taskName) => {
|
|
95
|
-
currentTask.name = taskName;
|
|
96
|
-
},
|
|
97
|
-
cpuRateLimit: configMessage.cpuRateLimit,
|
|
98
|
-
};
|
|
99
|
-
state = {
|
|
100
|
-
initialized: false, promise: instance.startInstance(config, platformBindings).then(([instance, bufferIndices]) => {
|
|
101
|
-
// `config.cpuRateLimit` is a floating point that should be between 0 and 1, while the value
|
|
102
|
-
// to pass as parameter must be between `0` and `2^32-1`.
|
|
103
|
-
// The few lines of code below should handle all possible values of `number`, including
|
|
104
|
-
// infinites and NaN.
|
|
105
|
-
let cpuRateLimit = Math.round(config.cpuRateLimit * 4294967295); // `2^32 - 1`
|
|
106
|
-
if (cpuRateLimit < 0)
|
|
107
|
-
cpuRateLimit = 0;
|
|
108
|
-
if (cpuRateLimit > 4294967295)
|
|
109
|
-
cpuRateLimit = 4294967295;
|
|
110
|
-
if (!Number.isFinite(cpuRateLimit))
|
|
111
|
-
cpuRateLimit = 4294967295; // User might have passed NaN
|
|
112
|
-
// Smoldot requires an initial call to the `init` function in order to do its internal
|
|
113
|
-
// configuration.
|
|
114
|
-
const [periodicallyYield, unregisterCallback] = platformBindings.registerShouldPeriodicallyYield((newValue) => {
|
|
115
|
-
if (state.initialized && !crashError.error) {
|
|
116
|
-
try {
|
|
117
|
-
state.instance.exports.set_periodically_yield(newValue ? 1 : 0);
|
|
65
|
+
const initPromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
const module = yield configMessage.wasmModule;
|
|
67
|
+
// Start initialization of the Wasm VM.
|
|
68
|
+
const config = {
|
|
69
|
+
onWasmPanic: (message) => {
|
|
70
|
+
// TODO: consider obtaining a backtrace here
|
|
71
|
+
crashError.error = new CrashError(message);
|
|
72
|
+
if (!printError.printError)
|
|
73
|
+
return;
|
|
74
|
+
console.error("Smoldot has panicked" +
|
|
75
|
+
(currentTask.name ? (" while executing task `" + currentTask.name + "`") : "") +
|
|
76
|
+
". This is a bug in smoldot. Please open an issue at " +
|
|
77
|
+
"https://github.com/smol-dot/smoldot/issues with the following message:\n" +
|
|
78
|
+
message);
|
|
79
|
+
for (const chain of Array.from(chains.values())) {
|
|
80
|
+
for (const promise of chain.jsonRpcResponsesPromises) {
|
|
81
|
+
promise.reject(crashError.error);
|
|
118
82
|
}
|
|
119
|
-
|
|
83
|
+
chain.jsonRpcResponsesPromises = [];
|
|
120
84
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
85
|
+
},
|
|
86
|
+
logCallback: (level, target, message) => {
|
|
87
|
+
configMessage.logCallback(level, target, message);
|
|
88
|
+
},
|
|
89
|
+
wasmModule: module,
|
|
90
|
+
jsonRpcResponsesNonEmptyCallback: (chainId) => {
|
|
91
|
+
// Notify every single promise found in `jsonRpcResponsesPromises`.
|
|
92
|
+
const promises = chains.get(chainId).jsonRpcResponsesPromises;
|
|
93
|
+
while (promises.length !== 0) {
|
|
94
|
+
promises.shift().resolve();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
currentTaskCallback: (taskName) => {
|
|
98
|
+
currentTask.name = taskName;
|
|
99
|
+
},
|
|
100
|
+
cpuRateLimit: configMessage.cpuRateLimit,
|
|
101
|
+
maxLogLevel: configMessage.maxLogLevel,
|
|
102
|
+
};
|
|
103
|
+
return yield instance.startInstance(config, platformBindings);
|
|
104
|
+
}))();
|
|
105
|
+
state = {
|
|
106
|
+
initialized: false, promise: initPromise.then(([instance, bufferIndices]) => {
|
|
107
|
+
state = { initialized: true, instance, bufferIndices };
|
|
124
108
|
return [instance, bufferIndices];
|
|
125
109
|
})
|
|
126
110
|
};
|
|
@@ -273,8 +257,6 @@ export function start(configMessage, platformBindings) {
|
|
|
273
257
|
// exception when the user wants the shutdown to happen.
|
|
274
258
|
if (crashError.error)
|
|
275
259
|
return;
|
|
276
|
-
if (state.initialized)
|
|
277
|
-
state.unregisterCallback();
|
|
278
260
|
try {
|
|
279
261
|
printError.printError = false;
|
|
280
262
|
instance.exports.start_shutdown();
|
|
@@ -15,25 +15,16 @@ export interface Config {
|
|
|
15
15
|
*/
|
|
16
16
|
onWasmPanic: (message: string) => void;
|
|
17
17
|
logCallback: (level: number, target: string, message: string) => void;
|
|
18
|
+
wasmModule: WebAssembly.Module;
|
|
18
19
|
jsonRpcResponsesNonEmptyCallback: (chainId: number) => void;
|
|
19
20
|
currentTaskCallback?: (taskName: string | null) => void;
|
|
21
|
+
maxLogLevel: number;
|
|
20
22
|
cpuRateLimit: number;
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
23
25
|
* Contains functions that the client will use when it needs to leverage the platform.
|
|
24
26
|
*/
|
|
25
27
|
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
28
|
/**
|
|
38
29
|
* Returns the number of milliseconds since an arbitrary epoch.
|
|
39
30
|
*/
|
|
@@ -22,27 +22,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
22
22
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
23
23
|
import { default as smoldotLightBindingsBuilder } from './bindings-smoldot-light.js';
|
|
24
24
|
import { default as wasiBindingsBuilder } from './bindings-wasi.js';
|
|
25
|
-
import { default as wasmBase64 } from './autogen/wasm.js';
|
|
26
25
|
export { ConnectionError } from './bindings-smoldot-light.js';
|
|
27
26
|
export function startInstance(config, platformBindings) {
|
|
28
27
|
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
-
// The actual Wasm bytecode is base64-decoded then deflate-decoded from a constant found in a
|
|
30
|
-
// different file.
|
|
31
|
-
// This is suboptimal compared to using `instantiateStreaming`, but it is the most
|
|
32
|
-
// cross-platform cross-bundler approach.
|
|
33
|
-
const wasmBytecode = yield platformBindings.trustedBase64DecodeAndZlibInflate(wasmBase64);
|
|
34
28
|
let killAll;
|
|
35
29
|
const bufferIndices = new Array;
|
|
30
|
+
// Callback called when `advance_execution_ready` is called by the Rust code, if any.
|
|
31
|
+
const advanceExecutionPromise = { value: null };
|
|
36
32
|
// Used to bind with the smoldot-light bindings. See the `bindings-smoldot-light.js` file.
|
|
37
|
-
const smoldotJsConfig = Object.assign({ bufferIndices,
|
|
33
|
+
const smoldotJsConfig = Object.assign({ bufferIndices, connect: platformBindings.connect, onPanic: (message) => {
|
|
38
34
|
killAll();
|
|
39
35
|
config.onWasmPanic(message);
|
|
40
36
|
throw new Error();
|
|
37
|
+
}, advanceExecutionReadyCallback: () => {
|
|
38
|
+
if (advanceExecutionPromise.value)
|
|
39
|
+
advanceExecutionPromise.value();
|
|
40
|
+
advanceExecutionPromise.value = null;
|
|
41
41
|
} }, config);
|
|
42
42
|
// Used to bind with the Wasi bindings. See the `bindings-wasi.js` file.
|
|
43
43
|
const wasiConfig = {
|
|
44
44
|
envVars: [],
|
|
45
45
|
getRandomValues: platformBindings.getRandomValues,
|
|
46
|
+
performanceNow: platformBindings.performanceNow,
|
|
46
47
|
onProcExit: (retCode) => {
|
|
47
48
|
killAll();
|
|
48
49
|
config.onWasmPanic(`proc_exit called: ${retCode}`);
|
|
@@ -54,15 +55,70 @@ export function startInstance(config, platformBindings) {
|
|
|
54
55
|
// Start the Wasm virtual machine.
|
|
55
56
|
// The Rust code defines a list of imports that must be fulfilled by the environment. The second
|
|
56
57
|
// parameter provides their implementations.
|
|
57
|
-
const result = yield WebAssembly.instantiate(
|
|
58
|
+
const result = yield WebAssembly.instantiate(config.wasmModule, {
|
|
58
59
|
// The functions with the "smoldot" prefix are specific to smoldot.
|
|
59
60
|
"smoldot": smoldotBindings,
|
|
60
61
|
// As the Rust code is compiled for wasi, some more wasi-specific imports exist.
|
|
61
62
|
"wasi_snapshot_preview1": wasiBindingsBuilder(wasiConfig),
|
|
62
63
|
});
|
|
63
|
-
const instance = result
|
|
64
|
+
const instance = result;
|
|
64
65
|
smoldotJsConfig.instance = instance;
|
|
65
66
|
wasiConfig.instance = instance;
|
|
67
|
+
// Smoldot requires an initial call to the `init` function in order to do its internal
|
|
68
|
+
// configuration.
|
|
69
|
+
instance.exports.init(config.maxLogLevel);
|
|
70
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
// In order to avoid calling `setTimeout` too often, we accumulate sleep up until
|
|
72
|
+
// a certain threshold.
|
|
73
|
+
let missingSleep = 0;
|
|
74
|
+
// Extract (to make sure the value doesn't change) and sanitize `cpuRateLimit`.
|
|
75
|
+
let cpuRateLimit = config.cpuRateLimit;
|
|
76
|
+
if (isNaN(cpuRateLimit))
|
|
77
|
+
cpuRateLimit = 1.0;
|
|
78
|
+
if (cpuRateLimit > 1.0)
|
|
79
|
+
cpuRateLimit = 1.0;
|
|
80
|
+
if (cpuRateLimit < 0.0)
|
|
81
|
+
cpuRateLimit = 0.0;
|
|
82
|
+
const periodicallyYield = { value: false };
|
|
83
|
+
const [periodicallyYieldInit, unregisterCallback] = platformBindings.registerShouldPeriodicallyYield((newValue) => {
|
|
84
|
+
periodicallyYield.value = newValue;
|
|
85
|
+
});
|
|
86
|
+
periodicallyYield.value = periodicallyYieldInit;
|
|
87
|
+
let now = platformBindings.performanceNow();
|
|
88
|
+
while (true) {
|
|
89
|
+
const whenReadyAgain = new Promise((resolve) => advanceExecutionPromise.value = resolve);
|
|
90
|
+
const outcome = instance.exports.advance_execution();
|
|
91
|
+
if (outcome === 0) {
|
|
92
|
+
unregisterCallback();
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
const afterExec = platformBindings.performanceNow();
|
|
96
|
+
const elapsed = afterExec - now;
|
|
97
|
+
now = afterExec;
|
|
98
|
+
// In order to enforce the rate limiting, we stop executing for a certain
|
|
99
|
+
// amount of time.
|
|
100
|
+
// The base equation here is: `(sleep + elapsed) * rateLimit == elapsed`,
|
|
101
|
+
// from which the calculation below is derived.
|
|
102
|
+
const sleep = elapsed * (1.0 / cpuRateLimit - 1.0);
|
|
103
|
+
missingSleep += sleep;
|
|
104
|
+
if (missingSleep > (periodicallyYield ? 5 : 1000)) {
|
|
105
|
+
// `setTimeout` has a maximum value, after which it will overflow. 🤦
|
|
106
|
+
// See <https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value>
|
|
107
|
+
// While adding a cap technically skews the CPU rate limiting algorithm, we don't
|
|
108
|
+
// really care for such extreme values.
|
|
109
|
+
if (missingSleep > 2147483646) // Doc says `> 2147483647`, but I don't really trust their pedanticism so let's be safe
|
|
110
|
+
missingSleep = 2147483646;
|
|
111
|
+
yield new Promise((resolve) => setTimeout(resolve, missingSleep));
|
|
112
|
+
missingSleep = 0;
|
|
113
|
+
}
|
|
114
|
+
yield whenReadyAgain;
|
|
115
|
+
const afterWait = platformBindings.performanceNow();
|
|
116
|
+
missingSleep -= (afterWait - now);
|
|
117
|
+
if (missingSleep < 0)
|
|
118
|
+
missingSleep = 0;
|
|
119
|
+
now = afterWait;
|
|
120
|
+
}
|
|
121
|
+
}))();
|
|
66
122
|
return [instance, bufferIndices];
|
|
67
123
|
});
|
|
68
124
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smoldot",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Light client that connects to Polkadot and Substrate-based blockchains",
|
|
5
5
|
"author": "Parity Technologies <admin@parity.io>",
|
|
6
6
|
"license": "GPL-3.0-or-later WITH Classpath-exception-2.0",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@types/ws": "^8.5.3",
|
|
47
47
|
"ava": "^5.0.1",
|
|
48
48
|
"dtslint": "^4.0.6",
|
|
49
|
-
"rimraf": "^
|
|
49
|
+
"rimraf": "^5.0.0",
|
|
50
50
|
"typedoc": "^0.24.1",
|
|
51
51
|
"typescript": "^5.0.2"
|
|
52
52
|
}
|