hd-wallet-wasm 0.1.0 → 0.1.5
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/dist/hd-wallet.js +0 -0
- package/dist/hd-wallet.wasm +0 -0
- package/dist/index.d.ts +10 -2
- package/package.json +10 -2
- package/src/aligned.d.ts +349 -0
- package/src/aligned.mjs +627 -0
- package/src/index.d.ts +10 -2
- package/src/index.mjs +27 -9
package/dist/hd-wallet.js
CHANGED
|
Binary file
|
package/dist/hd-wallet.wasm
CHANGED
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* - Transaction building and signing
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
// Import aligned API types
|
|
13
|
+
import { AlignedAPI } from './aligned';
|
|
14
|
+
|
|
12
15
|
// =============================================================================
|
|
13
16
|
// Module Types
|
|
14
17
|
// =============================================================================
|
|
@@ -62,6 +65,9 @@ export interface HDWalletModule {
|
|
|
62
65
|
|
|
63
66
|
// Utilities
|
|
64
67
|
utils: UtilsAPI;
|
|
68
|
+
|
|
69
|
+
// Aligned binary API for efficient batch operations
|
|
70
|
+
aligned: AlignedAPI;
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
// =============================================================================
|
|
@@ -600,10 +606,12 @@ export interface UtilsAPI {
|
|
|
600
606
|
|
|
601
607
|
/**
|
|
602
608
|
* Initialize the HD Wallet WASM module
|
|
609
|
+
* @param wasmPath - Optional path to WASM file
|
|
603
610
|
*/
|
|
604
|
-
export default function init(): Promise<HDWalletModule>;
|
|
611
|
+
export default function init(wasmPath?: string): Promise<HDWalletModule>;
|
|
605
612
|
|
|
606
613
|
/**
|
|
607
614
|
* Create HD Wallet instance (alternative syntax)
|
|
615
|
+
* @param wasmPath - Optional path to WASM file
|
|
608
616
|
*/
|
|
609
|
-
export function createHDWallet(): Promise<HDWalletModule>;
|
|
617
|
+
export function createHDWallet(wasmPath?: string): Promise<HDWalletModule>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hd-wallet-wasm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Comprehensive HD Wallet implementation in WebAssembly - BIP-32/39/44, multi-curve, multi-chain support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"import": "./src/index.mjs",
|
|
14
14
|
"require": "./dist/hd-wallet.cjs"
|
|
15
15
|
},
|
|
16
|
+
"./aligned": {
|
|
17
|
+
"types": "./src/aligned.d.ts",
|
|
18
|
+
"import": "./src/aligned.mjs"
|
|
19
|
+
},
|
|
16
20
|
"./wasm": {
|
|
17
21
|
"types": "./dist/index.d.ts",
|
|
18
22
|
"import": "./dist/hd-wallet.js"
|
|
@@ -21,6 +25,8 @@
|
|
|
21
25
|
"files": [
|
|
22
26
|
"src/index.mjs",
|
|
23
27
|
"src/index.d.ts",
|
|
28
|
+
"src/aligned.mjs",
|
|
29
|
+
"src/aligned.d.ts",
|
|
24
30
|
"dist/hd-wallet.js",
|
|
25
31
|
"dist/hd-wallet.wasm",
|
|
26
32
|
"dist/hd-wallet.cjs",
|
|
@@ -28,11 +34,13 @@
|
|
|
28
34
|
"README.md"
|
|
29
35
|
],
|
|
30
36
|
"scripts": {
|
|
31
|
-
"
|
|
37
|
+
"generate": "node ../scripts/generate-aligned.mjs",
|
|
38
|
+
"build": "npm run generate && cmake --build ../build-wasm --target hd_wallet_wasm_npm -j",
|
|
32
39
|
"test": "node test/test_all.mjs",
|
|
33
40
|
"test:bip39": "node test/test_bip39.mjs",
|
|
34
41
|
"test:bip32": "node test/test_bip32.mjs",
|
|
35
42
|
"test:vectors": "node test/test_vectors.mjs",
|
|
43
|
+
"test:aligned": "node test/test_aligned.mjs",
|
|
36
44
|
"prepublishOnly": "npm run build && npm test"
|
|
37
45
|
},
|
|
38
46
|
"repository": {
|
package/src/aligned.d.ts
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HD Wallet WASM - Aligned Binary API Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Provides efficient batch operations using aligned, fixed-size structs
|
|
5
|
+
* for zero-copy WASM interop.
|
|
6
|
+
*
|
|
7
|
+
* @module hd-wallet-wasm/aligned
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Enums
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Elliptic curve types for aligned operations
|
|
16
|
+
*/
|
|
17
|
+
export enum AlignedCurve {
|
|
18
|
+
SECP256K1 = 0,
|
|
19
|
+
ED25519 = 1,
|
|
20
|
+
P256 = 2,
|
|
21
|
+
P384 = 3,
|
|
22
|
+
X25519 = 4
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Error codes for aligned operations
|
|
27
|
+
*/
|
|
28
|
+
export enum AlignedError {
|
|
29
|
+
OK = 0,
|
|
30
|
+
UNKNOWN = 1,
|
|
31
|
+
INVALID_ARGUMENT = 2,
|
|
32
|
+
NOT_SUPPORTED = 3,
|
|
33
|
+
OUT_OF_MEMORY = 4,
|
|
34
|
+
INTERNAL = 5,
|
|
35
|
+
NO_ENTROPY = 100,
|
|
36
|
+
INSUFFICIENT_ENTROPY = 101,
|
|
37
|
+
INVALID_WORD = 200,
|
|
38
|
+
INVALID_CHECKSUM = 201,
|
|
39
|
+
INVALID_MNEMONIC_LENGTH = 202,
|
|
40
|
+
INVALID_ENTROPY_LENGTH = 203,
|
|
41
|
+
INVALID_SEED = 300,
|
|
42
|
+
INVALID_PATH = 301,
|
|
43
|
+
INVALID_CHILD_INDEX = 302,
|
|
44
|
+
HARDENED_FROM_PUBLIC = 303,
|
|
45
|
+
INVALID_EXTENDED_KEY = 304,
|
|
46
|
+
INVALID_PRIVATE_KEY = 400,
|
|
47
|
+
INVALID_PUBLIC_KEY = 401,
|
|
48
|
+
INVALID_SIGNATURE = 402,
|
|
49
|
+
VERIFICATION_FAILED = 403,
|
|
50
|
+
KEY_DERIVATION_FAILED = 404
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// =============================================================================
|
|
54
|
+
// Size Constants
|
|
55
|
+
// =============================================================================
|
|
56
|
+
|
|
57
|
+
export const EXTENDEDKEYDATA_SIZE: number;
|
|
58
|
+
export const BATCHDERIVEREQUEST_SIZE: number;
|
|
59
|
+
export const DERIVEDKEYENTRY_SIZE: number;
|
|
60
|
+
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// Result Types
|
|
63
|
+
// =============================================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Result of a single key derivation
|
|
67
|
+
*/
|
|
68
|
+
export interface DerivedKey {
|
|
69
|
+
/** Child index that was derived */
|
|
70
|
+
index: number;
|
|
71
|
+
/** Error code (AlignedError.OK on success) */
|
|
72
|
+
error: AlignedError;
|
|
73
|
+
/** Compressed public key (33 bytes), null on error */
|
|
74
|
+
publicKey: Uint8Array | null;
|
|
75
|
+
/** Private key (32 bytes), null on error or if base was neutered */
|
|
76
|
+
privateKey: Uint8Array | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Result of a single signing operation
|
|
81
|
+
*/
|
|
82
|
+
export interface SignatureResult {
|
|
83
|
+
/** Index of the hash that was signed */
|
|
84
|
+
index: number;
|
|
85
|
+
/** Error code (AlignedError.OK on success) */
|
|
86
|
+
error: AlignedError;
|
|
87
|
+
/** Signature (64 bytes r||s format), null on error */
|
|
88
|
+
signature: Uint8Array | null;
|
|
89
|
+
/** Recovery ID for recoverable signatures (-1 if not applicable) */
|
|
90
|
+
recoveryId: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Result of a single signature verification
|
|
95
|
+
*/
|
|
96
|
+
export interface VerifyResult {
|
|
97
|
+
/** Index of the entry that was verified */
|
|
98
|
+
index: number;
|
|
99
|
+
/** Whether the signature is valid */
|
|
100
|
+
valid: boolean;
|
|
101
|
+
/** Error code */
|
|
102
|
+
error: AlignedError;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Input for signature verification
|
|
107
|
+
*/
|
|
108
|
+
export interface VerifyEntry {
|
|
109
|
+
/** 32-byte message hash */
|
|
110
|
+
hash: Uint8Array;
|
|
111
|
+
/** 64-byte signature */
|
|
112
|
+
signature: Uint8Array;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Base key data for batch operations
|
|
117
|
+
*/
|
|
118
|
+
export interface BaseKeyData {
|
|
119
|
+
/** Elliptic curve */
|
|
120
|
+
curve?: AlignedCurve;
|
|
121
|
+
/** Key depth in derivation tree */
|
|
122
|
+
depth?: number;
|
|
123
|
+
/** Parent fingerprint */
|
|
124
|
+
parentFingerprint?: number;
|
|
125
|
+
/** Child index */
|
|
126
|
+
childIndex?: number;
|
|
127
|
+
/** Chain code (32 bytes) */
|
|
128
|
+
chainCode: Uint8Array;
|
|
129
|
+
/** Compressed public key (33 bytes) */
|
|
130
|
+
publicKey: Uint8Array;
|
|
131
|
+
/** Private key (32 bytes) */
|
|
132
|
+
privateKey: Uint8Array;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// =============================================================================
|
|
136
|
+
// View Classes (from generated code)
|
|
137
|
+
// =============================================================================
|
|
138
|
+
|
|
139
|
+
export class ExtendedKeyDataView {
|
|
140
|
+
constructor(buffer: ArrayBuffer, byteOffset?: number);
|
|
141
|
+
static fromMemory(memory: WebAssembly.Memory, ptr: number): ExtendedKeyDataView;
|
|
142
|
+
static fromBytes(bytes: Uint8Array, offset?: number): ExtendedKeyDataView;
|
|
143
|
+
static allocate(): ExtendedKeyDataView;
|
|
144
|
+
|
|
145
|
+
get curve(): AlignedCurve;
|
|
146
|
+
set curve(v: AlignedCurve);
|
|
147
|
+
get depth(): number;
|
|
148
|
+
set depth(v: number);
|
|
149
|
+
get parent_fingerprint(): number;
|
|
150
|
+
set parent_fingerprint(v: number);
|
|
151
|
+
get child_index(): number;
|
|
152
|
+
set child_index(v: number);
|
|
153
|
+
get chain_code_data(): Uint8Array;
|
|
154
|
+
get public_key_data(): Uint8Array;
|
|
155
|
+
get private_key_data(): Uint8Array;
|
|
156
|
+
get has_private_key(): number;
|
|
157
|
+
set has_private_key(v: number);
|
|
158
|
+
|
|
159
|
+
toObject(): Record<string, unknown>;
|
|
160
|
+
copyFrom(obj: Partial<Record<string, unknown>>): void;
|
|
161
|
+
copyTo(dest: Uint8Array, offset?: number): void;
|
|
162
|
+
getBytes(): Uint8Array;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export class BatchDeriveRequestView {
|
|
166
|
+
constructor(buffer: ArrayBuffer, byteOffset?: number);
|
|
167
|
+
static fromMemory(memory: WebAssembly.Memory, ptr: number): BatchDeriveRequestView;
|
|
168
|
+
static fromBytes(bytes: Uint8Array, offset?: number): BatchDeriveRequestView;
|
|
169
|
+
static allocate(): BatchDeriveRequestView;
|
|
170
|
+
|
|
171
|
+
get base_key_curve(): AlignedCurve;
|
|
172
|
+
set base_key_curve(v: AlignedCurve);
|
|
173
|
+
get base_key_depth(): number;
|
|
174
|
+
set base_key_depth(v: number);
|
|
175
|
+
get base_key_parent_fingerprint(): number;
|
|
176
|
+
set base_key_parent_fingerprint(v: number);
|
|
177
|
+
get base_key_child_index(): number;
|
|
178
|
+
set base_key_child_index(v: number);
|
|
179
|
+
get base_key_chain_code_data(): Uint8Array;
|
|
180
|
+
get base_key_public_key_data(): Uint8Array;
|
|
181
|
+
get base_key_private_key_data(): Uint8Array;
|
|
182
|
+
get base_key_has_private_key(): number;
|
|
183
|
+
set base_key_has_private_key(v: number);
|
|
184
|
+
get start_index(): number;
|
|
185
|
+
set start_index(v: number);
|
|
186
|
+
get count(): number;
|
|
187
|
+
set count(v: number);
|
|
188
|
+
get hardened(): number;
|
|
189
|
+
set hardened(v: number);
|
|
190
|
+
|
|
191
|
+
toObject(): Record<string, unknown>;
|
|
192
|
+
copyFrom(obj: Partial<Record<string, unknown>>): void;
|
|
193
|
+
copyTo(dest: Uint8Array, offset?: number): void;
|
|
194
|
+
getBytes(): Uint8Array;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export class DerivedKeyEntryView {
|
|
198
|
+
constructor(buffer: ArrayBuffer, byteOffset?: number);
|
|
199
|
+
static fromMemory(memory: WebAssembly.Memory, ptr: number): DerivedKeyEntryView;
|
|
200
|
+
static fromBytes(bytes: Uint8Array, offset?: number): DerivedKeyEntryView;
|
|
201
|
+
static allocate(): DerivedKeyEntryView;
|
|
202
|
+
|
|
203
|
+
get index(): number;
|
|
204
|
+
set index(v: number);
|
|
205
|
+
get error(): AlignedError;
|
|
206
|
+
set error(v: AlignedError);
|
|
207
|
+
get public_key_data(): Uint8Array;
|
|
208
|
+
get private_key_data(): Uint8Array;
|
|
209
|
+
|
|
210
|
+
toObject(): Record<string, unknown>;
|
|
211
|
+
copyFrom(obj: Partial<Record<string, unknown>>): void;
|
|
212
|
+
copyTo(dest: Uint8Array, offset?: number): void;
|
|
213
|
+
getBytes(): Uint8Array;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export class DerivedKeyEntryArrayView implements Iterable<DerivedKeyEntryView> {
|
|
217
|
+
constructor(buffer: ArrayBuffer, byteOffset: number, count: number);
|
|
218
|
+
static fromMemory(memory: WebAssembly.Memory, ptr: number, count: number): DerivedKeyEntryArrayView;
|
|
219
|
+
|
|
220
|
+
readonly length: number;
|
|
221
|
+
at(index: number): DerivedKeyEntryView;
|
|
222
|
+
[Symbol.iterator](): Iterator<DerivedKeyEntryView>;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// =============================================================================
|
|
226
|
+
// AlignedKeyDeriver
|
|
227
|
+
// =============================================================================
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Efficient batch key derivation using aligned binary format.
|
|
231
|
+
*/
|
|
232
|
+
export class AlignedKeyDeriver {
|
|
233
|
+
/**
|
|
234
|
+
* @param wasm - WASM module instance
|
|
235
|
+
*/
|
|
236
|
+
constructor(wasm: unknown);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Derive multiple child keys from a base key in batch.
|
|
240
|
+
*
|
|
241
|
+
* @param baseKey - Base HDKey or BaseKeyData
|
|
242
|
+
* @param startIndex - Starting child index
|
|
243
|
+
* @param count - Number of keys to derive
|
|
244
|
+
* @param hardened - Use hardened derivation
|
|
245
|
+
* @returns Array of derived keys
|
|
246
|
+
*/
|
|
247
|
+
deriveBatch(
|
|
248
|
+
baseKey: BaseKeyData | { _handle: unknown },
|
|
249
|
+
startIndex: number,
|
|
250
|
+
count: number,
|
|
251
|
+
hardened?: boolean
|
|
252
|
+
): DerivedKey[];
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Generator for streaming key derivation.
|
|
256
|
+
*
|
|
257
|
+
* @param baseKey - Base HDKey or BaseKeyData
|
|
258
|
+
* @param startIndex - Starting child index
|
|
259
|
+
* @param batchSize - Keys per batch
|
|
260
|
+
* @param hardened - Use hardened derivation
|
|
261
|
+
* @yields Batches of derived keys
|
|
262
|
+
*/
|
|
263
|
+
streamKeys(
|
|
264
|
+
baseKey: BaseKeyData | { _handle: unknown },
|
|
265
|
+
startIndex?: number,
|
|
266
|
+
batchSize?: number,
|
|
267
|
+
hardened?: boolean
|
|
268
|
+
): Generator<DerivedKey[], void, unknown>;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// =============================================================================
|
|
272
|
+
// AlignedSigner
|
|
273
|
+
// =============================================================================
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Efficient batch signing using aligned binary format.
|
|
277
|
+
*/
|
|
278
|
+
export class AlignedSigner {
|
|
279
|
+
/**
|
|
280
|
+
* @param wasm - WASM module instance
|
|
281
|
+
*/
|
|
282
|
+
constructor(wasm: unknown);
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Sign multiple message hashes with the same private key.
|
|
286
|
+
*
|
|
287
|
+
* @param privateKey - 32-byte private key
|
|
288
|
+
* @param hashes - Array of 32-byte message hashes
|
|
289
|
+
* @param curve - Elliptic curve
|
|
290
|
+
* @returns Array of signature results
|
|
291
|
+
*/
|
|
292
|
+
signBatch(
|
|
293
|
+
privateKey: Uint8Array,
|
|
294
|
+
hashes: Uint8Array[],
|
|
295
|
+
curve?: AlignedCurve
|
|
296
|
+
): SignatureResult[];
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Verify multiple signatures against the same public key.
|
|
300
|
+
*
|
|
301
|
+
* @param publicKey - 33-byte compressed public key
|
|
302
|
+
* @param entries - Hash/signature pairs
|
|
303
|
+
* @param curve - Elliptic curve
|
|
304
|
+
* @returns Array of verification results
|
|
305
|
+
*/
|
|
306
|
+
verifyBatch(
|
|
307
|
+
publicKey: Uint8Array,
|
|
308
|
+
entries: VerifyEntry[],
|
|
309
|
+
curve?: AlignedCurve
|
|
310
|
+
): VerifyResult[];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// =============================================================================
|
|
314
|
+
// AlignedAPI
|
|
315
|
+
// =============================================================================
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Combined aligned API providing access to batch operations.
|
|
319
|
+
*/
|
|
320
|
+
export class AlignedAPI {
|
|
321
|
+
/**
|
|
322
|
+
* @param wasm - WASM module instance
|
|
323
|
+
*/
|
|
324
|
+
constructor(wasm: unknown);
|
|
325
|
+
|
|
326
|
+
/** Key deriver for batch derivation */
|
|
327
|
+
readonly keyDeriver: AlignedKeyDeriver;
|
|
328
|
+
|
|
329
|
+
/** Signer for batch signing/verification */
|
|
330
|
+
readonly signer: AlignedSigner;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Convert HDKey to ExtendedKeyData bytes
|
|
334
|
+
* @param hdKey - HDKey instance
|
|
335
|
+
* @returns Extended key data bytes
|
|
336
|
+
*/
|
|
337
|
+
keyToBytes(hdKey: { _handle: unknown }): Uint8Array;
|
|
338
|
+
|
|
339
|
+
/** Size of DerivedKeyEntry struct */
|
|
340
|
+
readonly derivedKeyEntrySize: number;
|
|
341
|
+
|
|
342
|
+
/** Size of ExtendedKeyData struct */
|
|
343
|
+
readonly extendedKeyDataSize: number;
|
|
344
|
+
|
|
345
|
+
/** Size of BatchDeriveRequest struct */
|
|
346
|
+
readonly batchDeriveRequestSize: number;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export default AlignedAPI;
|
package/src/aligned.mjs
ADDED
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HD Wallet WASM - Aligned Binary API
|
|
3
|
+
*
|
|
4
|
+
* Provides efficient batch operations using aligned, fixed-size structs
|
|
5
|
+
* for zero-copy WASM interop. This module wraps the C++ aligned API
|
|
6
|
+
* with JavaScript-friendly interfaces.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Batch key derivation (derive 100s of keys efficiently)
|
|
10
|
+
* - Batch signing (sign multiple hashes with same key)
|
|
11
|
+
* - Batch verification
|
|
12
|
+
* - Streaming key derivation (generators for unlimited sequences)
|
|
13
|
+
*
|
|
14
|
+
* @module hd-wallet-wasm/aligned
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Import generated aligned struct definitions
|
|
18
|
+
import {
|
|
19
|
+
// Size constants
|
|
20
|
+
EXTENDEDKEYDATA_SIZE,
|
|
21
|
+
BATCHDERIVEREQUEST_SIZE,
|
|
22
|
+
DERIVEDKEYENTRY_SIZE,
|
|
23
|
+
BATCHSIGNREQUEST_SIZE,
|
|
24
|
+
HASHENTRY_SIZE,
|
|
25
|
+
SIGNATUREENTRY_SIZE,
|
|
26
|
+
BATCHVERIFYREQUEST_SIZE,
|
|
27
|
+
VERIFYENTRY_SIZE,
|
|
28
|
+
VERIFYRESULTENTRY_SIZE,
|
|
29
|
+
STREAMDERIVECONFIG_SIZE,
|
|
30
|
+
STREAMSTATUS_SIZE,
|
|
31
|
+
|
|
32
|
+
// View classes
|
|
33
|
+
ExtendedKeyDataView,
|
|
34
|
+
BatchDeriveRequestView,
|
|
35
|
+
DerivedKeyEntryView,
|
|
36
|
+
DerivedKeyEntryArrayView,
|
|
37
|
+
BatchSignRequestView,
|
|
38
|
+
HashEntryView,
|
|
39
|
+
HashEntryArrayView,
|
|
40
|
+
SignatureEntryView,
|
|
41
|
+
SignatureEntryArrayView,
|
|
42
|
+
BatchVerifyRequestView,
|
|
43
|
+
VerifyEntryView,
|
|
44
|
+
VerifyEntryArrayView,
|
|
45
|
+
VerifyResultEntryView,
|
|
46
|
+
VerifyResultEntryArrayView,
|
|
47
|
+
StreamDeriveConfigView,
|
|
48
|
+
StreamStatusView,
|
|
49
|
+
|
|
50
|
+
// Enums
|
|
51
|
+
Curve as AlignedCurve,
|
|
52
|
+
Error as AlignedErrorCode,
|
|
53
|
+
} from '../../generated/aligned/hd_wallet_aligned.mjs';
|
|
54
|
+
|
|
55
|
+
// Re-export for external use
|
|
56
|
+
export { AlignedCurve, AlignedErrorCode };
|
|
57
|
+
export {
|
|
58
|
+
EXTENDEDKEYDATA_SIZE,
|
|
59
|
+
BATCHDERIVEREQUEST_SIZE,
|
|
60
|
+
DERIVEDKEYENTRY_SIZE,
|
|
61
|
+
ExtendedKeyDataView,
|
|
62
|
+
BatchDeriveRequestView,
|
|
63
|
+
DerivedKeyEntryView,
|
|
64
|
+
DerivedKeyEntryArrayView,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// Error Handling
|
|
69
|
+
// =============================================================================
|
|
70
|
+
|
|
71
|
+
const ERROR_MESSAGES = {
|
|
72
|
+
[AlignedErrorCode.OK]: 'Success',
|
|
73
|
+
[AlignedErrorCode.UNKNOWN]: 'Unknown error',
|
|
74
|
+
[AlignedErrorCode.INVALID_ARGUMENT]: 'Invalid argument',
|
|
75
|
+
[AlignedErrorCode.NOT_SUPPORTED]: 'Operation not supported',
|
|
76
|
+
[AlignedErrorCode.OUT_OF_MEMORY]: 'Out of memory',
|
|
77
|
+
[AlignedErrorCode.INTERNAL]: 'Internal error',
|
|
78
|
+
[AlignedErrorCode.NO_ENTROPY]: 'No entropy available',
|
|
79
|
+
[AlignedErrorCode.INSUFFICIENT_ENTROPY]: 'Insufficient entropy',
|
|
80
|
+
[AlignedErrorCode.INVALID_PRIVATE_KEY]: 'Invalid private key',
|
|
81
|
+
[AlignedErrorCode.INVALID_PUBLIC_KEY]: 'Invalid public key',
|
|
82
|
+
[AlignedErrorCode.INVALID_SIGNATURE]: 'Invalid signature',
|
|
83
|
+
[AlignedErrorCode.VERIFICATION_FAILED]: 'Signature verification failed',
|
|
84
|
+
[AlignedErrorCode.KEY_DERIVATION_FAILED]: 'Key derivation failed',
|
|
85
|
+
[AlignedErrorCode.HARDENED_FROM_PUBLIC]: 'Cannot derive hardened from public key',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Aligned API Error
|
|
90
|
+
*/
|
|
91
|
+
export class AlignedError extends Error {
|
|
92
|
+
constructor(code, message) {
|
|
93
|
+
super(message || ERROR_MESSAGES[code] || `Error code: ${code}`);
|
|
94
|
+
this.name = 'AlignedError';
|
|
95
|
+
this.code = code;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// AlignedKeyDeriver - Batch Key Derivation
|
|
101
|
+
// =============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Efficient batch key derivation using aligned binary format.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```js
|
|
108
|
+
* const deriver = new AlignedKeyDeriver(wasm);
|
|
109
|
+
*
|
|
110
|
+
* // Derive 100 keys at once
|
|
111
|
+
* const keys = deriver.deriveBatch(masterKey, 0, 100);
|
|
112
|
+
* for (const key of keys) {
|
|
113
|
+
* console.log(key.index, key.publicKey);
|
|
114
|
+
* }
|
|
115
|
+
*
|
|
116
|
+
* // Stream unlimited keys
|
|
117
|
+
* for (const batch of deriver.streamKeys(masterKey, 0, 50)) {
|
|
118
|
+
* for (const key of batch) {
|
|
119
|
+
* processKey(key);
|
|
120
|
+
* }
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export class AlignedKeyDeriver {
|
|
125
|
+
#wasm;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @param {Object} wasm - WASM module instance
|
|
129
|
+
*/
|
|
130
|
+
constructor(wasm) {
|
|
131
|
+
this.#wasm = wasm;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Derive multiple child keys from a base key in batch.
|
|
136
|
+
*
|
|
137
|
+
* @param {Object} baseKey - Base HDKey or ExtendedKeyData
|
|
138
|
+
* @param {number} startIndex - Starting child index
|
|
139
|
+
* @param {number} count - Number of keys to derive
|
|
140
|
+
* @param {boolean} [hardened=false] - Use hardened derivation
|
|
141
|
+
* @returns {Array<DerivedKey>} Array of derived keys
|
|
142
|
+
*/
|
|
143
|
+
deriveBatch(baseKey, startIndex, count, hardened = false) {
|
|
144
|
+
const wasm = this.#wasm;
|
|
145
|
+
|
|
146
|
+
// Allocate request buffer
|
|
147
|
+
const requestPtr = wasm._hd_alloc(BATCHDERIVEREQUEST_SIZE);
|
|
148
|
+
if (!requestPtr) {
|
|
149
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// Fill request
|
|
154
|
+
const requestView = BatchDeriveRequestView.fromMemory(wasm.wasmMemory, requestPtr);
|
|
155
|
+
this.#fillBaseKey(requestView, baseKey);
|
|
156
|
+
requestView.start_index = startIndex >>> 0;
|
|
157
|
+
requestView.count = count >>> 0;
|
|
158
|
+
requestView.hardened = hardened ? 1 : 0;
|
|
159
|
+
|
|
160
|
+
// Allocate results buffer
|
|
161
|
+
const resultsSize = count * DERIVEDKEYENTRY_SIZE;
|
|
162
|
+
const resultsPtr = wasm._hd_alloc(resultsSize);
|
|
163
|
+
if (!resultsPtr) {
|
|
164
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
// Call WASM function
|
|
169
|
+
const derivedCount = wasm._hd_aligned_derive_batch(
|
|
170
|
+
requestPtr,
|
|
171
|
+
resultsPtr,
|
|
172
|
+
count
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (derivedCount < 0) {
|
|
176
|
+
throw new AlignedError(derivedCount);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Read results
|
|
180
|
+
const results = [];
|
|
181
|
+
const resultsArray = DerivedKeyEntryArrayView.fromMemory(
|
|
182
|
+
wasm.wasmMemory, resultsPtr, derivedCount
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
for (let i = 0; i < derivedCount; i++) {
|
|
186
|
+
const entry = resultsArray.at(i);
|
|
187
|
+
results.push({
|
|
188
|
+
index: entry.index,
|
|
189
|
+
error: entry.error,
|
|
190
|
+
publicKey: entry.error === AlignedErrorCode.OK
|
|
191
|
+
? new Uint8Array(entry.public_key_data)
|
|
192
|
+
: null,
|
|
193
|
+
privateKey: entry.error === AlignedErrorCode.OK
|
|
194
|
+
? new Uint8Array(entry.private_key_data)
|
|
195
|
+
: null,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return results;
|
|
200
|
+
} finally {
|
|
201
|
+
wasm._hd_dealloc(resultsPtr);
|
|
202
|
+
}
|
|
203
|
+
} finally {
|
|
204
|
+
wasm._hd_dealloc(requestPtr);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Generator for streaming key derivation.
|
|
210
|
+
*
|
|
211
|
+
* @param {Object} baseKey - Base HDKey or ExtendedKeyData
|
|
212
|
+
* @param {number} [startIndex=0] - Starting child index
|
|
213
|
+
* @param {number} [batchSize=100] - Keys per batch
|
|
214
|
+
* @param {boolean} [hardened=false] - Use hardened derivation
|
|
215
|
+
* @yields {Array<DerivedKey>} Batches of derived keys
|
|
216
|
+
*/
|
|
217
|
+
*streamKeys(baseKey, startIndex = 0, batchSize = 100, hardened = false) {
|
|
218
|
+
const wasm = this.#wasm;
|
|
219
|
+
|
|
220
|
+
// Allocate config buffer
|
|
221
|
+
const configPtr = wasm._hd_alloc(STREAMDERIVECONFIG_SIZE);
|
|
222
|
+
if (!configPtr) {
|
|
223
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Fill config
|
|
227
|
+
const configView = StreamDeriveConfigView.fromMemory(wasm.wasmMemory, configPtr);
|
|
228
|
+
this.#fillBaseKey(configView, baseKey);
|
|
229
|
+
configView.start_index = startIndex >>> 0;
|
|
230
|
+
configView.batch_size = batchSize >>> 0;
|
|
231
|
+
configView.hardened = hardened ? 1 : 0;
|
|
232
|
+
|
|
233
|
+
// Create stream
|
|
234
|
+
const streamHandle = wasm._hd_aligned_stream_create(configPtr);
|
|
235
|
+
wasm._hd_dealloc(configPtr);
|
|
236
|
+
|
|
237
|
+
if (!streamHandle) {
|
|
238
|
+
throw new AlignedError(AlignedErrorCode.INTERNAL, 'Failed to create stream');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Allocate results buffer
|
|
242
|
+
const resultsSize = batchSize * DERIVEDKEYENTRY_SIZE;
|
|
243
|
+
const resultsPtr = wasm._hd_alloc(resultsSize);
|
|
244
|
+
if (!resultsPtr) {
|
|
245
|
+
wasm._hd_aligned_stream_destroy(streamHandle);
|
|
246
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
while (true) {
|
|
251
|
+
const derivedCount = wasm._hd_aligned_stream_next(
|
|
252
|
+
streamHandle,
|
|
253
|
+
resultsPtr,
|
|
254
|
+
batchSize
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (derivedCount <= 0) {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Read batch results
|
|
262
|
+
const batch = [];
|
|
263
|
+
const resultsArray = DerivedKeyEntryArrayView.fromMemory(
|
|
264
|
+
wasm.wasmMemory, resultsPtr, derivedCount
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
for (let i = 0; i < derivedCount; i++) {
|
|
268
|
+
const entry = resultsArray.at(i);
|
|
269
|
+
batch.push({
|
|
270
|
+
index: entry.index,
|
|
271
|
+
error: entry.error,
|
|
272
|
+
publicKey: entry.error === AlignedErrorCode.OK
|
|
273
|
+
? new Uint8Array(entry.public_key_data)
|
|
274
|
+
: null,
|
|
275
|
+
privateKey: entry.error === AlignedErrorCode.OK
|
|
276
|
+
? new Uint8Array(entry.private_key_data)
|
|
277
|
+
: null,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
yield batch;
|
|
282
|
+
}
|
|
283
|
+
} finally {
|
|
284
|
+
wasm._hd_dealloc(resultsPtr);
|
|
285
|
+
wasm._hd_aligned_stream_destroy(streamHandle);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Fill base key data in request/config view
|
|
291
|
+
* @private
|
|
292
|
+
*/
|
|
293
|
+
#fillBaseKey(view, baseKey) {
|
|
294
|
+
if (baseKey._handle) {
|
|
295
|
+
// HDKey object - use conversion function
|
|
296
|
+
const keyDataPtr = this.#wasm._hd_alloc(EXTENDEDKEYDATA_SIZE);
|
|
297
|
+
try {
|
|
298
|
+
this.#wasm._hd_aligned_from_extended_key(baseKey._handle, keyDataPtr);
|
|
299
|
+
const keyDataView = ExtendedKeyDataView.fromMemory(
|
|
300
|
+
this.#wasm.wasmMemory, keyDataPtr
|
|
301
|
+
);
|
|
302
|
+
// Copy to request view's base_key fields
|
|
303
|
+
view.base_key_curve = keyDataView.curve;
|
|
304
|
+
view.base_key_depth = keyDataView.depth;
|
|
305
|
+
view.base_key_parent_fingerprint = keyDataView.parent_fingerprint;
|
|
306
|
+
view.base_key_child_index = keyDataView.child_index;
|
|
307
|
+
view.base_key_chain_code_data.set(keyDataView.chain_code_data);
|
|
308
|
+
view.base_key_public_key_data.set(keyDataView.public_key_data);
|
|
309
|
+
view.base_key_private_key_data.set(keyDataView.private_key_data);
|
|
310
|
+
view.base_key_has_private_key = keyDataView.has_private_key;
|
|
311
|
+
} finally {
|
|
312
|
+
this.#wasm._hd_dealloc(keyDataPtr);
|
|
313
|
+
}
|
|
314
|
+
} else if (baseKey.privateKey && baseKey.chainCode) {
|
|
315
|
+
// Plain object with key data
|
|
316
|
+
view.base_key_curve = baseKey.curve ?? AlignedCurve.SECP256K1;
|
|
317
|
+
view.base_key_depth = baseKey.depth ?? 0;
|
|
318
|
+
view.base_key_parent_fingerprint = baseKey.parentFingerprint ?? 0;
|
|
319
|
+
view.base_key_child_index = baseKey.childIndex ?? 0;
|
|
320
|
+
view.base_key_chain_code_data.set(baseKey.chainCode);
|
|
321
|
+
view.base_key_public_key_data.set(baseKey.publicKey);
|
|
322
|
+
view.base_key_private_key_data.set(baseKey.privateKey);
|
|
323
|
+
view.base_key_has_private_key = 1;
|
|
324
|
+
} else {
|
|
325
|
+
throw new AlignedError(
|
|
326
|
+
AlignedErrorCode.INVALID_ARGUMENT,
|
|
327
|
+
'baseKey must be an HDKey or have privateKey and chainCode'
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// =============================================================================
|
|
334
|
+
// AlignedSigner - Batch Signing
|
|
335
|
+
// =============================================================================
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Efficient batch signing using aligned binary format.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```js
|
|
342
|
+
* const signer = new AlignedSigner(wasm);
|
|
343
|
+
*
|
|
344
|
+
* // Sign multiple hashes
|
|
345
|
+
* const hashes = [hash1, hash2, hash3];
|
|
346
|
+
* const signatures = signer.signBatch(privateKey, hashes);
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
export class AlignedSigner {
|
|
350
|
+
#wasm;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* @param {Object} wasm - WASM module instance
|
|
354
|
+
*/
|
|
355
|
+
constructor(wasm) {
|
|
356
|
+
this.#wasm = wasm;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Sign multiple message hashes with the same private key.
|
|
361
|
+
*
|
|
362
|
+
* @param {Uint8Array} privateKey - 32-byte private key
|
|
363
|
+
* @param {Array<Uint8Array>} hashes - Array of 32-byte message hashes
|
|
364
|
+
* @param {number} [curve=AlignedCurve.SECP256K1] - Elliptic curve
|
|
365
|
+
* @returns {Array<SignatureResult>} Array of signature results
|
|
366
|
+
*/
|
|
367
|
+
signBatch(privateKey, hashes, curve = AlignedCurve.SECP256K1) {
|
|
368
|
+
const wasm = this.#wasm;
|
|
369
|
+
const count = hashes.length;
|
|
370
|
+
|
|
371
|
+
// Allocate request
|
|
372
|
+
const requestPtr = wasm._hd_alloc(BATCHSIGNREQUEST_SIZE);
|
|
373
|
+
if (!requestPtr) {
|
|
374
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
// Fill request
|
|
379
|
+
const requestView = BatchSignRequestView.fromMemory(wasm.wasmMemory, requestPtr);
|
|
380
|
+
requestView.private_key_data.set(privateKey);
|
|
381
|
+
requestView.curve = curve;
|
|
382
|
+
requestView.count = count;
|
|
383
|
+
|
|
384
|
+
// Allocate and fill hashes
|
|
385
|
+
const hashesPtr = wasm._hd_alloc(count * HASHENTRY_SIZE);
|
|
386
|
+
if (!hashesPtr) {
|
|
387
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
const hashesArray = HashEntryArrayView.fromMemory(wasm.wasmMemory, hashesPtr, count);
|
|
392
|
+
for (let i = 0; i < count; i++) {
|
|
393
|
+
const entry = hashesArray.at(i);
|
|
394
|
+
entry.index = i;
|
|
395
|
+
entry.hash_data.set(hashes[i]);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Allocate results
|
|
399
|
+
const resultsPtr = wasm._hd_alloc(count * SIGNATUREENTRY_SIZE);
|
|
400
|
+
if (!resultsPtr) {
|
|
401
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
// Call WASM function
|
|
406
|
+
const signedCount = wasm._hd_aligned_sign_batch(
|
|
407
|
+
requestPtr,
|
|
408
|
+
hashesPtr,
|
|
409
|
+
count,
|
|
410
|
+
resultsPtr,
|
|
411
|
+
count
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
if (signedCount < 0) {
|
|
415
|
+
throw new AlignedError(signedCount);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Read results
|
|
419
|
+
const results = [];
|
|
420
|
+
const resultsArray = SignatureEntryArrayView.fromMemory(
|
|
421
|
+
wasm.wasmMemory, resultsPtr, signedCount
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
for (let i = 0; i < signedCount; i++) {
|
|
425
|
+
const entry = resultsArray.at(i);
|
|
426
|
+
results.push({
|
|
427
|
+
index: entry.index,
|
|
428
|
+
error: entry.error,
|
|
429
|
+
signature: entry.error === AlignedErrorCode.OK
|
|
430
|
+
? new Uint8Array(entry.signature_data)
|
|
431
|
+
: null,
|
|
432
|
+
recoveryId: entry.recovery_id,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return results;
|
|
437
|
+
} finally {
|
|
438
|
+
wasm._hd_dealloc(resultsPtr);
|
|
439
|
+
}
|
|
440
|
+
} finally {
|
|
441
|
+
wasm._hd_dealloc(hashesPtr);
|
|
442
|
+
}
|
|
443
|
+
} finally {
|
|
444
|
+
wasm._hd_dealloc(requestPtr);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Verify multiple signatures against the same public key.
|
|
450
|
+
*
|
|
451
|
+
* @param {Uint8Array} publicKey - 33-byte compressed public key
|
|
452
|
+
* @param {Array<{hash: Uint8Array, signature: Uint8Array}>} entries - Hash/signature pairs
|
|
453
|
+
* @param {number} [curve=AlignedCurve.SECP256K1] - Elliptic curve
|
|
454
|
+
* @returns {Array<VerifyResult>} Array of verification results
|
|
455
|
+
*/
|
|
456
|
+
verifyBatch(publicKey, entries, curve = AlignedCurve.SECP256K1) {
|
|
457
|
+
const wasm = this.#wasm;
|
|
458
|
+
const count = entries.length;
|
|
459
|
+
|
|
460
|
+
// Allocate request
|
|
461
|
+
const requestPtr = wasm._hd_alloc(BATCHVERIFYREQUEST_SIZE);
|
|
462
|
+
if (!requestPtr) {
|
|
463
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
// Fill request
|
|
468
|
+
const requestView = BatchVerifyRequestView.fromMemory(wasm.wasmMemory, requestPtr);
|
|
469
|
+
requestView.public_key_data.set(publicKey);
|
|
470
|
+
requestView.curve = curve;
|
|
471
|
+
requestView.count = count;
|
|
472
|
+
|
|
473
|
+
// Allocate and fill entries
|
|
474
|
+
const entriesPtr = wasm._hd_alloc(count * VERIFYENTRY_SIZE);
|
|
475
|
+
if (!entriesPtr) {
|
|
476
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
try {
|
|
480
|
+
const entriesArray = VerifyEntryArrayView.fromMemory(wasm.wasmMemory, entriesPtr, count);
|
|
481
|
+
for (let i = 0; i < count; i++) {
|
|
482
|
+
const view = entriesArray.at(i);
|
|
483
|
+
view.index = i;
|
|
484
|
+
view.hash_data.set(entries[i].hash);
|
|
485
|
+
view.signature_data.set(entries[i].signature);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Allocate results
|
|
489
|
+
const resultsPtr = wasm._hd_alloc(count * VERIFYRESULTENTRY_SIZE);
|
|
490
|
+
if (!resultsPtr) {
|
|
491
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
// Call WASM function
|
|
496
|
+
const verifiedCount = wasm._hd_aligned_verify_batch(
|
|
497
|
+
requestPtr,
|
|
498
|
+
entriesPtr,
|
|
499
|
+
count,
|
|
500
|
+
resultsPtr,
|
|
501
|
+
count
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
if (verifiedCount < 0) {
|
|
505
|
+
throw new AlignedError(verifiedCount);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Read results
|
|
509
|
+
const results = [];
|
|
510
|
+
const resultsArray = VerifyResultEntryArrayView.fromMemory(
|
|
511
|
+
wasm.wasmMemory, resultsPtr, verifiedCount
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
for (let i = 0; i < verifiedCount; i++) {
|
|
515
|
+
const entry = resultsArray.at(i);
|
|
516
|
+
results.push({
|
|
517
|
+
index: entry.index,
|
|
518
|
+
valid: entry.error === AlignedErrorCode.OK,
|
|
519
|
+
error: entry.error,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return results;
|
|
524
|
+
} finally {
|
|
525
|
+
wasm._hd_dealloc(resultsPtr);
|
|
526
|
+
}
|
|
527
|
+
} finally {
|
|
528
|
+
wasm._hd_dealloc(entriesPtr);
|
|
529
|
+
}
|
|
530
|
+
} finally {
|
|
531
|
+
wasm._hd_dealloc(requestPtr);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// =============================================================================
|
|
537
|
+
// AlignedAPI - Combined API
|
|
538
|
+
// =============================================================================
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Combined aligned API providing access to batch operations.
|
|
542
|
+
*/
|
|
543
|
+
export class AlignedAPI {
|
|
544
|
+
#wasm;
|
|
545
|
+
#keyDeriver;
|
|
546
|
+
#signer;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @param {Object} wasm - WASM module instance
|
|
550
|
+
*/
|
|
551
|
+
constructor(wasm) {
|
|
552
|
+
this.#wasm = wasm;
|
|
553
|
+
this.#keyDeriver = null;
|
|
554
|
+
this.#signer = null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Get key deriver instance
|
|
559
|
+
* @returns {AlignedKeyDeriver}
|
|
560
|
+
*/
|
|
561
|
+
get keyDeriver() {
|
|
562
|
+
if (!this.#keyDeriver) {
|
|
563
|
+
this.#keyDeriver = new AlignedKeyDeriver(this.#wasm);
|
|
564
|
+
}
|
|
565
|
+
return this.#keyDeriver;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Get signer instance
|
|
570
|
+
* @returns {AlignedSigner}
|
|
571
|
+
*/
|
|
572
|
+
get signer() {
|
|
573
|
+
if (!this.#signer) {
|
|
574
|
+
this.#signer = new AlignedSigner(this.#wasm);
|
|
575
|
+
}
|
|
576
|
+
return this.#signer;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Convert HDKey to ExtendedKeyData bytes
|
|
581
|
+
* @param {Object} hdKey - HDKey instance
|
|
582
|
+
* @returns {Uint8Array} Extended key data bytes
|
|
583
|
+
*/
|
|
584
|
+
keyToBytes(hdKey) {
|
|
585
|
+
const wasm = this.#wasm;
|
|
586
|
+
const ptr = wasm._hd_alloc(EXTENDEDKEYDATA_SIZE);
|
|
587
|
+
if (!ptr) {
|
|
588
|
+
throw new AlignedError(AlignedErrorCode.OUT_OF_MEMORY);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
try {
|
|
592
|
+
const result = wasm._hd_aligned_from_extended_key(hdKey._handle, ptr);
|
|
593
|
+
if (result !== 0) {
|
|
594
|
+
throw new AlignedError(result);
|
|
595
|
+
}
|
|
596
|
+
return new Uint8Array(wasm.HEAPU8.buffer, ptr, EXTENDEDKEYDATA_SIZE).slice();
|
|
597
|
+
} finally {
|
|
598
|
+
wasm._hd_dealloc(ptr);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Get size of DerivedKeyEntry struct
|
|
604
|
+
* @returns {number}
|
|
605
|
+
*/
|
|
606
|
+
get derivedKeyEntrySize() {
|
|
607
|
+
return DERIVEDKEYENTRY_SIZE;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Get size of ExtendedKeyData struct
|
|
612
|
+
* @returns {number}
|
|
613
|
+
*/
|
|
614
|
+
get extendedKeyDataSize() {
|
|
615
|
+
return EXTENDEDKEYDATA_SIZE;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Get size of BatchDeriveRequest struct
|
|
620
|
+
* @returns {number}
|
|
621
|
+
*/
|
|
622
|
+
get batchDeriveRequestSize() {
|
|
623
|
+
return BATCHDERIVEREQUEST_SIZE;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export default AlignedAPI;
|
package/src/index.d.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* - Transaction building and signing
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
// Import aligned API types
|
|
13
|
+
import { AlignedAPI } from './aligned';
|
|
14
|
+
|
|
12
15
|
// =============================================================================
|
|
13
16
|
// Module Types
|
|
14
17
|
// =============================================================================
|
|
@@ -62,6 +65,9 @@ export interface HDWalletModule {
|
|
|
62
65
|
|
|
63
66
|
// Utilities
|
|
64
67
|
utils: UtilsAPI;
|
|
68
|
+
|
|
69
|
+
// Aligned binary API for efficient batch operations
|
|
70
|
+
aligned: AlignedAPI;
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
// =============================================================================
|
|
@@ -600,10 +606,12 @@ export interface UtilsAPI {
|
|
|
600
606
|
|
|
601
607
|
/**
|
|
602
608
|
* Initialize the HD Wallet WASM module
|
|
609
|
+
* @param wasmPath - Optional path to WASM file
|
|
603
610
|
*/
|
|
604
|
-
export default function init(): Promise<HDWalletModule>;
|
|
611
|
+
export default function init(wasmPath?: string): Promise<HDWalletModule>;
|
|
605
612
|
|
|
606
613
|
/**
|
|
607
614
|
* Create HD Wallet instance (alternative syntax)
|
|
615
|
+
* @param wasmPath - Optional path to WASM file
|
|
608
616
|
*/
|
|
609
|
-
export function createHDWallet(): Promise<HDWalletModule>;
|
|
617
|
+
export function createHDWallet(wasmPath?: string): Promise<HDWalletModule>;
|
package/src/index.mjs
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
* @version 0.1.0
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
// Import aligned API for batch operations
|
|
16
|
+
import { AlignedAPI } from './aligned.mjs';
|
|
17
|
+
|
|
15
18
|
// =============================================================================
|
|
16
19
|
// Enums (matching TypeScript definitions)
|
|
17
20
|
// =============================================================================
|
|
@@ -604,7 +607,7 @@ async function loadWasmModule(wasmPath) {
|
|
|
604
607
|
|
|
605
608
|
try {
|
|
606
609
|
// Try dynamic import (works in Node.js and bundlers)
|
|
607
|
-
const module = await import('
|
|
610
|
+
const module = await import('../dist/hd-wallet.js');
|
|
608
611
|
HDWalletWasm = module.default;
|
|
609
612
|
} catch (e) {
|
|
610
613
|
// Fallback for browsers without ES module support
|
|
@@ -768,8 +771,9 @@ function createModule(wasm) {
|
|
|
768
771
|
const outputPtr = wasm._hd_alloc(1024);
|
|
769
772
|
try {
|
|
770
773
|
wasm._hd_mnemonic_suggest_word(prefixPtr, language, outputPtr, 1024, maxSuggestions);
|
|
771
|
-
const
|
|
772
|
-
|
|
774
|
+
const text = readString(wasm, outputPtr);
|
|
775
|
+
// C API returns newline-separated words
|
|
776
|
+
return text ? text.split('\n').filter(w => w.length > 0) : [];
|
|
773
777
|
} finally {
|
|
774
778
|
wasm._hd_dealloc(prefixPtr);
|
|
775
779
|
wasm._hd_dealloc(outputPtr);
|
|
@@ -804,23 +808,24 @@ function createModule(wasm) {
|
|
|
804
808
|
const hdkey = {
|
|
805
809
|
/**
|
|
806
810
|
* Create master key from seed
|
|
807
|
-
* @param {Uint8Array} seed - 64
|
|
811
|
+
* @param {Uint8Array} seed - 16-64 byte seed (BIP-32 allows 128-512 bits)
|
|
808
812
|
* @param {number} [curve=Curve.SECP256K1] - Elliptic curve
|
|
809
813
|
* @returns {HDKey} Master HD key
|
|
810
814
|
*/
|
|
811
815
|
fromSeed(seed, curve = Curve.SECP256K1) {
|
|
812
|
-
|
|
813
|
-
|
|
816
|
+
// BIP-32 allows 128-512 bits (16-64 bytes)
|
|
817
|
+
if (seed.length < 16 || seed.length > 64) {
|
|
818
|
+
throw new HDWalletError(ErrorCode.INVALID_SEED, 'Seed must be 16-64 bytes');
|
|
814
819
|
}
|
|
815
820
|
const seedPtr = allocAndCopy(wasm, seed);
|
|
816
821
|
try {
|
|
817
|
-
const handle = wasm._hd_key_from_seed(seedPtr,
|
|
822
|
+
const handle = wasm._hd_key_from_seed(seedPtr, seed.length, curve);
|
|
818
823
|
if (!handle) {
|
|
819
824
|
throw new HDWalletError(ErrorCode.INVALID_SEED);
|
|
820
825
|
}
|
|
821
826
|
return new HDKey(wasm, handle, 'm');
|
|
822
827
|
} finally {
|
|
823
|
-
wasm._hd_secure_wipe(seedPtr,
|
|
828
|
+
wasm._hd_secure_wipe(seedPtr, seed.length);
|
|
824
829
|
wasm._hd_dealloc(seedPtr);
|
|
825
830
|
}
|
|
826
831
|
},
|
|
@@ -2444,7 +2449,20 @@ function createModule(wasm) {
|
|
|
2444
2449
|
polkadot,
|
|
2445
2450
|
hardware,
|
|
2446
2451
|
keyring,
|
|
2447
|
-
utils
|
|
2452
|
+
utils,
|
|
2453
|
+
|
|
2454
|
+
// Aligned binary API for efficient batch operations
|
|
2455
|
+
get aligned() {
|
|
2456
|
+
if (!this._aligned) {
|
|
2457
|
+
// Create a wrapper that exposes wasmMemory for the aligned API
|
|
2458
|
+
const wasmWithMemory = Object.create(wasm);
|
|
2459
|
+
Object.defineProperty(wasmWithMemory, 'wasmMemory', {
|
|
2460
|
+
get: () => ({ buffer: wasm.HEAPU8.buffer })
|
|
2461
|
+
});
|
|
2462
|
+
this._aligned = new AlignedAPI(wasmWithMemory);
|
|
2463
|
+
}
|
|
2464
|
+
return this._aligned;
|
|
2465
|
+
}
|
|
2448
2466
|
};
|
|
2449
2467
|
}
|
|
2450
2468
|
|