@totemsdk/root-identity 1.0.6 → 1.0.8
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/RootIdentityWallet.d.ts +6 -160
- package/dist/RootIdentityWallet.js +9 -368
- package/dist/UnifiedIdentityWallet.d.ts +158 -0
- package/dist/UnifiedIdentityWallet.js +361 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +13 -5
- package/package.json +17 -20
- package/rust/pkg/package.json +27 -0
- package/rust/pkg/root_identity_wasm.d.ts +6 -0
- package/rust/pkg/root_identity_wasm.js +9 -0
- package/rust/pkg/root_identity_wasm_bg.js +393 -0
- package/rust/pkg/root_identity_wasm_bg.wasm +0 -0
- package/rust/pkg/root_identity_wasm_bg.wasm.d.ts +13 -0
- package/rust/pkg-node/package.json +21 -0
- package/rust/pkg-node/root_identity_wasm.d.ts +6 -0
- package/rust/pkg-node/root_identity_wasm.js +400 -0
- package/rust/pkg-node/root_identity_wasm_bg.wasm +0 -0
- package/rust/pkg-node/root_identity_wasm_bg.wasm.d.ts +13 -0
|
@@ -1,164 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RootIdentityWallet —
|
|
2
|
+
* RootIdentityWallet — backward-compatibility re-export.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* baseSeed (32 bytes)
|
|
7
|
-
* │
|
|
8
|
-
* ├── Slot 0 createPerAddressTreeKey(baseSeed, 0) ← ROOT (signs ownership proofs)
|
|
9
|
-
* ├── Slot 1 createPerAddressTreeKey(baseSeed, 1) ← child 0
|
|
10
|
-
* ├── Slot 2 createPerAddressTreeKey(baseSeed, 2) ← child 1
|
|
11
|
-
* │ …
|
|
12
|
-
* └── Slot 64 createPerAddressTreeKey(baseSeed, 64) ← child 63
|
|
4
|
+
* The implementation has moved to UnifiedIdentityWallet which uses genuine
|
|
5
|
+
* hierarchical key derivation (root_priv_seed → child_seed_i).
|
|
13
6
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* SHA3-256( MiniData(baseSeed) ∥ MiniData(BigInteger(slot)) )
|
|
17
|
-
*
|
|
18
|
-
* Watermarks are tracked in memory. Callers that need persistence should read
|
|
19
|
-
* `getRootUses()` / `getChildUses(index)` after each signing operation and
|
|
20
|
-
* restore them via `setRootUses()` / `setChildUses()` on the next session.
|
|
7
|
+
* All existing call sites should be updated to import UnifiedIdentityWallet
|
|
8
|
+
* directly. This shim is kept only to avoid hard import errors during migration.
|
|
21
9
|
*/
|
|
22
|
-
|
|
23
|
-
import type { WotsProof, OwnershipProof } from './types.js';
|
|
24
|
-
export declare const MAX_CHILD_COUNT = 64;
|
|
25
|
-
export declare class RootIdentityWallet {
|
|
26
|
-
private readonly baseSeed;
|
|
27
|
-
private readonly childCount;
|
|
28
|
-
/** Cached TreeKey instances — created lazily to save memory. */
|
|
29
|
-
private readonly treeKeyCache;
|
|
30
|
-
/**
|
|
31
|
-
* Cached addresses and public keys per slot (slot 0 = root, slot i+1 = child i).
|
|
32
|
-
* Avoids expensive TreeKey regeneration on repeated address lookups.
|
|
33
|
-
*/
|
|
34
|
-
private readonly addressCache;
|
|
35
|
-
private readonly pubKeyCache;
|
|
36
|
-
/** Per-slot watermarks. */
|
|
37
|
-
private rootUses;
|
|
38
|
-
private readonly childUsesMap;
|
|
39
|
-
/**
|
|
40
|
-
* @param baseSeed - 32-byte raw seed (use `RootIdentityWallet.fromPhrase` for mnemonic input)
|
|
41
|
-
* @param childCount - Number of child addresses (1–64, default 64)
|
|
42
|
-
*/
|
|
43
|
-
constructor(baseSeed: Uint8Array, childCount?: number);
|
|
44
|
-
/** Create a wallet from a Minima-compatible BIP39 seed phrase. */
|
|
45
|
-
static fromPhrase(phrase: string, childCount?: number): RootIdentityWallet;
|
|
46
|
-
/** Generate a new random Minima-compatible 24-word seed phrase. */
|
|
47
|
-
static generatePhrase(): string;
|
|
48
|
-
/** Validate a Minima-compatible seed phrase. */
|
|
49
|
-
static validatePhrase(phrase: string): boolean;
|
|
50
|
-
/** Minima address for the root key (slot 0). */
|
|
51
|
-
getRootAddress(): string;
|
|
52
|
-
/** 64-char hex public key for the root key. */
|
|
53
|
-
getRootPublicKey(): string;
|
|
54
|
-
/** Minima address for child `index` (0-based). */
|
|
55
|
-
getChildAddress(index: number): string;
|
|
56
|
-
/** 64-char hex public key for child `index`. */
|
|
57
|
-
getChildPublicKey(index: number): string;
|
|
58
|
-
/** All addresses: root first, then all children in order. */
|
|
59
|
-
getAllAddresses(): string[];
|
|
60
|
-
/** Root address and children as a structured object. */
|
|
61
|
-
getAddressMap(): {
|
|
62
|
-
root: string;
|
|
63
|
-
children: string[];
|
|
64
|
-
};
|
|
65
|
-
/**
|
|
66
|
-
* Sign `message` with the root key.
|
|
67
|
-
* Hashes the message with SHA3-256 before signing — identical to `verifySignatureDetailed`.
|
|
68
|
-
* @throws if root TreeKey is exhausted (262 144 uses)
|
|
69
|
-
*/
|
|
70
|
-
signFromRoot(message: string): WotsProof;
|
|
71
|
-
/**
|
|
72
|
-
* Sign `message` with child key `index` (0-based).
|
|
73
|
-
* Each child maintains its own independent use counter.
|
|
74
|
-
* @throws if child TreeKey is exhausted or index out of range
|
|
75
|
-
*/
|
|
76
|
-
signFromChild(index: number, message: string): WotsProof;
|
|
77
|
-
/**
|
|
78
|
-
* Produce an ownership proof demonstrating that the root key controls all
|
|
79
|
-
* given child addresses.
|
|
80
|
-
*
|
|
81
|
-
* @param childIndices - Which children to include (0-based, order preserved)
|
|
82
|
-
*/
|
|
83
|
-
proveOwnership(childIndices: number[]): OwnershipProof;
|
|
84
|
-
/**
|
|
85
|
-
* Verify an ownership proof produced by `proveOwnership`.
|
|
86
|
-
*
|
|
87
|
-
* Returns `true` only when:
|
|
88
|
-
* - The canonical message is reconstructed correctly (child keys are sorted).
|
|
89
|
-
* - The root WOTS signature validates against the root public key and address.
|
|
90
|
-
* - Every child public key correctly derives the corresponding child address.
|
|
91
|
-
*
|
|
92
|
-
* Pure crypto — no network access required.
|
|
93
|
-
* Always returns `false` (never throws) on malformed or incomplete proof data.
|
|
94
|
-
*/
|
|
95
|
-
static verifyOwnershipProof(proof: OwnershipProof): boolean;
|
|
96
|
-
/**
|
|
97
|
-
* Get (or create and cache) the TreeKey for child `index` (0-based).
|
|
98
|
-
*
|
|
99
|
-
* Callers that need positional watermark control (e.g. `setUses(l1*64+l2)`)
|
|
100
|
-
* can use this to drive WOTS signing directly — the same path the Totem
|
|
101
|
-
* extension uses for transaction signing in RootTree mode.
|
|
102
|
-
*
|
|
103
|
-
* Note: This does NOT advance the high-level `childUsesMap` counter.
|
|
104
|
-
* Call `getChildUses` / `setChildUses` yourself if you need to track uses.
|
|
105
|
-
*/
|
|
106
|
-
getChildTreeKey(index: number): TreeKey;
|
|
107
|
-
/**
|
|
108
|
-
* Get (or create and cache) the root TreeKey (slot 0).
|
|
109
|
-
*
|
|
110
|
-
* Useful for callers that need direct access to the root key's TreeKey
|
|
111
|
-
* object for custom signing operations.
|
|
112
|
-
*/
|
|
113
|
-
getRootTreeKey(): TreeKey;
|
|
114
|
-
/** Number of times the root key has been used (for persistence). */
|
|
115
|
-
getRootUses(): number;
|
|
116
|
-
/** Restore root watermark from a previously persisted value. */
|
|
117
|
-
setRootUses(uses: number): void;
|
|
118
|
-
/** Number of times child `index` has signed (for persistence). */
|
|
119
|
-
getChildUses(index: number): number;
|
|
120
|
-
/** Restore child watermark from a previously persisted value. */
|
|
121
|
-
setChildUses(index: number, uses: number): void;
|
|
122
|
-
/** Number of child addresses configured for this wallet. */
|
|
123
|
-
getChildCount(): number;
|
|
124
|
-
/** Maximum one-time signatures available per slot (3 levels × 64 keys). */
|
|
125
|
-
getMaxUsesPerSlot(): number;
|
|
126
|
-
/**
|
|
127
|
-
* Return a serialisable snapshot of all current watermark counters.
|
|
128
|
-
*
|
|
129
|
-
* Persist the returned object (e.g. to encrypted storage) and pass it back
|
|
130
|
-
* to `restoreWatermarkState()` at the start of the next session so that no
|
|
131
|
-
* one-time-use signing slot is ever reused.
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* const state = wallet.getWatermarkState();
|
|
135
|
-
* await storage.set('watermarks', JSON.stringify(state));
|
|
136
|
-
*/
|
|
137
|
-
getWatermarkState(): {
|
|
138
|
-
rootUses: number;
|
|
139
|
-
childUses: Record<number, number>;
|
|
140
|
-
};
|
|
141
|
-
/**
|
|
142
|
-
* Restore watermark counters from a previously persisted snapshot.
|
|
143
|
-
*
|
|
144
|
-
* Call this immediately after constructing the wallet to prevent slot reuse
|
|
145
|
-
* across sessions. All values must be non-negative integers; out-of-range
|
|
146
|
-
* or invalid entries are silently skipped.
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* const saved = JSON.parse(await storage.get('watermarks') ?? '{}');
|
|
150
|
-
* wallet.restoreWatermarkState(saved);
|
|
151
|
-
*/
|
|
152
|
-
restoreWatermarkState(state: {
|
|
153
|
-
rootUses?: number;
|
|
154
|
-
childUses?: Record<number, number>;
|
|
155
|
-
}): void;
|
|
156
|
-
private assertChildIndex;
|
|
157
|
-
/**
|
|
158
|
-
* Get or create the TreeKey for a given slot.
|
|
159
|
-
* Also populates address and public key caches on first access.
|
|
160
|
-
*/
|
|
161
|
-
private getOrCreateTreeKey;
|
|
162
|
-
private slotPublicKey;
|
|
163
|
-
private slotAddress;
|
|
164
|
-
}
|
|
10
|
+
export { UnifiedIdentityWallet as RootIdentityWallet, MAX_CHILD_COUNT } from './UnifiedIdentityWallet.js';
|
|
@@ -1,374 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* RootIdentityWallet — single root identity controlling up to 64 on-chain addresses.
|
|
4
|
-
*
|
|
5
|
-
* Architecture
|
|
6
|
-
* ────────────
|
|
7
|
-
* baseSeed (32 bytes)
|
|
8
|
-
* │
|
|
9
|
-
* ├── Slot 0 createPerAddressTreeKey(baseSeed, 0) ← ROOT (signs ownership proofs)
|
|
10
|
-
* ├── Slot 1 createPerAddressTreeKey(baseSeed, 1) ← child 0
|
|
11
|
-
* ├── Slot 2 createPerAddressTreeKey(baseSeed, 2) ← child 1
|
|
12
|
-
* │ …
|
|
13
|
-
* └── Slot 64 createPerAddressTreeKey(baseSeed, 64) ← child 63
|
|
14
|
-
*
|
|
15
|
-
* Each slot gets a full independent 3-level TreeKey (64³ = 262 144 one-time signatures).
|
|
16
|
-
* Derivation matches Minima Wallet.java exactly:
|
|
17
|
-
* SHA3-256( MiniData(baseSeed) ∥ MiniData(BigInteger(slot)) )
|
|
18
|
-
*
|
|
19
|
-
* Watermarks are tracked in memory. Callers that need persistence should read
|
|
20
|
-
* `getRootUses()` / `getChildUses(index)` after each signing operation and
|
|
21
|
-
* restore them via `setRootUses()` / `setChildUses()` on the next session.
|
|
22
|
-
*/
|
|
23
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.
|
|
25
|
-
const sha3_js_1 = require("@noble/hashes/sha3.js");
|
|
26
|
-
const core_1 = require("@totemsdk/core");
|
|
27
|
-
exports.MAX_CHILD_COUNT = 64;
|
|
28
|
-
/**
|
|
29
|
-
* Canonical message prefix for ownership proofs.
|
|
30
|
-
* Bump the version if the message schema ever changes.
|
|
31
|
-
*/
|
|
32
|
-
const OWNERSHIP_OP = 'TOTEM_OWNERSHIP_PROOF_V1';
|
|
33
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
-
// Helpers
|
|
35
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
-
function hashMessage(message) {
|
|
37
|
-
return (0, sha3_js_1.sha3_256)(new TextEncoder().encode(message));
|
|
38
|
-
}
|
|
39
|
-
function addressFromPublicKeyBytes(pkBytes) {
|
|
40
|
-
const script = (0, core_1.scriptFromWotsPk)(pkBytes);
|
|
41
|
-
return (0, core_1.scriptToAddress)(script);
|
|
42
|
-
}
|
|
3
|
+
exports.MAX_CHILD_COUNT = exports.RootIdentityWallet = void 0;
|
|
43
4
|
/**
|
|
44
|
-
*
|
|
5
|
+
* RootIdentityWallet — backward-compatibility re-export.
|
|
45
6
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* the order in which indices were passed to `proveOwnership`.
|
|
7
|
+
* The implementation has moved to UnifiedIdentityWallet which uses genuine
|
|
8
|
+
* hierarchical key derivation (root_priv_seed → child_seed_i).
|
|
49
9
|
*
|
|
50
|
-
*
|
|
10
|
+
* All existing call sites should be updated to import UnifiedIdentityWallet
|
|
11
|
+
* directly. This shim is kept only to avoid hard import errors during migration.
|
|
51
12
|
*/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
rootAddress,
|
|
56
|
-
childPublicKeys: [...childPublicKeys].sort(),
|
|
57
|
-
timestamp,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
61
|
-
// RootIdentityWallet
|
|
62
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
63
|
-
class RootIdentityWallet {
|
|
64
|
-
// ── Construction ────────────────────────────────────────────────────────────
|
|
65
|
-
/**
|
|
66
|
-
* @param baseSeed - 32-byte raw seed (use `RootIdentityWallet.fromPhrase` for mnemonic input)
|
|
67
|
-
* @param childCount - Number of child addresses (1–64, default 64)
|
|
68
|
-
*/
|
|
69
|
-
constructor(baseSeed, childCount = exports.MAX_CHILD_COUNT) {
|
|
70
|
-
/** Cached TreeKey instances — created lazily to save memory. */
|
|
71
|
-
this.treeKeyCache = new Map();
|
|
72
|
-
/**
|
|
73
|
-
* Cached addresses and public keys per slot (slot 0 = root, slot i+1 = child i).
|
|
74
|
-
* Avoids expensive TreeKey regeneration on repeated address lookups.
|
|
75
|
-
*/
|
|
76
|
-
this.addressCache = new Map();
|
|
77
|
-
this.pubKeyCache = new Map();
|
|
78
|
-
/** Per-slot watermarks. */
|
|
79
|
-
this.rootUses = 0;
|
|
80
|
-
this.childUsesMap = new Map();
|
|
81
|
-
if (baseSeed.length !== 32) {
|
|
82
|
-
throw new Error(`baseSeed must be exactly 32 bytes, got ${baseSeed.length}`);
|
|
83
|
-
}
|
|
84
|
-
if (childCount < 1 || childCount > exports.MAX_CHILD_COUNT) {
|
|
85
|
-
throw new Error(`childCount must be 1–${exports.MAX_CHILD_COUNT}, got ${childCount}`);
|
|
86
|
-
}
|
|
87
|
-
this.baseSeed = baseSeed;
|
|
88
|
-
this.childCount = childCount;
|
|
89
|
-
}
|
|
90
|
-
/** Create a wallet from a Minima-compatible BIP39 seed phrase. */
|
|
91
|
-
static fromPhrase(phrase, childCount = exports.MAX_CHILD_COUNT) {
|
|
92
|
-
return new RootIdentityWallet((0, core_1.phraseToSeed)(phrase), childCount);
|
|
93
|
-
}
|
|
94
|
-
/** Generate a new random Minima-compatible 24-word seed phrase. */
|
|
95
|
-
static generatePhrase() {
|
|
96
|
-
return (0, core_1.generateSeedPhrase)();
|
|
97
|
-
}
|
|
98
|
-
/** Validate a Minima-compatible seed phrase. */
|
|
99
|
-
static validatePhrase(phrase) {
|
|
100
|
-
return (0, core_1.validatePhrase)(phrase);
|
|
101
|
-
}
|
|
102
|
-
// ── Address accessors ────────────────────────────────────────────────────────
|
|
103
|
-
/** Minima address for the root key (slot 0). */
|
|
104
|
-
getRootAddress() {
|
|
105
|
-
return this.slotAddress(0);
|
|
106
|
-
}
|
|
107
|
-
/** 64-char hex public key for the root key. */
|
|
108
|
-
getRootPublicKey() {
|
|
109
|
-
return this.slotPublicKey(0);
|
|
110
|
-
}
|
|
111
|
-
/** Minima address for child `index` (0-based). */
|
|
112
|
-
getChildAddress(index) {
|
|
113
|
-
this.assertChildIndex(index);
|
|
114
|
-
return this.slotAddress(index + 1);
|
|
115
|
-
}
|
|
116
|
-
/** 64-char hex public key for child `index`. */
|
|
117
|
-
getChildPublicKey(index) {
|
|
118
|
-
this.assertChildIndex(index);
|
|
119
|
-
return this.slotPublicKey(index + 1);
|
|
120
|
-
}
|
|
121
|
-
/** All addresses: root first, then all children in order. */
|
|
122
|
-
getAllAddresses() {
|
|
123
|
-
const out = [this.getRootAddress()];
|
|
124
|
-
for (let i = 0; i < this.childCount; i++) {
|
|
125
|
-
out.push(this.getChildAddress(i));
|
|
126
|
-
}
|
|
127
|
-
return out;
|
|
128
|
-
}
|
|
129
|
-
/** Root address and children as a structured object. */
|
|
130
|
-
getAddressMap() {
|
|
131
|
-
const children = [];
|
|
132
|
-
for (let i = 0; i < this.childCount; i++) {
|
|
133
|
-
children.push(this.getChildAddress(i));
|
|
134
|
-
}
|
|
135
|
-
return { root: this.getRootAddress(), children };
|
|
136
|
-
}
|
|
137
|
-
// ── Signing ─────────────────────────────────────────────────────────────────
|
|
138
|
-
/**
|
|
139
|
-
* Sign `message` with the root key.
|
|
140
|
-
* Hashes the message with SHA3-256 before signing — identical to `verifySignatureDetailed`.
|
|
141
|
-
* @throws if root TreeKey is exhausted (262 144 uses)
|
|
142
|
-
*/
|
|
143
|
-
signFromRoot(message) {
|
|
144
|
-
const treeKey = this.getOrCreateTreeKey(0);
|
|
145
|
-
treeKey.setUses(this.rootUses);
|
|
146
|
-
const sig = treeKey.sign(hashMessage(message));
|
|
147
|
-
this.rootUses++;
|
|
148
|
-
return {
|
|
149
|
-
address: this.getRootAddress(),
|
|
150
|
-
publicKey: this.getRootPublicKey(),
|
|
151
|
-
signature: (0, core_1.bytesToHex)((0, core_1.serializeTreeSignature)(sig)),
|
|
152
|
-
message,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Sign `message` with child key `index` (0-based).
|
|
157
|
-
* Each child maintains its own independent use counter.
|
|
158
|
-
* @throws if child TreeKey is exhausted or index out of range
|
|
159
|
-
*/
|
|
160
|
-
signFromChild(index, message) {
|
|
161
|
-
this.assertChildIndex(index);
|
|
162
|
-
const slot = index + 1;
|
|
163
|
-
const treeKey = this.getOrCreateTreeKey(slot);
|
|
164
|
-
const uses = this.childUsesMap.get(index) ?? 0;
|
|
165
|
-
treeKey.setUses(uses);
|
|
166
|
-
const sig = treeKey.sign(hashMessage(message));
|
|
167
|
-
this.childUsesMap.set(index, uses + 1);
|
|
168
|
-
return {
|
|
169
|
-
address: this.getChildAddress(index),
|
|
170
|
-
publicKey: this.getChildPublicKey(index),
|
|
171
|
-
signature: (0, core_1.bytesToHex)((0, core_1.serializeTreeSignature)(sig)),
|
|
172
|
-
message,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
// ── Ownership proofs ─────────────────────────────────────────────────────────
|
|
176
|
-
/**
|
|
177
|
-
* Produce an ownership proof demonstrating that the root key controls all
|
|
178
|
-
* given child addresses.
|
|
179
|
-
*
|
|
180
|
-
* @param childIndices - Which children to include (0-based, order preserved)
|
|
181
|
-
*/
|
|
182
|
-
proveOwnership(childIndices) {
|
|
183
|
-
if (childIndices.length === 0) {
|
|
184
|
-
throw new Error('childIndices must contain at least one index');
|
|
185
|
-
}
|
|
186
|
-
const seen = new Set();
|
|
187
|
-
for (const idx of childIndices) {
|
|
188
|
-
this.assertChildIndex(idx);
|
|
189
|
-
if (seen.has(idx))
|
|
190
|
-
throw new Error(`Duplicate child index: ${idx}`);
|
|
191
|
-
seen.add(idx);
|
|
192
|
-
}
|
|
193
|
-
const timestamp = new Date().toISOString();
|
|
194
|
-
const childAddresses = childIndices.map(i => this.getChildAddress(i));
|
|
195
|
-
const childPublicKeys = childIndices.map(i => this.getChildPublicKey(i));
|
|
196
|
-
const rootAddress = this.getRootAddress();
|
|
197
|
-
const rootPublicKey = this.getRootPublicKey();
|
|
198
|
-
const message = buildOwnershipMessage(rootAddress, childPublicKeys, timestamp);
|
|
199
|
-
const rootProof = this.signFromRoot(message);
|
|
200
|
-
return { rootAddress, rootPublicKey, childAddresses, childPublicKeys, rootProof, timestamp };
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Verify an ownership proof produced by `proveOwnership`.
|
|
204
|
-
*
|
|
205
|
-
* Returns `true` only when:
|
|
206
|
-
* - The canonical message is reconstructed correctly (child keys are sorted).
|
|
207
|
-
* - The root WOTS signature validates against the root public key and address.
|
|
208
|
-
* - Every child public key correctly derives the corresponding child address.
|
|
209
|
-
*
|
|
210
|
-
* Pure crypto — no network access required.
|
|
211
|
-
* Always returns `false` (never throws) on malformed or incomplete proof data.
|
|
212
|
-
*/
|
|
213
|
-
static verifyOwnershipProof(proof) {
|
|
214
|
-
try {
|
|
215
|
-
if (!proof || typeof proof !== 'object')
|
|
216
|
-
return false;
|
|
217
|
-
const { rootAddress, rootPublicKey, childAddresses, childPublicKeys, rootProof, timestamp } = proof;
|
|
218
|
-
if (!rootAddress || !rootPublicKey || !timestamp ||
|
|
219
|
-
!Array.isArray(childAddresses) || !Array.isArray(childPublicKeys) ||
|
|
220
|
-
!rootProof || typeof rootProof.signature !== 'string') {
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
if (childAddresses.length === 0)
|
|
224
|
-
return false;
|
|
225
|
-
if (childAddresses.length !== childPublicKeys.length)
|
|
226
|
-
return false;
|
|
227
|
-
const canonicalMessage = buildOwnershipMessage(rootAddress, childPublicKeys, timestamp);
|
|
228
|
-
if (rootProof.message !== canonicalMessage)
|
|
229
|
-
return false;
|
|
230
|
-
const sigResult = (0, core_1.verifySignatureDetailed)(rootAddress, canonicalMessage, rootProof.signature, rootPublicKey);
|
|
231
|
-
if (!sigResult.valid)
|
|
232
|
-
return false;
|
|
233
|
-
for (let i = 0; i < childPublicKeys.length; i++) {
|
|
234
|
-
const pkBytes = (0, core_1.hexToBytes)(childPublicKeys[i]);
|
|
235
|
-
const derivedAddress = addressFromPublicKeyBytes(pkBytes);
|
|
236
|
-
if (derivedAddress.toLowerCase() !== childAddresses[i].toLowerCase())
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
catch {
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
// ── TreeKey access (for low-level transaction signing) ───────────────────────
|
|
246
|
-
/**
|
|
247
|
-
* Get (or create and cache) the TreeKey for child `index` (0-based).
|
|
248
|
-
*
|
|
249
|
-
* Callers that need positional watermark control (e.g. `setUses(l1*64+l2)`)
|
|
250
|
-
* can use this to drive WOTS signing directly — the same path the Totem
|
|
251
|
-
* extension uses for transaction signing in RootTree mode.
|
|
252
|
-
*
|
|
253
|
-
* Note: This does NOT advance the high-level `childUsesMap` counter.
|
|
254
|
-
* Call `getChildUses` / `setChildUses` yourself if you need to track uses.
|
|
255
|
-
*/
|
|
256
|
-
getChildTreeKey(index) {
|
|
257
|
-
this.assertChildIndex(index);
|
|
258
|
-
return this.getOrCreateTreeKey(index + 1);
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Get (or create and cache) the root TreeKey (slot 0).
|
|
262
|
-
*
|
|
263
|
-
* Useful for callers that need direct access to the root key's TreeKey
|
|
264
|
-
* object for custom signing operations.
|
|
265
|
-
*/
|
|
266
|
-
getRootTreeKey() {
|
|
267
|
-
return this.getOrCreateTreeKey(0);
|
|
268
|
-
}
|
|
269
|
-
// ── Watermark access ─────────────────────────────────────────────────────────
|
|
270
|
-
/** Number of times the root key has been used (for persistence). */
|
|
271
|
-
getRootUses() { return this.rootUses; }
|
|
272
|
-
/** Restore root watermark from a previously persisted value. */
|
|
273
|
-
setRootUses(uses) {
|
|
274
|
-
if (uses < 0)
|
|
275
|
-
throw new Error('uses must be non-negative');
|
|
276
|
-
this.rootUses = uses;
|
|
277
|
-
}
|
|
278
|
-
/** Number of times child `index` has signed (for persistence). */
|
|
279
|
-
getChildUses(index) {
|
|
280
|
-
this.assertChildIndex(index);
|
|
281
|
-
return this.childUsesMap.get(index) ?? 0;
|
|
282
|
-
}
|
|
283
|
-
/** Restore child watermark from a previously persisted value. */
|
|
284
|
-
setChildUses(index, uses) {
|
|
285
|
-
this.assertChildIndex(index);
|
|
286
|
-
if (uses < 0)
|
|
287
|
-
throw new Error('uses must be non-negative');
|
|
288
|
-
this.childUsesMap.set(index, uses);
|
|
289
|
-
}
|
|
290
|
-
/** Number of child addresses configured for this wallet. */
|
|
291
|
-
getChildCount() { return this.childCount; }
|
|
292
|
-
/** Maximum one-time signatures available per slot (3 levels × 64 keys). */
|
|
293
|
-
getMaxUsesPerSlot() { return 64 * 64 * 64; }
|
|
294
|
-
/**
|
|
295
|
-
* Return a serialisable snapshot of all current watermark counters.
|
|
296
|
-
*
|
|
297
|
-
* Persist the returned object (e.g. to encrypted storage) and pass it back
|
|
298
|
-
* to `restoreWatermarkState()` at the start of the next session so that no
|
|
299
|
-
* one-time-use signing slot is ever reused.
|
|
300
|
-
*
|
|
301
|
-
* @example
|
|
302
|
-
* const state = wallet.getWatermarkState();
|
|
303
|
-
* await storage.set('watermarks', JSON.stringify(state));
|
|
304
|
-
*/
|
|
305
|
-
getWatermarkState() {
|
|
306
|
-
const childUses = {};
|
|
307
|
-
for (const [index, uses] of this.childUsesMap) {
|
|
308
|
-
childUses[index] = uses;
|
|
309
|
-
}
|
|
310
|
-
return { rootUses: this.rootUses, childUses };
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Restore watermark counters from a previously persisted snapshot.
|
|
314
|
-
*
|
|
315
|
-
* Call this immediately after constructing the wallet to prevent slot reuse
|
|
316
|
-
* across sessions. All values must be non-negative integers; out-of-range
|
|
317
|
-
* or invalid entries are silently skipped.
|
|
318
|
-
*
|
|
319
|
-
* @example
|
|
320
|
-
* const saved = JSON.parse(await storage.get('watermarks') ?? '{}');
|
|
321
|
-
* wallet.restoreWatermarkState(saved);
|
|
322
|
-
*/
|
|
323
|
-
restoreWatermarkState(state) {
|
|
324
|
-
if (typeof state?.rootUses === 'number' && state.rootUses >= 0) {
|
|
325
|
-
this.rootUses = Math.floor(state.rootUses);
|
|
326
|
-
}
|
|
327
|
-
if (state?.childUses && typeof state.childUses === 'object') {
|
|
328
|
-
for (const [key, uses] of Object.entries(state.childUses)) {
|
|
329
|
-
const index = Number(key);
|
|
330
|
-
if (Number.isInteger(index) && index >= 0 && index < this.childCount && typeof uses === 'number' && uses >= 0) {
|
|
331
|
-
this.childUsesMap.set(index, Math.floor(uses));
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
337
|
-
assertChildIndex(index) {
|
|
338
|
-
if (!Number.isInteger(index) || index < 0 || index >= this.childCount) {
|
|
339
|
-
throw new Error(`Child index ${index} is out of range [0, ${this.childCount - 1}]`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Get or create the TreeKey for a given slot.
|
|
344
|
-
* Also populates address and public key caches on first access.
|
|
345
|
-
*/
|
|
346
|
-
getOrCreateTreeKey(slot) {
|
|
347
|
-
const cached = this.treeKeyCache.get(slot);
|
|
348
|
-
if (cached)
|
|
349
|
-
return cached;
|
|
350
|
-
const treeKey = (0, core_1.createPerAddressTreeKey)(this.baseSeed, slot);
|
|
351
|
-
this.treeKeyCache.set(slot, treeKey);
|
|
352
|
-
// Cache address and public key while we have the TreeKey
|
|
353
|
-
const pkBytes = treeKey.getPublicKey();
|
|
354
|
-
const pkHex = (0, core_1.bytesToHex)(pkBytes);
|
|
355
|
-
this.pubKeyCache.set(slot, pkHex);
|
|
356
|
-
this.addressCache.set(slot, addressFromPublicKeyBytes(pkBytes));
|
|
357
|
-
return treeKey;
|
|
358
|
-
}
|
|
359
|
-
slotPublicKey(slot) {
|
|
360
|
-
const cached = this.pubKeyCache.get(slot);
|
|
361
|
-
if (cached)
|
|
362
|
-
return cached;
|
|
363
|
-
this.getOrCreateTreeKey(slot); // populates both caches
|
|
364
|
-
return this.pubKeyCache.get(slot);
|
|
365
|
-
}
|
|
366
|
-
slotAddress(slot) {
|
|
367
|
-
const cached = this.addressCache.get(slot);
|
|
368
|
-
if (cached)
|
|
369
|
-
return cached;
|
|
370
|
-
this.getOrCreateTreeKey(slot); // populates both caches
|
|
371
|
-
return this.addressCache.get(slot);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
exports.RootIdentityWallet = RootIdentityWallet;
|
|
13
|
+
var UnifiedIdentityWallet_js_1 = require("./UnifiedIdentityWallet.js");
|
|
14
|
+
Object.defineProperty(exports, "RootIdentityWallet", { enumerable: true, get: function () { return UnifiedIdentityWallet_js_1.UnifiedIdentityWallet; } });
|
|
15
|
+
Object.defineProperty(exports, "MAX_CHILD_COUNT", { enumerable: true, get: function () { return UnifiedIdentityWallet_js_1.MAX_CHILD_COUNT; } });
|