@quantovainc/qcore 0.5.5 → 0.5.7
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 +11 -1
- package/index.d.ts +3 -0
- package/package.json +1 -1
- package/pkg/README.md +11 -1
- package/pkg/package.json +1 -1
- package/pkg/qcore_js.d.ts +2 -0
- package/pkg/qcore_js_bg.js +17 -0
- package/pkg/qcore_js_bg.wasm +0 -0
- package/pkg/qcore_js_bg.wasm.d.ts +2 -0
- package/pkg-node/README.md +11 -1
- package/pkg-node/package.json +1 -1
- package/pkg-node/qcore_js.d.ts +2 -0
- package/pkg-node/qcore_js.js +17 -0
- package/pkg-node/qcore_js_bg.wasm +0 -0
- package/shared.js +28 -9
- package/shared.mjs +28 -9
package/README.md
CHANGED
|
@@ -134,6 +134,13 @@ Pass `addressBook`, a list of Q1 addresses you trust, in the Client options, and
|
|
|
134
134
|
a target that shares two or more leading and trailing characters with a known address, eight in all, but is a
|
|
135
135
|
different address. The sending accounts and every target signed for in the session are known as well.
|
|
136
136
|
|
|
137
|
+
Pass `fetch`, a function with the fetch signature, in the Client options to route every gateway request
|
|
138
|
+
through your own transport, for example one that enforces an address allow list. Without it the Client
|
|
139
|
+
uses the platform fetch, which on Node follows HTTPS_PROXY only when NODE_USE_ENV_PROXY is set. The
|
|
140
|
+
Client refuses to start when NODE_TLS_REJECT_UNAUTHORIZED is 0. Signing calls for one account run one at a
|
|
141
|
+
time across every Client in the process, and each Client keeps its own record of signed nonces, so keep
|
|
142
|
+
one Client per account to have every nonce tracked in one place.
|
|
143
|
+
|
|
137
144
|
The last argument to a Client signing call, transfer, register, call, or callSignedOrder, is a fee
|
|
138
145
|
ceiling, the most you are ever willing to pay in fees for one transaction. You choose it yourself, as a
|
|
139
146
|
fixed number your app decides on its own, before you sign. The Client reads the gateway's reported fee,
|
|
@@ -148,7 +155,10 @@ than you intended.
|
|
|
148
155
|
This comparison lives in the Client and only in the Client. The raw core.* signing functions, such as
|
|
149
156
|
core.sign_transfer, take the actual fee that goes on the wire, not a ceiling, and do no gateway
|
|
150
157
|
comparison at all. A developer who calls core.* directly has no fee cap, so reach for the Client whenever
|
|
151
|
-
a gateway you do not control reports the fee.
|
|
158
|
+
a gateway you do not control reports the fee. The raw signing functions also take the validity deadline
|
|
159
|
+
ready made and do not compare it with the chain head, so call core.checkValidUntil with a head you trust
|
|
160
|
+
first. A SigningSession reserves each nonce it hands out until it is signed, and a flow that stops early
|
|
161
|
+
frees its nonce with release.
|
|
152
162
|
|
|
153
163
|
A contract call pays one transfer fee for every started 1210 units of its meter limit, and the chain
|
|
154
164
|
refuses a call that pays less. The raw call signers, core.sign_call, core.signPayableCall, and
|
package/index.d.ts
CHANGED
|
@@ -212,6 +212,8 @@ export interface SigningSession {
|
|
|
212
212
|
rememberAddress(address: string): void;
|
|
213
213
|
checkTarget(address: string): void;
|
|
214
214
|
slot(key: string, reported: bigint, expected: bigint | undefined, order: boolean): bigint;
|
|
215
|
+
release(key: string, slot: bigint): void;
|
|
216
|
+
releasePending(key: string): void;
|
|
215
217
|
signed(key: string, slot: bigint): void;
|
|
216
218
|
outcome(key: string, slot: bigint, accepted: boolean): void;
|
|
217
219
|
nextNonce(key: string): bigint | undefined;
|
|
@@ -237,6 +239,7 @@ export interface ClientOptions {
|
|
|
237
239
|
network?: Network;
|
|
238
240
|
expectedChainId?: string;
|
|
239
241
|
addressBook?: ReadonlyArray<string>;
|
|
242
|
+
fetch?: (url: string, init: { method: string; headers: Record<string, string>; body: string; signal: AbortSignal; redirect: 'error' }) => Promise<Response>;
|
|
240
243
|
}
|
|
241
244
|
|
|
242
245
|
export class Client {
|
package/package.json
CHANGED
package/pkg/README.md
CHANGED
|
@@ -134,6 +134,13 @@ Pass `addressBook`, a list of Q1 addresses you trust, in the Client options, and
|
|
|
134
134
|
a target that shares two or more leading and trailing characters with a known address, eight in all, but is a
|
|
135
135
|
different address. The sending accounts and every target signed for in the session are known as well.
|
|
136
136
|
|
|
137
|
+
Pass `fetch`, a function with the fetch signature, in the Client options to route every gateway request
|
|
138
|
+
through your own transport, for example one that enforces an address allow list. Without it the Client
|
|
139
|
+
uses the platform fetch, which on Node follows HTTPS_PROXY only when NODE_USE_ENV_PROXY is set. The
|
|
140
|
+
Client refuses to start when NODE_TLS_REJECT_UNAUTHORIZED is 0. Signing calls for one account run one at a
|
|
141
|
+
time across every Client in the process, and each Client keeps its own record of signed nonces, so keep
|
|
142
|
+
one Client per account to have every nonce tracked in one place.
|
|
143
|
+
|
|
137
144
|
The last argument to a Client signing call, transfer, register, call, or callSignedOrder, is a fee
|
|
138
145
|
ceiling, the most you are ever willing to pay in fees for one transaction. You choose it yourself, as a
|
|
139
146
|
fixed number your app decides on its own, before you sign. The Client reads the gateway's reported fee,
|
|
@@ -148,7 +155,10 @@ than you intended.
|
|
|
148
155
|
This comparison lives in the Client and only in the Client. The raw core.* signing functions, such as
|
|
149
156
|
core.sign_transfer, take the actual fee that goes on the wire, not a ceiling, and do no gateway
|
|
150
157
|
comparison at all. A developer who calls core.* directly has no fee cap, so reach for the Client whenever
|
|
151
|
-
a gateway you do not control reports the fee.
|
|
158
|
+
a gateway you do not control reports the fee. The raw signing functions also take the validity deadline
|
|
159
|
+
ready made and do not compare it with the chain head, so call core.checkValidUntil with a head you trust
|
|
160
|
+
first. A SigningSession reserves each nonce it hands out until it is signed, and a flow that stops early
|
|
161
|
+
frees its nonce with release.
|
|
152
162
|
|
|
153
163
|
A contract call pays one transfer fee for every started 1210 units of its meter limit, and the chain
|
|
154
164
|
refuses a call that pays less. The raw call signers, core.sign_call, core.signPayableCall, and
|
package/pkg/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "qcore-js",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "The JavaScript binding for the Quantova client core, generated over QCore with wasm-bindgen. The core does the key derivation, the post quantum signing, and the wire, the page does the fetch.",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.7",
|
|
6
6
|
"license": "Apache-2.0 OR MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
package/pkg/qcore_js.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export class SigningSession {
|
|
|
9
9
|
constructor(book: string[]);
|
|
10
10
|
nextNonce(key: string): bigint | undefined;
|
|
11
11
|
outcome(key: string, slot: bigint, accepted: boolean): void;
|
|
12
|
+
release(key: string, slot: bigint): void;
|
|
13
|
+
releasePending(key: string): void;
|
|
12
14
|
rememberAddress(address: string): void;
|
|
13
15
|
signed(key: string, slot: bigint): void;
|
|
14
16
|
slot(key: string, reported: bigint, expected: bigint | null | undefined, order: boolean): bigint;
|
package/pkg/qcore_js_bg.js
CHANGED
|
@@ -65,6 +65,23 @@ export class SigningSession {
|
|
|
65
65
|
const len0 = WASM_VECTOR_LEN;
|
|
66
66
|
wasm.signingsession_outcome(this.__wbg_ptr, ptr0, len0, slot, accepted);
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} key
|
|
70
|
+
* @param {bigint} slot
|
|
71
|
+
*/
|
|
72
|
+
release(key, slot) {
|
|
73
|
+
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
74
|
+
const len0 = WASM_VECTOR_LEN;
|
|
75
|
+
wasm.signingsession_release(this.__wbg_ptr, ptr0, len0, slot);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @param {string} key
|
|
79
|
+
*/
|
|
80
|
+
releasePending(key) {
|
|
81
|
+
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
82
|
+
const len0 = WASM_VECTOR_LEN;
|
|
83
|
+
wasm.signingsession_releasePending(this.__wbg_ptr, ptr0, len0);
|
|
84
|
+
}
|
|
68
85
|
/**
|
|
69
86
|
* @param {string} address
|
|
70
87
|
*/
|
package/pkg/qcore_js_bg.wasm
CHANGED
|
Binary file
|
|
@@ -46,6 +46,8 @@ export const signingsession_isHeld: (a: number, b: number, c: number, d: bigint)
|
|
|
46
46
|
export const signingsession_new: (a: number, b: number) => [number, number, number];
|
|
47
47
|
export const signingsession_nextNonce: (a: number, b: number, c: number) => [number, bigint];
|
|
48
48
|
export const signingsession_outcome: (a: number, b: number, c: number, d: bigint, e: number) => void;
|
|
49
|
+
export const signingsession_release: (a: number, b: number, c: number, d: bigint) => void;
|
|
50
|
+
export const signingsession_releasePending: (a: number, b: number, c: number) => void;
|
|
49
51
|
export const signingsession_rememberAddress: (a: number, b: number, c: number) => void;
|
|
50
52
|
export const signingsession_signed: (a: number, b: number, c: number, d: bigint) => void;
|
|
51
53
|
export const signingsession_slot: (a: number, b: number, c: number, d: bigint, e: number, f: bigint, g: number) => [bigint, number, number];
|
package/pkg-node/README.md
CHANGED
|
@@ -134,6 +134,13 @@ Pass `addressBook`, a list of Q1 addresses you trust, in the Client options, and
|
|
|
134
134
|
a target that shares two or more leading and trailing characters with a known address, eight in all, but is a
|
|
135
135
|
different address. The sending accounts and every target signed for in the session are known as well.
|
|
136
136
|
|
|
137
|
+
Pass `fetch`, a function with the fetch signature, in the Client options to route every gateway request
|
|
138
|
+
through your own transport, for example one that enforces an address allow list. Without it the Client
|
|
139
|
+
uses the platform fetch, which on Node follows HTTPS_PROXY only when NODE_USE_ENV_PROXY is set. The
|
|
140
|
+
Client refuses to start when NODE_TLS_REJECT_UNAUTHORIZED is 0. Signing calls for one account run one at a
|
|
141
|
+
time across every Client in the process, and each Client keeps its own record of signed nonces, so keep
|
|
142
|
+
one Client per account to have every nonce tracked in one place.
|
|
143
|
+
|
|
137
144
|
The last argument to a Client signing call, transfer, register, call, or callSignedOrder, is a fee
|
|
138
145
|
ceiling, the most you are ever willing to pay in fees for one transaction. You choose it yourself, as a
|
|
139
146
|
fixed number your app decides on its own, before you sign. The Client reads the gateway's reported fee,
|
|
@@ -148,7 +155,10 @@ than you intended.
|
|
|
148
155
|
This comparison lives in the Client and only in the Client. The raw core.* signing functions, such as
|
|
149
156
|
core.sign_transfer, take the actual fee that goes on the wire, not a ceiling, and do no gateway
|
|
150
157
|
comparison at all. A developer who calls core.* directly has no fee cap, so reach for the Client whenever
|
|
151
|
-
a gateway you do not control reports the fee.
|
|
158
|
+
a gateway you do not control reports the fee. The raw signing functions also take the validity deadline
|
|
159
|
+
ready made and do not compare it with the chain head, so call core.checkValidUntil with a head you trust
|
|
160
|
+
first. A SigningSession reserves each nonce it hands out until it is signed, and a flow that stops early
|
|
161
|
+
frees its nonce with release.
|
|
152
162
|
|
|
153
163
|
A contract call pays one transfer fee for every started 1210 units of its meter limit, and the chain
|
|
154
164
|
refuses a call that pays less. The raw call signers, core.sign_call, core.signPayableCall, and
|
package/pkg-node/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qcore-js",
|
|
3
3
|
"description": "The JavaScript binding for the Quantova client core, generated over QCore with wasm-bindgen. The core does the key derivation, the post quantum signing, and the wire, the page does the fetch.",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.7",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/pkg-node/qcore_js.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export class SigningSession {
|
|
|
9
9
|
constructor(book: string[]);
|
|
10
10
|
nextNonce(key: string): bigint | undefined;
|
|
11
11
|
outcome(key: string, slot: bigint, accepted: boolean): void;
|
|
12
|
+
release(key: string, slot: bigint): void;
|
|
13
|
+
releasePending(key: string): void;
|
|
12
14
|
rememberAddress(address: string): void;
|
|
13
15
|
signed(key: string, slot: bigint): void;
|
|
14
16
|
slot(key: string, reported: bigint, expected: bigint | null | undefined, order: boolean): bigint;
|
package/pkg-node/qcore_js.js
CHANGED
|
@@ -67,6 +67,23 @@ class SigningSession {
|
|
|
67
67
|
const len0 = WASM_VECTOR_LEN;
|
|
68
68
|
wasm.signingsession_outcome(this.__wbg_ptr, ptr0, len0, slot, accepted);
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} key
|
|
72
|
+
* @param {bigint} slot
|
|
73
|
+
*/
|
|
74
|
+
release(key, slot) {
|
|
75
|
+
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
76
|
+
const len0 = WASM_VECTOR_LEN;
|
|
77
|
+
wasm.signingsession_release(this.__wbg_ptr, ptr0, len0, slot);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* @param {string} key
|
|
81
|
+
*/
|
|
82
|
+
releasePending(key) {
|
|
83
|
+
const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
84
|
+
const len0 = WASM_VECTOR_LEN;
|
|
85
|
+
wasm.signingsession_releasePending(this.__wbg_ptr, ptr0, len0);
|
|
86
|
+
}
|
|
70
87
|
/**
|
|
71
88
|
* @param {string} address
|
|
72
89
|
*/
|
|
Binary file
|
package/shared.js
CHANGED
|
@@ -59,6 +59,20 @@ function wholeNumber(value, label, max) {
|
|
|
59
59
|
return n;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const ACCOUNT_LOCKS = new Map();
|
|
63
|
+
|
|
64
|
+
function requireVerifiedTls() {
|
|
65
|
+
const env = typeof process !== 'undefined' && process && process.env ? process.env : null;
|
|
66
|
+
if (env && env.NODE_TLS_REJECT_UNAUTHORIZED === '0') {
|
|
67
|
+
throw new Error('this process has TLS certificate checks turned off with NODE_TLS_REJECT_UNAUTHORIZED=0, refusing to talk to a gateway');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function shown(value) {
|
|
72
|
+
const text = String(value).replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g, '');
|
|
73
|
+
return text.length > 96 ? text.slice(0, 96) + '...' : text;
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
function coreArg(kind, label, value) {
|
|
63
77
|
if (kind === 'text') {
|
|
64
78
|
if (typeof value !== 'string') throw new Error(`the ${label} must be a string`);
|
|
@@ -343,7 +357,10 @@ function makeClient(core) {
|
|
|
343
357
|
this._session = new core.SigningSession(book);
|
|
344
358
|
this._pinnedChainName = null;
|
|
345
359
|
this._headFloor = null;
|
|
346
|
-
|
|
360
|
+
if (opts.fetch != null && typeof opts.fetch !== 'function') throw new Error('the fetch option must be a function with the fetch signature');
|
|
361
|
+
this._fetch = opts.fetch || ((url, init) => fetch(url, init));
|
|
362
|
+
requireVerifiedTls();
|
|
363
|
+
this._locks = ACCOUNT_LOCKS;
|
|
347
364
|
for (const name of ['transfer', 'register', 'call', 'assetCall', 'payableCall']) {
|
|
348
365
|
const inner = this[name].bind(this);
|
|
349
366
|
this[name] = (seed, index, ...rest) => {
|
|
@@ -392,18 +409,18 @@ function makeClient(core) {
|
|
|
392
409
|
const mainnet = isMainnetChain(name);
|
|
393
410
|
if (configured) {
|
|
394
411
|
if (name !== configured) {
|
|
395
|
-
throw new Error(`the gateway reports chain ${name} but this client is configured for ${configured}; refusing to sign a transaction that would be valid on a network you did not choose`);
|
|
412
|
+
throw new Error(`the gateway reports chain ${shown(name)} but this client is configured for ${configured}; refusing to sign a transaction that would be valid on a network you did not choose`);
|
|
396
413
|
}
|
|
397
414
|
} else if (isPublicChain(name) && !(mainnet && this.acknowledgeMainnet)) {
|
|
398
|
-
throw new Error(`the gateway reports the public chain ${name} but this client was opened for an unnamed network; configure the testnet or mainnet network before signing for it`);
|
|
415
|
+
throw new Error(`the gateway reports the public chain ${shown(name)} but this client was opened for an unnamed network; configure the testnet or mainnet network before signing for it`);
|
|
399
416
|
}
|
|
400
417
|
if (mainnet && !this.acknowledgeMainnet) {
|
|
401
|
-
throw new Error(`the gateway reports the mainnet chain ${name}; refusing to sign a mainnet transaction without acknowledgeMainnet`);
|
|
418
|
+
throw new Error(`the gateway reports the mainnet chain ${shown(name)}; refusing to sign a mainnet transaction without acknowledgeMainnet`);
|
|
402
419
|
}
|
|
403
420
|
if (this._pinnedChainName === null) {
|
|
404
421
|
this._pinnedChainName = name;
|
|
405
422
|
} else if (this._pinnedChainName !== name) {
|
|
406
|
-
throw new Error(`the gateway reported chain ${this._pinnedChainName} earlier and now reports ${name}; refusing to sign, the endpoint is not naming one network`);
|
|
423
|
+
throw new Error(`the gateway reported chain ${shown(this._pinnedChainName)} earlier and now reports ${shown(name)}; refusing to sign, the endpoint is not naming one network`);
|
|
407
424
|
}
|
|
408
425
|
return BigInt(core.chainIdFromName(name));
|
|
409
426
|
}
|
|
@@ -412,7 +429,7 @@ function makeClient(core) {
|
|
|
412
429
|
const controller = new AbortController();
|
|
413
430
|
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
414
431
|
try {
|
|
415
|
-
const res = await
|
|
432
|
+
const res = await this._fetch(this.base + '/v1/' + method, {
|
|
416
433
|
method: 'POST',
|
|
417
434
|
headers: { 'Content-Type': 'application/json' },
|
|
418
435
|
body: body || '{}',
|
|
@@ -425,7 +442,7 @@ function makeClient(core) {
|
|
|
425
442
|
try { data = JSON.parse(text); } catch { throw new Error('the node returned a non JSON response'); }
|
|
426
443
|
if (res.status !== 200) {
|
|
427
444
|
const detail = data && typeof data === 'object' ? (data.message || data.error) : null;
|
|
428
|
-
throw new Error(detail
|
|
445
|
+
throw new Error(detail ? shown(detail) : ('status ' + res.status));
|
|
429
446
|
}
|
|
430
447
|
return data;
|
|
431
448
|
} finally {
|
|
@@ -441,7 +458,7 @@ function makeClient(core) {
|
|
|
441
458
|
throw new Error(`the gateway answered about ${address} without naming the account, refusing to trust it`);
|
|
442
459
|
}
|
|
443
460
|
if (acct.address !== address) {
|
|
444
|
-
throw new Error(`the gateway answered for ${acct.address} when asked about ${address}, refusing to trust it`);
|
|
461
|
+
throw new Error(`the gateway answered for ${shown(acct.address)} when asked about ${address}, refusing to trust it`);
|
|
445
462
|
}
|
|
446
463
|
return acct;
|
|
447
464
|
}
|
|
@@ -462,6 +479,7 @@ function makeClient(core) {
|
|
|
462
479
|
async _accountSlot(from, expectedNonce) {
|
|
463
480
|
const acct = await this.account(from);
|
|
464
481
|
const reported = gatewayInteger(acct.nonce, 'nonce');
|
|
482
|
+
this._session.releasePending(from);
|
|
465
483
|
return BigInt(this._session.slot(from, reported, this._expected(expectedNonce, 'expected nonce'), false));
|
|
466
484
|
}
|
|
467
485
|
|
|
@@ -469,7 +487,7 @@ function makeClient(core) {
|
|
|
469
487
|
for (const [key, slot] of slots) this._session.signed(key, slot);
|
|
470
488
|
const outcome = await this.submit(txHex);
|
|
471
489
|
const verdict = outcome && outcome.verdict;
|
|
472
|
-
if (verdict !== 'accepted' && verdict !== 'rejected') throw new Error(`unknown verdict ${verdict}`);
|
|
490
|
+
if (verdict !== 'accepted' && verdict !== 'rejected') throw new Error(`unknown verdict ${shown(verdict)}`);
|
|
473
491
|
for (const [key, slot] of slots) this._session.outcome(key, slot, verdict === 'accepted');
|
|
474
492
|
return outcome;
|
|
475
493
|
}
|
|
@@ -649,6 +667,7 @@ function makeClient(core) {
|
|
|
649
667
|
const signer = core.orderSigner(ownerSeed, accountIndex(ownerIndex));
|
|
650
668
|
const orderKey = contract + '/' + signer;
|
|
651
669
|
const reportedOrder = await this.contractNonce(contract, signer);
|
|
670
|
+
this._session.releasePending(orderKey);
|
|
652
671
|
const nonce = BigInt(this._session.slot(orderKey, reportedOrder, this._expected(expectedOrderNonce, 'expected order nonce'), true));
|
|
653
672
|
if (typeof orderSpec.layoutHash !== 'string') throw new Error('the order spec needs the layout hash the compiler emits for this entry');
|
|
654
673
|
const orderExpiry = wholeNumber(orderSpec.expiry, 'order expiry', U64_MAX);
|
package/shared.mjs
CHANGED
|
@@ -59,6 +59,20 @@ function wholeNumber(value, label, max) {
|
|
|
59
59
|
return n;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
const ACCOUNT_LOCKS = new Map();
|
|
63
|
+
|
|
64
|
+
function requireVerifiedTls() {
|
|
65
|
+
const env = typeof process !== 'undefined' && process && process.env ? process.env : null;
|
|
66
|
+
if (env && env.NODE_TLS_REJECT_UNAUTHORIZED === '0') {
|
|
67
|
+
throw new Error('this process has TLS certificate checks turned off with NODE_TLS_REJECT_UNAUTHORIZED=0, refusing to talk to a gateway');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function shown(value) {
|
|
72
|
+
const text = String(value).replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g, '');
|
|
73
|
+
return text.length > 96 ? text.slice(0, 96) + '...' : text;
|
|
74
|
+
}
|
|
75
|
+
|
|
62
76
|
function coreArg(kind, label, value) {
|
|
63
77
|
if (kind === 'text') {
|
|
64
78
|
if (typeof value !== 'string') throw new Error(`the ${label} must be a string`);
|
|
@@ -343,7 +357,10 @@ function makeClient(core) {
|
|
|
343
357
|
this._session = new core.SigningSession(book);
|
|
344
358
|
this._pinnedChainName = null;
|
|
345
359
|
this._headFloor = null;
|
|
346
|
-
|
|
360
|
+
if (opts.fetch != null && typeof opts.fetch !== 'function') throw new Error('the fetch option must be a function with the fetch signature');
|
|
361
|
+
this._fetch = opts.fetch || ((url, init) => fetch(url, init));
|
|
362
|
+
requireVerifiedTls();
|
|
363
|
+
this._locks = ACCOUNT_LOCKS;
|
|
347
364
|
for (const name of ['transfer', 'register', 'call', 'assetCall', 'payableCall']) {
|
|
348
365
|
const inner = this[name].bind(this);
|
|
349
366
|
this[name] = (seed, index, ...rest) => {
|
|
@@ -392,18 +409,18 @@ function makeClient(core) {
|
|
|
392
409
|
const mainnet = isMainnetChain(name);
|
|
393
410
|
if (configured) {
|
|
394
411
|
if (name !== configured) {
|
|
395
|
-
throw new Error(`the gateway reports chain ${name} but this client is configured for ${configured}; refusing to sign a transaction that would be valid on a network you did not choose`);
|
|
412
|
+
throw new Error(`the gateway reports chain ${shown(name)} but this client is configured for ${configured}; refusing to sign a transaction that would be valid on a network you did not choose`);
|
|
396
413
|
}
|
|
397
414
|
} else if (isPublicChain(name) && !(mainnet && this.acknowledgeMainnet)) {
|
|
398
|
-
throw new Error(`the gateway reports the public chain ${name} but this client was opened for an unnamed network; configure the testnet or mainnet network before signing for it`);
|
|
415
|
+
throw new Error(`the gateway reports the public chain ${shown(name)} but this client was opened for an unnamed network; configure the testnet or mainnet network before signing for it`);
|
|
399
416
|
}
|
|
400
417
|
if (mainnet && !this.acknowledgeMainnet) {
|
|
401
|
-
throw new Error(`the gateway reports the mainnet chain ${name}; refusing to sign a mainnet transaction without acknowledgeMainnet`);
|
|
418
|
+
throw new Error(`the gateway reports the mainnet chain ${shown(name)}; refusing to sign a mainnet transaction without acknowledgeMainnet`);
|
|
402
419
|
}
|
|
403
420
|
if (this._pinnedChainName === null) {
|
|
404
421
|
this._pinnedChainName = name;
|
|
405
422
|
} else if (this._pinnedChainName !== name) {
|
|
406
|
-
throw new Error(`the gateway reported chain ${this._pinnedChainName} earlier and now reports ${name}; refusing to sign, the endpoint is not naming one network`);
|
|
423
|
+
throw new Error(`the gateway reported chain ${shown(this._pinnedChainName)} earlier and now reports ${shown(name)}; refusing to sign, the endpoint is not naming one network`);
|
|
407
424
|
}
|
|
408
425
|
return BigInt(core.chainIdFromName(name));
|
|
409
426
|
}
|
|
@@ -412,7 +429,7 @@ function makeClient(core) {
|
|
|
412
429
|
const controller = new AbortController();
|
|
413
430
|
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
414
431
|
try {
|
|
415
|
-
const res = await
|
|
432
|
+
const res = await this._fetch(this.base + '/v1/' + method, {
|
|
416
433
|
method: 'POST',
|
|
417
434
|
headers: { 'Content-Type': 'application/json' },
|
|
418
435
|
body: body || '{}',
|
|
@@ -425,7 +442,7 @@ function makeClient(core) {
|
|
|
425
442
|
try { data = JSON.parse(text); } catch { throw new Error('the node returned a non JSON response'); }
|
|
426
443
|
if (res.status !== 200) {
|
|
427
444
|
const detail = data && typeof data === 'object' ? (data.message || data.error) : null;
|
|
428
|
-
throw new Error(detail
|
|
445
|
+
throw new Error(detail ? shown(detail) : ('status ' + res.status));
|
|
429
446
|
}
|
|
430
447
|
return data;
|
|
431
448
|
} finally {
|
|
@@ -441,7 +458,7 @@ function makeClient(core) {
|
|
|
441
458
|
throw new Error(`the gateway answered about ${address} without naming the account, refusing to trust it`);
|
|
442
459
|
}
|
|
443
460
|
if (acct.address !== address) {
|
|
444
|
-
throw new Error(`the gateway answered for ${acct.address} when asked about ${address}, refusing to trust it`);
|
|
461
|
+
throw new Error(`the gateway answered for ${shown(acct.address)} when asked about ${address}, refusing to trust it`);
|
|
445
462
|
}
|
|
446
463
|
return acct;
|
|
447
464
|
}
|
|
@@ -462,6 +479,7 @@ function makeClient(core) {
|
|
|
462
479
|
async _accountSlot(from, expectedNonce) {
|
|
463
480
|
const acct = await this.account(from);
|
|
464
481
|
const reported = gatewayInteger(acct.nonce, 'nonce');
|
|
482
|
+
this._session.releasePending(from);
|
|
465
483
|
return BigInt(this._session.slot(from, reported, this._expected(expectedNonce, 'expected nonce'), false));
|
|
466
484
|
}
|
|
467
485
|
|
|
@@ -469,7 +487,7 @@ function makeClient(core) {
|
|
|
469
487
|
for (const [key, slot] of slots) this._session.signed(key, slot);
|
|
470
488
|
const outcome = await this.submit(txHex);
|
|
471
489
|
const verdict = outcome && outcome.verdict;
|
|
472
|
-
if (verdict !== 'accepted' && verdict !== 'rejected') throw new Error(`unknown verdict ${verdict}`);
|
|
490
|
+
if (verdict !== 'accepted' && verdict !== 'rejected') throw new Error(`unknown verdict ${shown(verdict)}`);
|
|
473
491
|
for (const [key, slot] of slots) this._session.outcome(key, slot, verdict === 'accepted');
|
|
474
492
|
return outcome;
|
|
475
493
|
}
|
|
@@ -649,6 +667,7 @@ function makeClient(core) {
|
|
|
649
667
|
const signer = core.orderSigner(ownerSeed, accountIndex(ownerIndex));
|
|
650
668
|
const orderKey = contract + '/' + signer;
|
|
651
669
|
const reportedOrder = await this.contractNonce(contract, signer);
|
|
670
|
+
this._session.releasePending(orderKey);
|
|
652
671
|
const nonce = BigInt(this._session.slot(orderKey, reportedOrder, this._expected(expectedOrderNonce, 'expected order nonce'), true));
|
|
653
672
|
if (typeof orderSpec.layoutHash !== 'string') throw new Error('the order spec needs the layout hash the compiler emits for this entry');
|
|
654
673
|
const orderExpiry = wholeNumber(orderSpec.expiry, 'order expiry', U64_MAX);
|