@totemsdk/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/dist/Streamable.d.ts +268 -0
  2. package/dist/Streamable.js +416 -0
  3. package/dist/adapters/index.d.ts +190 -0
  4. package/dist/adapters/index.js +107 -0
  5. package/dist/adapters/types.d.ts +149 -0
  6. package/dist/adapters/types.js +67 -0
  7. package/dist/binio.d.ts +9 -0
  8. package/dist/binio.js +30 -0
  9. package/dist/bip39.d.ts +91 -0
  10. package/dist/bip39.js +417 -0
  11. package/dist/derive.d.ts +2 -0
  12. package/dist/derive.js +9 -0
  13. package/dist/index.d.ts +30 -0
  14. package/dist/index.js +53 -0
  15. package/dist/javaStreamables.d.ts +233 -0
  16. package/dist/javaStreamables.js +332 -0
  17. package/dist/lease/LeaseMonitor.d.ts +44 -0
  18. package/dist/lease/LeaseMonitor.js +107 -0
  19. package/dist/lease/LeaseStore.d.ts +57 -0
  20. package/dist/lease/LeaseStore.js +144 -0
  21. package/dist/lease/WatermarkStore.d.ts +61 -0
  22. package/dist/lease/WatermarkStore.js +202 -0
  23. package/dist/lease/index.d.ts +14 -0
  24. package/dist/lease/index.js +13 -0
  25. package/dist/lease-client.d.ts +31 -0
  26. package/dist/lease-client.js +39 -0
  27. package/dist/minima32.d.ts +18 -0
  28. package/dist/minima32.js +112 -0
  29. package/dist/minimaWireSerializer.d.ts +60 -0
  30. package/dist/minimaWireSerializer.js +71 -0
  31. package/dist/mmr.d.ts +156 -0
  32. package/dist/mmr.js +342 -0
  33. package/dist/mx.d.ts +1 -0
  34. package/dist/mx.js +1 -0
  35. package/dist/params.d.ts +29 -0
  36. package/dist/params.js +16 -0
  37. package/dist/script.d.ts +7 -0
  38. package/dist/script.js +22 -0
  39. package/dist/scripts/contract-helpers.d.ts +252 -0
  40. package/dist/scripts/contract-helpers.js +394 -0
  41. package/dist/scripts/dapp-types.d.ts +119 -0
  42. package/dist/scripts/dapp-types.js +72 -0
  43. package/dist/scripts/index.d.ts +4 -0
  44. package/dist/scripts/index.js +4 -0
  45. package/dist/scripts/types.d.ts +81 -0
  46. package/dist/scripts/types.js +145 -0
  47. package/dist/scripts/witness-serializer.d.ts +29 -0
  48. package/dist/scripts/witness-serializer.js +288 -0
  49. package/dist/transaction.d.ts +106 -0
  50. package/dist/transaction.js +284 -0
  51. package/dist/treekey.d.ts +383 -0
  52. package/dist/treekey.js +762 -0
  53. package/dist/tx/TransactionLifecycle.d.ts +55 -0
  54. package/dist/tx/TransactionLifecycle.js +189 -0
  55. package/dist/tx/TransactionReceiptStore.d.ts +36 -0
  56. package/dist/tx/TransactionReceiptStore.js +85 -0
  57. package/dist/tx/TransactionService.d.ts +50 -0
  58. package/dist/tx/TransactionService.js +178 -0
  59. package/dist/tx/index.d.ts +13 -0
  60. package/dist/tx/index.js +12 -0
  61. package/dist/tx/types.d.ts +114 -0
  62. package/dist/tx/types.js +5 -0
  63. package/dist/util.d.ts +1 -0
  64. package/dist/util.js +1 -0
  65. package/dist/utils.d.ts +8 -0
  66. package/dist/utils.js +35 -0
  67. package/dist/verify.d.ts +18 -0
  68. package/dist/verify.js +162 -0
  69. package/dist/version.d.ts +12 -0
  70. package/dist/version.js +12 -0
  71. package/dist/wots.d.ts +232 -0
  72. package/dist/wots.js +538 -0
  73. package/package.json +65 -0
  74. package/src/Streamable.d.ts +265 -0
  75. package/src/Streamable.js +431 -0
  76. package/src/Streamable.ts +567 -0
  77. package/src/__tests__/Streamable.parity.test.ts +422 -0
  78. package/src/__tests__/perAddressDerivation.parity.test.ts +152 -0
  79. package/src/__tests__/wots-parity.test.ts +289 -0
  80. package/src/adapters/index.d.ts +190 -0
  81. package/src/adapters/index.js +117 -0
  82. package/src/adapters/index.ts +301 -0
  83. package/src/adapters/types.ts +235 -0
  84. package/src/addr.oracle.test.ts +25 -0
  85. package/src/address.test.ts +55 -0
  86. package/src/binio.ts +21 -0
  87. package/src/bip39.d.ts +91 -0
  88. package/src/bip39.js +427 -0
  89. package/src/bip39.ts +432 -0
  90. package/src/derive.d.ts +2 -0
  91. package/src/derive.js +13 -0
  92. package/src/derive.ts +11 -0
  93. package/src/index.d.ts +26 -0
  94. package/src/index.js +128 -0
  95. package/src/index.test.ts +5 -0
  96. package/src/index.ts +206 -0
  97. package/src/javaStreamables.d.ts +233 -0
  98. package/src/javaStreamables.js +328 -0
  99. package/src/javaStreamables.ts +403 -0
  100. package/src/lease/LeaseMonitor.d.ts +44 -0
  101. package/src/lease/LeaseMonitor.js +111 -0
  102. package/src/lease/LeaseMonitor.ts +157 -0
  103. package/src/lease/LeaseStore.d.ts +57 -0
  104. package/src/lease/LeaseStore.js +148 -0
  105. package/src/lease/LeaseStore.ts +203 -0
  106. package/src/lease/WatermarkStore.d.ts +61 -0
  107. package/src/lease/WatermarkStore.js +206 -0
  108. package/src/lease/WatermarkStore.ts +270 -0
  109. package/src/lease/index.d.ts +14 -0
  110. package/src/lease/index.js +22 -0
  111. package/src/lease/index.ts +41 -0
  112. package/src/lease-client.d.ts +31 -0
  113. package/src/lease-client.js +44 -0
  114. package/src/lease-client.ts +69 -0
  115. package/src/minima32.d.ts +10 -0
  116. package/src/minima32.js +113 -0
  117. package/src/minima32.test.ts +11 -0
  118. package/src/minima32.ts +127 -0
  119. package/src/minimaWireSerializer.d.ts +189 -0
  120. package/src/minimaWireSerializer.js +311 -0
  121. package/src/minimaWireSerializer.test.ts +466 -0
  122. package/src/minimaWireSerializer.ts +117 -0
  123. package/src/mmr.d.ts +153 -0
  124. package/src/mmr.js +351 -0
  125. package/src/mmr.oracle.test.ts +28 -0
  126. package/src/mmr.ts +429 -0
  127. package/src/mx.test.ts +25 -0
  128. package/src/mx.ts +1 -0
  129. package/src/params.d.ts +29 -0
  130. package/src/params.d.ts.map +1 -0
  131. package/src/params.js +20 -0
  132. package/src/params.js.map +1 -0
  133. package/src/params.ts +44 -0
  134. package/src/script.d.ts +7 -0
  135. package/src/script.js +27 -0
  136. package/src/script.ts +24 -0
  137. package/src/scripts/contract-helpers.ts +609 -0
  138. package/src/scripts/dapp-types.ts +240 -0
  139. package/src/scripts/index.ts +22 -0
  140. package/src/scripts/types.ts +247 -0
  141. package/src/scripts/witness-serializer.ts +384 -0
  142. package/src/transaction.ts +430 -0
  143. package/src/treekey.d.ts +383 -0
  144. package/src/treekey.js +778 -0
  145. package/src/treekey.parity.test.ts +459 -0
  146. package/src/treekey.ts +979 -0
  147. package/src/tx/TransactionLifecycle.d.ts +55 -0
  148. package/src/tx/TransactionLifecycle.js +188 -0
  149. package/src/tx/TransactionLifecycle.ts +280 -0
  150. package/src/tx/TransactionReceiptStore.d.ts +36 -0
  151. package/src/tx/TransactionReceiptStore.js +89 -0
  152. package/src/tx/TransactionReceiptStore.ts +112 -0
  153. package/src/tx/TransactionService.d.ts +31 -0
  154. package/src/tx/TransactionService.js +140 -0
  155. package/src/tx/TransactionService.ts +250 -0
  156. package/src/tx/index.d.ts +13 -0
  157. package/src/tx/index.js +20 -0
  158. package/src/tx/index.ts +44 -0
  159. package/src/tx/types.d.ts +80 -0
  160. package/src/tx/types.js +6 -0
  161. package/src/tx/types.ts +127 -0
  162. package/src/util.d.ts +1 -0
  163. package/src/util.js +5 -0
  164. package/src/util.ts +1 -0
  165. package/src/utils.d.ts +8 -0
  166. package/src/utils.js +42 -0
  167. package/src/utils.ts +38 -0
  168. package/src/verify.ts +209 -0
  169. package/src/version.ts +13 -0
  170. package/src/wots.d.ts +224 -0
  171. package/src/wots.d.ts.map +1 -0
  172. package/src/wots.js +559 -0
  173. package/src/wots.js.map +1 -0
  174. package/src/wots.ts +636 -0
  175. package/test-vectors.json +45 -0
@@ -0,0 +1,466 @@
1
+ /**
2
+ * Unit tests for Minima Wire Format Serialization
3
+ *
4
+ * Tests byte-exact compatibility with Java Signature.java and SignatureProof.java
5
+ */
6
+ import { describe, it, expect } from 'vitest';
7
+
8
+ import {
9
+ serializeHierarchicalWitness,
10
+ serializeMiniNumber,
11
+ serializeMiniData,
12
+ fromHex,
13
+ toHex,
14
+ } from './minimaWireSerializer';
15
+
16
+ import {
17
+ writeMiniNumber,
18
+ writeMiniData,
19
+ writeHashToStream,
20
+ writeMMRData,
21
+ writeMMRProof,
22
+ writeSignatureProof,
23
+ writeSignature,
24
+ hexToBytes,
25
+ bytesToHex,
26
+ type MMRData,
27
+ type MMRProofChunk,
28
+ type MMRProof,
29
+ type SignatureProof,
30
+ type Signature,
31
+ } from './Streamable';
32
+
33
+ // Alias for test compatibility (tests use serialize* naming)
34
+ const serializeMMRData = writeMMRData;
35
+ const serializeMMRProof = writeMMRProof;
36
+ const serializeSignatureProof = writeSignatureProof;
37
+ const serializeSignature = writeSignature;
38
+
39
+ describe('MiniNumber Serialization', () => {
40
+ it('serializes zero correctly', () => {
41
+ const result = serializeMiniNumber(0n);
42
+ expect(Array.from(result)).toEqual([0, 1, 0x00]);
43
+ });
44
+
45
+ it('serializes small positive integers', () => {
46
+ const result = serializeMiniNumber(1n);
47
+ expect(result[0]).toBe(0); // scale
48
+ expect(result[1]).toBe(1); // length
49
+ expect(result[2]).toBe(1); // value
50
+ });
51
+
52
+ it('serializes count of 3 (typical for hierarchical proofs)', () => {
53
+ const result = serializeMiniNumber(3n);
54
+ expect(Array.from(result)).toEqual([0, 1, 3]);
55
+ });
56
+
57
+ it('serializes larger numbers with proper length', () => {
58
+ const result = serializeMiniNumber(256n);
59
+ expect(result[0]).toBe(0); // scale
60
+ expect(result[1]).toBe(2); // length (needs 2 bytes: 0x01 0x00)
61
+ expect(result[2]).toBe(1);
62
+ expect(result[3]).toBe(0);
63
+ });
64
+
65
+ it('adds leading zero for negative sign bit', () => {
66
+ const result = serializeMiniNumber(128n);
67
+ expect(result[0]).toBe(0); // scale
68
+ expect(result[1]).toBe(2); // length (0x80 has high bit set, needs 0x00 prefix)
69
+ expect(result[2]).toBe(0);
70
+ expect(result[3]).toBe(0x80);
71
+ });
72
+ });
73
+
74
+ describe('MiniData Serialization', () => {
75
+ it('serializes empty data with 4-byte zero length', () => {
76
+ const result = serializeMiniData(new Uint8Array(0));
77
+ expect(Array.from(result)).toEqual([0, 0, 0, 0]);
78
+ });
79
+
80
+ it('serializes 32-byte hash with proper length prefix', () => {
81
+ const hash = new Uint8Array(32).fill(0xAB);
82
+ const result = serializeMiniData(hash);
83
+
84
+ expect(result.length).toBe(4 + 32);
85
+ expect(result[0]).toBe(0);
86
+ expect(result[1]).toBe(0);
87
+ expect(result[2]).toBe(0);
88
+ expect(result[3]).toBe(32);
89
+ expect(result[4]).toBe(0xAB);
90
+ expect(result[35]).toBe(0xAB);
91
+ });
92
+
93
+ it('serializes WOTS signature (34*32 = 1088 bytes)', () => {
94
+ const signature = new Uint8Array(1088).fill(0xCD);
95
+ const result = serializeMiniData(signature);
96
+
97
+ expect(result.length).toBe(4 + 1088);
98
+ expect((result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3]).toBe(1088);
99
+ });
100
+ });
101
+
102
+ describe('Hash Stream Writing', () => {
103
+ it('writes 32-byte hash with 4-byte length prefix', () => {
104
+ const hash = new Uint8Array(32).fill(0xEF);
105
+ const result = writeHashToStream(hash);
106
+
107
+ // 4-byte prefix + 32 bytes = 36 bytes total
108
+ expect(result.length).toBe(36);
109
+ // First 4 bytes are big-endian length (32 = 0x00000020)
110
+ expect(result[0]).toBe(0);
111
+ expect(result[1]).toBe(0);
112
+ expect(result[2]).toBe(0);
113
+ expect(result[3]).toBe(32);
114
+ // Hash data follows
115
+ expect(result[4]).toBe(0xEF);
116
+ expect(result[35]).toBe(0xEF);
117
+ });
118
+
119
+ it('rejects inputs over 64 bytes (MINIMA_MAX_HASH_LENGTH)', () => {
120
+ expect(() => writeHashToStream(new Uint8Array(65))).toThrow('Hash too long');
121
+ });
122
+ });
123
+
124
+ describe('MMRData Serialization', () => {
125
+ it('serializes MMRData with hash (4-byte prefix) and value', () => {
126
+ const mmrData: MMRData = {
127
+ data: new Uint8Array(32).fill(0x11),
128
+ value: 1n
129
+ };
130
+
131
+ const result = serializeMMRData(mmrData);
132
+ // 4-byte length prefix + 32-byte hash + 3-byte MiniNumber = 39 bytes
133
+ expect(result.length).toBe(4 + 32 + 3);
134
+ // Length prefix (32 = 0x00000020)
135
+ expect(result[0]).toBe(0);
136
+ expect(result[1]).toBe(0);
137
+ expect(result[2]).toBe(0);
138
+ expect(result[3]).toBe(32);
139
+ // Hash data
140
+ expect(result[4]).toBe(0x11);
141
+ expect(result[35]).toBe(0x11);
142
+ // MiniNumber value (1)
143
+ expect(result[36]).toBe(0); // scale
144
+ expect(result[37]).toBe(1); // length
145
+ expect(result[38]).toBe(1); // value
146
+ });
147
+ });
148
+
149
+ describe('MMRProof Serialization', () => {
150
+ it('serializes empty proof', () => {
151
+ const proof: MMRProof = { blockTime: 0n, chunks: [] };
152
+ const result = serializeMMRProof(proof);
153
+
154
+ expect(result.length).toBe(6);
155
+ expect(Array.from(result.slice(0, 3))).toEqual([0, 1, 0]);
156
+ expect(Array.from(result.slice(3, 6))).toEqual([0, 1, 0]);
157
+ });
158
+
159
+ it('serializes proof with single chunk', () => {
160
+ const chunk: MMRProofChunk = {
161
+ isLeft: true,
162
+ mmrData: {
163
+ data: new Uint8Array(32).fill(0x22),
164
+ value: 0n
165
+ }
166
+ };
167
+ const proof: MMRProof = { blockTime: 0n, chunks: [chunk] };
168
+ const result = serializeMMRProof(proof);
169
+
170
+ const blockTimeLen = 3;
171
+ const countLen = 3;
172
+ // Chunk = 1 (isLeft) + 4 (hash length prefix) + 32 (hash) + 3 (MiniNumber.ZERO) = 40 bytes
173
+ const chunkLen = 1 + 4 + 32 + 3;
174
+ expect(result.length).toBe(blockTimeLen + countLen + chunkLen);
175
+
176
+ // isLeft byte
177
+ expect(result[blockTimeLen + countLen]).toBe(1);
178
+ // First byte of hash length prefix (0x00)
179
+ expect(result[blockTimeLen + countLen + 1]).toBe(0);
180
+ // Fifth byte is first byte of hash data
181
+ expect(result[blockTimeLen + countLen + 5]).toBe(0x22);
182
+ });
183
+
184
+ it('serializes isLeft correctly (0 or 1)', () => {
185
+ const leftChunk: MMRProofChunk = {
186
+ isLeft: true,
187
+ mmrData: { data: new Uint8Array(32), value: 0n }
188
+ };
189
+ const rightChunk: MMRProofChunk = {
190
+ isLeft: false,
191
+ mmrData: { data: new Uint8Array(32), value: 0n }
192
+ };
193
+
194
+ const leftProof = serializeMMRProof({ blockTime: 0n, chunks: [leftChunk] });
195
+ const rightProof = serializeMMRProof({ blockTime: 0n, chunks: [rightChunk] });
196
+
197
+ // isLeft byte is at offset 6 (after blockTime[3] + count[3])
198
+ expect(leftProof[6]).toBe(1);
199
+ expect(rightProof[6]).toBe(0);
200
+ });
201
+ });
202
+
203
+ describe('SignatureProof Serialization', () => {
204
+ it('serializes complete SignatureProof', () => {
205
+ const sigProof: SignatureProof = {
206
+ leafPubkey: new Uint8Array(32).fill(0xAA), // 32-byte WOTS digest
207
+ signature: new Uint8Array(1088).fill(0xBB),
208
+ mmrProof: { blockTime: 0n, chunks: [] }
209
+ };
210
+
211
+ const result = serializeSignatureProof(sigProof);
212
+
213
+ const pubKeyLen = 4 + 32; // 32-byte digest
214
+ const sigLen = 4 + 1088;
215
+ const proofLen = 6;
216
+ expect(result.length).toBe(pubKeyLen + sigLen + proofLen);
217
+
218
+ expect(result[0]).toBe(0);
219
+ expect(result[3]).toBe(32);
220
+ expect(result[4]).toBe(0xAA);
221
+
222
+ expect(result[pubKeyLen + 3]).toBe(0x20);
223
+ });
224
+ });
225
+
226
+ describe('Signature Serialization', () => {
227
+ it('serializes signature with 3 proofs (hierarchical TreeKey)', () => {
228
+ const createProof = (fill: number): SignatureProof => ({
229
+ leafPubkey: new Uint8Array(32).fill(fill), // 32-byte WOTS digest
230
+ signature: new Uint8Array(1088).fill(fill),
231
+ mmrProof: { blockTime: 0n, chunks: [] }
232
+ });
233
+
234
+ const signature: Signature = {
235
+ proofs: [createProof(0x11), createProof(0x22), createProof(0x33)]
236
+ };
237
+
238
+ const result = serializeSignature(signature);
239
+
240
+ expect(Array.from(result.slice(0, 3))).toEqual([0, 1, 3]);
241
+
242
+ const proofSize = 4 + 32 + 4 + 1088 + 6; // 32-byte digest, not 1088
243
+ expect(result.length).toBe(3 + 3 * proofSize);
244
+ });
245
+
246
+ it('serializes signature count correctly', () => {
247
+ const createProof = (): SignatureProof => ({
248
+ leafPubkey: new Uint8Array(32), // 32-byte WOTS digest
249
+ signature: new Uint8Array(1088),
250
+ mmrProof: { blockTime: 0n, chunks: [] }
251
+ });
252
+
253
+ const sig1: Signature = { proofs: [createProof()] };
254
+ const sig3: Signature = { proofs: [createProof(), createProof(), createProof()] };
255
+
256
+ expect(serializeSignature(sig1)[2]).toBe(1);
257
+ expect(serializeSignature(sig3)[2]).toBe(3);
258
+ });
259
+ });
260
+
261
+ describe('Hex Utilities', () => {
262
+ it('converts hex to bytes', () => {
263
+ const bytes = fromHex('0x0102030405');
264
+ expect(Array.from(bytes)).toEqual([1, 2, 3, 4, 5]);
265
+ });
266
+
267
+ it('converts bytes to hex with 0x prefix', () => {
268
+ const bytes = new Uint8Array([1, 2, 3, 4, 5]);
269
+ expect(toHex(bytes)).toBe('0x0102030405');
270
+ });
271
+
272
+ it('handles hex without 0x prefix', () => {
273
+ const bytes = fromHex('abcdef');
274
+ expect(Array.from(bytes)).toEqual([0xab, 0xcd, 0xef]);
275
+ });
276
+ });
277
+
278
+ describe('Hierarchical Witness Serialization', () => {
279
+ it('serializes complete hierarchical witness bundle', () => {
280
+ const emptyProof = '0x' + '00010000010000';
281
+
282
+ const bundle = {
283
+ addressIndex: 0,
284
+ l1: 0,
285
+ l2: 0,
286
+ rootPublicKey: '0x' + 'aa'.repeat(32),
287
+ proofs: [
288
+ {
289
+ leafPubkey: '0x' + '11'.repeat(32),
290
+ signature: '0x' + '22'.repeat(1088),
291
+ mmrProof: emptyProof
292
+ },
293
+ {
294
+ leafPubkey: '0x' + '33'.repeat(32),
295
+ signature: '0x' + '44'.repeat(1088),
296
+ mmrProof: emptyProof
297
+ },
298
+ {
299
+ leafPubkey: '0x' + '55'.repeat(32),
300
+ signature: '0x' + '66'.repeat(1088),
301
+ mmrProof: emptyProof
302
+ }
303
+ ]
304
+ };
305
+
306
+ const result = serializeHierarchicalWitness(bundle);
307
+
308
+ expect(result[0]).toBe(0);
309
+ expect(result[1]).toBe(1);
310
+ expect(result[2]).toBe(3);
311
+
312
+ expect(result.length).toBeGreaterThan(3300);
313
+ });
314
+ });
315
+
316
+ import {
317
+ serializeMMREntryNumber,
318
+ serializeMMREntry,
319
+ createMMREntryNumber,
320
+ serializeMMRData as serializeMMRDataJava,
321
+ type MMREntry as MMREntryType,
322
+ type MMREntryNumber as MMREntryNumberType,
323
+ type MMRData as MMRDataJavaType,
324
+ } from './javaStreamables';
325
+
326
+ describe('MMREntryNumber Serialization', () => {
327
+ it('serializes ZERO correctly', () => {
328
+ const entry: MMREntryNumberType = { scale: 0, unscaled: 0n };
329
+ const result = serializeMMREntryNumber(entry);
330
+ expect(Array.from(result)).toEqual([
331
+ 0, 1, 0, // MiniNumber(scale=0): scale(0) + len(1) + data(0)
332
+ 0, 0, 0, 1, 0 // MiniData(unscaled): 4-byte len(1) + data(0)
333
+ ]);
334
+ });
335
+
336
+ it('serializes small positive integers', () => {
337
+ const entry = createMMREntryNumber(63n);
338
+ const result = serializeMMREntryNumber(entry);
339
+ expect(result[0]).toBe(0); // scale byte
340
+ expect(result[1]).toBe(1); // length byte
341
+ expect(result[2]).toBe(0); // scale value = 0
342
+ expect(result.slice(3, 7)).toEqual(new Uint8Array([0, 0, 0, 1])); // MiniData length = 1
343
+ expect(result[7]).toBe(63); // unscaled value
344
+ });
345
+
346
+ it('serializes 128 with sign extension', () => {
347
+ const entry = createMMREntryNumber(128n);
348
+ const result = serializeMMREntryNumber(entry);
349
+ expect(result.slice(3, 7)).toEqual(new Uint8Array([0, 0, 0, 2])); // MiniData length = 2
350
+ expect(result[7]).toBe(0); // leading zero for sign
351
+ expect(result[8]).toBe(128); // value
352
+ });
353
+ });
354
+
355
+ describe('MMREntry Serialization', () => {
356
+ it('serializes complete MMREntry', () => {
357
+ const hash = new Uint8Array(32).fill(0xab);
358
+ const entry: MMREntryType = {
359
+ row: 0,
360
+ entryNumber: { scale: 0, unscaled: 5n },
361
+ mmrData: { data: hash, value: 0n }
362
+ };
363
+
364
+ const result = serializeMMREntry(entry);
365
+ expect(result[0]).toBe(0); // row scale
366
+ expect(result[1]).toBe(1); // row length
367
+ expect(result[2]).toBe(0); // row value = 0
368
+
369
+ expect(result.length).toBeGreaterThan(40);
370
+ });
371
+
372
+ it('serializes row 3 entry 10 correctly', () => {
373
+ const hash = new Uint8Array(32).fill(0xff);
374
+ const entry: MMREntryType = {
375
+ row: 3,
376
+ entryNumber: { scale: 0, unscaled: 10n },
377
+ mmrData: { data: hash, value: 0n }
378
+ };
379
+
380
+ const result = serializeMMREntry(entry);
381
+ expect(result[0]).toBe(0); // row scale
382
+ expect(result[1]).toBe(1); // row length
383
+ expect(result[2]).toBe(3); // row value = 3
384
+ });
385
+ });
386
+
387
+ describe('MMRData Java Serialization', () => {
388
+ it('serializes MMRData with hash and zero value', () => {
389
+ const hash = new Uint8Array(32).fill(0xcc);
390
+ const mmrData: MMRDataJavaType = { data: hash, value: 0n };
391
+
392
+ const result = serializeMMRDataJava(mmrData);
393
+ expect(result[0]).toBe(0); // 4-byte length prefix
394
+ expect(result[1]).toBe(0);
395
+ expect(result[2]).toBe(0);
396
+ expect(result[3]).toBe(32); // hash length
397
+ expect(result.slice(4, 36)).toEqual(hash);
398
+ expect(result.slice(36)).toEqual(new Uint8Array([0, 1, 0])); // MiniNumber.ZERO
399
+ });
400
+ });
401
+
402
+ describe('MMRProof round-trip', () => {
403
+ it('serializeMMRProof output can be parsed correctly', () => {
404
+ const hash1 = new Uint8Array(32).fill(0xaa);
405
+ const hash2 = new Uint8Array(32).fill(0xbb);
406
+ const proof: MMRProof = {
407
+ blockTime: 0n,
408
+ chunks: [
409
+ { isLeft: true, mmrData: { data: hash1, value: 0n } },
410
+ { isLeft: false, mmrData: { data: hash2, value: 0n } }
411
+ ]
412
+ };
413
+
414
+ const serialized = serializeMMRProof(proof);
415
+
416
+ expect(serialized[0]).toBe(0); // blockTime scale
417
+ expect(serialized[1]).toBe(1); // blockTime len
418
+ expect(serialized[2]).toBe(0); // blockTime value = 0
419
+ expect(serialized[3]).toBe(0); // chainLength scale
420
+ expect(serialized[4]).toBe(1); // chainLength len
421
+ expect(serialized[5]).toBe(2); // chainLength value = 2 (two chunks)
422
+
423
+ const chunkSize = 1 + 4 + 32 + 3; // isLeft + 4-byte prefix + 32-byte hash + MiniNumber.ZERO
424
+ expect(serialized.length).toBe(6 + chunkSize * 2);
425
+ });
426
+ });
427
+
428
+ describe('SDK consolidation verification', () => {
429
+ it('serializeMMRProof produces consistent output across calls', () => {
430
+ const hash1 = new Uint8Array(32);
431
+ for (let i = 0; i < 32; i++) hash1[i] = i;
432
+ const hash2 = new Uint8Array(32);
433
+ for (let i = 0; i < 32; i++) hash2[i] = 255 - i;
434
+ const hash3 = new Uint8Array(32).fill(0x42);
435
+
436
+ const proof: MMRProof = {
437
+ blockTime: 0n,
438
+ chunks: [
439
+ { isLeft: true, mmrData: { data: hash1, value: 0n } },
440
+ { isLeft: false, mmrData: { data: hash2, value: 100n } },
441
+ { isLeft: true, mmrData: { data: hash3, value: 1000000n } }
442
+ ]
443
+ };
444
+
445
+ // Verify serializeMMRProof produces consistent byte-exact output
446
+ const result1 = serializeMMRProof(proof);
447
+ const result2 = serializeMMRProof(proof);
448
+
449
+ expect(toHex(result1)).toBe(toHex(result2));
450
+ expect(result1.length).toBe(result2.length);
451
+
452
+ // Verify basic structure (exact size depends on value encoding)
453
+ // Minimum: blockTime(3) + chainLen(3) + 3 * (isLeft(1) + lengthPrefix(4) + hash(32) + value)
454
+ expect(result1.length).toBeGreaterThan(6 + 3 * (1 + 4 + 32)); // At least header + chunks without values
455
+ });
456
+
457
+ it('canonical serializer exports expected types', () => {
458
+ // Verify the exported types can be used correctly
459
+ const proof: MMRProof = { blockTime: 0n, chunks: [] };
460
+ const result = serializeMMRProof(proof);
461
+
462
+ // Empty proof should have: blockTime(3B) + chainLen(3B) = 6 bytes
463
+ expect(result.length).toBe(6);
464
+ expect(result[5]).toBe(0); // chainLen = 0
465
+ });
466
+ });
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Minima Wire Format Serializer
3
+ *
4
+ * High-level serialization for hex-string bundles (used in service.ts).
5
+ * Primitive serialization functions are imported from Streamable.ts.
6
+ *
7
+ * @deprecated (2026-01) - Most functions deprecated. Use Streamable.ts directly for
8
+ * byte-level serialization. This file remains for HierarchicalWitnessBundle hex APIs.
9
+ */
10
+
11
+ import {
12
+ concat,
13
+ hexToBytes,
14
+ bytesToHex,
15
+ writeMiniNumber,
16
+ writeMiniData,
17
+ type Bytes,
18
+ } from './Streamable.js';
19
+
20
+ // Re-export primitives for backward compatibility
21
+ export { writeMiniNumber as serializeMiniNumber, writeMiniData as serializeMiniData };
22
+ export { hexToBytes as fromHex, bytesToHex as toHex };
23
+ export type { Bytes };
24
+
25
+ /**
26
+ * SignatureProof with hex strings (for bundle transport)
27
+ */
28
+ export interface SignatureProofHex {
29
+ leafPubkey: string;
30
+ signature: string;
31
+ mmrProof: string;
32
+ }
33
+
34
+ /**
35
+ * HierarchicalWitnessBundle - used by service.ts for transaction signing
36
+ * All fields are hex strings for JSON transport
37
+ */
38
+ export interface HierarchicalWitnessBundle {
39
+ addressIndex: number;
40
+ l1: number;
41
+ l2: number;
42
+ rootPublicKey: string;
43
+ proofs: SignatureProofHex[];
44
+ }
45
+
46
+ /**
47
+ * Serialize a SignatureProof from pre-serialized byte arrays
48
+ *
49
+ * This avoids double-serialization by passing MMR proof bytes through directly.
50
+ * Format matches SignatureProof.writeDataStream():
51
+ * 1. MiniData (publicKey) - 4-byte length + bytes
52
+ * 2. MiniData (signature) - 4-byte length + bytes
53
+ * 3. MMRProof bytes (pre-serialized)
54
+ */
55
+ function serializeSignatureProofFromBytes(
56
+ publicKey: Bytes,
57
+ signature: Bytes,
58
+ mmrProofBytes: Bytes
59
+ ): Bytes {
60
+ return concat(
61
+ writeMiniData(publicKey),
62
+ writeMiniData(signature),
63
+ mmrProofBytes // Already serialized - pass through directly
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Serialize hierarchical witness bundle to Minima wire format
69
+ *
70
+ * This converts the HierarchicalWitnessBundle (with hex strings) to
71
+ * the proper Witness signature format matching Witness.java → Signature.java hierarchy.
72
+ *
73
+ * CRITICAL FIX (2026-01): Must use double-nesting to match Java format:
74
+ * - Witness.writeDataStream writes: [signatureCount][Signature[0].writeDataStream()]...
75
+ * - Signature.writeDataStream writes: [proofCount][SignatureProof[0]]...
76
+ *
77
+ * So the final format is:
78
+ * [1] = signatureCount (we have 1 Signature object in Witness)
79
+ * [N] = proofCount inside that Signature (2 for address-based TreeKey)
80
+ * [SignatureProof[0]]
81
+ * [SignatureProof[1]]
82
+ *
83
+ * IMPORTANT: Uses pre-serialized MMR proof bytes directly to avoid
84
+ * double-serialization (deserialize then re-serialize).
85
+ */
86
+ export function serializeHierarchicalWitness(bundle: HierarchicalWitnessBundle): Bytes {
87
+ const parts: Bytes[] = [];
88
+
89
+ // Witness.signatureCount (MiniNumber) - 1 Signature object containing N proofs
90
+ parts.push(writeMiniNumber(1n));
91
+
92
+ // Signature.proofCount (MiniNumber) - N SignatureProofs inside the Signature
93
+ parts.push(writeMiniNumber(BigInt(bundle.proofs.length)));
94
+
95
+ for (const proofHex of bundle.proofs) {
96
+ const publicKey = hexToBytes(proofHex.leafPubkey);
97
+ const signature = hexToBytes(proofHex.signature);
98
+ const mmrProofBytes = hexToBytes(proofHex.mmrProof); // Already serialized
99
+
100
+ // Serialize SignatureProof with pre-serialized MMR bytes
101
+ parts.push(serializeSignatureProofFromBytes(publicKey, signature, mmrProofBytes));
102
+ }
103
+
104
+ return concat(...parts);
105
+ }
106
+
107
+ /**
108
+ * @deprecated (2026-01) This function's output is NOT used in the main Totem transaction flow.
109
+ * MinimaTransactionBuilder.serializeWitness in the extension handles actual witness serialization.
110
+ * Kept for API compatibility and potential future direct SDK usage.
111
+ *
112
+ * Serialize hierarchical witness bundle to hex string for transport
113
+ */
114
+ export function serializeHierarchicalWitnessToHex(bundle: HierarchicalWitnessBundle): string {
115
+ const bytes = serializeHierarchicalWitness(bundle);
116
+ return bytesToHex(bytes);
117
+ }