@totemsdk/core 1.2.11 → 1.4.0
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/address-derivation.d.ts +36 -0
- package/dist/address-derivation.js +54 -0
- package/dist/bip39.js +1 -1
- package/dist/bytes.d.ts +11 -0
- package/dist/bytes.js +39 -0
- package/dist/canonical-hash.d.ts +1 -0
- package/dist/canonical-hash.js +12 -0
- package/dist/canonical.d.ts +6 -1
- package/dist/canonical.js +6 -5
- package/dist/cjs/address-derivation.d.ts +36 -0
- package/dist/cjs/address-derivation.js +60 -0
- package/dist/cjs/bytes.d.ts +11 -0
- package/dist/cjs/bytes.js +44 -0
- package/dist/cjs/canonical-hash.d.ts +1 -0
- package/dist/cjs/canonical-hash.js +15 -0
- package/dist/cjs/canonical.d.ts +6 -1
- package/dist/cjs/canonical.js +6 -6
- package/dist/cjs/index.d.ts +5 -2
- package/dist/cjs/index.js +60 -50
- package/dist/cjs/legacy.d.ts +15 -6
- package/dist/cjs/legacy.js +15 -6
- package/dist/cjs/mmr.d.ts +22 -0
- package/dist/cjs/mmr.js +33 -0
- package/dist/cjs/scripts/canonical.d.ts +20 -0
- package/dist/cjs/scripts/canonical.js +99 -0
- package/dist/cjs/scripts/contract-helpers.js +3 -3
- package/dist/cjs/scripts/index.d.ts +2 -1
- package/dist/cjs/scripts/index.js +4 -1
- package/dist/cjs/scripts/witness-serializer.d.ts +19 -0
- package/dist/cjs/scripts/witness-serializer.js +25 -5
- package/dist/cjs/wasm-sync.d.ts +3 -3
- package/dist/cjs/wasm-sync.js +10 -3
- package/dist/index.d.ts +5 -2
- package/dist/index.js +16 -11
- package/dist/javaStreamables.js +1 -1
- package/dist/legacy.d.ts +15 -6
- package/dist/legacy.js +15 -6
- package/dist/minima32.js +1 -1
- package/dist/mmr.d.ts +22 -0
- package/dist/mmr.js +34 -2
- package/dist/scripts/canonical.d.ts +20 -0
- package/dist/scripts/canonical.js +96 -0
- package/dist/scripts/contract-helpers.js +4 -4
- package/dist/scripts/index.d.ts +2 -1
- package/dist/scripts/index.js +2 -1
- package/dist/scripts/witness-serializer.d.ts +19 -0
- package/dist/scripts/witness-serializer.js +25 -6
- package/dist/transaction.js +1 -1
- package/dist/verify.js +1 -1
- package/dist/wasm-async.d.ts +81 -0
- package/dist/wasm-async.js +91 -0
- package/dist/wasm-sync.d.ts +3 -3
- package/dist/wasm-sync.js +7 -2
- package/dist/wots.js +1 -1
- package/package.json +47 -3
package/README.md
CHANGED
|
@@ -10,6 +10,64 @@ Zero production dependencies (only `@noble/hashes` as a peer). Provides WOTS (Wi
|
|
|
10
10
|
npm install @totemsdk/core @noble/hashes
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Bundling for edge / Workers
|
|
14
|
+
|
|
15
|
+
The root entry (`@totemsdk/core`) re-exports the synchronous WASM crypto bridge
|
|
16
|
+
(`@totemsdk/core/wasm`), which imports `@totemsdk/core-wasm` at module load. That
|
|
17
|
+
glue uses a bundler-target `import * as wasm from "./….wasm"` (and, on the Node
|
|
18
|
+
path, `fs`/`__dirname`), so a stock `esbuild` build targeting Cloudflare Workers
|
|
19
|
+
(or any async-WASM edge runtime) fails to resolve or instantiate it.
|
|
20
|
+
|
|
21
|
+
Import a wasm-free subpath instead of the root when you don't need the WASM
|
|
22
|
+
crypto:
|
|
23
|
+
|
|
24
|
+
| Need | Import | Notes |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Adapter contracts / types (`StorageAdapter`, `WebSocketFactory`, `WebSocketReadyState`, …) | `@totemsdk/core/adapters` | Same names as the root — safe drop-in. |
|
|
27
|
+
| Pure-JS crypto | `@totemsdk/core/legacy` | JS implementations are **`legacy`-prefixed** (`legacyWotsSign`, `legacyWotsVerify`, `legacyWotsKeypairFromSeed`, …); `verifySignature*` are unprefixed. **Not** a drop-in for the root's `wotsSign`. |
|
|
28
|
+
| WASM crypto (async, portable to edge/Workers/Bare) | `@totemsdk/core/wasm` | ESM-only; `await init()` once (optionally with explicit wasm bytes), then synchronous calls. Alias: `./wasm-async`. |
|
|
29
|
+
| WASM crypto (sync, Node/bundler with a `.wasm` loader) | `@totemsdk/core/wasm-sync` | Same names as the root; initializes at import. |
|
|
30
|
+
|
|
31
|
+
Immediate unblock without an SDK change (adapter-only consumers):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
esbuild --alias:@totemsdk/core=@totemsdk/core/adapters
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`@totemsdk/core` does not set `"sideEffects": false` (the WASM bridge
|
|
38
|
+
initializes at import), so bundlers will not tree-shake the WASM re-export away;
|
|
39
|
+
prefer explicit subpath imports over relying on tree-shaking.
|
|
40
|
+
|
|
41
|
+
### Deprecation & migration (RFC-031)
|
|
42
|
+
|
|
43
|
+
`@totemsdk/core/legacy` is **deprecated and frozen** — the pure-JS kernels will be
|
|
44
|
+
removed in a major release; no new kernels are added there. Prefer the WASM
|
|
45
|
+
surface (`@totemsdk/core/wasm` for synchronous use, `@totemsdk/core/wasm-async`
|
|
46
|
+
for edge/Workers). Typical migration:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// before — pure-JS, legacy-prefixed
|
|
50
|
+
import { legacyWotsSign, legacyWotsVerify } from '@totemsdk/core/legacy';
|
|
51
|
+
// after — async-init WASM, same names as the root
|
|
52
|
+
import { init, wotsSign, wotsVerify } from '@totemsdk/core/wasm-async';
|
|
53
|
+
await init();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use `/legacy` only where WASM genuinely cannot run; `verifySignature*` and the
|
|
57
|
+
adapter contracts are unaffected.
|
|
58
|
+
|
|
59
|
+
### Bare / Pear
|
|
60
|
+
|
|
61
|
+
Bare (Holepunch, V8-based) can run the portable `./wasm-async` entry, but does not
|
|
62
|
+
provide the `TextEncoder`/`TextDecoder` globals the wasm-bindgen glue uses — import
|
|
63
|
+
`bare-encoding/global` first:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import 'bare-encoding/global'; // Bare only
|
|
67
|
+
import { init, sha3_256 } from '@totemsdk/core/wasm-async';
|
|
68
|
+
await init(); // optionally with explicit wasm bytes
|
|
69
|
+
```
|
|
70
|
+
|
|
13
71
|
## What's inside
|
|
14
72
|
|
|
15
73
|
| Module | What it does |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic address derivation (CAN-005).
|
|
3
|
+
*
|
|
4
|
+
* Minima's `newaddress` is non-deterministic, and not every node build honours a
|
|
5
|
+
* `modifier:` argument, so an address taken from the node can differ across
|
|
6
|
+
* containers/restarts and break cross-container resolution. These helpers derive
|
|
7
|
+
* the address **from the seed** instead, using the same schemes the node uses, so
|
|
8
|
+
* the result is reproducible and (for the matching scheme) node-spendable.
|
|
9
|
+
*
|
|
10
|
+
* - `deriveDeterministicAddress` — Minima `Wallet.createNewKey()` parity:
|
|
11
|
+
* `privseed = hash(baseSeed, index)` → `TreeKey(privseed, 64, 3)` → pubkey → Mx.
|
|
12
|
+
* - `deriveUnifiedAddress` — RFC-037 hierarchical child key.
|
|
13
|
+
*
|
|
14
|
+
* `verifyDeterministicAddress` asserts an address a node handed back matches the
|
|
15
|
+
* seed-derived one — the fail-loud check that replaces a silent fallback.
|
|
16
|
+
*/
|
|
17
|
+
export type AddressScheme = 'per-address' | 'unified';
|
|
18
|
+
export interface DerivedAddress {
|
|
19
|
+
addressIndex: number;
|
|
20
|
+
/** 0x-hex 32-byte WOTS public key. */
|
|
21
|
+
publicKey: string;
|
|
22
|
+
/** Mx… address derived from the public key. */
|
|
23
|
+
address: string;
|
|
24
|
+
scheme: AddressScheme;
|
|
25
|
+
}
|
|
26
|
+
/** Minima Wallet.java parity: `baseSeed + index → privseed → TreeKey(64,3) → address`. */
|
|
27
|
+
export declare function deriveDeterministicAddress(baseSeed: Uint8Array, addressIndex: number): DerivedAddress;
|
|
28
|
+
/** RFC-037 unified hierarchical scheme: `baseSeed + index → child TreeKey → address`. */
|
|
29
|
+
export declare function deriveUnifiedAddress(baseSeed: Uint8Array, addressIndex: number): DerivedAddress;
|
|
30
|
+
/** Derive the address for a scheme (default: the node-parity per-address scheme). */
|
|
31
|
+
export declare function deriveAddress(baseSeed: Uint8Array, addressIndex: number, scheme?: AddressScheme): DerivedAddress;
|
|
32
|
+
/**
|
|
33
|
+
* Fail-loud check: does the node-returned `expectedAddress` equal the address
|
|
34
|
+
* derived from the seed? Use this instead of trusting `newaddress` blindly.
|
|
35
|
+
*/
|
|
36
|
+
export declare function verifyDeterministicAddress(baseSeed: Uint8Array, addressIndex: number, expectedAddress: string, scheme?: AddressScheme): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic address derivation (CAN-005).
|
|
3
|
+
*
|
|
4
|
+
* Minima's `newaddress` is non-deterministic, and not every node build honours a
|
|
5
|
+
* `modifier:` argument, so an address taken from the node can differ across
|
|
6
|
+
* containers/restarts and break cross-container resolution. These helpers derive
|
|
7
|
+
* the address **from the seed** instead, using the same schemes the node uses, so
|
|
8
|
+
* the result is reproducible and (for the matching scheme) node-spendable.
|
|
9
|
+
*
|
|
10
|
+
* - `deriveDeterministicAddress` — Minima `Wallet.createNewKey()` parity:
|
|
11
|
+
* `privseed = hash(baseSeed, index)` → `TreeKey(privseed, 64, 3)` → pubkey → Mx.
|
|
12
|
+
* - `deriveUnifiedAddress` — RFC-037 hierarchical child key.
|
|
13
|
+
*
|
|
14
|
+
* `verifyDeterministicAddress` asserts an address a node handed back matches the
|
|
15
|
+
* seed-derived one — the fail-loud check that replaces a silent fallback.
|
|
16
|
+
*/
|
|
17
|
+
import { derivePerAddressSeed } from './javaStreamables.js';
|
|
18
|
+
import { TreeKey, createUnifiedChildTreeKey } from './treekey.js';
|
|
19
|
+
import { deriveAddressFromPublicKey, bytesToHex } from './verify.js';
|
|
20
|
+
function assertIndex(addressIndex) {
|
|
21
|
+
if (!Number.isInteger(addressIndex) || addressIndex < 0) {
|
|
22
|
+
throw new Error(`addressIndex must be a non-negative integer, got ${JSON.stringify(addressIndex)}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function toDerived(addressIndex, scheme, publicKeyBytes) {
|
|
26
|
+
const publicKey = '0x' + bytesToHex(publicKeyBytes).toUpperCase();
|
|
27
|
+
return { addressIndex, publicKey, address: deriveAddressFromPublicKey(publicKey), scheme };
|
|
28
|
+
}
|
|
29
|
+
/** Minima Wallet.java parity: `baseSeed + index → privseed → TreeKey(64,3) → address`. */
|
|
30
|
+
export function deriveDeterministicAddress(baseSeed, addressIndex) {
|
|
31
|
+
assertIndex(addressIndex);
|
|
32
|
+
const privSeed = derivePerAddressSeed(baseSeed, addressIndex);
|
|
33
|
+
const publicKey = new TreeKey(privSeed, 64, 3).getPublicKey();
|
|
34
|
+
return toDerived(addressIndex, 'per-address', publicKey);
|
|
35
|
+
}
|
|
36
|
+
/** RFC-037 unified hierarchical scheme: `baseSeed + index → child TreeKey → address`. */
|
|
37
|
+
export function deriveUnifiedAddress(baseSeed, addressIndex) {
|
|
38
|
+
assertIndex(addressIndex);
|
|
39
|
+
const publicKey = createUnifiedChildTreeKey(baseSeed, addressIndex).getPublicKey();
|
|
40
|
+
return toDerived(addressIndex, 'unified', publicKey);
|
|
41
|
+
}
|
|
42
|
+
/** Derive the address for a scheme (default: the node-parity per-address scheme). */
|
|
43
|
+
export function deriveAddress(baseSeed, addressIndex, scheme = 'per-address') {
|
|
44
|
+
return scheme === 'unified'
|
|
45
|
+
? deriveUnifiedAddress(baseSeed, addressIndex)
|
|
46
|
+
: deriveDeterministicAddress(baseSeed, addressIndex);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fail-loud check: does the node-returned `expectedAddress` equal the address
|
|
50
|
+
* derived from the seed? Use this instead of trusting `newaddress` blindly.
|
|
51
|
+
*/
|
|
52
|
+
export function verifyDeterministicAddress(baseSeed, addressIndex, expectedAddress, scheme = 'per-address') {
|
|
53
|
+
return deriveAddress(baseSeed, addressIndex, scheme).address === expectedAddress;
|
|
54
|
+
}
|
package/dist/bip39.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - cleanSeedPhrase(): Normalizes input with prefix matching
|
|
12
12
|
* - convertStringToSeed(): Hashes phrase bytes with SHA3-256
|
|
13
13
|
*/
|
|
14
|
-
import { sha3_256 } from './wasm-
|
|
14
|
+
import { sha3_256 } from './wasm-async.js';
|
|
15
15
|
/**
|
|
16
16
|
* Official BIP39 English word list (2048 words)
|
|
17
17
|
* From https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
|
package/dist/bytes.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wasm-free byte utilities (RFC-031 Fix A).
|
|
3
|
+
*
|
|
4
|
+
* Byte-exact equivalents of the WASM kernels `bytes_to_hex` (uppercase, via
|
|
5
|
+
* `hex::encode_upper`), `hex_to_bytes` (strips a leading lowercase `0x`),
|
|
6
|
+
* and `concat_bytes`. Living outside `wasm-sync` lets pure consumers and the
|
|
7
|
+
* core root avoid loading the WASM artifact for hex/concat work.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
10
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
11
|
+
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
|
package/dist/bytes.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wasm-free byte utilities (RFC-031 Fix A).
|
|
3
|
+
*
|
|
4
|
+
* Byte-exact equivalents of the WASM kernels `bytes_to_hex` (uppercase, via
|
|
5
|
+
* `hex::encode_upper`), `hex_to_bytes` (strips a leading lowercase `0x`),
|
|
6
|
+
* and `concat_bytes`. Living outside `wasm-sync` lets pure consumers and the
|
|
7
|
+
* core root avoid loading the WASM artifact for hex/concat work.
|
|
8
|
+
*/
|
|
9
|
+
export function bytesToHex(bytes) {
|
|
10
|
+
let s = '';
|
|
11
|
+
for (let i = 0; i < bytes.length; i++)
|
|
12
|
+
s += bytes[i].toString(16).padStart(2, '0');
|
|
13
|
+
return s.toUpperCase();
|
|
14
|
+
}
|
|
15
|
+
export function hexToBytes(hex) {
|
|
16
|
+
const s = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
17
|
+
if (s.length % 2 !== 0)
|
|
18
|
+
throw new Error('Invalid hex: odd-length string');
|
|
19
|
+
const out = new Uint8Array(s.length >> 1);
|
|
20
|
+
for (let i = 0; i < out.length; i++) {
|
|
21
|
+
const pair = s.slice(i * 2, i * 2 + 2);
|
|
22
|
+
if (!/^[0-9a-fA-F]{2}$/.test(pair))
|
|
23
|
+
throw new Error(`Invalid hex: ${pair}`);
|
|
24
|
+
out[i] = parseInt(pair, 16);
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
export function concatBytes(...arrays) {
|
|
29
|
+
let len = 0;
|
|
30
|
+
for (const a of arrays)
|
|
31
|
+
len += a.length;
|
|
32
|
+
const out = new Uint8Array(len);
|
|
33
|
+
let offset = 0;
|
|
34
|
+
for (const a of arrays) {
|
|
35
|
+
out.set(a, offset);
|
|
36
|
+
offset += a.length;
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hashCanonical(domain: string, value: unknown): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON hashing (RFC-031 Fix A).
|
|
3
|
+
*
|
|
4
|
+
* Split out of `canonical.ts` so that `canonicalJson`/`toHex` are wasm-free:
|
|
5
|
+
* only `hashCanonical` needs the WASM `sha3_256` kernel.
|
|
6
|
+
*/
|
|
7
|
+
import { sha3_256 } from './wasm-async.js';
|
|
8
|
+
import { canonicalJson, toHex } from './canonical.js';
|
|
9
|
+
export function hashCanonical(domain, value) {
|
|
10
|
+
const input = domain + canonicalJson(value);
|
|
11
|
+
return toHex(sha3_256(new TextEncoder().encode(input)));
|
|
12
|
+
}
|
package/dist/canonical.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON + lowercase hex helpers (wasm-free).
|
|
3
|
+
*
|
|
4
|
+
* `hashCanonical` (which needs the WASM `sha3_256` kernel) lives in
|
|
5
|
+
* `canonical-hash.ts`.
|
|
6
|
+
*/
|
|
1
7
|
export declare function toHex(bytes: Uint8Array): string;
|
|
2
8
|
export declare function canonicalJson(value: unknown): string;
|
|
3
|
-
export declare function hashCanonical(domain: string, value: unknown): string;
|
package/dist/canonical.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON + lowercase hex helpers (wasm-free).
|
|
3
|
+
*
|
|
4
|
+
* `hashCanonical` (which needs the WASM `sha3_256` kernel) lives in
|
|
5
|
+
* `canonical-hash.ts`.
|
|
6
|
+
*/
|
|
2
7
|
export function toHex(bytes) {
|
|
3
8
|
return Array.from(bytes)
|
|
4
9
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
@@ -16,7 +21,3 @@ export function canonicalJson(value) {
|
|
|
16
21
|
const pairs = keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`);
|
|
17
22
|
return '{' + pairs.join(',') + '}';
|
|
18
23
|
}
|
|
19
|
-
export function hashCanonical(domain, value) {
|
|
20
|
-
const input = domain + canonicalJson(value);
|
|
21
|
-
return toHex(sha3_256(new TextEncoder().encode(input)));
|
|
22
|
-
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic address derivation (CAN-005).
|
|
3
|
+
*
|
|
4
|
+
* Minima's `newaddress` is non-deterministic, and not every node build honours a
|
|
5
|
+
* `modifier:` argument, so an address taken from the node can differ across
|
|
6
|
+
* containers/restarts and break cross-container resolution. These helpers derive
|
|
7
|
+
* the address **from the seed** instead, using the same schemes the node uses, so
|
|
8
|
+
* the result is reproducible and (for the matching scheme) node-spendable.
|
|
9
|
+
*
|
|
10
|
+
* - `deriveDeterministicAddress` — Minima `Wallet.createNewKey()` parity:
|
|
11
|
+
* `privseed = hash(baseSeed, index)` → `TreeKey(privseed, 64, 3)` → pubkey → Mx.
|
|
12
|
+
* - `deriveUnifiedAddress` — RFC-037 hierarchical child key.
|
|
13
|
+
*
|
|
14
|
+
* `verifyDeterministicAddress` asserts an address a node handed back matches the
|
|
15
|
+
* seed-derived one — the fail-loud check that replaces a silent fallback.
|
|
16
|
+
*/
|
|
17
|
+
export type AddressScheme = 'per-address' | 'unified';
|
|
18
|
+
export interface DerivedAddress {
|
|
19
|
+
addressIndex: number;
|
|
20
|
+
/** 0x-hex 32-byte WOTS public key. */
|
|
21
|
+
publicKey: string;
|
|
22
|
+
/** Mx… address derived from the public key. */
|
|
23
|
+
address: string;
|
|
24
|
+
scheme: AddressScheme;
|
|
25
|
+
}
|
|
26
|
+
/** Minima Wallet.java parity: `baseSeed + index → privseed → TreeKey(64,3) → address`. */
|
|
27
|
+
export declare function deriveDeterministicAddress(baseSeed: Uint8Array, addressIndex: number): DerivedAddress;
|
|
28
|
+
/** RFC-037 unified hierarchical scheme: `baseSeed + index → child TreeKey → address`. */
|
|
29
|
+
export declare function deriveUnifiedAddress(baseSeed: Uint8Array, addressIndex: number): DerivedAddress;
|
|
30
|
+
/** Derive the address for a scheme (default: the node-parity per-address scheme). */
|
|
31
|
+
export declare function deriveAddress(baseSeed: Uint8Array, addressIndex: number, scheme?: AddressScheme): DerivedAddress;
|
|
32
|
+
/**
|
|
33
|
+
* Fail-loud check: does the node-returned `expectedAddress` equal the address
|
|
34
|
+
* derived from the seed? Use this instead of trusting `newaddress` blindly.
|
|
35
|
+
*/
|
|
36
|
+
export declare function verifyDeterministicAddress(baseSeed: Uint8Array, addressIndex: number, expectedAddress: string, scheme?: AddressScheme): boolean;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic address derivation (CAN-005).
|
|
4
|
+
*
|
|
5
|
+
* Minima's `newaddress` is non-deterministic, and not every node build honours a
|
|
6
|
+
* `modifier:` argument, so an address taken from the node can differ across
|
|
7
|
+
* containers/restarts and break cross-container resolution. These helpers derive
|
|
8
|
+
* the address **from the seed** instead, using the same schemes the node uses, so
|
|
9
|
+
* the result is reproducible and (for the matching scheme) node-spendable.
|
|
10
|
+
*
|
|
11
|
+
* - `deriveDeterministicAddress` — Minima `Wallet.createNewKey()` parity:
|
|
12
|
+
* `privseed = hash(baseSeed, index)` → `TreeKey(privseed, 64, 3)` → pubkey → Mx.
|
|
13
|
+
* - `deriveUnifiedAddress` — RFC-037 hierarchical child key.
|
|
14
|
+
*
|
|
15
|
+
* `verifyDeterministicAddress` asserts an address a node handed back matches the
|
|
16
|
+
* seed-derived one — the fail-loud check that replaces a silent fallback.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.deriveDeterministicAddress = deriveDeterministicAddress;
|
|
20
|
+
exports.deriveUnifiedAddress = deriveUnifiedAddress;
|
|
21
|
+
exports.deriveAddress = deriveAddress;
|
|
22
|
+
exports.verifyDeterministicAddress = verifyDeterministicAddress;
|
|
23
|
+
const javaStreamables_js_1 = require("./javaStreamables.js");
|
|
24
|
+
const treekey_js_1 = require("./treekey.js");
|
|
25
|
+
const verify_js_1 = require("./verify.js");
|
|
26
|
+
function assertIndex(addressIndex) {
|
|
27
|
+
if (!Number.isInteger(addressIndex) || addressIndex < 0) {
|
|
28
|
+
throw new Error(`addressIndex must be a non-negative integer, got ${JSON.stringify(addressIndex)}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function toDerived(addressIndex, scheme, publicKeyBytes) {
|
|
32
|
+
const publicKey = '0x' + (0, verify_js_1.bytesToHex)(publicKeyBytes).toUpperCase();
|
|
33
|
+
return { addressIndex, publicKey, address: (0, verify_js_1.deriveAddressFromPublicKey)(publicKey), scheme };
|
|
34
|
+
}
|
|
35
|
+
/** Minima Wallet.java parity: `baseSeed + index → privseed → TreeKey(64,3) → address`. */
|
|
36
|
+
function deriveDeterministicAddress(baseSeed, addressIndex) {
|
|
37
|
+
assertIndex(addressIndex);
|
|
38
|
+
const privSeed = (0, javaStreamables_js_1.derivePerAddressSeed)(baseSeed, addressIndex);
|
|
39
|
+
const publicKey = new treekey_js_1.TreeKey(privSeed, 64, 3).getPublicKey();
|
|
40
|
+
return toDerived(addressIndex, 'per-address', publicKey);
|
|
41
|
+
}
|
|
42
|
+
/** RFC-037 unified hierarchical scheme: `baseSeed + index → child TreeKey → address`. */
|
|
43
|
+
function deriveUnifiedAddress(baseSeed, addressIndex) {
|
|
44
|
+
assertIndex(addressIndex);
|
|
45
|
+
const publicKey = (0, treekey_js_1.createUnifiedChildTreeKey)(baseSeed, addressIndex).getPublicKey();
|
|
46
|
+
return toDerived(addressIndex, 'unified', publicKey);
|
|
47
|
+
}
|
|
48
|
+
/** Derive the address for a scheme (default: the node-parity per-address scheme). */
|
|
49
|
+
function deriveAddress(baseSeed, addressIndex, scheme = 'per-address') {
|
|
50
|
+
return scheme === 'unified'
|
|
51
|
+
? deriveUnifiedAddress(baseSeed, addressIndex)
|
|
52
|
+
: deriveDeterministicAddress(baseSeed, addressIndex);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fail-loud check: does the node-returned `expectedAddress` equal the address
|
|
56
|
+
* derived from the seed? Use this instead of trusting `newaddress` blindly.
|
|
57
|
+
*/
|
|
58
|
+
function verifyDeterministicAddress(baseSeed, addressIndex, expectedAddress, scheme = 'per-address') {
|
|
59
|
+
return deriveAddress(baseSeed, addressIndex, scheme).address === expectedAddress;
|
|
60
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wasm-free byte utilities (RFC-031 Fix A).
|
|
3
|
+
*
|
|
4
|
+
* Byte-exact equivalents of the WASM kernels `bytes_to_hex` (uppercase, via
|
|
5
|
+
* `hex::encode_upper`), `hex_to_bytes` (strips a leading lowercase `0x`),
|
|
6
|
+
* and `concat_bytes`. Living outside `wasm-sync` lets pure consumers and the
|
|
7
|
+
* core root avoid loading the WASM artifact for hex/concat work.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
10
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
11
|
+
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Wasm-free byte utilities (RFC-031 Fix A).
|
|
4
|
+
*
|
|
5
|
+
* Byte-exact equivalents of the WASM kernels `bytes_to_hex` (uppercase, via
|
|
6
|
+
* `hex::encode_upper`), `hex_to_bytes` (strips a leading lowercase `0x`),
|
|
7
|
+
* and `concat_bytes`. Living outside `wasm-sync` lets pure consumers and the
|
|
8
|
+
* core root avoid loading the WASM artifact for hex/concat work.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.bytesToHex = bytesToHex;
|
|
12
|
+
exports.hexToBytes = hexToBytes;
|
|
13
|
+
exports.concatBytes = concatBytes;
|
|
14
|
+
function bytesToHex(bytes) {
|
|
15
|
+
let s = '';
|
|
16
|
+
for (let i = 0; i < bytes.length; i++)
|
|
17
|
+
s += bytes[i].toString(16).padStart(2, '0');
|
|
18
|
+
return s.toUpperCase();
|
|
19
|
+
}
|
|
20
|
+
function hexToBytes(hex) {
|
|
21
|
+
const s = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
22
|
+
if (s.length % 2 !== 0)
|
|
23
|
+
throw new Error('Invalid hex: odd-length string');
|
|
24
|
+
const out = new Uint8Array(s.length >> 1);
|
|
25
|
+
for (let i = 0; i < out.length; i++) {
|
|
26
|
+
const pair = s.slice(i * 2, i * 2 + 2);
|
|
27
|
+
if (!/^[0-9a-fA-F]{2}$/.test(pair))
|
|
28
|
+
throw new Error(`Invalid hex: ${pair}`);
|
|
29
|
+
out[i] = parseInt(pair, 16);
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function concatBytes(...arrays) {
|
|
34
|
+
let len = 0;
|
|
35
|
+
for (const a of arrays)
|
|
36
|
+
len += a.length;
|
|
37
|
+
const out = new Uint8Array(len);
|
|
38
|
+
let offset = 0;
|
|
39
|
+
for (const a of arrays) {
|
|
40
|
+
out.set(a, offset);
|
|
41
|
+
offset += a.length;
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hashCanonical(domain: string, value: unknown): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashCanonical = hashCanonical;
|
|
4
|
+
/**
|
|
5
|
+
* Canonical JSON hashing (RFC-031 Fix A).
|
|
6
|
+
*
|
|
7
|
+
* Split out of `canonical.ts` so that `canonicalJson`/`toHex` are wasm-free:
|
|
8
|
+
* only `hashCanonical` needs the WASM `sha3_256` kernel.
|
|
9
|
+
*/
|
|
10
|
+
const wasm_sync_js_1 = require("./wasm-sync.js");
|
|
11
|
+
const canonical_js_1 = require("./canonical.js");
|
|
12
|
+
function hashCanonical(domain, value) {
|
|
13
|
+
const input = domain + (0, canonical_js_1.canonicalJson)(value);
|
|
14
|
+
return (0, canonical_js_1.toHex)((0, wasm_sync_js_1.sha3_256)(new TextEncoder().encode(input)));
|
|
15
|
+
}
|
package/dist/cjs/canonical.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical JSON + lowercase hex helpers (wasm-free).
|
|
3
|
+
*
|
|
4
|
+
* `hashCanonical` (which needs the WASM `sha3_256` kernel) lives in
|
|
5
|
+
* `canonical-hash.ts`.
|
|
6
|
+
*/
|
|
1
7
|
export declare function toHex(bytes: Uint8Array): string;
|
|
2
8
|
export declare function canonicalJson(value: unknown): string;
|
|
3
|
-
export declare function hashCanonical(domain: string, value: unknown): string;
|
package/dist/cjs/canonical.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Canonical JSON + lowercase hex helpers (wasm-free).
|
|
4
|
+
*
|
|
5
|
+
* `hashCanonical` (which needs the WASM `sha3_256` kernel) lives in
|
|
6
|
+
* `canonical-hash.ts`.
|
|
7
|
+
*/
|
|
2
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
9
|
exports.toHex = toHex;
|
|
4
10
|
exports.canonicalJson = canonicalJson;
|
|
5
|
-
exports.hashCanonical = hashCanonical;
|
|
6
|
-
const wasm_sync_js_1 = require("./wasm-sync.js");
|
|
7
11
|
function toHex(bytes) {
|
|
8
12
|
return Array.from(bytes)
|
|
9
13
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
@@ -21,7 +25,3 @@ function canonicalJson(value) {
|
|
|
21
25
|
const pairs = keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`);
|
|
22
26
|
return '{' + pairs.join(',') + '}';
|
|
23
27
|
}
|
|
24
|
-
function hashCanonical(domain, value) {
|
|
25
|
-
const input = domain + canonicalJson(value);
|
|
26
|
-
return toHex((0, wasm_sync_js_1.sha3_256)(new TextEncoder().encode(input)));
|
|
27
|
-
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export * from './adapters/index.js';
|
|
|
10
10
|
export { LeaseStore, WatermarkStore, LeaseMonitor, prepareLease, finalizeLease, flatIndexFromLanes, type StoredLease, type LeaseStatus, type LeaseStoreConfig, type LeaseWotsIndices, type WatermarkState, type SyncResult, type WatermarkStoreConfig, type LeaseExpiryEvent, type LeaseExpiryCallback, type LeaseMonitorConfig, type PrepareArgs, type PrepareResp, } from './lease/index.js';
|
|
11
11
|
export { TransactionService, TransactionLifecycle, TransactionLifecycleError, WatermarkExhaustedError, TransactionReceiptStore, type TransactionServiceConfig, type WotsSigningDependencies, type TransactionLifecycleConfig, type WatermarkSyncFunction, type PrepareResult, type TransactionReceiptStoreConfig, type PrepareRequest, type PrepareResponse, type SignRequest, type SignResult, type HierarchicalWitnessBundle, type WitnessBundle, type FinalizeRequest, type FinalizeResponse, type TransactionMetadata, type TransactionReceipt, type TransactionError, } from './tx/index.js';
|
|
12
12
|
export type { WotsIndices } from './tx/index.js';
|
|
13
|
-
export { bytesToHex, hexToBytes, concatBytes } from './
|
|
13
|
+
export { bytesToHex, hexToBytes, concatBytes } from './bytes.js';
|
|
14
14
|
export { sha3_256 } from './wasm-sync.js';
|
|
15
15
|
export { expandPrivateKey, hashChain, derivePKdigest, deriveFullPublicKey, wotsSign, wotsVerify, wotsVerifyDigest, wotsPkFromSig, wotsPublicKeyFromSeed, wotsKeypairFromSeed, } from './wasm-sync.js';
|
|
16
16
|
export * from './params.js';
|
|
@@ -28,17 +28,20 @@ export { makeMxAddress as encodeMx, parseMxAddress as decodeMx } from './wasm-sy
|
|
|
28
28
|
export declare function mxToHex(address: string): string;
|
|
29
29
|
export * from './mmr.js';
|
|
30
30
|
export * from './scripts/index.js';
|
|
31
|
+
export { toNodeScript } from './scripts/canonical.js';
|
|
31
32
|
export { scriptToAddress, addressToRoot } from './derive.js';
|
|
32
33
|
export { WORD_LIST, convertStringToSeed, convertWordListToSeed, generateSeedPhrase, } from './bip39.js';
|
|
33
34
|
export { MiniNumber } from './MiniNumber.js';
|
|
34
35
|
export { type MinimaTransaction, type MinimaCoin, type ParsedMiniNumber, type MinimaToken, type StateVariable, type RawStateVariable, type CoinProofData, type SpendableCoinInput, type TransactionBuildResult, parseDecimalToMiniNumber, serializeCoin, serializeTransaction as serializeTransactionObject, computeTokenId, serializeTokenDescriptor, createDefaultTransaction, buildMinimaCoin, } from './transaction.js';
|
|
35
36
|
export { verifySignature, verifySignatureDetailed, verifyTreeSignatureDetailed, deriveAddressFromPublicKey, normalizeHex, type VerificationResult, } from './verify.js';
|
|
37
|
+
export { deriveDeterministicAddress, deriveUnifiedAddress, deriveAddress, verifyDeterministicAddress, type AddressScheme, type DerivedAddress, } from './address-derivation.js';
|
|
36
38
|
export { writeMiniByte, writeMMREntryNumber, writeSignature, writeSignatureProof, concat, bigIntToByteArray, type Bytes, } from './Streamable.js';
|
|
37
39
|
export { serializeMiniNumber, serializeMiniData, hashAllObjects, deriveChildTreeSeedJava, hashObject, serializeMiniNumberZERO, serializeMiniNumberONE, writeHashToStream, javaHashAllObjects, indexToMiniDataBytes, deriveUnifiedChildSeed, createMMREntryNumber, serializeMMREntryNumber, serializeMMRData, serializeMMREntry, type MMREntryNumber as JavaMMREntryNumber, type MMRData as JavaMMRData, type MMREntry as JavaMMREntry, } from './javaStreamables.js';
|
|
38
40
|
export { TreeKey, TreeKeyNode, serializeTreeSignature, deserializeTreeSignature, getRootPublicKey, DEFAULT_KEYS_PER_LEVEL, DEFAULT_LEVELS, createUnifiedChildTreeKeyAsync, createPerAddressTreeKey, createPerAddressTreeKeyAsync, setTreeKeyLogger, disableTreeKeyLogger, type KeyGenProgress, type ProgressCallback, type SignatureProof, type TreeSignature, } from './treekey.js';
|
|
39
41
|
export type { WotsKeypair, WotsSignature } from './wots.js';
|
|
40
42
|
export { F, hex, fromHex, setWotsLogger, disableWotsLogger, u16be, u32be, assert32, h, prfChainSeed, toWinternitzDigits, baseWWithChecksum, wotsSignLegacy, } from './wots.js';
|
|
41
|
-
export { toHex, canonicalJson
|
|
43
|
+
export { toHex, canonicalJson } from './canonical.js';
|
|
44
|
+
export { hashCanonical } from './canonical-hash.js';
|
|
42
45
|
export { utf8ToBytes, bytesToUtf8 } from './utils.js';
|
|
43
46
|
export declare const MINIMA_CONSTANTS: {
|
|
44
47
|
readonly WOTS_W: 8;
|