smoldot 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/cjs/bytecode-browser.d.ts +5 -0
- package/dist/cjs/bytecode-browser.js +42 -0
- package/dist/cjs/bytecode-deno.d.ts +5 -0
- package/dist/cjs/bytecode-deno.js +87 -0
- package/dist/cjs/bytecode-nodejs.d.ts +5 -0
- package/dist/cjs/bytecode-nodejs.js +41 -0
- package/dist/cjs/index-browser.d.ts +2 -2
- package/dist/cjs/index-browser.js +10 -507
- package/dist/cjs/index-deno.d.ts +2 -2
- package/dist/cjs/index-deno.js +10 -283
- package/dist/cjs/index-nodejs.d.ts +2 -2
- package/dist/cjs/index-nodejs.js +10 -211
- package/dist/cjs/internals/bytecode/wasm0.js +4 -0
- package/dist/cjs/internals/bytecode/wasm1.js +4 -0
- package/dist/cjs/internals/bytecode/wasm2.js +4 -0
- package/dist/cjs/{instance/bindings-smoldot-light.d.ts → internals/client.d.ts} +15 -34
- package/dist/cjs/internals/client.js +376 -0
- package/dist/cjs/internals/local-instance.d.ts +144 -0
- package/dist/cjs/internals/local-instance.js +566 -0
- package/dist/cjs/internals/remote-instance.d.ts +38 -0
- package/dist/cjs/internals/remote-instance.js +374 -0
- package/dist/cjs/no-auto-bytecode-browser.d.ts +11 -0
- package/dist/cjs/no-auto-bytecode-browser.js +481 -0
- package/dist/cjs/no-auto-bytecode-deno.d.ts +10 -0
- package/dist/cjs/no-auto-bytecode-deno.js +220 -0
- package/dist/cjs/no-auto-bytecode-nodejs.d.ts +10 -0
- package/dist/cjs/no-auto-bytecode-nodejs.js +189 -0
- package/dist/cjs/{client.d.ts → public-types.d.ts} +51 -3
- package/dist/cjs/public-types.js +76 -0
- package/dist/cjs/worker-browser.d.ts +11 -0
- package/dist/cjs/worker-browser.js +63 -0
- package/dist/cjs/worker-deno.d.ts +11 -0
- package/dist/cjs/worker-deno.js +54 -0
- package/dist/cjs/worker-nodejs.d.ts +11 -0
- package/dist/cjs/worker-nodejs.js +55 -0
- package/dist/mjs/bytecode-browser.d.ts +5 -0
- package/dist/mjs/bytecode-browser.js +38 -0
- package/dist/mjs/bytecode-deno.d.ts +5 -0
- package/dist/mjs/bytecode-deno.js +83 -0
- package/dist/mjs/bytecode-nodejs.d.ts +5 -0
- package/dist/mjs/bytecode-nodejs.js +37 -0
- package/dist/mjs/index-browser.d.ts +2 -2
- package/dist/mjs/index-browser.js +4 -501
- package/dist/mjs/index-deno.d.ts +2 -2
- package/dist/mjs/index-deno.js +4 -277
- package/dist/mjs/index-nodejs.d.ts +2 -2
- package/dist/mjs/index-nodejs.js +4 -205
- package/dist/mjs/internals/bytecode/wasm0.js +1 -0
- package/dist/mjs/internals/bytecode/wasm1.js +1 -0
- package/dist/mjs/internals/bytecode/wasm2.js +1 -0
- package/dist/mjs/{instance/bindings-smoldot-light.d.ts → internals/client.d.ts} +15 -34
- package/dist/mjs/internals/client.js +372 -0
- package/dist/mjs/internals/local-instance.d.ts +144 -0
- package/dist/mjs/internals/local-instance.js +561 -0
- package/dist/mjs/internals/remote-instance.d.ts +38 -0
- package/dist/mjs/internals/remote-instance.js +369 -0
- package/dist/mjs/no-auto-bytecode-browser.d.ts +11 -0
- package/dist/mjs/no-auto-bytecode-browser.js +471 -0
- package/dist/mjs/no-auto-bytecode-deno.d.ts +10 -0
- package/dist/mjs/no-auto-bytecode-deno.js +210 -0
- package/dist/mjs/no-auto-bytecode-nodejs.d.ts +10 -0
- package/dist/mjs/no-auto-bytecode-nodejs.js +179 -0
- package/dist/mjs/{client.d.ts → public-types.d.ts} +51 -3
- package/dist/mjs/public-types.js +67 -0
- package/dist/mjs/worker-browser.d.ts +11 -0
- package/dist/mjs/worker-browser.js +59 -0
- package/dist/mjs/worker-deno.d.ts +11 -0
- package/dist/mjs/worker-deno.js +50 -0
- package/dist/mjs/worker-nodejs.d.ts +11 -0
- package/dist/mjs/worker-nodejs.js +51 -0
- package/package.json +40 -8
- package/dist/cjs/client.js +0 -172
- package/dist/cjs/instance/autogen/wasm0.js +0 -4
- package/dist/cjs/instance/autogen/wasm1.js +0 -4
- package/dist/cjs/instance/autogen/wasm2.js +0 -4
- package/dist/cjs/instance/bindings-smoldot-light.js +0 -285
- package/dist/cjs/instance/bindings-wasi.d.ts +0 -27
- package/dist/cjs/instance/bindings-wasi.js +0 -172
- package/dist/cjs/instance/bindings.d.ts +0 -31
- package/dist/cjs/instance/bindings.js +0 -5
- package/dist/cjs/instance/instance.d.ts +0 -45
- package/dist/cjs/instance/instance.js +0 -277
- package/dist/cjs/instance/raw-instance.d.ts +0 -56
- package/dist/cjs/instance/raw-instance.js +0 -129
- package/dist/mjs/client.js +0 -162
- package/dist/mjs/instance/autogen/wasm0.js +0 -1
- package/dist/mjs/instance/autogen/wasm1.js +0 -1
- package/dist/mjs/instance/autogen/wasm2.js +0 -1
- package/dist/mjs/instance/bindings-smoldot-light.js +0 -280
- package/dist/mjs/instance/bindings-wasi.d.ts +0 -27
- package/dist/mjs/instance/bindings-wasi.js +0 -170
- package/dist/mjs/instance/bindings.d.ts +0 -31
- package/dist/mjs/instance/bindings.js +0 -4
- package/dist/mjs/instance/instance.d.ts +0 -45
- package/dist/mjs/instance/instance.js +0 -269
- package/dist/mjs/instance/raw-instance.d.ts +0 -56
- package/dist/mjs/instance/raw-instance.js +0 -124
- /package/dist/cjs/{base64.d.ts → internals/base64.d.ts} +0 -0
- /package/dist/cjs/{base64.js → internals/base64.js} +0 -0
- /package/dist/cjs/{instance → internals}/buffer.d.ts +0 -0
- /package/dist/cjs/{instance → internals}/buffer.js +0 -0
- /package/dist/cjs/{instance/autogen → internals/bytecode}/wasm.d.ts +0 -0
- /package/dist/cjs/{instance/autogen → internals/bytecode}/wasm.js +0 -0
- /package/dist/cjs/{instance/autogen → internals/bytecode}/wasm0.d.ts +0 -0
- /package/dist/cjs/{instance/autogen → internals/bytecode}/wasm1.d.ts +0 -0
- /package/dist/cjs/{instance/autogen → internals/bytecode}/wasm2.d.ts +0 -0
- /package/dist/mjs/{base64.d.ts → internals/base64.d.ts} +0 -0
- /package/dist/mjs/{base64.js → internals/base64.js} +0 -0
- /package/dist/mjs/{instance → internals}/buffer.d.ts +0 -0
- /package/dist/mjs/{instance → internals}/buffer.js +0 -0
- /package/dist/mjs/{instance/autogen → internals/bytecode}/wasm.d.ts +0 -0
- /package/dist/mjs/{instance/autogen → internals/bytecode}/wasm.js +0 -0
- /package/dist/mjs/{instance/autogen → internals/bytecode}/wasm0.d.ts +0 -0
- /package/dist/mjs/{instance/autogen → internals/bytecode}/wasm1.d.ts +0 -0
- /package/dist/mjs/{instance/autogen → internals/bytecode}/wasm2.d.ts +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
5
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
6
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
7
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
8
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
9
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
10
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
import { start as innerStart } from './internals/client.js';
|
|
14
|
+
export { AddChainError, AlreadyDestroyedError, CrashError, MalformedJsonRpcError, QueueFullError, JsonRpcDisabledError } from './public-types.js';
|
|
15
|
+
/**
|
|
16
|
+
* Initializes a new client. This is a pre-requisite to connecting to a blockchain.
|
|
17
|
+
*
|
|
18
|
+
* Can never fail.
|
|
19
|
+
*
|
|
20
|
+
* @param options Configuration of the client.
|
|
21
|
+
*/
|
|
22
|
+
export function startWithBytecode(options) {
|
|
23
|
+
options.forbidWebRtc = true;
|
|
24
|
+
return innerStart(options || {}, options.bytecode, {
|
|
25
|
+
performanceNow: () => {
|
|
26
|
+
return performance.now();
|
|
27
|
+
},
|
|
28
|
+
getRandomValues: (buffer) => {
|
|
29
|
+
const crypto = globalThis.crypto;
|
|
30
|
+
if (!crypto)
|
|
31
|
+
throw new Error('randomness not available');
|
|
32
|
+
crypto.getRandomValues(buffer);
|
|
33
|
+
},
|
|
34
|
+
connect: (config) => {
|
|
35
|
+
return connect(config);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Tries to open a new connection using the given configuration.
|
|
41
|
+
*
|
|
42
|
+
* @see Connection
|
|
43
|
+
* @throws {@link ConnectionError} If the multiaddress couldn't be parsed or contains an invalid protocol.
|
|
44
|
+
*/
|
|
45
|
+
function connect(config) {
|
|
46
|
+
if (config.address.ty === "websocket") {
|
|
47
|
+
const socket = new WebSocket(config.address.url);
|
|
48
|
+
socket.binaryType = 'arraybuffer';
|
|
49
|
+
const bufferedAmountCheck = { quenedUnreportedBytes: 0, nextTimeout: 10 };
|
|
50
|
+
const checkBufferedAmount = () => {
|
|
51
|
+
if (socket.readyState != 1)
|
|
52
|
+
return;
|
|
53
|
+
// Note that we might expect `bufferedAmount` to always be <= the sum of the lengths
|
|
54
|
+
// of all the data that has been sent, but that might not be the case. For this
|
|
55
|
+
// reason, we use `bufferedAmount` as a hint rather than a correct value.
|
|
56
|
+
const bufferedAmount = socket.bufferedAmount;
|
|
57
|
+
let wasSent = bufferedAmountCheck.quenedUnreportedBytes - bufferedAmount;
|
|
58
|
+
if (wasSent < 0)
|
|
59
|
+
wasSent = 0;
|
|
60
|
+
bufferedAmountCheck.quenedUnreportedBytes -= wasSent;
|
|
61
|
+
if (bufferedAmountCheck.quenedUnreportedBytes != 0) {
|
|
62
|
+
setTimeout(checkBufferedAmount, bufferedAmountCheck.nextTimeout);
|
|
63
|
+
bufferedAmountCheck.nextTimeout *= 2;
|
|
64
|
+
if (bufferedAmountCheck.nextTimeout > 500)
|
|
65
|
+
bufferedAmountCheck.nextTimeout = 500;
|
|
66
|
+
}
|
|
67
|
+
// Note: it is important to call `onWritableBytes` at the very end, as it might
|
|
68
|
+
// trigger a call to `send`.
|
|
69
|
+
if (wasSent != 0)
|
|
70
|
+
config.onWritableBytes(wasSent);
|
|
71
|
+
};
|
|
72
|
+
socket.onopen = () => {
|
|
73
|
+
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: false });
|
|
74
|
+
};
|
|
75
|
+
socket.onclose = (event) => {
|
|
76
|
+
const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
|
|
77
|
+
config.onConnectionReset(message);
|
|
78
|
+
};
|
|
79
|
+
socket.onmessage = (msg) => {
|
|
80
|
+
config.onMessage(new Uint8Array(msg.data));
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
reset: () => {
|
|
84
|
+
// We can't set these fields to null because the TypeScript definitions don't
|
|
85
|
+
// allow it, but we can set them to dummy values.
|
|
86
|
+
socket.onopen = () => { };
|
|
87
|
+
socket.onclose = () => { };
|
|
88
|
+
socket.onmessage = () => { };
|
|
89
|
+
socket.onerror = () => { };
|
|
90
|
+
socket.close();
|
|
91
|
+
},
|
|
92
|
+
send: (data) => {
|
|
93
|
+
// The WebSocket library that we use seems to spontaneously transition connections
|
|
94
|
+
// to the "closed" state but not call the `onclosed` callback immediately. Calling
|
|
95
|
+
// `send` on that object throws an exception. In order to avoid panicking smoldot,
|
|
96
|
+
// we thus absorb any exception thrown here.
|
|
97
|
+
// See also <https://github.com/paritytech/smoldot/issues/2937>.
|
|
98
|
+
try {
|
|
99
|
+
socket.send(data);
|
|
100
|
+
if (bufferedAmountCheck.quenedUnreportedBytes == 0) {
|
|
101
|
+
bufferedAmountCheck.nextTimeout = 10;
|
|
102
|
+
setTimeout(checkBufferedAmount, 10);
|
|
103
|
+
}
|
|
104
|
+
bufferedAmountCheck.quenedUnreportedBytes += data.length;
|
|
105
|
+
}
|
|
106
|
+
catch (_error) { }
|
|
107
|
+
},
|
|
108
|
+
closeSend: () => { throw new Error('Wrong connection type'); },
|
|
109
|
+
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
else if (config.address.ty === "tcp") {
|
|
113
|
+
const socket = {
|
|
114
|
+
destroyed: false,
|
|
115
|
+
inner: Deno.connect({
|
|
116
|
+
hostname: config.address.hostname,
|
|
117
|
+
port: config.address.port,
|
|
118
|
+
}).catch((error) => {
|
|
119
|
+
socket.destroyed = true;
|
|
120
|
+
config.onConnectionReset(error.toString());
|
|
121
|
+
return null;
|
|
122
|
+
})
|
|
123
|
+
};
|
|
124
|
+
socket.inner = socket.inner.then((established) => {
|
|
125
|
+
// TODO: at the time of writing of this comment, `setNoDelay` is still unstable
|
|
126
|
+
//established.setNoDelay();
|
|
127
|
+
if (socket.destroyed)
|
|
128
|
+
return established;
|
|
129
|
+
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: true });
|
|
130
|
+
// Spawns an asynchronous task that continuously reads from the socket.
|
|
131
|
+
// Every time data is read, the task re-executes itself in order to continue reading.
|
|
132
|
+
// The task ends automatically if an EOF or error is detected, which should also happen
|
|
133
|
+
// if the user calls `close()`.
|
|
134
|
+
const read = (readBuffer) => __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
if (socket.destroyed || established === null)
|
|
136
|
+
return;
|
|
137
|
+
let outcome = null;
|
|
138
|
+
try {
|
|
139
|
+
outcome = yield established.read(readBuffer);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// The type of `error` is unclear, but we assume that it implements `Error`
|
|
143
|
+
outcome = error.toString();
|
|
144
|
+
}
|
|
145
|
+
if (socket.destroyed)
|
|
146
|
+
return;
|
|
147
|
+
if (typeof outcome !== 'number' || outcome === null) {
|
|
148
|
+
// The socket is reported closed, but `socket.destroyed` is still `false` (see
|
|
149
|
+
// check above). As such, we must inform the inner layers.
|
|
150
|
+
socket.destroyed = true;
|
|
151
|
+
config.onConnectionReset(outcome === null ? "EOF when reading socket" : outcome);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.assert(outcome !== 0); // `read` guarantees to return a non-zero value.
|
|
155
|
+
config.onMessage(readBuffer.slice(0, outcome));
|
|
156
|
+
return read(readBuffer);
|
|
157
|
+
});
|
|
158
|
+
read(new Uint8Array(32768));
|
|
159
|
+
return established;
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
reset: () => {
|
|
163
|
+
socket.destroyed = true;
|
|
164
|
+
socket.inner.then((connec) => connec.close());
|
|
165
|
+
},
|
|
166
|
+
send: (data) => {
|
|
167
|
+
let dataCopy = Uint8Array.from(data); // Deep copy of the data
|
|
168
|
+
socket.inner = socket.inner.then((c) => __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
while (dataCopy.length > 0) {
|
|
170
|
+
if (socket.destroyed || c === null)
|
|
171
|
+
return c;
|
|
172
|
+
let outcome;
|
|
173
|
+
try {
|
|
174
|
+
outcome = yield c.write(dataCopy);
|
|
175
|
+
config.onWritableBytes(dataCopy.length);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
// The type of `error` is unclear, but we assume that it implements `Error`
|
|
179
|
+
outcome = error.toString();
|
|
180
|
+
}
|
|
181
|
+
if (typeof outcome !== 'number') {
|
|
182
|
+
// The socket is reported closed, but `socket.destroyed` is still
|
|
183
|
+
// `false` (see check above). As such, we must inform the inner layers.
|
|
184
|
+
socket.destroyed = true;
|
|
185
|
+
config.onConnectionReset(outcome);
|
|
186
|
+
return c;
|
|
187
|
+
}
|
|
188
|
+
// Note that, contrary to `read`, it is possible for `outcome` to be 0.
|
|
189
|
+
// This happen if the write had to be interrupted, and the only thing
|
|
190
|
+
// we have to do is try writing again.
|
|
191
|
+
dataCopy = dataCopy.slice(outcome);
|
|
192
|
+
}
|
|
193
|
+
return c;
|
|
194
|
+
}));
|
|
195
|
+
},
|
|
196
|
+
closeSend: () => {
|
|
197
|
+
socket.inner = socket.inner.then((c) => __awaiter(this, void 0, void 0, function* () {
|
|
198
|
+
yield (c === null || c === void 0 ? void 0 : c.closeWrite());
|
|
199
|
+
return c;
|
|
200
|
+
}));
|
|
201
|
+
},
|
|
202
|
+
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// Should never happen, as we tweak the options to refuse connection types that
|
|
207
|
+
// we don't support.
|
|
208
|
+
throw new Error();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Client, ClientOptionsWithBytecode } from './public-types.js';
|
|
2
|
+
export { AddChainError, AddChainOptions, AlreadyDestroyedError, Chain, Client, ClientOptions, ClientOptionsWithBytecode, SmoldotBytecode, CrashError, MalformedJsonRpcError, QueueFullError, JsonRpcDisabledError, LogCallback } from './public-types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Initializes a new client. This is a pre-requisite to connecting to a blockchain.
|
|
5
|
+
*
|
|
6
|
+
* Can never fail.
|
|
7
|
+
*
|
|
8
|
+
* @param options Configuration of the client.
|
|
9
|
+
*/
|
|
10
|
+
export declare function startWithBytecode(options: ClientOptionsWithBytecode): Client;
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
import { start as innerStart } from './internals/client.js';
|
|
5
|
+
import { WebSocket } from 'ws';
|
|
6
|
+
import { performance } from 'node:perf_hooks';
|
|
7
|
+
import { createConnection as nodeCreateConnection } from 'node:net';
|
|
8
|
+
import { randomFillSync } from 'node:crypto';
|
|
9
|
+
export { AddChainError, AlreadyDestroyedError, CrashError, MalformedJsonRpcError, QueueFullError, JsonRpcDisabledError } from './public-types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Initializes a new client. This is a pre-requisite to connecting to a blockchain.
|
|
12
|
+
*
|
|
13
|
+
* Can never fail.
|
|
14
|
+
*
|
|
15
|
+
* @param options Configuration of the client.
|
|
16
|
+
*/
|
|
17
|
+
export function startWithBytecode(options) {
|
|
18
|
+
options.forbidWebRtc = true;
|
|
19
|
+
return innerStart(options || {}, options.bytecode, {
|
|
20
|
+
performanceNow: () => {
|
|
21
|
+
return performance.now();
|
|
22
|
+
},
|
|
23
|
+
getRandomValues: (buffer) => {
|
|
24
|
+
if (buffer.length >= 1024 * 1024)
|
|
25
|
+
throw new Error('getRandomValues buffer too large');
|
|
26
|
+
randomFillSync(buffer);
|
|
27
|
+
},
|
|
28
|
+
connect: (config) => {
|
|
29
|
+
return connect(config);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Tries to open a new connection using the given configuration.
|
|
35
|
+
*
|
|
36
|
+
* @see Connection
|
|
37
|
+
* @throws {@link ConnectionError} If the multiaddress couldn't be parsed or contains an invalid protocol.
|
|
38
|
+
*/
|
|
39
|
+
function connect(config) {
|
|
40
|
+
if (config.address.ty === "websocket") {
|
|
41
|
+
const socket = new WebSocket(config.address.url);
|
|
42
|
+
socket.binaryType = 'arraybuffer';
|
|
43
|
+
const bufferedAmountCheck = { quenedUnreportedBytes: 0, nextTimeout: 10 };
|
|
44
|
+
const checkBufferedAmount = () => {
|
|
45
|
+
if (socket.readyState != 1)
|
|
46
|
+
return;
|
|
47
|
+
// Note that we might expect `bufferedAmount` to always be <= the sum of the lengths
|
|
48
|
+
// of all the data that has been sent, but that seems to not be the case. It is
|
|
49
|
+
// unclear whether this is intended or a bug, but is is likely that `bufferedAmount`
|
|
50
|
+
// also includes WebSocket headers. For this reason, we use `bufferedAmount` as a hint
|
|
51
|
+
// rather than a correct value.
|
|
52
|
+
const bufferedAmount = socket.bufferedAmount;
|
|
53
|
+
let wasSent = bufferedAmountCheck.quenedUnreportedBytes - bufferedAmount;
|
|
54
|
+
if (wasSent < 0)
|
|
55
|
+
wasSent = 0;
|
|
56
|
+
bufferedAmountCheck.quenedUnreportedBytes -= wasSent;
|
|
57
|
+
if (bufferedAmountCheck.quenedUnreportedBytes != 0) {
|
|
58
|
+
setTimeout(checkBufferedAmount, bufferedAmountCheck.nextTimeout);
|
|
59
|
+
bufferedAmountCheck.nextTimeout *= 2;
|
|
60
|
+
if (bufferedAmountCheck.nextTimeout > 500)
|
|
61
|
+
bufferedAmountCheck.nextTimeout = 500;
|
|
62
|
+
}
|
|
63
|
+
// Note: it is important to call `onWritableBytes` at the very end, as it might
|
|
64
|
+
// trigger a call to `send`.
|
|
65
|
+
if (wasSent != 0)
|
|
66
|
+
config.onWritableBytes(wasSent);
|
|
67
|
+
};
|
|
68
|
+
socket.onopen = () => {
|
|
69
|
+
config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: false });
|
|
70
|
+
};
|
|
71
|
+
socket.onclose = (event) => {
|
|
72
|
+
const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
|
|
73
|
+
config.onConnectionReset(message);
|
|
74
|
+
socket.onopen = () => { };
|
|
75
|
+
socket.onclose = () => { };
|
|
76
|
+
socket.onmessage = () => { };
|
|
77
|
+
socket.onerror = () => { };
|
|
78
|
+
};
|
|
79
|
+
socket.onerror = (event) => {
|
|
80
|
+
config.onConnectionReset(event.message);
|
|
81
|
+
socket.onopen = () => { };
|
|
82
|
+
socket.onclose = () => { };
|
|
83
|
+
socket.onmessage = () => { };
|
|
84
|
+
socket.onerror = () => { };
|
|
85
|
+
};
|
|
86
|
+
socket.onmessage = (msg) => {
|
|
87
|
+
config.onMessage(new Uint8Array(msg.data));
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
reset: () => {
|
|
91
|
+
// We can't set these fields to null because the TypeScript definitions don't
|
|
92
|
+
// allow it, but we can set them to dummy values.
|
|
93
|
+
socket.onopen = () => { };
|
|
94
|
+
socket.onclose = () => { };
|
|
95
|
+
socket.onmessage = () => { };
|
|
96
|
+
socket.onerror = () => { };
|
|
97
|
+
socket.close();
|
|
98
|
+
},
|
|
99
|
+
send: (data) => {
|
|
100
|
+
socket.send(data);
|
|
101
|
+
if (bufferedAmountCheck.quenedUnreportedBytes == 0) {
|
|
102
|
+
bufferedAmountCheck.nextTimeout = 10;
|
|
103
|
+
setTimeout(checkBufferedAmount, 10);
|
|
104
|
+
}
|
|
105
|
+
bufferedAmountCheck.quenedUnreportedBytes += data.length;
|
|
106
|
+
},
|
|
107
|
+
closeSend: () => { throw new Error('Wrong connection type'); },
|
|
108
|
+
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
else if (config.address.ty === "tcp") {
|
|
112
|
+
const socket = nodeCreateConnection({
|
|
113
|
+
host: config.address.hostname,
|
|
114
|
+
port: config.address.port,
|
|
115
|
+
});
|
|
116
|
+
// Number of bytes queued using `socket.write` and where `write` has returned false.
|
|
117
|
+
const drainingBytes = { num: 0 };
|
|
118
|
+
socket.setNoDelay();
|
|
119
|
+
socket.on('connect', () => {
|
|
120
|
+
if (socket.destroyed)
|
|
121
|
+
return;
|
|
122
|
+
config.onOpen({
|
|
123
|
+
type: 'single-stream', handshake: 'multistream-select-noise-yamux',
|
|
124
|
+
initialWritableBytes: socket.writableHighWaterMark, writeClosable: true
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
socket.on('close', (hasError) => {
|
|
128
|
+
if (socket.destroyed)
|
|
129
|
+
return;
|
|
130
|
+
// NodeJS doesn't provide a reason why the closing happened, but only
|
|
131
|
+
// whether it was caused by an error.
|
|
132
|
+
const message = hasError ? "Error" : "Closed gracefully";
|
|
133
|
+
config.onConnectionReset(message);
|
|
134
|
+
});
|
|
135
|
+
socket.on('error', () => { });
|
|
136
|
+
socket.on('data', (message) => {
|
|
137
|
+
if (socket.destroyed)
|
|
138
|
+
return;
|
|
139
|
+
config.onMessage(new Uint8Array(message.buffer));
|
|
140
|
+
});
|
|
141
|
+
socket.on('drain', () => {
|
|
142
|
+
// The bytes queued using `socket.write` and where `write` has returned false have now
|
|
143
|
+
// been sent. Notify the API that it can write more data.
|
|
144
|
+
if (socket.destroyed)
|
|
145
|
+
return;
|
|
146
|
+
const val = drainingBytes.num;
|
|
147
|
+
drainingBytes.num = 0;
|
|
148
|
+
config.onWritableBytes(val);
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
reset: () => {
|
|
152
|
+
socket.destroy();
|
|
153
|
+
},
|
|
154
|
+
send: (data) => {
|
|
155
|
+
const dataLen = data.length;
|
|
156
|
+
const allWritten = socket.write(data);
|
|
157
|
+
if (allWritten) {
|
|
158
|
+
setImmediate(() => {
|
|
159
|
+
if (!socket.writable)
|
|
160
|
+
return;
|
|
161
|
+
config.onWritableBytes(dataLen);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
drainingBytes.num += dataLen;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
closeSend: () => {
|
|
169
|
+
socket.end();
|
|
170
|
+
},
|
|
171
|
+
openOutSubstream: () => { throw new Error('Wrong connection type'); }
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Should never happen, as we tweak the options to refuse connection types that
|
|
176
|
+
// we don't support.
|
|
177
|
+
throw new Error();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { PlatformBindings } from './instance/instance.js';
|
|
2
|
-
export { MalformedJsonRpcError, QueueFullError, CrashError } from './instance/instance.js';
|
|
3
1
|
/**
|
|
4
2
|
* Thrown in case of a problem when initializing the chain.
|
|
5
3
|
*/
|
|
@@ -19,6 +17,32 @@ export declare class AlreadyDestroyedError extends Error {
|
|
|
19
17
|
export declare class JsonRpcDisabledError extends Error {
|
|
20
18
|
constructor();
|
|
21
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Thrown in case the underlying client encounters an unexpected crash.
|
|
22
|
+
*
|
|
23
|
+
* This is always an internal bug in smoldot and is never supposed to happen.
|
|
24
|
+
*/
|
|
25
|
+
export declare class CrashError extends Error {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thrown in case a malformed JSON-RPC request is sent.
|
|
30
|
+
*/
|
|
31
|
+
export declare class MalformedJsonRpcError extends Error {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Thrown in case the buffer of JSON-RPC requests is full and cannot accept any more request.
|
|
36
|
+
*/
|
|
37
|
+
export declare class QueueFullError extends Error {
|
|
38
|
+
constructor();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Bytecode of the smoldot client.
|
|
42
|
+
*/
|
|
43
|
+
export interface SmoldotBytecode {
|
|
44
|
+
readonly wasm: WebAssembly.Module;
|
|
45
|
+
}
|
|
22
46
|
/**
|
|
23
47
|
* Client with zero or more active connections to blockchains.
|
|
24
48
|
*/
|
|
@@ -150,6 +174,19 @@ export type LogCallback = (level: number, target: string, message: string) => vo
|
|
|
150
174
|
* Configuration of a client.
|
|
151
175
|
*/
|
|
152
176
|
export interface ClientOptions {
|
|
177
|
+
/**
|
|
178
|
+
* One end of a port (created using `new MessageChannel`). The other end must be passed to the
|
|
179
|
+
* `run` function found in `smoldot/worker`.
|
|
180
|
+
*
|
|
181
|
+
* If this option isn't set then the smoldot light client will run entirely on the "current
|
|
182
|
+
* thread", which might slow down other components that also run on this thread.
|
|
183
|
+
* If this option is set, then the {@link Client} that is created is merely a frontend that
|
|
184
|
+
* sends messages on the provided port.
|
|
185
|
+
*
|
|
186
|
+
* It is intended that the other end of this port is sent to a background worker, and the
|
|
187
|
+
* `run` function be called in this background worker. However this is in no way mandatory.
|
|
188
|
+
*/
|
|
189
|
+
portToWorker?: MessagePort;
|
|
153
190
|
/**
|
|
154
191
|
* Callback that the client will invoke in order to report a log event.
|
|
155
192
|
*
|
|
@@ -176,6 +213,9 @@ export interface ClientOptions {
|
|
|
176
213
|
* Note that this is implemented by sleeping for certain amounts of time in order for the average
|
|
177
214
|
* CPU consumption to not go beyond the given limit. It is therefore still possible for the
|
|
178
215
|
* client to use high amounts of CPU for short amounts of time.
|
|
216
|
+
*
|
|
217
|
+
* If {@link ClientOptions.portToWorker} is set, then the CPU rate limit applies to the
|
|
218
|
+
* worker.
|
|
179
219
|
*/
|
|
180
220
|
cpuRateLimit?: number;
|
|
181
221
|
/**
|
|
@@ -233,6 +273,15 @@ export interface ClientOptions {
|
|
|
233
273
|
*/
|
|
234
274
|
forbidWebRtc?: boolean;
|
|
235
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Configuration of a client.
|
|
278
|
+
*/
|
|
279
|
+
export interface ClientOptionsWithBytecode extends ClientOptions {
|
|
280
|
+
/**
|
|
281
|
+
* Smoldot bytecode. Can be either fully loaded or a `Promise`.
|
|
282
|
+
*/
|
|
283
|
+
bytecode: SmoldotBytecode | Promise<SmoldotBytecode>;
|
|
284
|
+
}
|
|
236
285
|
/**
|
|
237
286
|
* Configuration of a blockchain.
|
|
238
287
|
*/
|
|
@@ -317,4 +366,3 @@ export interface AddChainOptions {
|
|
|
317
366
|
*/
|
|
318
367
|
disableJsonRpc?: boolean;
|
|
319
368
|
}
|
|
320
|
-
export declare function start(options: ClientOptions, wasmModule: Promise<WebAssembly.Module>, platformBindings: PlatformBindings): Client;
|
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
/**
|
|
15
|
+
* Thrown in case of a problem when initializing the chain.
|
|
16
|
+
*/
|
|
17
|
+
export class AddChainError extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = "AddChainError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Thrown in case the API user tries to use a chain or client that has already been destroyed.
|
|
25
|
+
*/
|
|
26
|
+
export class AlreadyDestroyedError extends Error {
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this.name = "AlreadyDestroyedError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Thrown when trying to send a JSON-RPC message to a chain whose JSON-RPC system hasn't been
|
|
34
|
+
* enabled.
|
|
35
|
+
*/
|
|
36
|
+
export class JsonRpcDisabledError extends Error {
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
this.name = "JsonRpcDisabledError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Thrown in case the underlying client encounters an unexpected crash.
|
|
44
|
+
*
|
|
45
|
+
* This is always an internal bug in smoldot and is never supposed to happen.
|
|
46
|
+
*/
|
|
47
|
+
export class CrashError extends Error {
|
|
48
|
+
constructor(message) {
|
|
49
|
+
super(message);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Thrown in case a malformed JSON-RPC request is sent.
|
|
54
|
+
*/
|
|
55
|
+
export class MalformedJsonRpcError extends Error {
|
|
56
|
+
constructor() {
|
|
57
|
+
super("JSON-RPC request is malformed");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Thrown in case the buffer of JSON-RPC requests is full and cannot accept any more request.
|
|
62
|
+
*/
|
|
63
|
+
export class QueueFullError extends Error {
|
|
64
|
+
constructor() {
|
|
65
|
+
super("JSON-RPC requests queue is full");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs the CPU-heavy parts of smoldot. Must be passed a port whose other end is passed to
|
|
3
|
+
* `ClientOptions.portToWorker`.
|
|
4
|
+
*
|
|
5
|
+
* Returns a `Promise` that is ready when the smoldot client is shut down (either because it
|
|
6
|
+
* crashes or intentionally with a call to `Client.terminate`).
|
|
7
|
+
* Since this function is asynchronous, this `Promise` is wrapped around another `Promise`. In
|
|
8
|
+
* other words, the outer `Promise` is ready when execution starts, and the inner `Promise` is
|
|
9
|
+
* ready when execution ends.
|
|
10
|
+
*/
|
|
11
|
+
export declare function run(messagePort: MessagePort): Promise<Promise<void>>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Smoldot
|
|
2
|
+
// Copyright (C) 2023 Pierre Krieger
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
4
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
5
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
6
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
7
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
8
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
9
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
10
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
// This program is free software: you can redistribute it and/or modify
|
|
14
|
+
// it under the terms of the GNU General Public License as published by
|
|
15
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
16
|
+
// (at your option) any later version.
|
|
17
|
+
// This program is distributed in the hope that it will be useful,
|
|
18
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20
|
+
// GNU General Public License for more details.
|
|
21
|
+
// You should have received a copy of the GNU General Public License
|
|
22
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
23
|
+
import * as instance from './internals/remote-instance.js';
|
|
24
|
+
/**
|
|
25
|
+
* Runs the CPU-heavy parts of smoldot. Must be passed a port whose other end is passed to
|
|
26
|
+
* `ClientOptions.portToWorker`.
|
|
27
|
+
*
|
|
28
|
+
* Returns a `Promise` that is ready when the smoldot client is shut down (either because it
|
|
29
|
+
* crashes or intentionally with a call to `Client.terminate`).
|
|
30
|
+
* Since this function is asynchronous, this `Promise` is wrapped around another `Promise`. In
|
|
31
|
+
* other words, the outer `Promise` is ready when execution starts, and the inner `Promise` is
|
|
32
|
+
* ready when execution ends.
|
|
33
|
+
*/
|
|
34
|
+
export function run(messagePort) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const whenShutdown = yield instance.startInstanceServer({
|
|
37
|
+
envVars: [],
|
|
38
|
+
performanceNow: () => {
|
|
39
|
+
return performance.now();
|
|
40
|
+
},
|
|
41
|
+
getRandomValues: (buffer) => {
|
|
42
|
+
const crypto = globalThis.crypto;
|
|
43
|
+
if (!crypto)
|
|
44
|
+
throw new Error('randomness not available');
|
|
45
|
+
// Browsers have this completely undocumented behavior (it's not even part of a spec)
|
|
46
|
+
// that for some reason `getRandomValues` can't be called on arrayviews back by
|
|
47
|
+
// `SharedArrayBuffer`s and they throw an exception if you try.
|
|
48
|
+
if (buffer.buffer instanceof ArrayBuffer)
|
|
49
|
+
crypto.getRandomValues(buffer);
|
|
50
|
+
else {
|
|
51
|
+
const tmpArray = new Uint8Array(buffer.length);
|
|
52
|
+
crypto.getRandomValues(tmpArray);
|
|
53
|
+
buffer.set(tmpArray);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
}, messagePort);
|
|
57
|
+
return whenShutdown;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs the CPU-heavy parts of smoldot. Must be passed a port whose other end is passed to
|
|
3
|
+
* `ClientOptions.portToWorker`.
|
|
4
|
+
*
|
|
5
|
+
* Returns a `Promise` that is ready when the smoldot client is shut down (either because it
|
|
6
|
+
* crashes or intentionally with a call to `Client.terminate`).
|
|
7
|
+
* Since this function is asynchronous, this `Promise` is wrapped around another `Promise`. In
|
|
8
|
+
* other words, the outer `Promise` is ready when execution starts, and the inner `Promise` is
|
|
9
|
+
* ready when execution ends.
|
|
10
|
+
*/
|
|
11
|
+
export declare function run(messagePort: MessagePort): Promise<Promise<void>>;
|