@quantovainc/qcore 0.1.4 → 0.1.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 +69 -7
- package/index.d.ts +1 -0
- package/package.json +7 -1
- package/pkg/package.json +1 -1
- package/pkg/qcore_js.d.ts +1 -1
- package/pkg/qcore_js_bg.js +3 -2
- package/pkg/qcore_js_bg.wasm +0 -0
- package/pkg/qcore_js_bg.wasm.d.ts +1 -1
- package/pkg-node/package.json +1 -1
- package/pkg-node/qcore_js.d.ts +1 -1
- package/pkg-node/qcore_js.js +3 -2
- package/pkg-node/qcore_js_bg.wasm +0 -0
- package/shared.js +9 -8
package/README.md
CHANGED
|
@@ -1,14 +1,56 @@
|
|
|
1
1
|
# QCore.js
|
|
2
2
|
|
|
3
|
+
[](https://github.com/Quantova/QCore.js/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@quantovainc/qcore)
|
|
5
|
+
|
|
6
|
+
The post quantum client core for Quantova. It signs with ML-DSA-65, builds Q1 addresses, and runs the same compiled core in the browser and in Node.
|
|
7
|
+
|
|
3
8
|
**This npm account and this package belong to Quantova Inc. It is the official Quantova client SDK. Any similarly named package published under a different account is not this one and is not from Quantova.**
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
npm install @quantovainc/qcore
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quickstart
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const { Client, core, generateSeed } = require('@quantovainc/qcore');
|
|
20
|
+
|
|
21
|
+
async function main() {
|
|
22
|
+
// Create an account. The seed is the only backup and it never leaves the device.
|
|
23
|
+
const seed = generateSeed();
|
|
24
|
+
const phrase = core.mnemonicFromSeed(seed);
|
|
25
|
+
|
|
26
|
+
// Derive the Q1 address for account index zero, and a recipient at index one.
|
|
27
|
+
const from = core.address(seed, 0n);
|
|
28
|
+
const to = core.address(seed, 1n);
|
|
29
|
+
console.log('from', from);
|
|
30
|
+
|
|
31
|
+
// Open a client against a node you trust. Plaintext http is allowed only to a loopback node.
|
|
32
|
+
const client = new Client('http://127.0.0.1:8645');
|
|
6
33
|
|
|
7
|
-
|
|
34
|
+
// A fixed ceiling your app is willing to pay in fees, chosen here and never read back from the gateway.
|
|
35
|
+
const MAX_FEE_QUON = '2000';
|
|
36
|
+
|
|
37
|
+
// Amounts and the ceiling are decimal strings or BigInt values, never JavaScript numbers.
|
|
38
|
+
const { signed, outcome } = await client.transfer(seed, 0, to, '1000', MAX_FEE_QUON);
|
|
39
|
+
console.log('submitted', signed.tx_id, outcome.verdict);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
main().catch((err) => { console.error(err.message); process.exit(1); });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
A runnable copy of this lives in [examples/quickstart.js](examples/quickstart.js).
|
|
46
|
+
|
|
47
|
+
## Security posture
|
|
48
|
+
|
|
49
|
+
Every signature is ML-DSA-65 as standardised in FIPS 204, and addresses and transaction digests are built with SHA-3. There is no elliptic curve anywhere in this package, no secp256k1 and no ECDSA. When you send through the Client you pass a maximum fee that you choose yourself, and the Client refuses to sign when the gateway reports a fee above it, so a hostile gateway cannot inflate the fee and drain the balance. The Client also refuses an unsafe transport. A remote gateway must be reached over https, and plaintext http is allowed only to a loopback node, because a fee and a nonce read back over plaintext could be rewritten on the wire. This package is at the testnet and pre audit stage, so please do not read it as audited.
|
|
8
50
|
|
|
9
51
|
## What this library is for
|
|
10
52
|
|
|
11
|
-
Any browser app, extension, or Node service that holds a Quantova account, reads the chain, and sends signed transactions. A wallet, a dApp, a block explorer front end, or a faucet page. Your code does the network fetch it prefers and QCore.js derives the key, signs the transaction, and builds the request body, so nothing about the cryptography or the byte layout of a transaction is written in JavaScript.
|
|
53
|
+
Any browser app, extension, or Node service that holds a Quantova account, reads the chain, and sends signed transactions. A wallet, a dApp, a block explorer front end, or a faucet page. Your code does the network fetch it prefers and QCore.js derives the key, signs the transaction, and builds the request body, so nothing about the cryptography or the byte layout of a transaction is written in JavaScript. A second implementation of the signing would be a second chance to be wrong with a user's money, so there is only ever the one.
|
|
12
54
|
|
|
13
55
|
## The post quantum cryptography it handles
|
|
14
56
|
|
|
@@ -100,6 +142,10 @@ const signed = JSON.parse(
|
|
|
100
142
|
|
|
101
143
|
The compiled core in this package is only the shipping form of the one Rust core, built so the same key derivation, signing, and wire code runs in a browser and in Node. It is not a virtual machine and it is not what the chain runs. The Quantova chain executes only the QVM, the container machine that runs contracts. The compiled core here is a client side build artifact and nothing more.
|
|
102
144
|
|
|
145
|
+
## TypeScript
|
|
146
|
+
|
|
147
|
+
The package ships TypeScript types in `index.d.ts`, so the Client, the core functions, and the Network helpers are all typed with no extra install.
|
|
148
|
+
|
|
103
149
|
## Builds
|
|
104
150
|
|
|
105
151
|
The package ships one guarded surface, the Client with the fee cap and the amount guard, over two
|
|
@@ -115,17 +161,33 @@ and `@quantovainc/qcore/pkg-node` for the nodejs build. Those export only the co
|
|
|
115
161
|
Client, no fee cap, and no amount guard, and are for an advanced caller who assembles the fee comparison
|
|
116
162
|
alone. The default browser, import, node, and require resolutions never reach them.
|
|
117
163
|
|
|
118
|
-
To build both from source
|
|
164
|
+
To build both from source, run the two documented builds or `npm run build`.
|
|
119
165
|
|
|
120
166
|
```
|
|
121
167
|
wasm-pack build --target bundler --out-dir pkg
|
|
122
168
|
wasm-pack build --target nodejs --out-dir pkg-node
|
|
123
169
|
```
|
|
124
170
|
|
|
125
|
-
|
|
171
|
+
The offline test scripts run with `npm test`, and they need no network and no running gateway.
|
|
172
|
+
|
|
173
|
+
## Conformance vectors
|
|
174
|
+
|
|
175
|
+
The frozen conformance vectors live in the [conformance](conformance) folder of this repository. The address derivation is in `conformance/address.derivation.json`, the transfer in `conformance/transaction.transfer.json`, and the payable call in `conformance/transaction.payable.json`. The offline test `test-conformance.js` signs from these vectors and checks the binding matches them byte for byte, so a change that would move a signed transaction fails the test.
|
|
176
|
+
|
|
177
|
+
## Examples
|
|
178
|
+
|
|
179
|
+
The [examples](examples) folder holds a runnable quickstart that mirrors the one above. For fuller starters, see the Q-Scaffold and the Q-Forge repositories under the Quantova org, at https://github.com/Quantova/Q-Scaffold and https://github.com/Quantova/Q-Forge.
|
|
180
|
+
|
|
181
|
+
## Releases and provenance
|
|
182
|
+
|
|
183
|
+
Releases are published from CI by the publish workflow in `.github/workflows/publish.yml`, which runs on a pushed git tag. It builds the two wasm targets, runs the offline tests, and publishes with npm provenance, so the copy of the package on the registry carries a verified provenance badge that ties it to this exact public commit. Nothing is published to a registry without an explicit tag. This package handles a user's keys and a published version cannot be taken back.
|
|
184
|
+
|
|
185
|
+
For the provenance publish to run, the maintainer adds an `NPM_TOKEN` automation secret to the repository, or configures npm trusted publishing for the package so the workflow authenticates over OIDC with no stored token.
|
|
186
|
+
|
|
187
|
+
## Security
|
|
126
188
|
|
|
127
|
-
|
|
189
|
+
To report a vulnerability, see [SECURITY.md](SECURITY.md). Please report privately and not through a public issue.
|
|
128
190
|
|
|
129
191
|
## Ownership and license
|
|
130
192
|
|
|
131
|
-
QCore.js is built and owned by Quantova Inc, and the signing lives in one compiled core and is never rewritten in JavaScript. It carries none of the industry stack and inherits nothing from it. It is
|
|
193
|
+
QCore.js is built and owned by Quantova Inc, and the signing lives in one compiled core and is never rewritten in JavaScript. It carries none of the industry stack and inherits nothing from it. It is dual licensed under the Apache 2.0 and MIT licenses so any wallet, explorer, or service may build on it, and the copyright stays with Quantova Inc.
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantovainc/qcore",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Quantova's post quantum client core for JavaScript. Signs with ML-DSA-65 and builds Q1 addresses.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "wasm-pack build --target bundler --out-dir pkg && wasm-pack build --target nodejs --out-dir pkg-node",
|
|
7
|
+
"test": "node test-conformance.js && node test-sign-validate.js && node test-transport.js && node test-fee-cap.js"
|
|
8
|
+
},
|
|
5
9
|
"keywords": [
|
|
6
10
|
"post-quantum",
|
|
7
11
|
"quantum-resistant",
|
|
@@ -40,7 +44,9 @@
|
|
|
40
44
|
"default": "./index.js"
|
|
41
45
|
},
|
|
42
46
|
"./pkg": "./pkg/qcore_js.js",
|
|
47
|
+
"./pkg/*": "./pkg/*",
|
|
43
48
|
"./pkg-node": "./pkg-node/qcore_js.js",
|
|
49
|
+
"./pkg-node/*": "./pkg-node/*",
|
|
44
50
|
"./package.json": "./package.json"
|
|
45
51
|
},
|
|
46
52
|
"license": "(Apache-2.0 OR MIT)",
|
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.1.
|
|
5
|
+
"version": "0.1.6",
|
|
6
6
|
"license": "Apache-2.0 OR MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
package/pkg/qcore_js.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export function address(seed_hex: string, index: bigint): string;
|
|
|
7
7
|
|
|
8
8
|
export function block_by_height_body(height: bigint): string;
|
|
9
9
|
|
|
10
|
-
export function buildSignedOrderCall(contract: string, selector_hex: string, scheme_off: bigint, ptr_off: bigint, field_offs_csv: string, fields_csv: string, region_off: bigint, owner_seed_hex: string, owner_index: bigint, nonce: bigint): string;
|
|
10
|
+
export function buildSignedOrderCall(chain_id: bigint, contract: string, selector_hex: string, scheme_off: bigint, ptr_off: bigint, field_offs_csv: string, fields_csv: string, region_off: bigint, owner_seed_hex: string, owner_index: bigint, nonce: bigint): string;
|
|
11
11
|
|
|
12
12
|
export function chainIdFromName(name: string): bigint;
|
|
13
13
|
|
package/pkg/qcore_js_bg.js
CHANGED
|
@@ -61,6 +61,7 @@ export function block_by_height_body(height) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
+
* @param {bigint} chain_id
|
|
64
65
|
* @param {string} contract
|
|
65
66
|
* @param {string} selector_hex
|
|
66
67
|
* @param {bigint} scheme_off
|
|
@@ -73,7 +74,7 @@ export function block_by_height_body(height) {
|
|
|
73
74
|
* @param {bigint} nonce
|
|
74
75
|
* @returns {string}
|
|
75
76
|
*/
|
|
76
|
-
export function buildSignedOrderCall(contract, selector_hex, scheme_off, ptr_off, field_offs_csv, fields_csv, region_off, owner_seed_hex, owner_index, nonce) {
|
|
77
|
+
export function buildSignedOrderCall(chain_id, contract, selector_hex, scheme_off, ptr_off, field_offs_csv, fields_csv, region_off, owner_seed_hex, owner_index, nonce) {
|
|
77
78
|
let deferred7_0;
|
|
78
79
|
let deferred7_1;
|
|
79
80
|
try {
|
|
@@ -87,7 +88,7 @@ export function buildSignedOrderCall(contract, selector_hex, scheme_off, ptr_off
|
|
|
87
88
|
const len3 = WASM_VECTOR_LEN;
|
|
88
89
|
const ptr4 = passStringToWasm0(owner_seed_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
89
90
|
const len4 = WASM_VECTOR_LEN;
|
|
90
|
-
const ret = wasm.buildSignedOrderCall(ptr0, len0, ptr1, len1, scheme_off, ptr_off, ptr2, len2, ptr3, len3, region_off, ptr4, len4, owner_index, nonce);
|
|
91
|
+
const ret = wasm.buildSignedOrderCall(chain_id, ptr0, len0, ptr1, len1, scheme_off, ptr_off, ptr2, len2, ptr3, len3, region_off, ptr4, len4, owner_index, nonce);
|
|
91
92
|
var ptr6 = ret[0];
|
|
92
93
|
var len6 = ret[1];
|
|
93
94
|
if (ret[3]) {
|
package/pkg/qcore_js_bg.wasm
CHANGED
|
Binary file
|
|
@@ -4,7 +4,7 @@ export const memory: WebAssembly.Memory;
|
|
|
4
4
|
export const account_body: (a: number, b: number) => [number, number];
|
|
5
5
|
export const address: (a: number, b: number, c: bigint) => [number, number, number, number];
|
|
6
6
|
export const block_by_height_body: (a: bigint) => [number, number];
|
|
7
|
-
export const buildSignedOrderCall: (a:
|
|
7
|
+
export const buildSignedOrderCall: (a: bigint, b: number, c: number, d: number, e: number, f: bigint, g: bigint, h: number, i: number, j: number, k: number, l: bigint, m: number, n: number, o: bigint, p: bigint) => [number, number, number, number];
|
|
8
8
|
export const chainIdFromName: (a: number, b: number) => bigint;
|
|
9
9
|
export const contractAddress: (a: number, b: number, c: bigint) => [number, number];
|
|
10
10
|
export const eventsBody: (a: bigint) => [number, number];
|
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.1.
|
|
4
|
+
"version": "0.1.6",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/pkg-node/qcore_js.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export function address(seed_hex: string, index: bigint): string;
|
|
|
7
7
|
|
|
8
8
|
export function block_by_height_body(height: bigint): string;
|
|
9
9
|
|
|
10
|
-
export function buildSignedOrderCall(contract: string, selector_hex: string, scheme_off: bigint, ptr_off: bigint, field_offs_csv: string, fields_csv: string, region_off: bigint, owner_seed_hex: string, owner_index: bigint, nonce: bigint): string;
|
|
10
|
+
export function buildSignedOrderCall(chain_id: bigint, contract: string, selector_hex: string, scheme_off: bigint, ptr_off: bigint, field_offs_csv: string, fields_csv: string, region_off: bigint, owner_seed_hex: string, owner_index: bigint, nonce: bigint): string;
|
|
11
11
|
|
|
12
12
|
export function chainIdFromName(name: string): bigint;
|
|
13
13
|
|
package/pkg-node/qcore_js.js
CHANGED
|
@@ -66,6 +66,7 @@ function block_by_height_body(height) {
|
|
|
66
66
|
exports.block_by_height_body = block_by_height_body;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
+
* @param {bigint} chain_id
|
|
69
70
|
* @param {string} contract
|
|
70
71
|
* @param {string} selector_hex
|
|
71
72
|
* @param {bigint} scheme_off
|
|
@@ -78,7 +79,7 @@ exports.block_by_height_body = block_by_height_body;
|
|
|
78
79
|
* @param {bigint} nonce
|
|
79
80
|
* @returns {string}
|
|
80
81
|
*/
|
|
81
|
-
function buildSignedOrderCall(contract, selector_hex, scheme_off, ptr_off, field_offs_csv, fields_csv, region_off, owner_seed_hex, owner_index, nonce) {
|
|
82
|
+
function buildSignedOrderCall(chain_id, contract, selector_hex, scheme_off, ptr_off, field_offs_csv, fields_csv, region_off, owner_seed_hex, owner_index, nonce) {
|
|
82
83
|
let deferred7_0;
|
|
83
84
|
let deferred7_1;
|
|
84
85
|
try {
|
|
@@ -92,7 +93,7 @@ function buildSignedOrderCall(contract, selector_hex, scheme_off, ptr_off, field
|
|
|
92
93
|
const len3 = WASM_VECTOR_LEN;
|
|
93
94
|
const ptr4 = passStringToWasm0(owner_seed_hex, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
94
95
|
const len4 = WASM_VECTOR_LEN;
|
|
95
|
-
const ret = wasm.buildSignedOrderCall(ptr0, len0, ptr1, len1, scheme_off, ptr_off, ptr2, len2, ptr3, len3, region_off, ptr4, len4, owner_index, nonce);
|
|
96
|
+
const ret = wasm.buildSignedOrderCall(chain_id, ptr0, len0, ptr1, len1, scheme_off, ptr_off, ptr2, len2, ptr3, len3, region_off, ptr4, len4, owner_index, nonce);
|
|
96
97
|
var ptr6 = ret[0];
|
|
97
98
|
var len6 = ret[1];
|
|
98
99
|
if (ret[3]) {
|
|
Binary file
|
package/shared.js
CHANGED
|
@@ -286,9 +286,18 @@ function makeClient(core) {
|
|
|
286
286
|
async callSignedOrder(callerSeedHex, callerIndex, contract, selectorHex, layout, fields, ownerSeedHex, ownerIndex, meterLimit, maxFeeQuon) {
|
|
287
287
|
if (!core.valid_address(contract)) throw new Error('the contract is not a q1 address');
|
|
288
288
|
const ceiling = feeCeiling(maxFeeQuon);
|
|
289
|
+
const info = await this.nodeInfo();
|
|
290
|
+
this._guardMainnet();
|
|
291
|
+
const chainId = signingChainId(info);
|
|
292
|
+
const fee = info && info.fee && info.fee.transfer_quon;
|
|
293
|
+
if (fee == null) throw new Error('the gateway did not report a transfer fee');
|
|
294
|
+
if (BigInt(fee) > ceiling) {
|
|
295
|
+
throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
|
|
296
|
+
}
|
|
289
297
|
const signer = core.orderSigner(ownerSeedHex, BigInt(ownerIndex));
|
|
290
298
|
const nonce = await this.contractNonce(contract, signer);
|
|
291
299
|
const order = JSON.parse(core.buildSignedOrderCall(
|
|
300
|
+
chainId,
|
|
292
301
|
contract,
|
|
293
302
|
selectorHex,
|
|
294
303
|
BigInt(layout.schemeOff),
|
|
@@ -300,14 +309,6 @@ function makeClient(core) {
|
|
|
300
309
|
BigInt(ownerIndex),
|
|
301
310
|
nonce,
|
|
302
311
|
));
|
|
303
|
-
const info = await this.nodeInfo();
|
|
304
|
-
this._guardMainnet();
|
|
305
|
-
const chainId = signingChainId(info);
|
|
306
|
-
const fee = info && info.fee && info.fee.transfer_quon;
|
|
307
|
-
if (fee == null) throw new Error('the gateway did not report a transfer fee');
|
|
308
|
-
if (BigInt(fee) > ceiling) {
|
|
309
|
-
throw new Error(`the gateway fee ${fee} is above the maximum you allowed ${maxFeeQuon}, refusing to sign`);
|
|
310
|
-
}
|
|
311
312
|
const from = core.address(callerSeedHex, BigInt(callerIndex));
|
|
312
313
|
const acct = await this.account(from);
|
|
313
314
|
if (!acct || acct.nonce == null) throw new Error('the gateway did not report a nonce');
|