@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.
- package/dist/Streamable.d.ts +268 -0
- package/dist/Streamable.js +416 -0
- package/dist/adapters/index.d.ts +190 -0
- package/dist/adapters/index.js +107 -0
- package/dist/adapters/types.d.ts +149 -0
- package/dist/adapters/types.js +67 -0
- package/dist/binio.d.ts +9 -0
- package/dist/binio.js +30 -0
- package/dist/bip39.d.ts +91 -0
- package/dist/bip39.js +417 -0
- package/dist/derive.d.ts +2 -0
- package/dist/derive.js +9 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +53 -0
- package/dist/javaStreamables.d.ts +233 -0
- package/dist/javaStreamables.js +332 -0
- package/dist/lease/LeaseMonitor.d.ts +44 -0
- package/dist/lease/LeaseMonitor.js +107 -0
- package/dist/lease/LeaseStore.d.ts +57 -0
- package/dist/lease/LeaseStore.js +144 -0
- package/dist/lease/WatermarkStore.d.ts +61 -0
- package/dist/lease/WatermarkStore.js +202 -0
- package/dist/lease/index.d.ts +14 -0
- package/dist/lease/index.js +13 -0
- package/dist/lease-client.d.ts +31 -0
- package/dist/lease-client.js +39 -0
- package/dist/minima32.d.ts +18 -0
- package/dist/minima32.js +112 -0
- package/dist/minimaWireSerializer.d.ts +60 -0
- package/dist/minimaWireSerializer.js +71 -0
- package/dist/mmr.d.ts +156 -0
- package/dist/mmr.js +342 -0
- package/dist/mx.d.ts +1 -0
- package/dist/mx.js +1 -0
- package/dist/params.d.ts +29 -0
- package/dist/params.js +16 -0
- package/dist/script.d.ts +7 -0
- package/dist/script.js +22 -0
- package/dist/scripts/contract-helpers.d.ts +252 -0
- package/dist/scripts/contract-helpers.js +394 -0
- package/dist/scripts/dapp-types.d.ts +119 -0
- package/dist/scripts/dapp-types.js +72 -0
- package/dist/scripts/index.d.ts +4 -0
- package/dist/scripts/index.js +4 -0
- package/dist/scripts/types.d.ts +81 -0
- package/dist/scripts/types.js +145 -0
- package/dist/scripts/witness-serializer.d.ts +29 -0
- package/dist/scripts/witness-serializer.js +288 -0
- package/dist/transaction.d.ts +106 -0
- package/dist/transaction.js +284 -0
- package/dist/treekey.d.ts +383 -0
- package/dist/treekey.js +762 -0
- package/dist/tx/TransactionLifecycle.d.ts +55 -0
- package/dist/tx/TransactionLifecycle.js +189 -0
- package/dist/tx/TransactionReceiptStore.d.ts +36 -0
- package/dist/tx/TransactionReceiptStore.js +85 -0
- package/dist/tx/TransactionService.d.ts +50 -0
- package/dist/tx/TransactionService.js +178 -0
- package/dist/tx/index.d.ts +13 -0
- package/dist/tx/index.js +12 -0
- package/dist/tx/types.d.ts +114 -0
- package/dist/tx/types.js +5 -0
- package/dist/util.d.ts +1 -0
- package/dist/util.js +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +35 -0
- package/dist/verify.d.ts +18 -0
- package/dist/verify.js +162 -0
- package/dist/version.d.ts +12 -0
- package/dist/version.js +12 -0
- package/dist/wots.d.ts +232 -0
- package/dist/wots.js +538 -0
- package/package.json +65 -0
- package/src/Streamable.d.ts +265 -0
- package/src/Streamable.js +431 -0
- package/src/Streamable.ts +567 -0
- package/src/__tests__/Streamable.parity.test.ts +422 -0
- package/src/__tests__/perAddressDerivation.parity.test.ts +152 -0
- package/src/__tests__/wots-parity.test.ts +289 -0
- package/src/adapters/index.d.ts +190 -0
- package/src/adapters/index.js +117 -0
- package/src/adapters/index.ts +301 -0
- package/src/adapters/types.ts +235 -0
- package/src/addr.oracle.test.ts +25 -0
- package/src/address.test.ts +55 -0
- package/src/binio.ts +21 -0
- package/src/bip39.d.ts +91 -0
- package/src/bip39.js +427 -0
- package/src/bip39.ts +432 -0
- package/src/derive.d.ts +2 -0
- package/src/derive.js +13 -0
- package/src/derive.ts +11 -0
- package/src/index.d.ts +26 -0
- package/src/index.js +128 -0
- package/src/index.test.ts +5 -0
- package/src/index.ts +206 -0
- package/src/javaStreamables.d.ts +233 -0
- package/src/javaStreamables.js +328 -0
- package/src/javaStreamables.ts +403 -0
- package/src/lease/LeaseMonitor.d.ts +44 -0
- package/src/lease/LeaseMonitor.js +111 -0
- package/src/lease/LeaseMonitor.ts +157 -0
- package/src/lease/LeaseStore.d.ts +57 -0
- package/src/lease/LeaseStore.js +148 -0
- package/src/lease/LeaseStore.ts +203 -0
- package/src/lease/WatermarkStore.d.ts +61 -0
- package/src/lease/WatermarkStore.js +206 -0
- package/src/lease/WatermarkStore.ts +270 -0
- package/src/lease/index.d.ts +14 -0
- package/src/lease/index.js +22 -0
- package/src/lease/index.ts +41 -0
- package/src/lease-client.d.ts +31 -0
- package/src/lease-client.js +44 -0
- package/src/lease-client.ts +69 -0
- package/src/minima32.d.ts +10 -0
- package/src/minima32.js +113 -0
- package/src/minima32.test.ts +11 -0
- package/src/minima32.ts +127 -0
- package/src/minimaWireSerializer.d.ts +189 -0
- package/src/minimaWireSerializer.js +311 -0
- package/src/minimaWireSerializer.test.ts +466 -0
- package/src/minimaWireSerializer.ts +117 -0
- package/src/mmr.d.ts +153 -0
- package/src/mmr.js +351 -0
- package/src/mmr.oracle.test.ts +28 -0
- package/src/mmr.ts +429 -0
- package/src/mx.test.ts +25 -0
- package/src/mx.ts +1 -0
- package/src/params.d.ts +29 -0
- package/src/params.d.ts.map +1 -0
- package/src/params.js +20 -0
- package/src/params.js.map +1 -0
- package/src/params.ts +44 -0
- package/src/script.d.ts +7 -0
- package/src/script.js +27 -0
- package/src/script.ts +24 -0
- package/src/scripts/contract-helpers.ts +609 -0
- package/src/scripts/dapp-types.ts +240 -0
- package/src/scripts/index.ts +22 -0
- package/src/scripts/types.ts +247 -0
- package/src/scripts/witness-serializer.ts +384 -0
- package/src/transaction.ts +430 -0
- package/src/treekey.d.ts +383 -0
- package/src/treekey.js +778 -0
- package/src/treekey.parity.test.ts +459 -0
- package/src/treekey.ts +979 -0
- package/src/tx/TransactionLifecycle.d.ts +55 -0
- package/src/tx/TransactionLifecycle.js +188 -0
- package/src/tx/TransactionLifecycle.ts +280 -0
- package/src/tx/TransactionReceiptStore.d.ts +36 -0
- package/src/tx/TransactionReceiptStore.js +89 -0
- package/src/tx/TransactionReceiptStore.ts +112 -0
- package/src/tx/TransactionService.d.ts +31 -0
- package/src/tx/TransactionService.js +140 -0
- package/src/tx/TransactionService.ts +250 -0
- package/src/tx/index.d.ts +13 -0
- package/src/tx/index.js +20 -0
- package/src/tx/index.ts +44 -0
- package/src/tx/types.d.ts +80 -0
- package/src/tx/types.js +6 -0
- package/src/tx/types.ts +127 -0
- package/src/util.d.ts +1 -0
- package/src/util.js +5 -0
- package/src/util.ts +1 -0
- package/src/utils.d.ts +8 -0
- package/src/utils.js +42 -0
- package/src/utils.ts +38 -0
- package/src/verify.ts +209 -0
- package/src/version.ts +13 -0
- package/src/wots.d.ts +224 -0
- package/src/wots.d.ts.map +1 -0
- package/src/wots.js +559 -0
- package/src/wots.js.map +1 -0
- package/src/wots.ts +636 -0
- package/test-vectors.json +45 -0
package/dist/mmr.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
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';
|
|
4
|
+
import { serializeMiniNumber, serializeMiniData, javaHashAllObjects } from "./javaStreamables.js";
|
|
5
|
+
// Import canonical wire serialization from Streamable.ts (single source of truth)
|
|
6
|
+
import { writeMMRProof } from "./Streamable.js";
|
|
7
|
+
// Re-export for API compatibility (existing code imports from mmr.ts)
|
|
8
|
+
// Using a wrapper function for better compatibility with dynamic imports
|
|
9
|
+
export function serializeMMRProof(proof, blockTime = 0n) {
|
|
10
|
+
// Convert to Streamable format and serialize
|
|
11
|
+
const streamableProof = {
|
|
12
|
+
blockTime: blockTime,
|
|
13
|
+
chunks: proof.chunks.map(c => ({
|
|
14
|
+
isLeft: c.isLeft,
|
|
15
|
+
mmrData: { data: c.mmrData.data, value: c.mmrData.value }
|
|
16
|
+
}))
|
|
17
|
+
};
|
|
18
|
+
return writeMMRProof(streamableProof);
|
|
19
|
+
}
|
|
20
|
+
export const serializeRealMMRProof = serializeMMRProof;
|
|
21
|
+
/** SHA3-256 helper returning 32 bytes */
|
|
22
|
+
function sha3(data) {
|
|
23
|
+
return nobleSha3(data);
|
|
24
|
+
}
|
|
25
|
+
/** concat utility */
|
|
26
|
+
function concat(...parts) {
|
|
27
|
+
const total = parts.reduce((n, p) => n + p.length, 0);
|
|
28
|
+
const out = new Uint8Array(total);
|
|
29
|
+
let off = 0;
|
|
30
|
+
for (const p of parts) {
|
|
31
|
+
out.set(p, off);
|
|
32
|
+
off += p.length;
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/** big-endian u32 */
|
|
37
|
+
const u32be = (n) => new Uint8Array([(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff]);
|
|
38
|
+
/**
|
|
39
|
+
* EXACT MiniNumber.ZERO wire format from Java:
|
|
40
|
+
* scale (1 byte) = 0x00
|
|
41
|
+
* length (1 byte) = 0x01
|
|
42
|
+
* unscaled bytes (length=1) = 0x00
|
|
43
|
+
* => 00 01 00
|
|
44
|
+
*/
|
|
45
|
+
function encodeMiniNumberZERO() {
|
|
46
|
+
return new Uint8Array([0x00, 0x01, 0x00]);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* EXACT MiniString wire format (MiniData of UTF-8):
|
|
50
|
+
* 4-byte big-endian length
|
|
51
|
+
* raw UTF-8 bytes
|
|
52
|
+
*/
|
|
53
|
+
function encodeMiniStringUTF8(text) {
|
|
54
|
+
const utf8 = new TextEncoder().encode(text);
|
|
55
|
+
const len = u32be(utf8.length);
|
|
56
|
+
return concat(len, utf8);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Byte-exact one-leaf MMR leaf used by Minima Address.java path:
|
|
60
|
+
* sha3( MiniNumber.ZERO || MiniString(script) || MiniNumber.ZERO )
|
|
61
|
+
*/
|
|
62
|
+
export function mmrLeafExact(script) {
|
|
63
|
+
const zero = encodeMiniNumberZERO();
|
|
64
|
+
const mstr = encodeMiniStringUTF8(script);
|
|
65
|
+
const all = concat(zero, mstr, zero);
|
|
66
|
+
return sha3(all); // 32-byte leaf hash
|
|
67
|
+
}
|
|
68
|
+
/** In a single-leaf MMR the root equals the leaf commitment. */
|
|
69
|
+
export function mmrRootFromSingleLeaf(script) {
|
|
70
|
+
return mmrLeafExact(script);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create MMRData leaf node matching Minima's MMRData.CreateMMRDataLeafNode
|
|
74
|
+
*
|
|
75
|
+
* From MMRData.java:
|
|
76
|
+
* MiniData hash = Crypto.getInstance().hashAllObjects(MiniNumber.ZERO, zData, zSumValue);
|
|
77
|
+
*
|
|
78
|
+
* CRITICAL: Crypto.hashAllObjects uses writeDataStream for Streamables:
|
|
79
|
+
* - MiniNumber: scale + len + data (see serializeMiniNumber)
|
|
80
|
+
* - MiniData: 4-byte length + data (see serializeMiniData)
|
|
81
|
+
*
|
|
82
|
+
* For TreeKeyNode, zData is the Winternitz public key (MiniData) and zSumValue is ZERO
|
|
83
|
+
*
|
|
84
|
+
* Serialization order:
|
|
85
|
+
* 1. MiniNumber.ZERO: [0x00, 0x01, 0x00]
|
|
86
|
+
* 2. MiniData (pubkey): [4-byte length] + [bytes] (writeDataStream, NOT writeHashToStream)
|
|
87
|
+
* 3. MiniNumber.ZERO: [0x00, 0x01, 0x00]
|
|
88
|
+
*/
|
|
89
|
+
export function createMMRDataLeafNode(pubkey, sumValue = 0n) {
|
|
90
|
+
// Use cached ZERO serialization for efficiency
|
|
91
|
+
const zero = new Uint8Array([0x00, 0x01, 0x00]); // MiniNumber.ZERO
|
|
92
|
+
// IMPORTANT: hashAllObjects uses writeDataStream which is 4-byte length prefixed
|
|
93
|
+
const pubkeySerialized = serializeMiniData(pubkey);
|
|
94
|
+
// For sumValue, serialize as MiniNumber
|
|
95
|
+
let sumSerialized;
|
|
96
|
+
if (sumValue === 0n) {
|
|
97
|
+
sumSerialized = new Uint8Array([0x00, 0x01, 0x00]); // MiniNumber.ZERO
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
sumSerialized = serializeMiniNumber(Number(sumValue));
|
|
101
|
+
}
|
|
102
|
+
const hash = javaHashAllObjects(zero, pubkeySerialized, sumSerialized);
|
|
103
|
+
return { data: hash, value: sumValue };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create MMRData parent node matching Minima's MMRData.CreateMMRDataParentNode
|
|
107
|
+
*
|
|
108
|
+
* From MMRData.java:
|
|
109
|
+
* MiniNumber sumvalue = zLeft.getValue().add(zRight.getValue());
|
|
110
|
+
* MiniData combinedhash = Crypto.getInstance().hashAllObjects(
|
|
111
|
+
* MiniNumber.ONE, zLeft.getData(), zRight.getData(), sumvalue);
|
|
112
|
+
*
|
|
113
|
+
* CRITICAL: The getData() returns MiniData (the hash), which is serialized
|
|
114
|
+
* with writeDataStream (4-byte length) in hashAllObjects.
|
|
115
|
+
*
|
|
116
|
+
* Serialization order:
|
|
117
|
+
* 1. MiniNumber.ONE: [0x00, 0x01, 0x01]
|
|
118
|
+
* 2. MiniData (left.data): [4-byte length] + [bytes]
|
|
119
|
+
* 3. MiniData (right.data): [4-byte length] + [bytes]
|
|
120
|
+
* 4. MiniNumber (sumvalue): serialized MiniNumber
|
|
121
|
+
*/
|
|
122
|
+
export function createMMRDataParentNode(left, right) {
|
|
123
|
+
const sumValue = left.value + right.value;
|
|
124
|
+
// MiniNumber.ONE: [0x00, 0x01, 0x01]
|
|
125
|
+
const one = new Uint8Array([0x00, 0x01, 0x01]);
|
|
126
|
+
// Serialize left and right data as MiniData (4-byte length-prefixed)
|
|
127
|
+
const leftDataSerialized = serializeMiniData(left.data);
|
|
128
|
+
const rightDataSerialized = serializeMiniData(right.data);
|
|
129
|
+
// Serialize sum value
|
|
130
|
+
let sumSerialized;
|
|
131
|
+
if (sumValue === 0n) {
|
|
132
|
+
sumSerialized = new Uint8Array([0x00, 0x01, 0x00]); // MiniNumber.ZERO
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
sumSerialized = serializeMiniNumber(Number(sumValue));
|
|
136
|
+
}
|
|
137
|
+
const hash = javaHashAllObjects(one, leftDataSerialized, rightDataSerialized, sumSerialized);
|
|
138
|
+
return { data: hash, value: sumValue };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Simple MMR Tree for TreeKeyNode
|
|
142
|
+
* Builds a perfect binary tree from N entries (N must be power of 2 for simplicity)
|
|
143
|
+
*
|
|
144
|
+
* This matches TreeKeyNode.java which always uses 64 leaves (2^6)
|
|
145
|
+
*/
|
|
146
|
+
export class MMRTree {
|
|
147
|
+
constructor() {
|
|
148
|
+
this.entries = new Map();
|
|
149
|
+
this.leafCount = 0;
|
|
150
|
+
this.maxRow = 0;
|
|
151
|
+
}
|
|
152
|
+
getKey(row, entryNumber) {
|
|
153
|
+
return `${row}:${entryNumber}`;
|
|
154
|
+
}
|
|
155
|
+
setEntry(row, entryNumber, data) {
|
|
156
|
+
const entry = { row, entryNumber, mmrData: data };
|
|
157
|
+
this.entries.set(this.getKey(row, entryNumber), entry);
|
|
158
|
+
if (row > this.maxRow)
|
|
159
|
+
this.maxRow = row;
|
|
160
|
+
return entry;
|
|
161
|
+
}
|
|
162
|
+
getEntry(row, entryNumber) {
|
|
163
|
+
return this.entries.get(this.getKey(row, entryNumber));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Add a leaf entry to the MMR
|
|
167
|
+
* Matches MMR.java addEntry() but simplified for power-of-2 trees
|
|
168
|
+
*/
|
|
169
|
+
addLeaf(data) {
|
|
170
|
+
const entryNumber = BigInt(this.leafCount);
|
|
171
|
+
const entry = this.setEntry(0, entryNumber, data);
|
|
172
|
+
this.leafCount++;
|
|
173
|
+
// Propagate up the tree
|
|
174
|
+
let current = entry;
|
|
175
|
+
while (current.entryNumber % 2n === 1n) { // Is right child
|
|
176
|
+
const siblingNumber = current.entryNumber - 1n;
|
|
177
|
+
const sibling = this.getEntry(current.row, siblingNumber);
|
|
178
|
+
if (!sibling)
|
|
179
|
+
break;
|
|
180
|
+
// Create parent node
|
|
181
|
+
const parentData = createMMRDataParentNode(sibling.mmrData, current.mmrData);
|
|
182
|
+
const parentRow = current.row + 1;
|
|
183
|
+
const parentEntry = current.entryNumber / 2n;
|
|
184
|
+
current = this.setEntry(parentRow, parentEntry, parentData);
|
|
185
|
+
}
|
|
186
|
+
return entry;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Build tree from array of Winternitz public keys
|
|
190
|
+
* Used by TreeKeyNode to compute wallet public key
|
|
191
|
+
*/
|
|
192
|
+
static fromPublicKeys(pubkeys) {
|
|
193
|
+
const tree = new MMRTree();
|
|
194
|
+
for (const pk of pubkeys) {
|
|
195
|
+
const leafData = createMMRDataLeafNode(pk, 0n);
|
|
196
|
+
tree.addLeaf(leafData);
|
|
197
|
+
}
|
|
198
|
+
return tree;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get the root of the tree
|
|
202
|
+
* For a perfect binary tree with N leaves, root is at row log2(N), entry 0
|
|
203
|
+
*/
|
|
204
|
+
getRoot() {
|
|
205
|
+
if (this.leafCount === 0)
|
|
206
|
+
return null;
|
|
207
|
+
// For power of 2 leaves, there's exactly one peak at the top
|
|
208
|
+
const topRow = Math.floor(Math.log2(this.leafCount));
|
|
209
|
+
const root = this.getEntry(topRow, 0n);
|
|
210
|
+
return root?.mmrData || null;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get proof for a leaf at given index
|
|
214
|
+
* Matches MMR.java getProofToPeak()
|
|
215
|
+
*/
|
|
216
|
+
getProof(leafIndex) {
|
|
217
|
+
const chunks = [];
|
|
218
|
+
let row = 0;
|
|
219
|
+
let entryNumber = BigInt(leafIndex);
|
|
220
|
+
// Walk up the tree collecting siblings
|
|
221
|
+
while (row < this.maxRow) {
|
|
222
|
+
const siblingNumber = entryNumber % 2n === 0n
|
|
223
|
+
? entryNumber + 1n // Current is left, sibling is right
|
|
224
|
+
: entryNumber - 1n; // Current is right, sibling is left
|
|
225
|
+
const sibling = this.getEntry(row, siblingNumber);
|
|
226
|
+
if (!sibling)
|
|
227
|
+
break;
|
|
228
|
+
const isLeft = siblingNumber < entryNumber;
|
|
229
|
+
chunks.push({ isLeft, mmrData: sibling.mmrData });
|
|
230
|
+
// Move to parent
|
|
231
|
+
row++;
|
|
232
|
+
entryNumber = entryNumber / 2n;
|
|
233
|
+
}
|
|
234
|
+
return { chunks };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the leaf MMRData at a specific index
|
|
238
|
+
*/
|
|
239
|
+
getLeaf(index) {
|
|
240
|
+
const entry = this.getEntry(0, BigInt(index));
|
|
241
|
+
return entry?.mmrData || null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Calculate root from leaf data and proof
|
|
246
|
+
* Matches SignatureProof.getRootPublicKey() in Java
|
|
247
|
+
*
|
|
248
|
+
* From SignatureProof.java:
|
|
249
|
+
* MMRData pubentry = MMRData.CreateMMRDataLeafNode(mPublicKey, MiniNumber.ZERO);
|
|
250
|
+
* return mProof.calculateProof(pubentry).getData();
|
|
251
|
+
*/
|
|
252
|
+
export function calculateProofRoot(leafData, proof) {
|
|
253
|
+
let current = leafData;
|
|
254
|
+
for (const chunk of proof.chunks) {
|
|
255
|
+
if (chunk.isLeft) {
|
|
256
|
+
// Sibling is on the left, current is on the right
|
|
257
|
+
current = createMMRDataParentNode(chunk.mmrData, current);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Sibling is on the right, current is on the left
|
|
261
|
+
current = createMMRDataParentNode(current, chunk.mmrData);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return current.data;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Verify a proof: check that leaf + proof computes to expected root
|
|
268
|
+
*/
|
|
269
|
+
export function verifyMMRProof(leafPubkey, proof, expectedRoot) {
|
|
270
|
+
const leafData = createMMRDataLeafNode(leafPubkey, 0n);
|
|
271
|
+
const computedRoot = calculateProofRoot(leafData, proof);
|
|
272
|
+
if (computedRoot.length !== expectedRoot.length)
|
|
273
|
+
return false;
|
|
274
|
+
for (let i = 0; i < computedRoot.length; i++) {
|
|
275
|
+
if (computedRoot[i] !== expectedRoot[i])
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
// NOTE: serializeMMRProof is now imported from minimaWireSerializer.ts and re-exported above
|
|
281
|
+
// This consolidates wire format serialization into a single source of truth.
|
|
282
|
+
/**
|
|
283
|
+
* Deserialize MMRProof from bytes matching Minima's MMRProof.readDataStream()
|
|
284
|
+
*
|
|
285
|
+
* Format:
|
|
286
|
+
* 1. blockTime (MiniNumber)
|
|
287
|
+
* 2. chain length (MiniNumber)
|
|
288
|
+
* 3. Each chunk: isLeft (1 byte) + MMRData (hash with 4-byte length prefix + value MiniNumber)
|
|
289
|
+
*
|
|
290
|
+
* CRITICAL: Java MMRData.readDataStream uses mData.readHashFromStream() which reads
|
|
291
|
+
* a 4-byte big-endian length prefix followed by the hash bytes.
|
|
292
|
+
*
|
|
293
|
+
* @returns { proof: MMRProof, blockTime: bigint }
|
|
294
|
+
*/
|
|
295
|
+
export function parseMMRProofFromHex(data) {
|
|
296
|
+
let offset = 0;
|
|
297
|
+
// Read blockTime as MiniNumber
|
|
298
|
+
const { value: blockTime, bytesRead: btRead } = readMiniNumber(data, offset);
|
|
299
|
+
offset += btRead;
|
|
300
|
+
// Read chain length as MiniNumber
|
|
301
|
+
const { value: numChunks, bytesRead: ncRead } = readMiniNumber(data, offset);
|
|
302
|
+
offset += ncRead;
|
|
303
|
+
const chunks = [];
|
|
304
|
+
for (let i = 0; i < Number(numChunks); i++) {
|
|
305
|
+
// isLeft flag (1 byte)
|
|
306
|
+
const isLeft = data[offset] === 1;
|
|
307
|
+
offset += 1;
|
|
308
|
+
// MMRData: hash (4-byte length prefix + hash bytes) + value (MiniNumber)
|
|
309
|
+
// Read 4-byte big-endian length
|
|
310
|
+
const hashLength = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
|
|
311
|
+
offset += 4;
|
|
312
|
+
const hashData = data.slice(offset, offset + hashLength);
|
|
313
|
+
offset += hashLength;
|
|
314
|
+
const { value: chunkValue, bytesRead: cvRead } = readMiniNumber(data, offset);
|
|
315
|
+
offset += cvRead;
|
|
316
|
+
chunks.push({ isLeft, mmrData: { data: hashData, value: chunkValue } });
|
|
317
|
+
}
|
|
318
|
+
return { proof: { chunks }, blockTime };
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Read a MiniNumber from bytes at the given offset
|
|
322
|
+
* Format: [scale: 1 byte] [length: 1 byte] [data: N bytes]
|
|
323
|
+
*/
|
|
324
|
+
function readMiniNumber(data, offset) {
|
|
325
|
+
const scale = data[offset];
|
|
326
|
+
const length = data[offset + 1];
|
|
327
|
+
if (length === 0) {
|
|
328
|
+
// Zero value (shouldn't happen with proper serialization)
|
|
329
|
+
return { value: 0n, bytesRead: 2 };
|
|
330
|
+
}
|
|
331
|
+
const dataBytes = data.slice(offset + 2, offset + 2 + length);
|
|
332
|
+
// Convert bytes to bigint
|
|
333
|
+
let value = 0n;
|
|
334
|
+
for (let i = 0; i < dataBytes.length; i++) {
|
|
335
|
+
value = (value << 8n) | BigInt(dataBytes[i]);
|
|
336
|
+
}
|
|
337
|
+
// Apply scale if non-zero (for decimal numbers)
|
|
338
|
+
// For TreeKey MMR, scale is always 0
|
|
339
|
+
return { value, bytesRead: 2 + length };
|
|
340
|
+
}
|
|
341
|
+
/** @deprecated Use parseMMRProofFromHex */
|
|
342
|
+
export const deserializeMMRProof = parseMMRProofFromHex;
|
package/dist/mx.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { hexToMx, mxToHex, makeMinimaAddress, convertMinimaAddress, encodeMx, decodeMx } from './minima32.js';
|
package/dist/mx.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { hexToMx, mxToHex, makeMinimaAddress, convertMinimaAddress, encodeMx, decodeMx } from './minima32.js';
|
package/dist/params.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
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/dist/params.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const WOTS_MINIMA = {
|
|
2
|
+
name: 'minima',
|
|
3
|
+
n: 256,
|
|
4
|
+
w: 8,
|
|
5
|
+
L: 34,
|
|
6
|
+
messageSize: 32,
|
|
7
|
+
checksumSize: 14,
|
|
8
|
+
checksumDigits: 2,
|
|
9
|
+
maxDigit: 255,
|
|
10
|
+
};
|
|
11
|
+
// Legacy aliases for backwards compatibility during migration
|
|
12
|
+
export const WOTS_V1_DEV = WOTS_MINIMA;
|
|
13
|
+
export const WOTS_V2_SPEC = WOTS_MINIMA;
|
|
14
|
+
export function getParamSet(_env) {
|
|
15
|
+
return WOTS_MINIMA;
|
|
16
|
+
}
|
package/dist/script.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WotsKeypair } from "./wots.js";
|
|
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/dist/script.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { bytesToHex } from "./util.js";
|
|
2
|
+
import { scriptToAddress } from "./derive.js";
|
|
3
|
+
/** Produce KISSVM script that authorizes with a WOTS PK digest (32 bytes). */
|
|
4
|
+
export function scriptFromWotsPk(pkDigest32) {
|
|
5
|
+
// Minima uses SHA3-256 in SIGNEDBY paths; here we embed the digest as hex.
|
|
6
|
+
return `RETURN SIGNEDBY(${bytesToHex(pkDigest32)})`;
|
|
7
|
+
}
|
|
8
|
+
/** Address from WOTS keypair (client-only): script -> MMR leaf -> Mx */
|
|
9
|
+
export function wotsAddressFromKeypair(kp) {
|
|
10
|
+
const script = scriptFromWotsPk(kp.pk);
|
|
11
|
+
return scriptToAddress(script);
|
|
12
|
+
}
|
|
13
|
+
/** tiny utils */
|
|
14
|
+
export function hexToBytes(hex) {
|
|
15
|
+
let h = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
16
|
+
if (h.length % 2)
|
|
17
|
+
h = "0" + h;
|
|
18
|
+
const out = new Uint8Array(h.length / 2);
|
|
19
|
+
for (let i = 0; i < out.length; i++)
|
|
20
|
+
out[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract Helpers
|
|
3
|
+
*
|
|
4
|
+
* Provides high-level helpers for building specific contract types:
|
|
5
|
+
* - Timelock contracts
|
|
6
|
+
* - HTLC (Hashed Timelock Contracts)
|
|
7
|
+
* - MAST (Merkelized Abstract Syntax Trees)
|
|
8
|
+
* - Exchange contracts
|
|
9
|
+
* - Vault/covenant contracts
|
|
10
|
+
* - Flash/slow cash
|
|
11
|
+
* - Stateful game contracts
|
|
12
|
+
*
|
|
13
|
+
* Each helper validates inputs and builds the appropriate ScriptDescriptor
|
|
14
|
+
* for use with the EnhancedTransactionBuilder.
|
|
15
|
+
*/
|
|
16
|
+
import type { ScriptDescriptor, StateValue } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Timelock Helper
|
|
19
|
+
*
|
|
20
|
+
* Creates timelocked scripts that can only be spent after a certain block.
|
|
21
|
+
*/
|
|
22
|
+
export declare class TimelockHelper {
|
|
23
|
+
/**
|
|
24
|
+
* Create a timelock script that unlocks at a specific block.
|
|
25
|
+
*/
|
|
26
|
+
static createBlockTimelock(publicKey: string, unlockBlock: bigint): {
|
|
27
|
+
script: string;
|
|
28
|
+
address: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Create a timelock script based on coin age.
|
|
32
|
+
*/
|
|
33
|
+
static createCoinageTimelock(publicKey: string, minCoinAge: bigint): {
|
|
34
|
+
script: string;
|
|
35
|
+
address: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Check if a timelock is satisfied given current block.
|
|
39
|
+
*/
|
|
40
|
+
static isUnlocked(unlockBlock: bigint, currentBlock: bigint): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Build ScriptDescriptor for a timelock spend.
|
|
43
|
+
*/
|
|
44
|
+
static buildDescriptor(address: string, publicKey: string, unlockBlock: bigint): ScriptDescriptor;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* HTLC Helper
|
|
48
|
+
*
|
|
49
|
+
* Creates Hashed Timelock Contracts for atomic swaps and lightning-style payments.
|
|
50
|
+
*/
|
|
51
|
+
export declare class HTLCHelper {
|
|
52
|
+
/**
|
|
53
|
+
* Generate a random preimage and its hash.
|
|
54
|
+
*/
|
|
55
|
+
static generateSecret(): {
|
|
56
|
+
preimage: string;
|
|
57
|
+
hash: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Hash a preimage using SHA3 (default) or SHA2.
|
|
61
|
+
*/
|
|
62
|
+
static hashPreimage(preimage: string, algorithm?: 'sha3' | 'sha2'): string;
|
|
63
|
+
/**
|
|
64
|
+
* Verify a preimage matches a hash.
|
|
65
|
+
*/
|
|
66
|
+
static verifyPreimage(preimage: string, expectedHash: string, algorithm?: 'sha3' | 'sha2'): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Create an HTLC script.
|
|
69
|
+
*
|
|
70
|
+
* The script allows:
|
|
71
|
+
* - Recipient to claim with preimage before timeout
|
|
72
|
+
* - Sender to refund after timeout
|
|
73
|
+
*/
|
|
74
|
+
static createHTLC(senderPublicKey: string, recipientPublicKey: string, hashLock: string, timeoutBlock: bigint, algorithm?: 'sha3' | 'sha2'): {
|
|
75
|
+
script: string;
|
|
76
|
+
address: string;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Build ScriptDescriptor to claim HTLC with preimage.
|
|
80
|
+
*/
|
|
81
|
+
static buildClaimDescriptor(address: string, senderPublicKey: string, recipientPublicKey: string, hashLock: string, timeoutBlock: bigint, preimage: string): ScriptDescriptor;
|
|
82
|
+
/**
|
|
83
|
+
* Build ScriptDescriptor to refund HTLC after timeout.
|
|
84
|
+
*/
|
|
85
|
+
static buildRefundDescriptor(address: string, senderPublicKey: string, recipientPublicKey: string, hashLock: string, timeoutBlock: bigint): ScriptDescriptor;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* MAST Helper
|
|
89
|
+
*
|
|
90
|
+
* Creates Merkelized Abstract Syntax Tree contracts for privacy and scalability.
|
|
91
|
+
*/
|
|
92
|
+
export declare class MASTHelper {
|
|
93
|
+
/**
|
|
94
|
+
* Compute hash of a script for MAST leaf.
|
|
95
|
+
*/
|
|
96
|
+
static hashScript(script: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* Build a simple MAST tree from multiple scripts.
|
|
99
|
+
* Returns the root hash and proofs for each script.
|
|
100
|
+
*
|
|
101
|
+
* For a proper implementation, this should call the mmrcreate RPC.
|
|
102
|
+
* This is a simplified local version for 2 scripts.
|
|
103
|
+
*/
|
|
104
|
+
static buildSimpleTree(scripts: string[]): {
|
|
105
|
+
root: string;
|
|
106
|
+
proofs: Map<string, {
|
|
107
|
+
proof: string;
|
|
108
|
+
index: number;
|
|
109
|
+
}>;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Create a MAST script with the given root hash.
|
|
113
|
+
*/
|
|
114
|
+
static createMASTScript(rootHash: string): {
|
|
115
|
+
script: string;
|
|
116
|
+
address: string;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Build ScriptDescriptor for spending a MAST branch.
|
|
120
|
+
*/
|
|
121
|
+
static buildDescriptor(address: string, rootHash: string, branchScript: string, branchProof: string, wotsPublicKey?: string): ScriptDescriptor;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Exchange Contract Helper
|
|
125
|
+
*
|
|
126
|
+
* Creates DEX-style exchange contracts using VERIFYOUT.
|
|
127
|
+
*/
|
|
128
|
+
export declare class ExchangeHelper {
|
|
129
|
+
/**
|
|
130
|
+
* Create an exchange offer script.
|
|
131
|
+
*
|
|
132
|
+
* Owner can cancel, or anyone can take the offer by providing
|
|
133
|
+
* the specified output.
|
|
134
|
+
*/
|
|
135
|
+
static createOffer(ownerPublicKey: string, desiredAddress: string, desiredAmount: string, desiredTokenId: string): {
|
|
136
|
+
script: string;
|
|
137
|
+
address: string;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Build state variables for an exchange offer.
|
|
141
|
+
*/
|
|
142
|
+
static buildOfferState(ownerPublicKey: string, desiredAddress: string, desiredAmount: string, desiredTokenId: string): StateValue[];
|
|
143
|
+
/**
|
|
144
|
+
* Build ScriptDescriptor for taking an exchange offer.
|
|
145
|
+
*/
|
|
146
|
+
static buildTakeOfferDescriptor(address: string, ownerPublicKey: string, desiredAddress: string, desiredAmount: string, desiredTokenId: string): ScriptDescriptor;
|
|
147
|
+
/**
|
|
148
|
+
* Validate VERIFYOUT for an exchange transaction.
|
|
149
|
+
*/
|
|
150
|
+
static validateExchange(outputs: Array<{
|
|
151
|
+
address: string;
|
|
152
|
+
amount: string;
|
|
153
|
+
tokenId: string;
|
|
154
|
+
}>, expectedAddress: string, expectedAmount: string, expectedTokenId: string, inputIndex: number): {
|
|
155
|
+
valid: boolean;
|
|
156
|
+
error?: string;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Vault Helper
|
|
161
|
+
*
|
|
162
|
+
* Creates vault/covenant contracts with safe house enforcement.
|
|
163
|
+
*/
|
|
164
|
+
export declare class VaultHelper {
|
|
165
|
+
/**
|
|
166
|
+
* Generate safe house script from vault parameters.
|
|
167
|
+
*/
|
|
168
|
+
static generateSafeHouseScript(coldKey: string, hotKey: string, cooldownBlocks?: bigint): string;
|
|
169
|
+
/**
|
|
170
|
+
* Create a vault script.
|
|
171
|
+
*/
|
|
172
|
+
static createVault(coldKey: string, hotKey: string, cooldownBlocks?: bigint): {
|
|
173
|
+
vaultScript: string;
|
|
174
|
+
vaultAddress: string;
|
|
175
|
+
safeHouseScript: string;
|
|
176
|
+
safeHouseAddress: string;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Build state for vault withdrawal.
|
|
180
|
+
*/
|
|
181
|
+
static buildWithdrawalState(amount: string, recipientAddress: string): StateValue[];
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Flash Cash Helper
|
|
185
|
+
*
|
|
186
|
+
* Creates flash loan contracts for single-transaction borrowing.
|
|
187
|
+
*/
|
|
188
|
+
export declare class FlashCashHelper {
|
|
189
|
+
/**
|
|
190
|
+
* Create a flash cash contract.
|
|
191
|
+
*/
|
|
192
|
+
static createFlashCash(ownerPublicKey: string, interestMultiplier?: string): {
|
|
193
|
+
script: string;
|
|
194
|
+
address: string;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Calculate return amount with interest.
|
|
198
|
+
*/
|
|
199
|
+
static calculateReturn(borrowAmount: bigint, interestMultiplier: number): bigint;
|
|
200
|
+
/**
|
|
201
|
+
* Build ScriptDescriptor for borrowing flash cash.
|
|
202
|
+
*/
|
|
203
|
+
static buildBorrowDescriptor(address: string, ownerPublicKey: string, interestMultiplier?: string): ScriptDescriptor;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Slow Cash Helper
|
|
207
|
+
*
|
|
208
|
+
* Creates rate-limited withdrawal contracts.
|
|
209
|
+
*/
|
|
210
|
+
export declare class SlowCashHelper {
|
|
211
|
+
/**
|
|
212
|
+
* Create a slow cash contract.
|
|
213
|
+
*/
|
|
214
|
+
static createSlowCash(ownerPublicKey: string, withdrawalPercent?: string, cooldownBlocks?: bigint): {
|
|
215
|
+
script: string;
|
|
216
|
+
address: string;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Calculate withdrawal amount.
|
|
220
|
+
*/
|
|
221
|
+
static calculateWithdrawal(currentAmount: bigint, withdrawalPercent: number): {
|
|
222
|
+
withdrawal: bigint;
|
|
223
|
+
remaining: bigint;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Check if withdrawal is allowed based on coin age.
|
|
227
|
+
*/
|
|
228
|
+
static canWithdraw(coinAge: bigint, cooldownBlocks: bigint): boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Build ScriptDescriptor for slow cash withdrawal.
|
|
231
|
+
*/
|
|
232
|
+
static buildWithdrawalDescriptor(address: string, ownerPublicKey: string, withdrawalPercent?: string, cooldownBlocks?: bigint): ScriptDescriptor;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Stateful Game Helper
|
|
236
|
+
*
|
|
237
|
+
* Creates multi-round stateful contracts (like coin flip).
|
|
238
|
+
*/
|
|
239
|
+
export declare class StatefulGameHelper {
|
|
240
|
+
/**
|
|
241
|
+
* Create a round increment assertion.
|
|
242
|
+
*/
|
|
243
|
+
static createRoundCheck(): string;
|
|
244
|
+
/**
|
|
245
|
+
* Build state for next round.
|
|
246
|
+
*/
|
|
247
|
+
static buildNextRoundState(currentRound: number, preservedPorts: number[], newStates: StateValue[]): StateValue[];
|
|
248
|
+
/**
|
|
249
|
+
* Validate round progression.
|
|
250
|
+
*/
|
|
251
|
+
static validateRound(previousRound: number, currentRound: number): boolean;
|
|
252
|
+
}
|