@quantovainc/qcore 0.2.2 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantovainc/qcore",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Quantova's post quantum client core for JavaScript. Signs with ML-DSA-65 and builds Q1 addresses.",
5
5
  "scripts": {
6
6
  "build": "bash scripts/build.sh",
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.2.2",
5
+ "version": "0.2.3",
6
6
  "license": "Apache-2.0 OR MIT",
7
7
  "repository": {
8
8
  "type": "git",
Binary file
@@ -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.2.2",
4
+ "version": "0.2.3",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "repository": {
7
7
  "type": "git",
Binary file
package/shared.js CHANGED
@@ -8,14 +8,7 @@ function feeCeiling(maxFeeQuon) {
8
8
  if (typeof maxFeeQuon === 'number') {
9
9
  throw new Error('pass the maximum fee as a decimal string or a BigInt, never a JavaScript number, because a number silently rounds above 2^53 and could set the ceiling higher than you intended');
10
10
  }
11
- let ceiling;
12
- try {
13
- ceiling = BigInt(maxFeeQuon);
14
- } catch {
15
- throw new Error('the maximum fee must be an integer number of Quon');
16
- }
17
- if (ceiling < 0n) throw new Error('the maximum fee cannot be negative');
18
- return ceiling;
11
+ return wholeNumber(maxFeeQuon, 'maximum fee', U128_MAX);
19
12
  }
20
13
 
21
14
  function checkAmount(amount) {
@@ -28,41 +21,18 @@ function checkAmount(amount) {
28
21
  }
29
22
 
30
23
  function accountIndex(index) {
31
- if (typeof index === 'number' && !Number.isSafeInteger(index)) {
32
- throw new Error('the account index must be a whole number in the safe integer range, a number that large silently rounds and would sign with a different account key');
33
- }
34
- let i;
35
- try {
36
- i = BigInt(index);
37
- } catch {
38
- throw new Error('the account index must be a whole number');
39
- }
40
- if (i < 0n || i > 0xffffffffffffffffn) {
41
- throw new Error('the account index must fit in an unsigned 64 bit integer');
42
- }
43
- return i;
24
+ return wholeNumber(index, 'account index', U64_MAX);
44
25
  }
45
26
 
46
27
  function accountNonce(nonce) {
47
- if (typeof nonce === 'number' && !Number.isSafeInteger(nonce)) {
48
- throw new Error('the gateway reported a nonce outside the safe integer range, a number that large silently rounds and would sign a different nonce');
49
- }
50
- let n;
51
- try {
52
- n = BigInt(nonce);
53
- } catch {
54
- throw new Error('the gateway reported a nonce that is not a whole number');
55
- }
56
- if (n < 0n || n > 0xffffffffffffffffn) {
57
- throw new Error('the gateway reported a nonce outside the unsigned 64 bit range');
58
- }
59
- return n;
28
+ return wholeNumber(nonce, 'nonce', U64_MAX);
60
29
  }
61
30
 
62
31
  const VALIDITY_BLOCKS = 300n;
63
32
  const MAX_PLAUSIBLE_HEAD = 1n << 40n;
64
33
  const HEAD_BLOCKS_PER_SEC = 4n;
65
34
  const HEAD_SLACK_SECS = 60n;
35
+ const GENESIS_FLOOR_SECS = 1735689600n;
66
36
  const U64_MAX = 0xffffffffffffffffn;
67
37
  const U128_MAX = (1n << 128n) - 1n;
68
38
 
@@ -141,19 +111,7 @@ function isPublicChain(name) {
141
111
  }
142
112
 
143
113
  function meterLimitOf(meterLimit) {
144
- if (typeof meterLimit === 'number' && !Number.isSafeInteger(meterLimit)) {
145
- throw new Error('the meter limit must be a whole number in the safe integer range');
146
- }
147
- let meter;
148
- try {
149
- meter = BigInt(meterLimit);
150
- } catch {
151
- throw new Error('the meter limit must be a whole number');
152
- }
153
- if (meter < 0n || meter > 0xffffffffffffffffn) {
154
- throw new Error('the meter limit must fit in an unsigned 64 bit integer');
155
- }
156
- return meter;
114
+ return wholeNumber(meterLimit, 'meter limit', U64_MAX);
157
115
  }
158
116
 
159
117
  function validUntil(info) {
@@ -175,17 +133,7 @@ function validUntil(info) {
175
133
  }
176
134
 
177
135
  function gatewayFee(fee) {
178
- if (typeof fee === 'number' && !Number.isSafeInteger(fee)) {
179
- throw new Error('the gateway reported a fee outside the safe integer range, a number that large silently rounds and would sign a different fee');
180
- }
181
- let f;
182
- try {
183
- f = BigInt(fee);
184
- } catch {
185
- throw new Error('the gateway reported a fee that is not a whole number');
186
- }
187
- if (f < 0n) throw new Error('the gateway reported a negative fee');
188
- return f;
136
+ return wholeNumber(fee, 'fee', U128_MAX);
189
137
  }
190
138
 
191
139
  async function readBounded(res) {
@@ -435,9 +383,6 @@ function makeClient(core) {
435
383
  const next = BigInt(used) + 1n;
436
384
  const local = this._nextNonces.get(key);
437
385
  if (local == null || local < next) this._nextNonces.set(key, next);
438
- } else if (verdict === 'rejected') {
439
- const held = this._signedNonces && this._signedNonces.get(key);
440
- if (held) held.delete(String(used));
441
386
  }
442
387
  }
443
388
 
@@ -445,6 +390,10 @@ function makeClient(core) {
445
390
  const until = validUntil(info);
446
391
  const head = until - VALIDITY_BLOCKS;
447
392
  const now = BigInt(Math.floor(Date.now() / 1000));
393
+ if (now > GENESIS_FLOOR_SECS) {
394
+ const maxHead = (now - GENESIS_FLOOR_SECS + HEAD_SLACK_SECS) * HEAD_BLOCKS_PER_SEC;
395
+ if (head > maxHead) throw new Error(`the gateway reports head ${head} further ahead than wall-clock time allows, refusing to sign`);
396
+ }
448
397
  if (this._headFloor) {
449
398
  const { height, at } = this._headFloor;
450
399
  if (head < height) throw new Error(`the gateway reports head ${head} below the ${height} it reported earlier, refusing to sign`);
package/shared.mjs CHANGED
@@ -8,14 +8,7 @@ function feeCeiling(maxFeeQuon) {
8
8
  if (typeof maxFeeQuon === 'number') {
9
9
  throw new Error('pass the maximum fee as a decimal string or a BigInt, never a JavaScript number, because a number silently rounds above 2^53 and could set the ceiling higher than you intended');
10
10
  }
11
- let ceiling;
12
- try {
13
- ceiling = BigInt(maxFeeQuon);
14
- } catch {
15
- throw new Error('the maximum fee must be an integer number of Quon');
16
- }
17
- if (ceiling < 0n) throw new Error('the maximum fee cannot be negative');
18
- return ceiling;
11
+ return wholeNumber(maxFeeQuon, 'maximum fee', U128_MAX);
19
12
  }
20
13
 
21
14
  function checkAmount(amount) {
@@ -28,41 +21,18 @@ function checkAmount(amount) {
28
21
  }
29
22
 
30
23
  function accountIndex(index) {
31
- if (typeof index === 'number' && !Number.isSafeInteger(index)) {
32
- throw new Error('the account index must be a whole number in the safe integer range, a number that large silently rounds and would sign with a different account key');
33
- }
34
- let i;
35
- try {
36
- i = BigInt(index);
37
- } catch {
38
- throw new Error('the account index must be a whole number');
39
- }
40
- if (i < 0n || i > 0xffffffffffffffffn) {
41
- throw new Error('the account index must fit in an unsigned 64 bit integer');
42
- }
43
- return i;
24
+ return wholeNumber(index, 'account index', U64_MAX);
44
25
  }
45
26
 
46
27
  function accountNonce(nonce) {
47
- if (typeof nonce === 'number' && !Number.isSafeInteger(nonce)) {
48
- throw new Error('the gateway reported a nonce outside the safe integer range, a number that large silently rounds and would sign a different nonce');
49
- }
50
- let n;
51
- try {
52
- n = BigInt(nonce);
53
- } catch {
54
- throw new Error('the gateway reported a nonce that is not a whole number');
55
- }
56
- if (n < 0n || n > 0xffffffffffffffffn) {
57
- throw new Error('the gateway reported a nonce outside the unsigned 64 bit range');
58
- }
59
- return n;
28
+ return wholeNumber(nonce, 'nonce', U64_MAX);
60
29
  }
61
30
 
62
31
  const VALIDITY_BLOCKS = 300n;
63
32
  const MAX_PLAUSIBLE_HEAD = 1n << 40n;
64
33
  const HEAD_BLOCKS_PER_SEC = 4n;
65
34
  const HEAD_SLACK_SECS = 60n;
35
+ const GENESIS_FLOOR_SECS = 1735689600n;
66
36
  const U64_MAX = 0xffffffffffffffffn;
67
37
  const U128_MAX = (1n << 128n) - 1n;
68
38
 
@@ -141,19 +111,7 @@ function isPublicChain(name) {
141
111
  }
142
112
 
143
113
  function meterLimitOf(meterLimit) {
144
- if (typeof meterLimit === 'number' && !Number.isSafeInteger(meterLimit)) {
145
- throw new Error('the meter limit must be a whole number in the safe integer range');
146
- }
147
- let meter;
148
- try {
149
- meter = BigInt(meterLimit);
150
- } catch {
151
- throw new Error('the meter limit must be a whole number');
152
- }
153
- if (meter < 0n || meter > 0xffffffffffffffffn) {
154
- throw new Error('the meter limit must fit in an unsigned 64 bit integer');
155
- }
156
- return meter;
114
+ return wholeNumber(meterLimit, 'meter limit', U64_MAX);
157
115
  }
158
116
 
159
117
  function validUntil(info) {
@@ -175,17 +133,7 @@ function validUntil(info) {
175
133
  }
176
134
 
177
135
  function gatewayFee(fee) {
178
- if (typeof fee === 'number' && !Number.isSafeInteger(fee)) {
179
- throw new Error('the gateway reported a fee outside the safe integer range, a number that large silently rounds and would sign a different fee');
180
- }
181
- let f;
182
- try {
183
- f = BigInt(fee);
184
- } catch {
185
- throw new Error('the gateway reported a fee that is not a whole number');
186
- }
187
- if (f < 0n) throw new Error('the gateway reported a negative fee');
188
- return f;
136
+ return wholeNumber(fee, 'fee', U128_MAX);
189
137
  }
190
138
 
191
139
  async function readBounded(res) {
@@ -435,9 +383,6 @@ function makeClient(core) {
435
383
  const next = BigInt(used) + 1n;
436
384
  const local = this._nextNonces.get(key);
437
385
  if (local == null || local < next) this._nextNonces.set(key, next);
438
- } else if (verdict === 'rejected') {
439
- const held = this._signedNonces && this._signedNonces.get(key);
440
- if (held) held.delete(String(used));
441
386
  }
442
387
  }
443
388
 
@@ -445,6 +390,10 @@ function makeClient(core) {
445
390
  const until = validUntil(info);
446
391
  const head = until - VALIDITY_BLOCKS;
447
392
  const now = BigInt(Math.floor(Date.now() / 1000));
393
+ if (now > GENESIS_FLOOR_SECS) {
394
+ const maxHead = (now - GENESIS_FLOOR_SECS + HEAD_SLACK_SECS) * HEAD_BLOCKS_PER_SEC;
395
+ if (head > maxHead) throw new Error(`the gateway reports head ${head} further ahead than wall-clock time allows, refusing to sign`);
396
+ }
448
397
  if (this._headFloor) {
449
398
  const { height, at } = this._headFloor;
450
399
  if (head < height) throw new Error(`the gateway reports head ${head} below the ${height} it reported earlier, refusing to sign`);