@totemsdk/core 1.0.9 → 1.0.10
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.js +94 -64
- package/package.json +31 -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.d.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
export declare function serializeMMRProof(proof: {
|
|
2
|
-
chunks: Array<{
|
|
3
|
-
isLeft: boolean;
|
|
4
|
-
mmrData: {
|
|
5
|
-
data: Bytes;
|
|
6
|
-
value: bigint;
|
|
7
|
-
};
|
|
8
|
-
}>;
|
|
9
|
-
}, blockTime?: bigint): Bytes;
|
|
10
|
-
export declare const serializeRealMMRProof: typeof serializeMMRProof;
|
|
11
|
-
export type Bytes = Uint8Array;
|
|
12
|
-
/**
|
|
13
|
-
* Byte-exact one-leaf MMR leaf used by Minima Address.java path:
|
|
14
|
-
* sha3( MiniNumber.ZERO || MiniString(script) || MiniNumber.ZERO )
|
|
15
|
-
*/
|
|
16
|
-
export declare function mmrLeafExact(script: string): Bytes;
|
|
17
|
-
/** In a single-leaf MMR the root equals the leaf commitment. */
|
|
18
|
-
export declare function mmrRootFromSingleLeaf(script: string): Bytes;
|
|
19
|
-
/**
|
|
20
|
-
* MMRData structure matching Minima's MMRData.java
|
|
21
|
-
* Contains hash and value (for sum tree functionality)
|
|
22
|
-
*/
|
|
23
|
-
export interface MMRData {
|
|
24
|
-
data: Bytes;
|
|
25
|
-
value: bigint;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* MMREntry structure matching Minima's MMREntry.java
|
|
29
|
-
* Represents a node in the MMR at a specific row and position
|
|
30
|
-
*/
|
|
31
|
-
export interface MMREntry {
|
|
32
|
-
row: number;
|
|
33
|
-
entryNumber: bigint;
|
|
34
|
-
mmrData: MMRData;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* MMRProofChunk - one step in the proof path
|
|
38
|
-
* Matches Minima's MMRProof structure
|
|
39
|
-
*/
|
|
40
|
-
export interface MMRProofChunk {
|
|
41
|
-
isLeft: boolean;
|
|
42
|
-
mmrData: MMRData;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* MMRProof structure matching Minima's MMRProof.java
|
|
46
|
-
* Contains proof chunks to verify leaf membership in the tree
|
|
47
|
-
*/
|
|
48
|
-
export interface MMRProof {
|
|
49
|
-
chunks: MMRProofChunk[];
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Create MMRData leaf node matching Minima's MMRData.CreateMMRDataLeafNode
|
|
53
|
-
*
|
|
54
|
-
* From MMRData.java:
|
|
55
|
-
* MiniData hash = Crypto.getInstance().hashAllObjects(MiniNumber.ZERO, zData, zSumValue);
|
|
56
|
-
*
|
|
57
|
-
* CRITICAL: Crypto.hashAllObjects uses writeDataStream for Streamables:
|
|
58
|
-
* - MiniNumber: scale + len + data (see serializeMiniNumber)
|
|
59
|
-
* - MiniData: 4-byte length + data (see serializeMiniData)
|
|
60
|
-
*
|
|
61
|
-
* For TreeKeyNode, zData is the Winternitz public key (MiniData) and zSumValue is ZERO
|
|
62
|
-
*
|
|
63
|
-
* Serialization order:
|
|
64
|
-
* 1. MiniNumber.ZERO: [0x00, 0x01, 0x00]
|
|
65
|
-
* 2. MiniData (pubkey): [4-byte length] + [bytes] (writeDataStream, NOT writeHashToStream)
|
|
66
|
-
* 3. MiniNumber.ZERO: [0x00, 0x01, 0x00]
|
|
67
|
-
*/
|
|
68
|
-
export declare function createMMRDataLeafNode(pubkey: Bytes, sumValue?: bigint): MMRData;
|
|
69
|
-
/**
|
|
70
|
-
* Create MMRData parent node matching Minima's MMRData.CreateMMRDataParentNode
|
|
71
|
-
*
|
|
72
|
-
* From MMRData.java:
|
|
73
|
-
* MiniNumber sumvalue = zLeft.getValue().add(zRight.getValue());
|
|
74
|
-
* MiniData combinedhash = Crypto.getInstance().hashAllObjects(
|
|
75
|
-
* MiniNumber.ONE, zLeft.getData(), zRight.getData(), sumvalue);
|
|
76
|
-
*
|
|
77
|
-
* CRITICAL: The getData() returns MiniData (the hash), which is serialized
|
|
78
|
-
* with writeDataStream (4-byte length) in hashAllObjects.
|
|
79
|
-
*
|
|
80
|
-
* Serialization order:
|
|
81
|
-
* 1. MiniNumber.ONE: [0x00, 0x01, 0x01]
|
|
82
|
-
* 2. MiniData (left.data): [4-byte length] + [bytes]
|
|
83
|
-
* 3. MiniData (right.data): [4-byte length] + [bytes]
|
|
84
|
-
* 4. MiniNumber (sumvalue): serialized MiniNumber
|
|
85
|
-
*/
|
|
86
|
-
export declare function createMMRDataParentNode(left: MMRData, right: MMRData): MMRData;
|
|
87
|
-
/**
|
|
88
|
-
* Simple MMR Tree for TreeKeyNode
|
|
89
|
-
* Builds a perfect binary tree from N entries (N must be power of 2 for simplicity)
|
|
90
|
-
*
|
|
91
|
-
* This matches TreeKeyNode.java which always uses 64 leaves (2^6)
|
|
92
|
-
*/
|
|
93
|
-
export declare class MMRTree {
|
|
94
|
-
private entries;
|
|
95
|
-
private leafCount;
|
|
96
|
-
private maxRow;
|
|
97
|
-
private getKey;
|
|
98
|
-
private setEntry;
|
|
99
|
-
private getEntry;
|
|
100
|
-
/**
|
|
101
|
-
* Add a leaf entry to the MMR
|
|
102
|
-
* Matches MMR.java addEntry() but simplified for power-of-2 trees
|
|
103
|
-
*/
|
|
104
|
-
addLeaf(data: MMRData): MMREntry;
|
|
105
|
-
/**
|
|
106
|
-
* Build tree from array of Winternitz public keys
|
|
107
|
-
* Used by TreeKeyNode to compute wallet public key
|
|
108
|
-
*/
|
|
109
|
-
static fromPublicKeys(pubkeys: Bytes[]): MMRTree;
|
|
110
|
-
/**
|
|
111
|
-
* Get the root of the tree
|
|
112
|
-
* For a perfect binary tree with N leaves, root is at row log2(N), entry 0
|
|
113
|
-
*/
|
|
114
|
-
getRoot(): MMRData | null;
|
|
115
|
-
/**
|
|
116
|
-
* Get proof for a leaf at given index
|
|
117
|
-
* Matches MMR.java getProofToPeak()
|
|
118
|
-
*/
|
|
119
|
-
getProof(leafIndex: number): MMRProof;
|
|
120
|
-
/**
|
|
121
|
-
* Get the leaf MMRData at a specific index
|
|
122
|
-
*/
|
|
123
|
-
getLeaf(index: number): MMRData | null;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Calculate root from leaf data and proof
|
|
127
|
-
* Matches SignatureProof.getRootPublicKey() in Java
|
|
128
|
-
*
|
|
129
|
-
* From SignatureProof.java:
|
|
130
|
-
* MMRData pubentry = MMRData.CreateMMRDataLeafNode(mPublicKey, MiniNumber.ZERO);
|
|
131
|
-
* return mProof.calculateProof(pubentry).getData();
|
|
132
|
-
*/
|
|
133
|
-
export declare function calculateProofRoot(leafData: MMRData, proof: MMRProof): Bytes;
|
|
134
|
-
/**
|
|
135
|
-
* Verify a proof: check that leaf + proof computes to expected root
|
|
136
|
-
*/
|
|
137
|
-
export declare function verifyMMRProof(leafPubkey: Bytes, proof: MMRProof, expectedRoot: Bytes): boolean;
|
|
138
|
-
/**
|
|
139
|
-
* Deserialize MMRProof from bytes matching Minima's MMRProof.readDataStream()
|
|
140
|
-
*
|
|
141
|
-
* Format:
|
|
142
|
-
* 1. blockTime (MiniNumber)
|
|
143
|
-
* 2. chain length (MiniNumber)
|
|
144
|
-
* 3. Each chunk: isLeft (1 byte) + MMRData (hash with 4-byte length prefix + value MiniNumber)
|
|
145
|
-
*
|
|
146
|
-
* CRITICAL: Java MMRData.readDataStream uses mData.readHashFromStream() which reads
|
|
147
|
-
* a 4-byte big-endian length prefix followed by the hash bytes.
|
|
148
|
-
*
|
|
149
|
-
* @returns { proof: MMRProof, blockTime: bigint }
|
|
150
|
-
*/
|
|
151
|
-
export declare function parseMMRProofFromHex(data: Bytes): {
|
|
152
|
-
proof: MMRProof;
|
|
153
|
-
blockTime: bigint;
|
|
154
|
-
bytesRead: number;
|
|
155
|
-
};
|
|
156
|
-
/** @deprecated Use parseMMRProofFromHex */
|
|
157
|
-
export declare const deserializeMMRProof: typeof parseMMRProofFromHex;
|
package/src/mmr.js
DELETED
|
@@ -1,340 +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, 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
|
-
entries = new Map();
|
|
148
|
-
leafCount = 0;
|
|
149
|
-
maxRow = 0;
|
|
150
|
-
getKey(row, entryNumber) {
|
|
151
|
-
return `${row}:${entryNumber}`;
|
|
152
|
-
}
|
|
153
|
-
setEntry(row, entryNumber, data) {
|
|
154
|
-
const entry = { row, entryNumber, mmrData: data };
|
|
155
|
-
this.entries.set(this.getKey(row, entryNumber), entry);
|
|
156
|
-
if (row > this.maxRow)
|
|
157
|
-
this.maxRow = row;
|
|
158
|
-
return entry;
|
|
159
|
-
}
|
|
160
|
-
getEntry(row, entryNumber) {
|
|
161
|
-
return this.entries.get(this.getKey(row, entryNumber));
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Add a leaf entry to the MMR
|
|
165
|
-
* Matches MMR.java addEntry() but simplified for power-of-2 trees
|
|
166
|
-
*/
|
|
167
|
-
addLeaf(data) {
|
|
168
|
-
const entryNumber = BigInt(this.leafCount);
|
|
169
|
-
const entry = this.setEntry(0, entryNumber, data);
|
|
170
|
-
this.leafCount++;
|
|
171
|
-
// Propagate up the tree
|
|
172
|
-
let current = entry;
|
|
173
|
-
while (current.entryNumber % 2n === 1n) { // Is right child
|
|
174
|
-
const siblingNumber = current.entryNumber - 1n;
|
|
175
|
-
const sibling = this.getEntry(current.row, siblingNumber);
|
|
176
|
-
if (!sibling)
|
|
177
|
-
break;
|
|
178
|
-
// Create parent node
|
|
179
|
-
const parentData = createMMRDataParentNode(sibling.mmrData, current.mmrData);
|
|
180
|
-
const parentRow = current.row + 1;
|
|
181
|
-
const parentEntry = current.entryNumber / 2n;
|
|
182
|
-
current = this.setEntry(parentRow, parentEntry, parentData);
|
|
183
|
-
}
|
|
184
|
-
return entry;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Build tree from array of Winternitz public keys
|
|
188
|
-
* Used by TreeKeyNode to compute wallet public key
|
|
189
|
-
*/
|
|
190
|
-
static fromPublicKeys(pubkeys) {
|
|
191
|
-
const tree = new MMRTree();
|
|
192
|
-
for (const pk of pubkeys) {
|
|
193
|
-
const leafData = createMMRDataLeafNode(pk, 0n);
|
|
194
|
-
tree.addLeaf(leafData);
|
|
195
|
-
}
|
|
196
|
-
return tree;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Get the root of the tree
|
|
200
|
-
* For a perfect binary tree with N leaves, root is at row log2(N), entry 0
|
|
201
|
-
*/
|
|
202
|
-
getRoot() {
|
|
203
|
-
if (this.leafCount === 0)
|
|
204
|
-
return null;
|
|
205
|
-
// For power of 2 leaves, there's exactly one peak at the top
|
|
206
|
-
const topRow = Math.floor(Math.log2(this.leafCount));
|
|
207
|
-
const root = this.getEntry(topRow, 0n);
|
|
208
|
-
return root?.mmrData || null;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Get proof for a leaf at given index
|
|
212
|
-
* Matches MMR.java getProofToPeak()
|
|
213
|
-
*/
|
|
214
|
-
getProof(leafIndex) {
|
|
215
|
-
const chunks = [];
|
|
216
|
-
let row = 0;
|
|
217
|
-
let entryNumber = BigInt(leafIndex);
|
|
218
|
-
// Walk up the tree collecting siblings
|
|
219
|
-
while (row < this.maxRow) {
|
|
220
|
-
const siblingNumber = entryNumber % 2n === 0n
|
|
221
|
-
? entryNumber + 1n // Current is left, sibling is right
|
|
222
|
-
: entryNumber - 1n; // Current is right, sibling is left
|
|
223
|
-
const sibling = this.getEntry(row, siblingNumber);
|
|
224
|
-
if (!sibling)
|
|
225
|
-
break;
|
|
226
|
-
const isLeft = siblingNumber < entryNumber;
|
|
227
|
-
chunks.push({ isLeft, mmrData: sibling.mmrData });
|
|
228
|
-
// Move to parent
|
|
229
|
-
row++;
|
|
230
|
-
entryNumber = entryNumber / 2n;
|
|
231
|
-
}
|
|
232
|
-
return { chunks };
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Get the leaf MMRData at a specific index
|
|
236
|
-
*/
|
|
237
|
-
getLeaf(index) {
|
|
238
|
-
const entry = this.getEntry(0, BigInt(index));
|
|
239
|
-
return entry?.mmrData || null;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Calculate root from leaf data and proof
|
|
244
|
-
* Matches SignatureProof.getRootPublicKey() in Java
|
|
245
|
-
*
|
|
246
|
-
* From SignatureProof.java:
|
|
247
|
-
* MMRData pubentry = MMRData.CreateMMRDataLeafNode(mPublicKey, MiniNumber.ZERO);
|
|
248
|
-
* return mProof.calculateProof(pubentry).getData();
|
|
249
|
-
*/
|
|
250
|
-
export function calculateProofRoot(leafData, proof) {
|
|
251
|
-
let current = leafData;
|
|
252
|
-
for (const chunk of proof.chunks) {
|
|
253
|
-
if (chunk.isLeft) {
|
|
254
|
-
// Sibling is on the left, current is on the right
|
|
255
|
-
current = createMMRDataParentNode(chunk.mmrData, current);
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
// Sibling is on the right, current is on the left
|
|
259
|
-
current = createMMRDataParentNode(current, chunk.mmrData);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return current.data;
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Verify a proof: check that leaf + proof computes to expected root
|
|
266
|
-
*/
|
|
267
|
-
export function verifyMMRProof(leafPubkey, proof, expectedRoot) {
|
|
268
|
-
const leafData = createMMRDataLeafNode(leafPubkey, 0n);
|
|
269
|
-
const computedRoot = calculateProofRoot(leafData, proof);
|
|
270
|
-
if (computedRoot.length !== expectedRoot.length)
|
|
271
|
-
return false;
|
|
272
|
-
for (let i = 0; i < computedRoot.length; i++) {
|
|
273
|
-
if (computedRoot[i] !== expectedRoot[i])
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
// NOTE: serializeMMRProof is now imported from minimaWireSerializer.ts and re-exported above
|
|
279
|
-
// This consolidates wire format serialization into a single source of truth.
|
|
280
|
-
/**
|
|
281
|
-
* Deserialize MMRProof from bytes matching Minima's MMRProof.readDataStream()
|
|
282
|
-
*
|
|
283
|
-
* Format:
|
|
284
|
-
* 1. blockTime (MiniNumber)
|
|
285
|
-
* 2. chain length (MiniNumber)
|
|
286
|
-
* 3. Each chunk: isLeft (1 byte) + MMRData (hash with 4-byte length prefix + value MiniNumber)
|
|
287
|
-
*
|
|
288
|
-
* CRITICAL: Java MMRData.readDataStream uses mData.readHashFromStream() which reads
|
|
289
|
-
* a 4-byte big-endian length prefix followed by the hash bytes.
|
|
290
|
-
*
|
|
291
|
-
* @returns { proof: MMRProof, blockTime: bigint }
|
|
292
|
-
*/
|
|
293
|
-
export function parseMMRProofFromHex(data) {
|
|
294
|
-
let offset = 0;
|
|
295
|
-
// Read blockTime as MiniNumber
|
|
296
|
-
const { value: blockTime, bytesRead: btRead } = readMiniNumber(data, offset);
|
|
297
|
-
offset += btRead;
|
|
298
|
-
// Read chain length as MiniNumber
|
|
299
|
-
const { value: numChunks, bytesRead: ncRead } = readMiniNumber(data, offset);
|
|
300
|
-
offset += ncRead;
|
|
301
|
-
const chunks = [];
|
|
302
|
-
for (let i = 0; i < Number(numChunks); i++) {
|
|
303
|
-
// isLeft flag (1 byte)
|
|
304
|
-
const isLeft = data[offset] === 1;
|
|
305
|
-
offset += 1;
|
|
306
|
-
// MMRData: hash (4-byte length prefix + hash bytes) + value (MiniNumber)
|
|
307
|
-
// Read 4-byte big-endian length
|
|
308
|
-
const hashLength = (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
|
|
309
|
-
offset += 4;
|
|
310
|
-
const hashData = data.slice(offset, offset + hashLength);
|
|
311
|
-
offset += hashLength;
|
|
312
|
-
const { value: chunkValue, bytesRead: cvRead } = readMiniNumber(data, offset);
|
|
313
|
-
offset += cvRead;
|
|
314
|
-
chunks.push({ isLeft, mmrData: { data: hashData, value: chunkValue } });
|
|
315
|
-
}
|
|
316
|
-
return { proof: { chunks }, blockTime, bytesRead: offset };
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Read a MiniNumber from bytes at the given offset
|
|
320
|
-
* Format: [scale: 1 byte] [length: 1 byte] [data: N bytes]
|
|
321
|
-
*/
|
|
322
|
-
function readMiniNumber(data, offset) {
|
|
323
|
-
const scale = data[offset];
|
|
324
|
-
const length = data[offset + 1];
|
|
325
|
-
if (length === 0) {
|
|
326
|
-
// Zero value (shouldn't happen with proper serialization)
|
|
327
|
-
return { value: 0n, bytesRead: 2 };
|
|
328
|
-
}
|
|
329
|
-
const dataBytes = data.slice(offset + 2, offset + 2 + length);
|
|
330
|
-
// Convert bytes to bigint
|
|
331
|
-
let value = 0n;
|
|
332
|
-
for (let i = 0; i < dataBytes.length; i++) {
|
|
333
|
-
value = (value << 8n) | BigInt(dataBytes[i]);
|
|
334
|
-
}
|
|
335
|
-
// Apply scale if non-zero (for decimal numbers)
|
|
336
|
-
// For TreeKey MMR, scale is always 0
|
|
337
|
-
return { value, bytesRead: 2 + length };
|
|
338
|
-
}
|
|
339
|
-
/** @deprecated Use parseMMRProofFromHex */
|
|
340
|
-
export const deserializeMMRProof = parseMMRProofFromHex;
|
package/src/mmr.oracle.test.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { mmrRootFromSingleLeaf } from "./mmr";
|
|
3
|
-
import { bytesToHex } from "@noble/hashes/utils.js";
|
|
4
|
-
|
|
5
|
-
const enc = (s: string) => new TextEncoder().encode(s);
|
|
6
|
-
|
|
7
|
-
describe("MMR roots (single-leaf) – Minima oracles", () => {
|
|
8
|
-
it("RETURN TRUE", () => {
|
|
9
|
-
const root = mmrRootFromSingleLeaf("RETURN TRUE");
|
|
10
|
-
expect(bytesToHex(root).toUpperCase()).toBe(
|
|
11
|
-
"582AA40EA996419A3D8A5D26039A4E9584B746C2C26F860DAC8372BEA4457AF6"
|
|
12
|
-
);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("LET x 1 RETURN x == 1", () => {
|
|
16
|
-
const root = mmrRootFromSingleLeaf("LET x 1 RETURN x == 1");
|
|
17
|
-
expect(bytesToHex(root).toUpperCase()).toBe(
|
|
18
|
-
"B3FA26AEA3FFD4367C23DFFEB55C17BE75AE4A923E4A3A9E790CFEFC5A96FB93"
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("RETURN SIGNEDBY(0xFFFF)", () => {
|
|
23
|
-
const root = mmrRootFromSingleLeaf("RETURN SIGNEDBY(0xFFFF)");
|
|
24
|
-
expect(bytesToHex(root).toUpperCase()).toBe(
|
|
25
|
-
"65EC70E6DEF38CBC8E1556305F06637BF8D6B0EF5719C3CD9386981D250B9E95"
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
});
|