@totemsdk/core 1.0.9 → 1.0.11
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/LICENSE +21 -0
- package/README.md +5 -5
- package/dist/Streamable.js +54 -31
- package/dist/adapters/index.js +18 -8
- package/dist/adapters/types.js +19 -8
- package/dist/binio.js +5 -1
- package/dist/bip39.js +24 -14
- package/dist/derive.js +11 -7
- package/dist/index.js +130 -22
- package/dist/javaStreamables.js +49 -30
- package/dist/lease/LeaseMonitor.js +7 -3
- package/dist/lease/LeaseStore.js +7 -3
- package/dist/lease/WatermarkStore.js +7 -3
- package/dist/lease/index.js +13 -4
- package/dist/lease-client.js +8 -3
- package/dist/minima32.js +22 -13
- package/dist/minimaWireSerializer.js +20 -14
- package/dist/mmr.js +35 -23
- package/dist/mx.js +10 -1
- package/dist/params.js +9 -5
- package/dist/script.js +12 -7
- package/dist/scripts/contract-helpers.js +37 -26
- package/dist/scripts/dapp-types.js +11 -6
- package/dist/scripts/index.js +38 -4
- package/dist/scripts/types.js +26 -12
- package/dist/scripts/witness-serializer.js +48 -30
- package/dist/transaction.js +55 -46
- package/dist/treekey.js +74 -58
- package/dist/tx/TransactionLifecycle.js +11 -5
- package/dist/tx/TransactionReceiptStore.js +7 -3
- package/dist/tx/TransactionService.js +14 -10
- package/dist/tx/index.js +11 -3
- package/dist/tx/types.js +2 -1
- package/dist/util.js +5 -1
- package/dist/utils.js +12 -5
- package/dist/verify.js +36 -25
- package/dist/version.js +5 -2
- package/dist/wots.d.ts +2 -2
- package/dist/wots.js +94 -64
- package/package.json +50 -6
- package/src/Streamable.d.ts +0 -268
- package/src/Streamable.js +0 -416
- package/src/Streamable.ts +0 -567
- package/src/__tests__/Streamable.parity.test.ts +0 -422
- package/src/__tests__/perAddressDerivation.parity.test.ts +0 -152
- package/src/__tests__/roottree-prepare.test.ts +0 -101
- package/src/__tests__/wots-parity.test.ts +0 -289
- package/src/adapters/index.d.ts +0 -190
- package/src/adapters/index.js +0 -117
- package/src/adapters/index.ts +0 -301
- package/src/adapters/types.ts +0 -235
- package/src/addr.oracle.test.ts +0 -25
- package/src/address.test.ts +0 -55
- package/src/binio.ts +0 -21
- package/src/bip39.d.ts +0 -91
- package/src/bip39.js +0 -427
- package/src/bip39.ts +0 -432
- package/src/derive.d.ts +0 -2
- package/src/derive.js +0 -13
- package/src/derive.ts +0 -11
- package/src/index.d.ts +0 -26
- package/src/index.js +0 -128
- package/src/index.test.ts +0 -5
- package/src/index.ts +0 -221
- package/src/javaStreamables.d.ts +0 -233
- package/src/javaStreamables.js +0 -332
- package/src/javaStreamables.ts +0 -403
- package/src/lease/LeaseMonitor.d.ts +0 -44
- package/src/lease/LeaseMonitor.js +0 -111
- package/src/lease/LeaseMonitor.ts +0 -157
- package/src/lease/LeaseStore.d.ts +0 -57
- package/src/lease/LeaseStore.js +0 -148
- package/src/lease/LeaseStore.ts +0 -203
- package/src/lease/WatermarkStore.d.ts +0 -61
- package/src/lease/WatermarkStore.js +0 -206
- package/src/lease/WatermarkStore.ts +0 -270
- package/src/lease/index.d.ts +0 -14
- package/src/lease/index.js +0 -22
- package/src/lease/index.ts +0 -41
- package/src/lease-client.d.ts +0 -31
- package/src/lease-client.js +0 -44
- package/src/lease-client.ts +0 -69
- package/src/minima32.d.ts +0 -10
- package/src/minima32.js +0 -113
- package/src/minima32.test.ts +0 -11
- package/src/minima32.ts +0 -127
- package/src/minimaWireSerializer.d.ts +0 -189
- package/src/minimaWireSerializer.js +0 -311
- package/src/minimaWireSerializer.test.ts +0 -466
- package/src/minimaWireSerializer.ts +0 -117
- package/src/mmr.d.ts +0 -157
- package/src/mmr.js +0 -340
- package/src/mmr.oracle.test.ts +0 -28
- package/src/mmr.ts +0 -429
- package/src/mx.test.ts +0 -25
- package/src/mx.ts +0 -1
- package/src/params.d.ts +0 -29
- package/src/params.d.ts.map +0 -1
- package/src/params.js +0 -20
- package/src/params.js.map +0 -1
- package/src/params.ts +0 -44
- package/src/script.d.ts +0 -7
- package/src/script.js +0 -27
- package/src/script.ts +0 -24
- package/src/scripts/contract-helpers.ts +0 -609
- package/src/scripts/dapp-types.ts +0 -240
- package/src/scripts/index.ts +0 -22
- package/src/scripts/types.d.ts +0 -81
- package/src/scripts/types.js +0 -145
- package/src/scripts/types.ts +0 -247
- package/src/scripts/witness-serializer.ts +0 -384
- package/src/transaction.ts +0 -430
- package/src/treekey.d.ts +0 -383
- package/src/treekey.js +0 -774
- package/src/treekey.parity.test.ts +0 -459
- package/src/treekey.ts +0 -975
- package/src/tx/TransactionLifecycle.d.ts +0 -55
- package/src/tx/TransactionLifecycle.js +0 -188
- package/src/tx/TransactionLifecycle.ts +0 -280
- package/src/tx/TransactionReceiptStore.d.ts +0 -36
- package/src/tx/TransactionReceiptStore.js +0 -89
- package/src/tx/TransactionReceiptStore.ts +0 -112
- package/src/tx/TransactionService.d.ts +0 -31
- package/src/tx/TransactionService.js +0 -140
- package/src/tx/TransactionService.ts +0 -252
- package/src/tx/index.d.ts +0 -13
- package/src/tx/index.js +0 -20
- package/src/tx/index.ts +0 -44
- package/src/tx/types.d.ts +0 -80
- package/src/tx/types.js +0 -6
- package/src/tx/types.ts +0 -130
- package/src/util.d.ts +0 -1
- package/src/util.js +0 -5
- package/src/util.ts +0 -1
- package/src/utils.d.ts +0 -8
- package/src/utils.js +0 -42
- package/src/utils.ts +0 -38
- package/src/verify.ts +0 -210
- package/src/version.ts +0 -13
- package/src/wots.d.ts +0 -224
- package/src/wots.d.ts.map +0 -1
- package/src/wots.js +0 -565
- package/src/wots.js.map +0 -1
- package/src/wots.ts +0 -651
- package/test-vectors.json +0 -45
package/src/mmr.ts
DELETED
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
// packages/totem-sdk/packages/core/src/mmr.ts
|
|
2
|
-
// Full MMR implementation matching Minima's MMR.java, MMRData.java, and TreeKeyNode.java
|
|
3
|
-
import { sha3_256 as nobleSha3 } from '@noble/hashes/sha3.js';
|
|
4
|
-
import { serializeMiniNumber, serializeMiniData, writeHashToStream, javaHashAllObjects } from "./javaStreamables.js";
|
|
5
|
-
|
|
6
|
-
// Import canonical wire serialization from Streamable.ts (single source of truth)
|
|
7
|
-
import { writeMMRProof, type MMRProof as StreamableMMRProof } from "./Streamable.js";
|
|
8
|
-
|
|
9
|
-
// Re-export for API compatibility (existing code imports from mmr.ts)
|
|
10
|
-
// Using a wrapper function for better compatibility with dynamic imports
|
|
11
|
-
export function serializeMMRProof(proof: { chunks: Array<{ isLeft: boolean; mmrData: { data: Bytes; value: bigint } }> }, blockTime: bigint = 0n): Bytes {
|
|
12
|
-
// Convert to Streamable format and serialize
|
|
13
|
-
const streamableProof: StreamableMMRProof = {
|
|
14
|
-
blockTime: blockTime,
|
|
15
|
-
chunks: proof.chunks.map(c => ({
|
|
16
|
-
isLeft: c.isLeft,
|
|
17
|
-
mmrData: { data: c.mmrData.data, value: c.mmrData.value }
|
|
18
|
-
}))
|
|
19
|
-
};
|
|
20
|
-
return writeMMRProof(streamableProof);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const serializeRealMMRProof = serializeMMRProof;
|
|
24
|
-
|
|
25
|
-
export type Bytes = Uint8Array;
|
|
26
|
-
|
|
27
|
-
/** SHA3-256 helper returning 32 bytes */
|
|
28
|
-
function sha3(data: Bytes): Bytes {
|
|
29
|
-
return nobleSha3(data);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** concat utility */
|
|
33
|
-
function concat(...parts: Bytes[]): Bytes {
|
|
34
|
-
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
35
|
-
const out = new Uint8Array(total);
|
|
36
|
-
let off = 0;
|
|
37
|
-
for (const p of parts) { out.set(p, off); off += p.length; }
|
|
38
|
-
return out;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** big-endian u32 */
|
|
42
|
-
const u32be = (n: number) => new Uint8Array([(n>>>24)&0xff,(n>>>16)&0xff,(n>>>8)&0xff,n&0xff]);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* EXACT MiniNumber.ZERO wire format from Java:
|
|
46
|
-
* scale (1 byte) = 0x00
|
|
47
|
-
* length (1 byte) = 0x01
|
|
48
|
-
* unscaled bytes (length=1) = 0x00
|
|
49
|
-
* => 00 01 00
|
|
50
|
-
*/
|
|
51
|
-
function encodeMiniNumberZERO(): Bytes {
|
|
52
|
-
return new Uint8Array([0x00, 0x01, 0x00]);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* EXACT MiniString wire format (MiniData of UTF-8):
|
|
57
|
-
* 4-byte big-endian length
|
|
58
|
-
* raw UTF-8 bytes
|
|
59
|
-
*/
|
|
60
|
-
function encodeMiniStringUTF8(text: string): Bytes {
|
|
61
|
-
const utf8 = new TextEncoder().encode(text);
|
|
62
|
-
const len = u32be(utf8.length);
|
|
63
|
-
return concat(len, utf8);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Byte-exact one-leaf MMR leaf used by Minima Address.java path:
|
|
68
|
-
* sha3( MiniNumber.ZERO || MiniString(script) || MiniNumber.ZERO )
|
|
69
|
-
*/
|
|
70
|
-
export function mmrLeafExact(script: string): Bytes {
|
|
71
|
-
const zero = encodeMiniNumberZERO();
|
|
72
|
-
const mstr = encodeMiniStringUTF8(script);
|
|
73
|
-
const all = concat(zero, mstr, zero);
|
|
74
|
-
return sha3(all); // 32-byte leaf hash
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** In a single-leaf MMR the root equals the leaf commitment. */
|
|
78
|
-
export function mmrRootFromSingleLeaf(script: string): Bytes {
|
|
79
|
-
return mmrLeafExact(script);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ============================================================================
|
|
83
|
-
// MMR Tree Implementation matching Minima's MMR.java and MMRData.java
|
|
84
|
-
// Used by TreeKeyNode for computing wallet public key from 64 Winternitz keys
|
|
85
|
-
// ============================================================================
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* MMRData structure matching Minima's MMRData.java
|
|
89
|
-
* Contains hash and value (for sum tree functionality)
|
|
90
|
-
*/
|
|
91
|
-
export interface MMRData {
|
|
92
|
-
data: Bytes; // 32-byte hash
|
|
93
|
-
value: bigint; // Sum value (always 0n for TreeKeyNode)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* MMREntry structure matching Minima's MMREntry.java
|
|
98
|
-
* Represents a node in the MMR at a specific row and position
|
|
99
|
-
*/
|
|
100
|
-
export interface MMREntry {
|
|
101
|
-
row: number; // Tree level (0 = leaves)
|
|
102
|
-
entryNumber: bigint; // Position within the row
|
|
103
|
-
mmrData: MMRData;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* MMRProofChunk - one step in the proof path
|
|
108
|
-
* Matches Minima's MMRProof structure
|
|
109
|
-
*/
|
|
110
|
-
export interface MMRProofChunk {
|
|
111
|
-
isLeft: boolean; // Is this sibling on the left?
|
|
112
|
-
mmrData: MMRData; // Sibling's data
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* MMRProof structure matching Minima's MMRProof.java
|
|
117
|
-
* Contains proof chunks to verify leaf membership in the tree
|
|
118
|
-
*/
|
|
119
|
-
export interface MMRProof {
|
|
120
|
-
chunks: MMRProofChunk[];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Create MMRData leaf node matching Minima's MMRData.CreateMMRDataLeafNode
|
|
125
|
-
*
|
|
126
|
-
* From MMRData.java:
|
|
127
|
-
* MiniData hash = Crypto.getInstance().hashAllObjects(MiniNumber.ZERO, zData, zSumValue);
|
|
128
|
-
*
|
|
129
|
-
* CRITICAL: Crypto.hashAllObjects uses writeDataStream for Streamables:
|
|
130
|
-
* - MiniNumber: scale + len + data (see serializeMiniNumber)
|
|
131
|
-
* - MiniData: 4-byte length + data (see serializeMiniData)
|
|
132
|
-
*
|
|
133
|
-
* For TreeKeyNode, zData is the Winternitz public key (MiniData) and zSumValue is ZERO
|
|
134
|
-
*
|
|
135
|
-
* Serialization order:
|
|
136
|
-
* 1. MiniNumber.ZERO: [0x00, 0x01, 0x00]
|
|
137
|
-
* 2. MiniData (pubkey): [4-byte length] + [bytes] (writeDataStream, NOT writeHashToStream)
|
|
138
|
-
* 3. MiniNumber.ZERO: [0x00, 0x01, 0x00]
|
|
139
|
-
*/
|
|
140
|
-
export function createMMRDataLeafNode(pubkey: Bytes, sumValue: bigint = 0n): MMRData {
|
|
141
|
-
// Use cached ZERO serialization for efficiency
|
|
142
|
-
const zero = new Uint8Array([0x00, 0x01, 0x00]); // MiniNumber.ZERO
|
|
143
|
-
|
|
144
|
-
// IMPORTANT: hashAllObjects uses writeDataStream which is 4-byte length prefixed
|
|
145
|
-
const pubkeySerialized = serializeMiniData(pubkey);
|
|
146
|
-
|
|
147
|
-
// For sumValue, serialize as MiniNumber
|
|
148
|
-
let sumSerialized: Uint8Array;
|
|
149
|
-
if (sumValue === 0n) {
|
|
150
|
-
sumSerialized = new Uint8Array([0x00, 0x01, 0x00]); // MiniNumber.ZERO
|
|
151
|
-
} else {
|
|
152
|
-
sumSerialized = serializeMiniNumber(Number(sumValue));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const hash = javaHashAllObjects(zero, pubkeySerialized, sumSerialized);
|
|
156
|
-
return { data: hash, value: sumValue };
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Create MMRData parent node matching Minima's MMRData.CreateMMRDataParentNode
|
|
161
|
-
*
|
|
162
|
-
* From MMRData.java:
|
|
163
|
-
* MiniNumber sumvalue = zLeft.getValue().add(zRight.getValue());
|
|
164
|
-
* MiniData combinedhash = Crypto.getInstance().hashAllObjects(
|
|
165
|
-
* MiniNumber.ONE, zLeft.getData(), zRight.getData(), sumvalue);
|
|
166
|
-
*
|
|
167
|
-
* CRITICAL: The getData() returns MiniData (the hash), which is serialized
|
|
168
|
-
* with writeDataStream (4-byte length) in hashAllObjects.
|
|
169
|
-
*
|
|
170
|
-
* Serialization order:
|
|
171
|
-
* 1. MiniNumber.ONE: [0x00, 0x01, 0x01]
|
|
172
|
-
* 2. MiniData (left.data): [4-byte length] + [bytes]
|
|
173
|
-
* 3. MiniData (right.data): [4-byte length] + [bytes]
|
|
174
|
-
* 4. MiniNumber (sumvalue): serialized MiniNumber
|
|
175
|
-
*/
|
|
176
|
-
export function createMMRDataParentNode(left: MMRData, right: MMRData): MMRData {
|
|
177
|
-
const sumValue = left.value + right.value;
|
|
178
|
-
|
|
179
|
-
// MiniNumber.ONE: [0x00, 0x01, 0x01]
|
|
180
|
-
const one = new Uint8Array([0x00, 0x01, 0x01]);
|
|
181
|
-
|
|
182
|
-
// Serialize left and right data as MiniData (4-byte length-prefixed)
|
|
183
|
-
const leftDataSerialized = serializeMiniData(left.data);
|
|
184
|
-
const rightDataSerialized = serializeMiniData(right.data);
|
|
185
|
-
|
|
186
|
-
// Serialize sum value
|
|
187
|
-
let sumSerialized: Uint8Array;
|
|
188
|
-
if (sumValue === 0n) {
|
|
189
|
-
sumSerialized = new Uint8Array([0x00, 0x01, 0x00]); // MiniNumber.ZERO
|
|
190
|
-
} else {
|
|
191
|
-
sumSerialized = serializeMiniNumber(Number(sumValue));
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const hash = javaHashAllObjects(one, leftDataSerialized, rightDataSerialized, sumSerialized);
|
|
195
|
-
return { data: hash, value: sumValue };
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Simple MMR Tree for TreeKeyNode
|
|
200
|
-
* Builds a perfect binary tree from N entries (N must be power of 2 for simplicity)
|
|
201
|
-
*
|
|
202
|
-
* This matches TreeKeyNode.java which always uses 64 leaves (2^6)
|
|
203
|
-
*/
|
|
204
|
-
export class MMRTree {
|
|
205
|
-
private entries: Map<string, MMREntry> = new Map();
|
|
206
|
-
private leafCount: number = 0;
|
|
207
|
-
private maxRow: number = 0;
|
|
208
|
-
|
|
209
|
-
private getKey(row: number, entryNumber: bigint): string {
|
|
210
|
-
return `${row}:${entryNumber}`;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private setEntry(row: number, entryNumber: bigint, data: MMRData): MMREntry {
|
|
214
|
-
const entry: MMREntry = { row, entryNumber, mmrData: data };
|
|
215
|
-
this.entries.set(this.getKey(row, entryNumber), entry);
|
|
216
|
-
if (row > this.maxRow) this.maxRow = row;
|
|
217
|
-
return entry;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
private getEntry(row: number, entryNumber: bigint): MMREntry | undefined {
|
|
221
|
-
return this.entries.get(this.getKey(row, entryNumber));
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Add a leaf entry to the MMR
|
|
226
|
-
* Matches MMR.java addEntry() but simplified for power-of-2 trees
|
|
227
|
-
*/
|
|
228
|
-
addLeaf(data: MMRData): MMREntry {
|
|
229
|
-
const entryNumber = BigInt(this.leafCount);
|
|
230
|
-
const entry = this.setEntry(0, entryNumber, data);
|
|
231
|
-
this.leafCount++;
|
|
232
|
-
|
|
233
|
-
// Propagate up the tree
|
|
234
|
-
let current = entry;
|
|
235
|
-
while (current.entryNumber % 2n === 1n) { // Is right child
|
|
236
|
-
const siblingNumber = current.entryNumber - 1n;
|
|
237
|
-
const sibling = this.getEntry(current.row, siblingNumber);
|
|
238
|
-
if (!sibling) break;
|
|
239
|
-
|
|
240
|
-
// Create parent node
|
|
241
|
-
const parentData = createMMRDataParentNode(sibling.mmrData, current.mmrData);
|
|
242
|
-
const parentRow = current.row + 1;
|
|
243
|
-
const parentEntry = current.entryNumber / 2n;
|
|
244
|
-
current = this.setEntry(parentRow, parentEntry, parentData);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return entry;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Build tree from array of Winternitz public keys
|
|
252
|
-
* Used by TreeKeyNode to compute wallet public key
|
|
253
|
-
*/
|
|
254
|
-
static fromPublicKeys(pubkeys: Bytes[]): MMRTree {
|
|
255
|
-
const tree = new MMRTree();
|
|
256
|
-
for (const pk of pubkeys) {
|
|
257
|
-
const leafData = createMMRDataLeafNode(pk, 0n);
|
|
258
|
-
tree.addLeaf(leafData);
|
|
259
|
-
}
|
|
260
|
-
return tree;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Get the root of the tree
|
|
265
|
-
* For a perfect binary tree with N leaves, root is at row log2(N), entry 0
|
|
266
|
-
*/
|
|
267
|
-
getRoot(): MMRData | null {
|
|
268
|
-
if (this.leafCount === 0) return null;
|
|
269
|
-
|
|
270
|
-
// For power of 2 leaves, there's exactly one peak at the top
|
|
271
|
-
const topRow = Math.floor(Math.log2(this.leafCount));
|
|
272
|
-
const root = this.getEntry(topRow, 0n);
|
|
273
|
-
return root?.mmrData || null;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Get proof for a leaf at given index
|
|
278
|
-
* Matches MMR.java getProofToPeak()
|
|
279
|
-
*/
|
|
280
|
-
getProof(leafIndex: number): MMRProof {
|
|
281
|
-
const chunks: MMRProofChunk[] = [];
|
|
282
|
-
let row = 0;
|
|
283
|
-
let entryNumber = BigInt(leafIndex);
|
|
284
|
-
|
|
285
|
-
// Walk up the tree collecting siblings
|
|
286
|
-
while (row < this.maxRow) {
|
|
287
|
-
const siblingNumber = entryNumber % 2n === 0n
|
|
288
|
-
? entryNumber + 1n // Current is left, sibling is right
|
|
289
|
-
: entryNumber - 1n; // Current is right, sibling is left
|
|
290
|
-
|
|
291
|
-
const sibling = this.getEntry(row, siblingNumber);
|
|
292
|
-
if (!sibling) break;
|
|
293
|
-
|
|
294
|
-
const isLeft = siblingNumber < entryNumber;
|
|
295
|
-
chunks.push({ isLeft, mmrData: sibling.mmrData });
|
|
296
|
-
|
|
297
|
-
// Move to parent
|
|
298
|
-
row++;
|
|
299
|
-
entryNumber = entryNumber / 2n;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
return { chunks };
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Get the leaf MMRData at a specific index
|
|
307
|
-
*/
|
|
308
|
-
getLeaf(index: number): MMRData | null {
|
|
309
|
-
const entry = this.getEntry(0, BigInt(index));
|
|
310
|
-
return entry?.mmrData || null;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Calculate root from leaf data and proof
|
|
316
|
-
* Matches SignatureProof.getRootPublicKey() in Java
|
|
317
|
-
*
|
|
318
|
-
* From SignatureProof.java:
|
|
319
|
-
* MMRData pubentry = MMRData.CreateMMRDataLeafNode(mPublicKey, MiniNumber.ZERO);
|
|
320
|
-
* return mProof.calculateProof(pubentry).getData();
|
|
321
|
-
*/
|
|
322
|
-
export function calculateProofRoot(leafData: MMRData, proof: MMRProof): Bytes {
|
|
323
|
-
let current = leafData;
|
|
324
|
-
|
|
325
|
-
for (const chunk of proof.chunks) {
|
|
326
|
-
if (chunk.isLeft) {
|
|
327
|
-
// Sibling is on the left, current is on the right
|
|
328
|
-
current = createMMRDataParentNode(chunk.mmrData, current);
|
|
329
|
-
} else {
|
|
330
|
-
// Sibling is on the right, current is on the left
|
|
331
|
-
current = createMMRDataParentNode(current, chunk.mmrData);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return current.data;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Verify a proof: check that leaf + proof computes to expected root
|
|
340
|
-
*/
|
|
341
|
-
export function verifyMMRProof(leafPubkey: Bytes, proof: MMRProof, expectedRoot: Bytes): boolean {
|
|
342
|
-
const leafData = createMMRDataLeafNode(leafPubkey, 0n);
|
|
343
|
-
const computedRoot = calculateProofRoot(leafData, proof);
|
|
344
|
-
|
|
345
|
-
if (computedRoot.length !== expectedRoot.length) return false;
|
|
346
|
-
for (let i = 0; i < computedRoot.length; i++) {
|
|
347
|
-
if (computedRoot[i] !== expectedRoot[i]) return false;
|
|
348
|
-
}
|
|
349
|
-
return true;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// NOTE: serializeMMRProof is now imported from minimaWireSerializer.ts and re-exported above
|
|
353
|
-
// This consolidates wire format serialization into a single source of truth.
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Deserialize MMRProof from bytes matching Minima's MMRProof.readDataStream()
|
|
357
|
-
*
|
|
358
|
-
* Format:
|
|
359
|
-
* 1. blockTime (MiniNumber)
|
|
360
|
-
* 2. chain length (MiniNumber)
|
|
361
|
-
* 3. Each chunk: isLeft (1 byte) + MMRData (hash with 4-byte length prefix + value MiniNumber)
|
|
362
|
-
*
|
|
363
|
-
* CRITICAL: Java MMRData.readDataStream uses mData.readHashFromStream() which reads
|
|
364
|
-
* a 4-byte big-endian length prefix followed by the hash bytes.
|
|
365
|
-
*
|
|
366
|
-
* @returns { proof: MMRProof, blockTime: bigint }
|
|
367
|
-
*/
|
|
368
|
-
export function parseMMRProofFromHex(data: Bytes): { proof: MMRProof; blockTime: bigint; bytesRead: number } {
|
|
369
|
-
let offset = 0;
|
|
370
|
-
|
|
371
|
-
// Read blockTime as MiniNumber
|
|
372
|
-
const { value: blockTime, bytesRead: btRead } = readMiniNumber(data, offset);
|
|
373
|
-
offset += btRead;
|
|
374
|
-
|
|
375
|
-
// Read chain length as MiniNumber
|
|
376
|
-
const { value: numChunks, bytesRead: ncRead } = readMiniNumber(data, offset);
|
|
377
|
-
offset += ncRead;
|
|
378
|
-
|
|
379
|
-
const chunks: MMRProofChunk[] = [];
|
|
380
|
-
for (let i = 0; i < Number(numChunks); i++) {
|
|
381
|
-
// isLeft flag (1 byte)
|
|
382
|
-
const isLeft = data[offset] === 1;
|
|
383
|
-
offset += 1;
|
|
384
|
-
|
|
385
|
-
// MMRData: hash (4-byte length prefix + hash bytes) + value (MiniNumber)
|
|
386
|
-
// Read 4-byte big-endian length
|
|
387
|
-
const hashLength = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
|
|
388
|
-
offset += 4;
|
|
389
|
-
const hashData = data.slice(offset, offset + hashLength);
|
|
390
|
-
offset += hashLength;
|
|
391
|
-
|
|
392
|
-
const { value: chunkValue, bytesRead: cvRead } = readMiniNumber(data, offset);
|
|
393
|
-
offset += cvRead;
|
|
394
|
-
|
|
395
|
-
chunks.push({ isLeft, mmrData: { data: hashData, value: chunkValue } });
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return { proof: { chunks }, blockTime, bytesRead: offset };
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Read a MiniNumber from bytes at the given offset
|
|
403
|
-
* Format: [scale: 1 byte] [length: 1 byte] [data: N bytes]
|
|
404
|
-
*/
|
|
405
|
-
function readMiniNumber(data: Bytes, offset: number): { value: bigint; bytesRead: number } {
|
|
406
|
-
const scale = data[offset];
|
|
407
|
-
const length = data[offset + 1];
|
|
408
|
-
|
|
409
|
-
if (length === 0) {
|
|
410
|
-
// Zero value (shouldn't happen with proper serialization)
|
|
411
|
-
return { value: 0n, bytesRead: 2 };
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const dataBytes = data.slice(offset + 2, offset + 2 + length);
|
|
415
|
-
|
|
416
|
-
// Convert bytes to bigint
|
|
417
|
-
let value = 0n;
|
|
418
|
-
for (let i = 0; i < dataBytes.length; i++) {
|
|
419
|
-
value = (value << 8n) | BigInt(dataBytes[i]);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Apply scale if non-zero (for decimal numbers)
|
|
423
|
-
// For TreeKey MMR, scale is always 0
|
|
424
|
-
|
|
425
|
-
return { value, bytesRead: 2 + length };
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/** @deprecated Use parseMMRProofFromHex */
|
|
429
|
-
export const deserializeMMRProof = parseMMRProofFromHex;
|
package/src/mx.test.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { makeMinimaAddress, convertMinimaAddress } from "../src/mx";
|
|
3
|
-
|
|
4
|
-
describe("Mx address compat", () => {
|
|
5
|
-
it("roundtrips fixed vector", () => {
|
|
6
|
-
const hex = "0x01E41FCB7F956F510CA1F656C8CF991594BEE6F3A05F489EDA5798F485F21026";
|
|
7
|
-
const mx = "MxG0801SGFSMVSYDT8GP8FMAR4CV68YWWVEDST0BT49TMWNJ3Q8BSGG4PT3A5RW";
|
|
8
|
-
|
|
9
|
-
expect(makeMinimaAddress(hex)).toBe(mx);
|
|
10
|
-
expect(convertMinimaAddress(mx)).toBe(hex.toUpperCase());
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("various lengths behave like Java tests", () => {
|
|
14
|
-
const samples = [
|
|
15
|
-
"0xFFFF",
|
|
16
|
-
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
|
|
17
|
-
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
|
|
18
|
-
];
|
|
19
|
-
for (const h of samples) {
|
|
20
|
-
const mx = makeMinimaAddress(h);
|
|
21
|
-
const back = convertMinimaAddress(mx);
|
|
22
|
-
expect(back).toBe(h.toUpperCase());
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
});
|
package/src/mx.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { hexToMx, mxToHex, makeMinimaAddress, convertMinimaAddress, encodeMx, decodeMx } from './minima32.js';
|
package/src/params.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WOTS Parameter Set - BouncyCastle Compatible (w=8)
|
|
3
|
-
*
|
|
4
|
-
* Matches Minima Java implementation which uses BouncyCastle:
|
|
5
|
-
* - Winternitz.java: WINTERNITZ_VALUE = 8
|
|
6
|
-
* - WinternitzOTSignature.java: w=8 means 8 BITS per digit (not base-8)
|
|
7
|
-
* - SHA3-256 hash function (mdsize = 32 bytes)
|
|
8
|
-
*
|
|
9
|
-
* Chain count calculation (from WinternitzOTSignature constructor):
|
|
10
|
-
* messagesize = ((mdsize << 3) + w - 1) / w = (256 + 7) / 8 = 32
|
|
11
|
-
* checksumsize = getLog((messagesize << w) + 1) = getLog(8193) = 14 bits
|
|
12
|
-
* keysize = messagesize + (checksumsize + w - 1) / w = 32 + (14 + 7) / 8 = 34
|
|
13
|
-
*
|
|
14
|
-
* So L = 34 chains total, each chain value is 0-255 (8-bit digit)
|
|
15
|
-
*/
|
|
16
|
-
export type ParamSet = {
|
|
17
|
-
name: 'minima';
|
|
18
|
-
n: 256;
|
|
19
|
-
w: 8;
|
|
20
|
-
L: 34;
|
|
21
|
-
messageSize: 32;
|
|
22
|
-
checksumSize: 14;
|
|
23
|
-
checksumDigits: 2;
|
|
24
|
-
maxDigit: 255;
|
|
25
|
-
};
|
|
26
|
-
export declare const WOTS_MINIMA: ParamSet;
|
|
27
|
-
export declare const WOTS_V1_DEV: ParamSet;
|
|
28
|
-
export declare const WOTS_V2_SPEC: ParamSet;
|
|
29
|
-
export declare function getParamSet(_env?: string): ParamSet;
|
package/src/params.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"params.d.ts","sourceRoot":"","sources":["params.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,CAAC,EAAE,GAAG,CAAC;IACP,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;IACX,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,QAMzB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,QAM1B,CAAC;AAEF,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,CAGlD"}
|
package/src/params.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WOTS_V2_SPEC = exports.WOTS_V1_DEV = exports.WOTS_MINIMA = void 0;
|
|
4
|
-
exports.getParamSet = getParamSet;
|
|
5
|
-
exports.WOTS_MINIMA = {
|
|
6
|
-
name: 'minima',
|
|
7
|
-
n: 256,
|
|
8
|
-
w: 8,
|
|
9
|
-
L: 34,
|
|
10
|
-
messageSize: 32,
|
|
11
|
-
checksumSize: 14,
|
|
12
|
-
checksumDigits: 2,
|
|
13
|
-
maxDigit: 255,
|
|
14
|
-
};
|
|
15
|
-
// Legacy aliases for backwards compatibility during migration
|
|
16
|
-
exports.WOTS_V1_DEV = exports.WOTS_MINIMA;
|
|
17
|
-
exports.WOTS_V2_SPEC = exports.WOTS_MINIMA;
|
|
18
|
-
function getParamSet(_env) {
|
|
19
|
-
return exports.WOTS_MINIMA;
|
|
20
|
-
}
|
package/src/params.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"params.js","sourceRoot":"","sources":["params.ts"],"names":[],"mappings":";;;AAwBA,kCAGC;AAnBY,QAAA,WAAW,GAAa;IACnC,IAAI,EAAE,QAAQ;IACd,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,GAAG,EAAE,eAAe;CACrB,CAAC;AAEW,QAAA,YAAY,GAAa;IACpC,IAAI,EAAE,SAAS;IACf,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,EAAE;IACL,GAAG,EAAE,eAAe;CACrB,CAAC;AAEF,SAAgB,WAAW,CAAC,GAAY;IACtC,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1H,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAW,CAAC,CAAC,CAAC,oBAAY,CAAC;AACvD,CAAC"}
|
package/src/params.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WOTS Parameter Set - BouncyCastle Compatible (w=8)
|
|
3
|
-
*
|
|
4
|
-
* Matches Minima Java implementation which uses BouncyCastle:
|
|
5
|
-
* - Winternitz.java: WINTERNITZ_VALUE = 8
|
|
6
|
-
* - WinternitzOTSignature.java: w=8 means 8 BITS per digit (not base-8)
|
|
7
|
-
* - SHA3-256 hash function (mdsize = 32 bytes)
|
|
8
|
-
*
|
|
9
|
-
* Chain count calculation (from WinternitzOTSignature constructor):
|
|
10
|
-
* messagesize = ((mdsize << 3) + w - 1) / w = (256 + 7) / 8 = 32
|
|
11
|
-
* checksumsize = getLog((messagesize << w) + 1) = getLog(8193) = 14 bits
|
|
12
|
-
* keysize = messagesize + (checksumsize + w - 1) / w = 32 + (14 + 7) / 8 = 34
|
|
13
|
-
*
|
|
14
|
-
* So L = 34 chains total, each chain value is 0-255 (8-bit digit)
|
|
15
|
-
*/
|
|
16
|
-
export type ParamSet = {
|
|
17
|
-
name: 'minima';
|
|
18
|
-
n: 256; // hash output bits
|
|
19
|
-
w: 8; // Winternitz parameter: 8 BITS per digit
|
|
20
|
-
L: 34; // Total chains: 32 message + 2 checksum
|
|
21
|
-
messageSize: 32; // Number of message digits
|
|
22
|
-
checksumSize: 14; // Checksum bits
|
|
23
|
-
checksumDigits: 2; // Number of checksum digits
|
|
24
|
-
maxDigit: 255; // Maximum digit value (2^w - 1)
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const WOTS_MINIMA: ParamSet = {
|
|
28
|
-
name: 'minima',
|
|
29
|
-
n: 256,
|
|
30
|
-
w: 8,
|
|
31
|
-
L: 34,
|
|
32
|
-
messageSize: 32,
|
|
33
|
-
checksumSize: 14,
|
|
34
|
-
checksumDigits: 2,
|
|
35
|
-
maxDigit: 255,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// Legacy aliases for backwards compatibility during migration
|
|
39
|
-
export const WOTS_V1_DEV = WOTS_MINIMA;
|
|
40
|
-
export const WOTS_V2_SPEC = WOTS_MINIMA;
|
|
41
|
-
|
|
42
|
-
export function getParamSet(_env?: string): ParamSet {
|
|
43
|
-
return WOTS_MINIMA;
|
|
44
|
-
}
|
package/src/script.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { WotsKeypair } from "./wots";
|
|
2
|
-
/** Produce KISSVM script that authorizes with a WOTS PK digest (32 bytes). */
|
|
3
|
-
export declare function scriptFromWotsPk(pkDigest32: Uint8Array): string;
|
|
4
|
-
/** Address from WOTS keypair (client-only): script -> MMR leaf -> Mx */
|
|
5
|
-
export declare function wotsAddressFromKeypair(kp: WotsKeypair): string;
|
|
6
|
-
/** tiny utils */
|
|
7
|
-
export declare function hexToBytes(hex: string): Uint8Array;
|
package/src/script.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.scriptFromWotsPk = scriptFromWotsPk;
|
|
4
|
-
exports.wotsAddressFromKeypair = wotsAddressFromKeypair;
|
|
5
|
-
exports.hexToBytes = hexToBytes;
|
|
6
|
-
const util_1 = require("./util");
|
|
7
|
-
const derive_1 = require("./derive");
|
|
8
|
-
/** Produce KISSVM script that authorizes with a WOTS PK digest (32 bytes). */
|
|
9
|
-
function scriptFromWotsPk(pkDigest32) {
|
|
10
|
-
// Minima uses SHA3-256 in SIGNEDBY paths; here we embed the digest as hex.
|
|
11
|
-
return `RETURN SIGNEDBY(${(0, util_1.bytesToHex)(pkDigest32)})`;
|
|
12
|
-
}
|
|
13
|
-
/** Address from WOTS keypair (client-only): script -> MMR leaf -> Mx */
|
|
14
|
-
function wotsAddressFromKeypair(kp) {
|
|
15
|
-
const script = scriptFromWotsPk(kp.pk);
|
|
16
|
-
return (0, derive_1.scriptToAddress)(script);
|
|
17
|
-
}
|
|
18
|
-
/** tiny utils */
|
|
19
|
-
function hexToBytes(hex) {
|
|
20
|
-
let h = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
21
|
-
if (h.length % 2)
|
|
22
|
-
h = "0" + h;
|
|
23
|
-
const out = new Uint8Array(h.length / 2);
|
|
24
|
-
for (let i = 0; i < out.length; i++)
|
|
25
|
-
out[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
|
|
26
|
-
return out;
|
|
27
|
-
}
|
package/src/script.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { bytesToHex } from "./util.js";
|
|
2
|
-
import { scriptToAddress } from "./derive.js";
|
|
3
|
-
import type { WotsKeypair } from "./wots.js";
|
|
4
|
-
|
|
5
|
-
/** Produce KISSVM script that authorizes with a WOTS PK digest (32 bytes). */
|
|
6
|
-
export function scriptFromWotsPk(pkDigest32: Uint8Array): string {
|
|
7
|
-
// Minima uses SHA3-256 in SIGNEDBY paths; here we embed the digest as hex.
|
|
8
|
-
return `RETURN SIGNEDBY(${bytesToHex(pkDigest32)})`;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Address from WOTS keypair (client-only): script -> MMR leaf -> Mx */
|
|
12
|
-
export function wotsAddressFromKeypair(kp: WotsKeypair): string {
|
|
13
|
-
const script = scriptFromWotsPk(kp.pk);
|
|
14
|
-
return scriptToAddress(script);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** tiny utils */
|
|
18
|
-
export function hexToBytes(hex: string): Uint8Array {
|
|
19
|
-
let h = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
20
|
-
if (h.length % 2) h = "0" + h;
|
|
21
|
-
const out = new Uint8Array(h.length / 2);
|
|
22
|
-
for (let i = 0; i < out.length; i++) out[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
|
|
23
|
-
return out;
|
|
24
|
-
}
|