@totemsdk/core 1.2.5 → 1.2.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/dist/MiniNumber.js +19 -21
- package/dist/Streamable.js +31 -54
- package/dist/adapters/index.js +9 -18
- package/dist/adapters/types.js +11 -19
- package/dist/binio.js +7 -20
- package/dist/bip39.js +14 -24
- package/dist/derive.js +7 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -143
- package/dist/javaStreamables.js +34 -55
- package/dist/lease/LeaseMonitor.js +10 -10
- package/dist/lease/LeaseStore.js +8 -9
- package/dist/lease/WatermarkStore.js +8 -9
- package/dist/lease/index.js +4 -13
- package/dist/lease-client.js +3 -8
- package/dist/legacy.js +13 -140
- package/dist/minima32.js +13 -22
- package/dist/minimaWireSerializer.js +14 -20
- package/dist/mmr.js +26 -40
- package/dist/mx.js +1 -10
- package/dist/params.js +5 -9
- package/dist/script.js +7 -12
- package/dist/scripts/contract-helpers.js +25 -36
- package/dist/scripts/dapp-types.js +6 -11
- package/dist/scripts/index.js +4 -38
- package/dist/scripts/types.js +12 -26
- package/dist/scripts/witness-serializer.js +30 -48
- package/dist/transaction.js +46 -55
- package/dist/treekey.js +82 -89
- package/dist/tx/TransactionLifecycle.js +15 -11
- package/dist/tx/TransactionReceiptStore.js +9 -9
- package/dist/tx/TransactionService.js +14 -14
- package/dist/tx/index.js +3 -11
- package/dist/tx/types.js +1 -2
- package/dist/util.js +1 -5
- package/dist/utils.js +5 -12
- package/dist/verify.js +25 -36
- package/dist/version.js +2 -5
- package/dist/wasm-sync.js +56 -61
- package/dist/wots.d.ts +1 -1
- package/dist/wots.js +64 -94
- package/package.json +1 -1
package/dist/treekey.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* TreeKey/TreeKeyNode Implementation matching Minima's TreeKey.java and TreeKeyNode.java
|
|
4
3
|
*
|
|
@@ -9,29 +8,14 @@
|
|
|
9
8
|
*
|
|
10
9
|
* Default structure: 3 levels x 64 keys = 64^3 = 262,144 one-time signatures
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
exports.verifyTreeSignature = verifyTreeSignature;
|
|
19
|
-
exports.serializeTreeSignature = serializeTreeSignature;
|
|
20
|
-
exports.deserializeTreeSignature = deserializeTreeSignature;
|
|
21
|
-
exports.createUnifiedChildTreeKey = createUnifiedChildTreeKey;
|
|
22
|
-
exports.createUnifiedChildTreeKeyAsync = createUnifiedChildTreeKeyAsync;
|
|
23
|
-
exports.createUnifiedRootTreeKey = createUnifiedRootTreeKey;
|
|
24
|
-
exports.deriveUnifiedAddressPublicKey = deriveUnifiedAddressPublicKey;
|
|
25
|
-
exports.createPerAddressTreeKey = createPerAddressTreeKey;
|
|
26
|
-
exports.createPerAddressTreeKeyAsync = createPerAddressTreeKeyAsync;
|
|
27
|
-
const javaStreamables_js_1 = require("./javaStreamables.js");
|
|
28
|
-
const wots_js_1 = require("./wots.js");
|
|
29
|
-
const params_js_1 = require("./params.js");
|
|
30
|
-
const mmr_js_1 = require("./mmr.js");
|
|
31
|
-
const Streamable_js_1 = require("./Streamable.js");
|
|
32
|
-
const index_js_1 = require("./adapters/index.js");
|
|
11
|
+
import { deriveChainSeedJava, hashObject, deriveRootPrivSeed, deriveUnifiedChildSeed, derivePerAddressSeed } from './javaStreamables.js';
|
|
12
|
+
import { derivePKdigest, deriveFullPublicKey, wotsSign, wotsVerifyDigest, hex } from './wots.js';
|
|
13
|
+
import { getParamSet } from './params.js';
|
|
14
|
+
import { MMRTree, createMMRDataLeafNode, calculateProofRoot, parseMMRProofFromHex } from './mmr.js';
|
|
15
|
+
import { writeSignature } from './Streamable.js';
|
|
16
|
+
import { NoopLogger } from './adapters/index.js';
|
|
33
17
|
// Module-level debug logger - can be set externally for parity testing
|
|
34
|
-
let treeKeyLogger = new
|
|
18
|
+
let treeKeyLogger = new NoopLogger();
|
|
35
19
|
let treeKeyDebugEnabled = false;
|
|
36
20
|
/**
|
|
37
21
|
* Enable TreeKey debug logging with a custom logger
|
|
@@ -41,32 +25,32 @@ let treeKeyDebugEnabled = false;
|
|
|
41
25
|
* seeds and private key derivation. NEVER enable in production builds.
|
|
42
26
|
* This is intended for development/testing parity verification only.
|
|
43
27
|
*/
|
|
44
|
-
function setTreeKeyLogger(logger) {
|
|
28
|
+
export function setTreeKeyLogger(logger) {
|
|
45
29
|
treeKeyLogger = logger;
|
|
46
30
|
treeKeyDebugEnabled = true;
|
|
47
31
|
}
|
|
48
32
|
/**
|
|
49
33
|
* Disable TreeKey debug logging
|
|
50
34
|
*/
|
|
51
|
-
function disableTreeKeyLogger() {
|
|
52
|
-
treeKeyLogger = new
|
|
35
|
+
export function disableTreeKeyLogger() {
|
|
36
|
+
treeKeyLogger = new NoopLogger();
|
|
53
37
|
treeKeyDebugEnabled = false;
|
|
54
38
|
}
|
|
55
39
|
/**
|
|
56
40
|
* Check if TreeKey debug logging is enabled
|
|
57
41
|
*/
|
|
58
|
-
function isTreeKeyDebugEnabled() {
|
|
42
|
+
export function isTreeKeyDebugEnabled() {
|
|
59
43
|
return treeKeyDebugEnabled;
|
|
60
44
|
}
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
export const DEFAULT_KEYS_PER_LEVEL = 64;
|
|
46
|
+
export const DEFAULT_LEVELS = 3;
|
|
63
47
|
/**
|
|
64
48
|
* Compute root public key from a Winternitz signature proof
|
|
65
49
|
* Matches SignatureProof.getRootPublicKey() in Java
|
|
66
50
|
*/
|
|
67
|
-
function getRootPublicKey(proof) {
|
|
68
|
-
const leafData =
|
|
69
|
-
return
|
|
51
|
+
export function getRootPublicKey(proof) {
|
|
52
|
+
const leafData = createMMRDataLeafNode(proof.leafPubkey, 0n);
|
|
53
|
+
return calculateProofRoot(leafData, proof.mmrProof);
|
|
70
54
|
}
|
|
71
55
|
/**
|
|
72
56
|
* TreeKeyNode - One node in the key tree containing 64 Winternitz keys
|
|
@@ -91,15 +75,21 @@ function getRootPublicKey(proof) {
|
|
|
91
75
|
* IMPORTANT: Minima NEVER stores the 1088-byte full WOTS public key.
|
|
92
76
|
* Only the 32-byte digest is stored and used for MMR construction.
|
|
93
77
|
*/
|
|
94
|
-
class TreeKeyNode {
|
|
95
|
-
|
|
96
|
-
|
|
78
|
+
export class TreeKeyNode {
|
|
79
|
+
seed;
|
|
80
|
+
childSeed;
|
|
81
|
+
keysPerLevel;
|
|
82
|
+
publicKeyDigests; // 32-byte SHA3 digests - matches Java's Winternitz.getPublicKey() which returns SHA3-256(full_key)
|
|
83
|
+
mmrTree;
|
|
84
|
+
rootPubkey;
|
|
85
|
+
childCache = new Map();
|
|
86
|
+
constructor(privateSeed, keysPerLevel = DEFAULT_KEYS_PER_LEVEL) {
|
|
97
87
|
if (privateSeed.length !== 32) {
|
|
98
88
|
throw new Error('Private seed must be 32 bytes');
|
|
99
89
|
}
|
|
100
90
|
if (treeKeyDebugEnabled) {
|
|
101
91
|
treeKeyLogger.debug(`[TreeKeyNode] ========== INIT START ==========`);
|
|
102
|
-
treeKeyLogger.debug(`[TreeKeyNode] Private seed: ${
|
|
92
|
+
treeKeyLogger.debug(`[TreeKeyNode] Private seed: ${hex(privateSeed)}`);
|
|
103
93
|
treeKeyLogger.debug(`[TreeKeyNode] Keys per level: ${keysPerLevel}`);
|
|
104
94
|
}
|
|
105
95
|
this.seed = privateSeed;
|
|
@@ -107,9 +97,9 @@ class TreeKeyNode {
|
|
|
107
97
|
// Hash the seed to create child seed (for deriving child nodes)
|
|
108
98
|
// From TreeKeyNode.java: mChildSeed = Crypto.getInstance().hashObject(zPrivateSeed);
|
|
109
99
|
// hashObject serializes as MiniData (length-prefixed) then hashes
|
|
110
|
-
this.childSeed =
|
|
100
|
+
this.childSeed = hashObject(privateSeed);
|
|
111
101
|
if (treeKeyDebugEnabled) {
|
|
112
|
-
treeKeyLogger.debug(`[TreeKeyNode] Child seed (hashObject): ${
|
|
102
|
+
treeKeyLogger.debug(`[TreeKeyNode] Child seed (hashObject): ${hex(this.childSeed)}`);
|
|
113
103
|
}
|
|
114
104
|
// Generate all Winternitz public key DIGESTS (32 bytes each)
|
|
115
105
|
// Java's Winternitz.getPublicKey() returns SHA3-256(full_key) = 32 bytes
|
|
@@ -119,22 +109,22 @@ class TreeKeyNode {
|
|
|
119
109
|
this.publicKeyDigests = [];
|
|
120
110
|
for (let i = 0; i < keysPerLevel; i++) {
|
|
121
111
|
// Digest (32 bytes) - used for MMR tree, SignatureProof.leafPubkey, and address derivation
|
|
122
|
-
const pkDigest =
|
|
112
|
+
const pkDigest = derivePKdigest(privateSeed, i, getParamSet());
|
|
123
113
|
this.publicKeyDigests.push(pkDigest);
|
|
124
114
|
// Log first few keys for debugging
|
|
125
115
|
if (treeKeyDebugEnabled && i < 3) {
|
|
126
|
-
treeKeyLogger.debug(`[TreeKeyNode] WOTS key[${i}] digest (32B): ${
|
|
116
|
+
treeKeyLogger.debug(`[TreeKeyNode] WOTS key[${i}] digest (32B): ${hex(pkDigest)}`);
|
|
127
117
|
}
|
|
128
118
|
}
|
|
129
119
|
if (treeKeyDebugEnabled) {
|
|
130
120
|
treeKeyLogger.debug(`[TreeKeyNode] Generated ${this.publicKeyDigests.length} WOTS key digests`);
|
|
131
121
|
const lastIdx = this.publicKeyDigests.length - 1;
|
|
132
|
-
treeKeyLogger.debug(`[TreeKeyNode] WOTS key[${lastIdx}] digest: ${
|
|
122
|
+
treeKeyLogger.debug(`[TreeKeyNode] WOTS key[${lastIdx}] digest: ${hex(this.publicKeyDigests[lastIdx])}`);
|
|
133
123
|
}
|
|
134
124
|
// Build MMR tree from 32-byte PUBLIC KEY DIGESTS (Java-compatible)
|
|
135
125
|
// Java's TreeKeyNode: MMRData.CreateMMRDataLeafNode(wots.getPublicKey(), ZERO)
|
|
136
126
|
// where getPublicKey() returns SHA3-256(full_key) = 32-byte DIGEST
|
|
137
|
-
this.mmrTree =
|
|
127
|
+
this.mmrTree = MMRTree.fromPublicKeys(this.publicKeyDigests);
|
|
138
128
|
// Root public key = MMR root
|
|
139
129
|
const root = this.mmrTree.getRoot();
|
|
140
130
|
if (!root) {
|
|
@@ -142,7 +132,7 @@ class TreeKeyNode {
|
|
|
142
132
|
}
|
|
143
133
|
this.rootPubkey = root.data;
|
|
144
134
|
if (treeKeyDebugEnabled) {
|
|
145
|
-
treeKeyLogger.debug(`[TreeKeyNode] MMR root (node public key): ${
|
|
135
|
+
treeKeyLogger.debug(`[TreeKeyNode] MMR root (node public key): ${hex(this.rootPubkey)}`);
|
|
146
136
|
treeKeyLogger.debug(`[TreeKeyNode] ========== INIT END ==========`);
|
|
147
137
|
}
|
|
148
138
|
}
|
|
@@ -150,14 +140,14 @@ class TreeKeyNode {
|
|
|
150
140
|
* Async factory method for TreeKeyNode with progress reporting
|
|
151
141
|
* Yields to event loop every few keys to keep UI responsive
|
|
152
142
|
*/
|
|
153
|
-
static async createWithProgress(privateSeed, keysPerLevel =
|
|
143
|
+
static async createWithProgress(privateSeed, keysPerLevel = DEFAULT_KEYS_PER_LEVEL, onProgress) {
|
|
154
144
|
if (privateSeed.length !== 32) {
|
|
155
145
|
throw new Error('Private seed must be 32 bytes');
|
|
156
146
|
}
|
|
157
147
|
const node = Object.create(TreeKeyNode.prototype);
|
|
158
148
|
node.seed = privateSeed;
|
|
159
149
|
node.keysPerLevel = keysPerLevel;
|
|
160
|
-
node.childSeed =
|
|
150
|
+
node.childSeed = hashObject(privateSeed);
|
|
161
151
|
node.publicKeyDigests = [];
|
|
162
152
|
node.childCache = new Map();
|
|
163
153
|
// Generate Winternitz public key DIGESTS with progress (32 bytes each)
|
|
@@ -165,7 +155,7 @@ class TreeKeyNode {
|
|
|
165
155
|
// See WinternitzOTSignature.getPublicKey() lines 103-121: hashes full 1088B key, returns 32B digest
|
|
166
156
|
for (let i = 0; i < keysPerLevel; i++) {
|
|
167
157
|
// Digest (32 bytes) - used for MMR tree, SignatureProof.leafPubkey, and address derivation
|
|
168
|
-
const pkDigest =
|
|
158
|
+
const pkDigest = derivePKdigest(privateSeed, i, getParamSet());
|
|
169
159
|
node.publicKeyDigests.push(pkDigest);
|
|
170
160
|
// Report progress every 4 keys and yield to event loop
|
|
171
161
|
if (onProgress && (i + 1) % 4 === 0) {
|
|
@@ -191,7 +181,7 @@ class TreeKeyNode {
|
|
|
191
181
|
// Build MMR tree from 32-byte PUBLIC KEY DIGESTS (Java-compatible)
|
|
192
182
|
// Java's TreeKeyNode: MMRData.CreateMMRDataLeafNode(wots.getPublicKey(), ZERO)
|
|
193
183
|
// where getPublicKey() returns SHA3-256(full_key) = 32-byte DIGEST
|
|
194
|
-
node.mmrTree =
|
|
184
|
+
node.mmrTree = MMRTree.fromPublicKeys(node.publicKeyDigests);
|
|
195
185
|
const root = node.mmrTree.getRoot();
|
|
196
186
|
if (!root) {
|
|
197
187
|
throw new Error('Failed to compute MMR root');
|
|
@@ -228,7 +218,7 @@ class TreeKeyNode {
|
|
|
228
218
|
throw new Error(`Key index ${index} out of range [0, ${this.keysPerLevel})`);
|
|
229
219
|
}
|
|
230
220
|
// Derive on-demand - expensive but only used for tests
|
|
231
|
-
return
|
|
221
|
+
return deriveFullPublicKey(this.seed, index, getParamSet());
|
|
232
222
|
}
|
|
233
223
|
/**
|
|
234
224
|
* Get the Winternitz public key digest at a specific index (0-63)
|
|
@@ -268,14 +258,14 @@ class TreeKeyNode {
|
|
|
268
258
|
if (treeKeyDebugEnabled) {
|
|
269
259
|
treeKeyLogger.debug(`[TreeKeyNode.sign] ========== SIGN START ==========`);
|
|
270
260
|
treeKeyLogger.debug(`[TreeKeyNode.sign] keyIndex: ${keyIndex}`);
|
|
271
|
-
treeKeyLogger.debug(`[TreeKeyNode.sign] input data (${data.length}B): ${
|
|
261
|
+
treeKeyLogger.debug(`[TreeKeyNode.sign] input data (${data.length}B): ${hex(data).substring(0, 64)}${data.length > 32 ? '...' : ''}`);
|
|
272
262
|
}
|
|
273
263
|
// Get the 32-byte Winternitz public key DIGEST for SignatureProof
|
|
274
264
|
// CRITICAL FIX: Java's Winternitz.getPublicKey() returns SHA3-256(full_key) = 32 bytes!
|
|
275
265
|
// NOT the 1088-byte full key! This was the bug causing "allsignaturesvalid":false errors.
|
|
276
266
|
const leafPubkey = this.publicKeyDigests[keyIndex];
|
|
277
267
|
if (treeKeyDebugEnabled) {
|
|
278
|
-
treeKeyLogger.debug(`[TreeKeyNode.sign] leafPubkey (${leafPubkey.length}B DIGEST): ${
|
|
268
|
+
treeKeyLogger.debug(`[TreeKeyNode.sign] leafPubkey (${leafPubkey.length}B DIGEST): ${hex(leafPubkey)}`);
|
|
279
269
|
}
|
|
280
270
|
// CRITICAL: Do NOT pre-hash here! wotsSign() now handles internal hashing (matching Java/BouncyCastle).
|
|
281
271
|
// Java's WinternitzOTSignature.getSignature() hashes internally, so wotsSign() does too.
|
|
@@ -284,12 +274,12 @@ class TreeKeyNode {
|
|
|
284
274
|
// - For parent→child: the 32-byte childRoot (MMR root of child proof)
|
|
285
275
|
// WOTS hashes once internally - this is by design for Minima compatibility.
|
|
286
276
|
if (treeKeyDebugEnabled) {
|
|
287
|
-
treeKeyLogger.debug(`[TreeKeyNode.sign] dataToSign (raw input, WOTS hashes internally): ${
|
|
277
|
+
treeKeyLogger.debug(`[TreeKeyNode.sign] dataToSign (raw input, WOTS hashes internally): ${hex(data)}`);
|
|
288
278
|
}
|
|
289
279
|
// Sign data directly - wotsSign() handles internal hashing
|
|
290
|
-
const signature =
|
|
280
|
+
const signature = wotsSign(this.seed, keyIndex, data, getParamSet());
|
|
291
281
|
if (treeKeyDebugEnabled) {
|
|
292
|
-
treeKeyLogger.debug(`[TreeKeyNode.sign] signature (${signature.length}B): ${
|
|
282
|
+
treeKeyLogger.debug(`[TreeKeyNode.sign] signature (${signature.length}B): ${hex(signature).substring(0, 64)}...${hex(signature).substring(hex(signature).length - 32)}`);
|
|
293
283
|
}
|
|
294
284
|
// Get the MMR proof for this key
|
|
295
285
|
const mmrProof = this.getProof(keyIndex);
|
|
@@ -321,24 +311,23 @@ class TreeKeyNode {
|
|
|
321
311
|
}
|
|
322
312
|
if (treeKeyDebugEnabled) {
|
|
323
313
|
treeKeyLogger.debug(`[TreeKeyNode.getChild] Creating child at index ${childIndex}`);
|
|
324
|
-
treeKeyLogger.debug(`[TreeKeyNode.getChild] Parent childSeed: ${
|
|
314
|
+
treeKeyLogger.debug(`[TreeKeyNode.getChild] Parent childSeed: ${hex(this.childSeed)}`);
|
|
325
315
|
}
|
|
326
316
|
// From TreeKeyNode.java:
|
|
327
317
|
// MiniData seed = Crypto.getInstance().hashAllObjects(new MiniNumber(zChild), mChildSeed);
|
|
328
|
-
const childSeed =
|
|
318
|
+
const childSeed = deriveChainSeedJava(this.childSeed, childIndex);
|
|
329
319
|
if (treeKeyDebugEnabled) {
|
|
330
|
-
treeKeyLogger.debug(`[TreeKeyNode.getChild] Derived childSeed: ${
|
|
320
|
+
treeKeyLogger.debug(`[TreeKeyNode.getChild] Derived childSeed: ${hex(childSeed)}`);
|
|
331
321
|
}
|
|
332
322
|
const child = new TreeKeyNode(childSeed, this.keysPerLevel);
|
|
333
323
|
if (treeKeyDebugEnabled) {
|
|
334
|
-
treeKeyLogger.debug(`[TreeKeyNode.getChild] Child public key: ${
|
|
324
|
+
treeKeyLogger.debug(`[TreeKeyNode.getChild] Child public key: ${hex(child.getPublicKey())}`);
|
|
335
325
|
}
|
|
336
326
|
// Cache for future use
|
|
337
327
|
this.childCache.set(childIndex, child);
|
|
338
328
|
return child;
|
|
339
329
|
}
|
|
340
330
|
}
|
|
341
|
-
exports.TreeKeyNode = TreeKeyNode;
|
|
342
331
|
/**
|
|
343
332
|
* TreeKey - Full hierarchical key tree with multiple levels
|
|
344
333
|
*
|
|
@@ -347,21 +336,26 @@ exports.TreeKeyNode = TreeKeyNode;
|
|
|
347
336
|
* - Tracks usage count to determine which key to use
|
|
348
337
|
* - Produces multi-level signatures
|
|
349
338
|
*/
|
|
350
|
-
class TreeKey {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
339
|
+
export class TreeKey {
|
|
340
|
+
privateSeed;
|
|
341
|
+
levels;
|
|
342
|
+
keysPerLevel;
|
|
343
|
+
root;
|
|
344
|
+
publicKey;
|
|
345
|
+
uses = 0;
|
|
346
|
+
/**
|
|
347
|
+
* Parent-child signature cache for efficiency
|
|
348
|
+
* Key format: "l1" for root->level1, "l1,l2" for level1->level2
|
|
349
|
+
* Value: SignatureProof for parent signing child's pubkey
|
|
350
|
+
*/
|
|
351
|
+
parentChildSigCache = new Map();
|
|
352
|
+
constructor(privateSeed, keysPerLevel = DEFAULT_KEYS_PER_LEVEL, levels = DEFAULT_LEVELS) {
|
|
359
353
|
if (privateSeed.length !== 32) {
|
|
360
354
|
throw new Error('Private seed must be 32 bytes');
|
|
361
355
|
}
|
|
362
356
|
if (treeKeyDebugEnabled) {
|
|
363
357
|
treeKeyLogger.debug(`[TreeKey] ========== WALLET INIT START ==========`);
|
|
364
|
-
treeKeyLogger.debug(`[TreeKey] Private seed: ${
|
|
358
|
+
treeKeyLogger.debug(`[TreeKey] Private seed: ${hex(privateSeed)}`);
|
|
365
359
|
treeKeyLogger.debug(`[TreeKey] Levels: ${levels}, Keys per level: ${keysPerLevel}`);
|
|
366
360
|
treeKeyLogger.debug(`[TreeKey] Total capacity: ${Math.pow(keysPerLevel, levels)} signatures`);
|
|
367
361
|
}
|
|
@@ -372,7 +366,7 @@ class TreeKey {
|
|
|
372
366
|
this.root = new TreeKeyNode(privateSeed, keysPerLevel);
|
|
373
367
|
this.publicKey = this.root.getPublicKey();
|
|
374
368
|
if (treeKeyDebugEnabled) {
|
|
375
|
-
treeKeyLogger.debug(`[TreeKey] Root public key: ${
|
|
369
|
+
treeKeyLogger.debug(`[TreeKey] Root public key: ${hex(this.publicKey)}`);
|
|
376
370
|
treeKeyLogger.debug(`[TreeKey] ========== WALLET INIT END ==========`);
|
|
377
371
|
}
|
|
378
372
|
}
|
|
@@ -380,7 +374,7 @@ class TreeKey {
|
|
|
380
374
|
* Async factory method for TreeKey with progress reporting
|
|
381
375
|
* Reports progress as the root TreeKeyNode generates its 64 signing keys
|
|
382
376
|
*/
|
|
383
|
-
static async createWithProgress(privateSeed, keysPerLevel =
|
|
377
|
+
static async createWithProgress(privateSeed, keysPerLevel = DEFAULT_KEYS_PER_LEVEL, levels = DEFAULT_LEVELS, onProgress) {
|
|
384
378
|
if (privateSeed.length !== 32) {
|
|
385
379
|
throw new Error('Private seed must be 32 bytes');
|
|
386
380
|
}
|
|
@@ -599,7 +593,6 @@ class TreeKey {
|
|
|
599
593
|
return level2Node.getPublicKey();
|
|
600
594
|
}
|
|
601
595
|
}
|
|
602
|
-
exports.TreeKey = TreeKey;
|
|
603
596
|
/**
|
|
604
597
|
* Verify a tree signature against expected public key and data
|
|
605
598
|
*
|
|
@@ -608,14 +601,14 @@ exports.TreeKey = TreeKey;
|
|
|
608
601
|
* - Each intermediate proof must sign the next level's root
|
|
609
602
|
* - Final proof must verify against the actual data
|
|
610
603
|
*/
|
|
611
|
-
function verifyTreeSignature(expectedPubkey, data, signature) {
|
|
604
|
+
export function verifyTreeSignature(expectedPubkey, data, signature) {
|
|
612
605
|
const { proofs } = signature;
|
|
613
606
|
if (proofs.length === 0) {
|
|
614
607
|
return false;
|
|
615
608
|
}
|
|
616
609
|
// Import wotsVerify for verification
|
|
617
610
|
// wotsVerify already imported at top of file
|
|
618
|
-
const paramSet =
|
|
611
|
+
const paramSet = getParamSet();
|
|
619
612
|
for (let depth = 0; depth < proofs.length; depth++) {
|
|
620
613
|
const proof = proofs[depth];
|
|
621
614
|
// Compute the root public key from this proof
|
|
@@ -640,7 +633,7 @@ function verifyTreeSignature(expectedPubkey, data, signature) {
|
|
|
640
633
|
}
|
|
641
634
|
// Verify the Winternitz signature
|
|
642
635
|
// Use wotsVerifyDigest since leafPubkey is now a 32-byte digest (not 1088-byte full key)
|
|
643
|
-
if (!
|
|
636
|
+
if (!wotsVerifyDigest(proof.signature, signedData, proof.leafPubkey, paramSet)) {
|
|
644
637
|
return false;
|
|
645
638
|
}
|
|
646
639
|
}
|
|
@@ -652,7 +645,7 @@ function verifyTreeSignature(expectedPubkey, data, signature) {
|
|
|
652
645
|
* Uses Streamable.writeSignature() for byte-exact compatibility
|
|
653
646
|
* with Java's Signature.writeDataStream().
|
|
654
647
|
*/
|
|
655
|
-
function serializeTreeSignature(sig) {
|
|
648
|
+
export function serializeTreeSignature(sig) {
|
|
656
649
|
// Convert to Streamable format (adds blockTime to MMRProof)
|
|
657
650
|
const streamableSig = {
|
|
658
651
|
proofs: sig.proofs.map(proof => ({
|
|
@@ -665,7 +658,7 @@ function serializeTreeSignature(sig) {
|
|
|
665
658
|
}))
|
|
666
659
|
};
|
|
667
660
|
// Use canonical serializer for byte-exact Java compatibility
|
|
668
|
-
return
|
|
661
|
+
return writeSignature(streamableSig);
|
|
669
662
|
}
|
|
670
663
|
/**
|
|
671
664
|
* Deserialize a TreeSignature from bytes
|
|
@@ -674,7 +667,7 @@ function serializeTreeSignature(sig) {
|
|
|
674
667
|
* - Number of proofs: MiniNumber format
|
|
675
668
|
* - Each SignatureProof: MiniData(pubkey) + MiniData(signature) + MMRProof
|
|
676
669
|
*/
|
|
677
|
-
function deserializeTreeSignature(data) {
|
|
670
|
+
export function deserializeTreeSignature(data) {
|
|
678
671
|
let offset = 0;
|
|
679
672
|
// Read MiniNumber for proof count
|
|
680
673
|
// Format: [scale: 1 byte] [length: 1 byte] [data: N bytes]
|
|
@@ -700,7 +693,7 @@ function deserializeTreeSignature(data) {
|
|
|
700
693
|
const signature = data.slice(offset, offset + sigLen);
|
|
701
694
|
offset += sigLen;
|
|
702
695
|
// MMR proof - returns { proof, blockTime, bytesRead }
|
|
703
|
-
const { proof: mmrProof, bytesRead: mmrProofLen } =
|
|
696
|
+
const { proof: mmrProof, bytesRead: mmrProofLen } = parseMMRProofFromHex(data.slice(offset));
|
|
704
697
|
offset += mmrProofLen;
|
|
705
698
|
proofs.push({ leafPubkey, signature, mmrProof });
|
|
706
699
|
}
|
|
@@ -736,8 +729,8 @@ function deserializeTreeSignature(data) {
|
|
|
736
729
|
* @param index - Address index (0-63)
|
|
737
730
|
* @returns TreeKey for this spend address with size=64, depth=3
|
|
738
731
|
*/
|
|
739
|
-
function createUnifiedChildTreeKey(baseSeed, index) {
|
|
740
|
-
const childSeed =
|
|
732
|
+
export function createUnifiedChildTreeKey(baseSeed, index) {
|
|
733
|
+
const childSeed = deriveUnifiedChildSeed(baseSeed, index);
|
|
741
734
|
return new TreeKey(childSeed, 64, 3);
|
|
742
735
|
}
|
|
743
736
|
/**
|
|
@@ -748,8 +741,8 @@ function createUnifiedChildTreeKey(baseSeed, index) {
|
|
|
748
741
|
* @param onProgress - Optional progress callback
|
|
749
742
|
* @returns Promise resolving to the child TreeKey
|
|
750
743
|
*/
|
|
751
|
-
async function createUnifiedChildTreeKeyAsync(baseSeed, index, onProgress) {
|
|
752
|
-
const childSeed =
|
|
744
|
+
export async function createUnifiedChildTreeKeyAsync(baseSeed, index, onProgress) {
|
|
745
|
+
const childSeed = deriveUnifiedChildSeed(baseSeed, index);
|
|
753
746
|
return TreeKey.createWithProgress(childSeed, 64, 3, onProgress);
|
|
754
747
|
}
|
|
755
748
|
/**
|
|
@@ -761,8 +754,8 @@ async function createUnifiedChildTreeKeyAsync(baseSeed, index, onProgress) {
|
|
|
761
754
|
* @param baseSeed - 32-byte wallet base seed (from mnemonic)
|
|
762
755
|
* @returns Root identity TreeKey with size=64, depth=3
|
|
763
756
|
*/
|
|
764
|
-
function createUnifiedRootTreeKey(baseSeed) {
|
|
765
|
-
const rootPrivSeed =
|
|
757
|
+
export function createUnifiedRootTreeKey(baseSeed) {
|
|
758
|
+
const rootPrivSeed = deriveRootPrivSeed(baseSeed);
|
|
766
759
|
return new TreeKey(rootPrivSeed, 64, 3);
|
|
767
760
|
}
|
|
768
761
|
/**
|
|
@@ -773,7 +766,7 @@ function createUnifiedRootTreeKey(baseSeed) {
|
|
|
773
766
|
* @param index - Address index (0-63)
|
|
774
767
|
* @returns 32-byte address public key (MMR root of child TreeKey)
|
|
775
768
|
*/
|
|
776
|
-
function deriveUnifiedAddressPublicKey(baseSeed, index) {
|
|
769
|
+
export function deriveUnifiedAddressPublicKey(baseSeed, index) {
|
|
777
770
|
return createUnifiedChildTreeKey(baseSeed, index).getPublicKey();
|
|
778
771
|
}
|
|
779
772
|
// ---------------------------------------------------------------------------
|
|
@@ -788,15 +781,15 @@ function deriveUnifiedAddressPublicKey(baseSeed, index) {
|
|
|
788
781
|
* New code must use createUnifiedChildTreeKey which applies the unified
|
|
789
782
|
* two-step derivation (root_priv_seed → child_seed_i).
|
|
790
783
|
*/
|
|
791
|
-
function createPerAddressTreeKey(baseSeed, addressIndex) {
|
|
792
|
-
const perAddressSeed =
|
|
784
|
+
export function createPerAddressTreeKey(baseSeed, addressIndex) {
|
|
785
|
+
const perAddressSeed = derivePerAddressSeed(baseSeed instanceof Uint8Array ? baseSeed : new Uint8Array(baseSeed), addressIndex);
|
|
793
786
|
return new TreeKey(perAddressSeed, 64, 3);
|
|
794
787
|
}
|
|
795
788
|
/**
|
|
796
789
|
* @deprecated Use {@link createUnifiedChildTreeKeyAsync} instead.
|
|
797
790
|
* Preserves legacy per-address seed derivation for backward compatibility.
|
|
798
791
|
*/
|
|
799
|
-
async function createPerAddressTreeKeyAsync(baseSeed, addressIndex, onProgress) {
|
|
800
|
-
const perAddressSeed =
|
|
792
|
+
export async function createPerAddressTreeKeyAsync(baseSeed, addressIndex, onProgress) {
|
|
793
|
+
const perAddressSeed = derivePerAddressSeed(baseSeed instanceof Uint8Array ? baseSeed : new Uint8Array(baseSeed), addressIndex);
|
|
801
794
|
return TreeKey.createWithProgress(perAddressSeed, 64, 3, onProgress);
|
|
802
795
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @module TransactionLifecycle
|
|
4
3
|
* Orchestrates the complete transaction flow with lease and watermark management
|
|
@@ -9,10 +8,10 @@
|
|
|
9
8
|
* - WatermarkStore for index tracking
|
|
10
9
|
* - TransactionReceiptStore for history
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
import { NoopLogger, NoopMetrics } from '../adapters/index.js';
|
|
12
|
+
export class TransactionLifecycleError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
userMessage;
|
|
16
15
|
constructor(message, code, userMessage) {
|
|
17
16
|
super(message);
|
|
18
17
|
this.code = code;
|
|
@@ -20,16 +19,22 @@ class TransactionLifecycleError extends Error {
|
|
|
20
19
|
this.name = 'TransactionLifecycleError';
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
|
-
|
|
24
|
-
class WatermarkExhaustedError extends Error {
|
|
22
|
+
export class WatermarkExhaustedError extends Error {
|
|
25
23
|
constructor() {
|
|
26
24
|
super('All WOTS signatures have been used. Please create a new wallet.');
|
|
27
25
|
this.name = 'WatermarkExhaustedError';
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
export class TransactionLifecycle {
|
|
29
|
+
txService;
|
|
30
|
+
leaseStore;
|
|
31
|
+
watermarkStore;
|
|
32
|
+
receiptStore;
|
|
33
|
+
logger;
|
|
34
|
+
metrics;
|
|
35
|
+
config;
|
|
36
|
+
syncWatermark;
|
|
37
|
+
constructor(txService, leaseStore, watermarkStore, receiptStore, logger = new NoopLogger(), metrics = new NoopMetrics(), config = {}) {
|
|
33
38
|
this.txService = txService;
|
|
34
39
|
this.leaseStore = leaseStore;
|
|
35
40
|
this.watermarkStore = watermarkStore;
|
|
@@ -192,4 +197,3 @@ class TransactionLifecycle {
|
|
|
192
197
|
};
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
|
-
exports.TransactionLifecycle = TransactionLifecycle;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @module TransactionReceiptStore
|
|
4
3
|
* Persistent storage for transaction receipts
|
|
@@ -9,15 +8,17 @@
|
|
|
9
8
|
* - Sync getters read from memory cache (fast, no I/O)
|
|
10
9
|
* - Mutators update memory cache AND persist to storage
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
exports.TransactionReceiptStore = void 0;
|
|
14
|
-
const index_js_1 = require("../adapters/index.js");
|
|
11
|
+
import { NoopLogger } from '../adapters/index.js';
|
|
15
12
|
const DEFAULT_STORAGE_KEY = 'totem_transaction_receipts';
|
|
16
13
|
const DEFAULT_MAX_RECEIPTS = 1000;
|
|
17
|
-
class TransactionReceiptStore {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
export class TransactionReceiptStore {
|
|
15
|
+
receipts = [];
|
|
16
|
+
_initialized = false;
|
|
17
|
+
storageKey;
|
|
18
|
+
maxReceipts;
|
|
19
|
+
storage;
|
|
20
|
+
logger;
|
|
21
|
+
constructor(storage, logger = new NoopLogger(), config = {}) {
|
|
21
22
|
this.storage = storage;
|
|
22
23
|
this.logger = logger;
|
|
23
24
|
this.storageKey = config.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
@@ -86,4 +87,3 @@ class TransactionReceiptStore {
|
|
|
86
87
|
return this.receipts.length;
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
|
-
exports.TransactionReceiptStore = TransactionReceiptStore;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @module TransactionService
|
|
4
3
|
* Handles WOTS signing flow: prepare → sign → finalize
|
|
@@ -9,14 +8,16 @@
|
|
|
9
8
|
* matching Minima Wallet.java exactly. Each address has its own TreeKey derived
|
|
10
9
|
* from the wallet seed. Signing produces 3 proofs (Root→L1→L2→DATA).
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
import { NoopLogger, NoopMetrics } from '../adapters/index.js';
|
|
12
|
+
import { createUnifiedChildTreeKey, serializeTreeSignature } from '../treekey.js';
|
|
13
|
+
import { serializeMMRProof } from '../mmr.js';
|
|
14
|
+
import { fromHex } from '../wots.js';
|
|
15
|
+
export class TransactionService {
|
|
16
|
+
http;
|
|
17
|
+
logger;
|
|
18
|
+
metrics;
|
|
19
|
+
config;
|
|
20
|
+
constructor(http, config, logger = new NoopLogger(), metrics = new NoopMetrics()) {
|
|
20
21
|
this.http = http;
|
|
21
22
|
this.config = config;
|
|
22
23
|
this.logger = logger;
|
|
@@ -100,13 +101,13 @@ class TransactionService {
|
|
|
100
101
|
if (addressIndex < 0 || addressIndex >= 64) {
|
|
101
102
|
throw new Error(`Invalid addressIndex: ${addressIndex}. Must be 0-63.`);
|
|
102
103
|
}
|
|
103
|
-
const digestBytes =
|
|
104
|
+
const digestBytes = fromHex(digestTx);
|
|
104
105
|
if (digestBytes.length !== 32) {
|
|
105
106
|
throw new Error(`Invalid digest length: ${digestBytes.length}, expected 32 bytes`);
|
|
106
107
|
}
|
|
107
108
|
// Derive unified child TreeKey for this address index
|
|
108
109
|
this.logger.debug(` Deriving unified child TreeKey for index ${addressIndex}...`);
|
|
109
|
-
const treeKey =
|
|
110
|
+
const treeKey = createUnifiedChildTreeKey(seed, addressIndex);
|
|
110
111
|
// Convert (l1, l2) to Java-compatible 'uses' counter
|
|
111
112
|
// Java's TreeKey.sign() uses baseConversion(uses) to compute path
|
|
112
113
|
const KEYS_PER_LEVEL = 64;
|
|
@@ -127,11 +128,11 @@ class TransactionService {
|
|
|
127
128
|
proofs: treeSignature.proofs.map((proof) => ({
|
|
128
129
|
leafPubkey: `0x${this.bytesToHex(proof.leafPubkey)}`,
|
|
129
130
|
signature: `0x${this.bytesToHex(proof.signature)}`,
|
|
130
|
-
mmrProof: `0x${this.bytesToHex(
|
|
131
|
+
mmrProof: `0x${this.bytesToHex(serializeMMRProof(proof.mmrProof))}`,
|
|
131
132
|
})),
|
|
132
133
|
};
|
|
133
134
|
// Serialize to byte-exact Java-compatible wire format
|
|
134
|
-
const serializedBytes =
|
|
135
|
+
const serializedBytes = serializeTreeSignature(treeSignature);
|
|
135
136
|
const signedHex = `0x${this.bytesToHex(serializedBytes)}`;
|
|
136
137
|
const elapsed = Date.now() - startTime;
|
|
137
138
|
this.metrics.timing('tx_sign_duration_ms', elapsed);
|
|
@@ -179,4 +180,3 @@ class TransactionService {
|
|
|
179
180
|
.join('');
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
|
-
exports.TransactionService = TransactionService;
|
package/dist/tx/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* @module @totemsdk/core/tx
|
|
4
3
|
* Transaction service and lifecycle management
|
|
@@ -8,13 +7,6 @@
|
|
|
8
7
|
* - TransactionLifecycle: Orchestrates flow with lease/watermark stores
|
|
9
8
|
* - TransactionReceiptStore: Persistent transaction history
|
|
10
9
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Object.defineProperty(exports, "TransactionService", { enumerable: true, get: function () { return TransactionService_js_1.TransactionService; } });
|
|
15
|
-
var TransactionLifecycle_js_1 = require("./TransactionLifecycle.js");
|
|
16
|
-
Object.defineProperty(exports, "TransactionLifecycle", { enumerable: true, get: function () { return TransactionLifecycle_js_1.TransactionLifecycle; } });
|
|
17
|
-
Object.defineProperty(exports, "TransactionLifecycleError", { enumerable: true, get: function () { return TransactionLifecycle_js_1.TransactionLifecycleError; } });
|
|
18
|
-
Object.defineProperty(exports, "WatermarkExhaustedError", { enumerable: true, get: function () { return TransactionLifecycle_js_1.WatermarkExhaustedError; } });
|
|
19
|
-
var TransactionReceiptStore_js_1 = require("./TransactionReceiptStore.js");
|
|
20
|
-
Object.defineProperty(exports, "TransactionReceiptStore", { enumerable: true, get: function () { return TransactionReceiptStore_js_1.TransactionReceiptStore; } });
|
|
10
|
+
export { TransactionService, } from './TransactionService.js';
|
|
11
|
+
export { TransactionLifecycle, TransactionLifecycleError, WatermarkExhaustedError, } from './TransactionLifecycle.js';
|
|
12
|
+
export { TransactionReceiptStore, } from './TransactionReceiptStore.js';
|
package/dist/tx/types.js
CHANGED
package/dist/util.js
CHANGED
|
@@ -1,5 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bytesToHex = void 0;
|
|
4
|
-
const bytesToHex = (b) => Array.from(b).map(x => x.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
5
|
-
exports.bytesToHex = bytesToHex;
|
|
1
|
+
export const bytesToHex = (b) => Array.from(b).map(x => x.toString(16).padStart(2, "0")).join("").toUpperCase();
|