@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/src/tx/types.js
ADDED
package/src/tx/types.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Transaction Types
|
|
3
|
+
* Type definitions for transaction service
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface WotsIndices {
|
|
7
|
+
addressIndex: number;
|
|
8
|
+
l1: number;
|
|
9
|
+
l2: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PrepareRequest {
|
|
13
|
+
to: string;
|
|
14
|
+
amount: string;
|
|
15
|
+
tokenId?: string;
|
|
16
|
+
burn?: string;
|
|
17
|
+
txId?: string;
|
|
18
|
+
addressIndex?: number; // Required by v2-spec backend (0-63, must match the coin's address)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface PrepareResponse {
|
|
22
|
+
addressIndex: number;
|
|
23
|
+
l1: number;
|
|
24
|
+
l2: number;
|
|
25
|
+
leaseToken: string;
|
|
26
|
+
digestTx: string;
|
|
27
|
+
digestL2: string | null;
|
|
28
|
+
digestL3: string | null;
|
|
29
|
+
txId: string;
|
|
30
|
+
rootPublicKey: string;
|
|
31
|
+
paramSet: string;
|
|
32
|
+
leaseId: string;
|
|
33
|
+
leaseTTL: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SignRequest {
|
|
37
|
+
addressIndex: number;
|
|
38
|
+
l1: number;
|
|
39
|
+
l2: number;
|
|
40
|
+
digestTx: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Per-proof entry in a hierarchical TreeKey signature chain.
|
|
45
|
+
* Each proof represents one parent→child signing step.
|
|
46
|
+
*/
|
|
47
|
+
export interface SignatureProofHex {
|
|
48
|
+
leafPubkey: string; // 32-byte WOTS public key DIGEST as hex (SHA3-256 of full key)
|
|
49
|
+
signature: string; // 1088-byte WOTS signature as hex
|
|
50
|
+
mmrProof: string; // Serialized MMR proof as hex
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Hierarchical witness bundle produced by per-address TreeKey signing.
|
|
55
|
+
*
|
|
56
|
+
* Index mapping:
|
|
57
|
+
* addressIndex — which HD address (0-63)
|
|
58
|
+
* l1 — L1 index within per-address TreeKey (0-63)
|
|
59
|
+
* l2 — L2 index within per-address TreeKey (0-63)
|
|
60
|
+
*
|
|
61
|
+
* proofs contains 3 entries for depth-3 TreeKeys (Root→L1→L2→DATA),
|
|
62
|
+
* matching Minima's TreeKey.sign() exactly.
|
|
63
|
+
*/
|
|
64
|
+
export interface HierarchicalWitnessBundle {
|
|
65
|
+
addressIndex: number;
|
|
66
|
+
l1: number;
|
|
67
|
+
l2: number;
|
|
68
|
+
rootPublicKey: string;
|
|
69
|
+
proofs: SignatureProofHex[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @deprecated Use HierarchicalWitnessBundle. Kept for backward compatibility.
|
|
74
|
+
*/
|
|
75
|
+
export interface WitnessBundle {
|
|
76
|
+
addressIndex: number;
|
|
77
|
+
l1: number;
|
|
78
|
+
l2: number;
|
|
79
|
+
signatures: {
|
|
80
|
+
l1Proof: string[];
|
|
81
|
+
l2Proof: string[];
|
|
82
|
+
l3Proof: string[];
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface SignResult {
|
|
87
|
+
witnessBundle: HierarchicalWitnessBundle;
|
|
88
|
+
signedHex: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface FinalizeRequest {
|
|
92
|
+
leaseToken: string;
|
|
93
|
+
signedHex?: string; // Hex-encoded signed transaction
|
|
94
|
+
signedBase64?: string; // Base64-encoded signed transaction (preferred, bypasses WAF)
|
|
95
|
+
transactionHex?: string; // Optional: the unsigned transaction body
|
|
96
|
+
importId?: string; // Optional: client-provided import ID for txnimport
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface FinalizeResponse {
|
|
100
|
+
ok: boolean;
|
|
101
|
+
leaseId: string;
|
|
102
|
+
txpowid: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface TransactionMetadata {
|
|
106
|
+
to: string;
|
|
107
|
+
amount: string;
|
|
108
|
+
tokenId: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface TransactionReceipt {
|
|
112
|
+
txpowid: string;
|
|
113
|
+
timestamp: number;
|
|
114
|
+
to: string;
|
|
115
|
+
amount: string;
|
|
116
|
+
tokenId: string;
|
|
117
|
+
indices: WotsIndices;
|
|
118
|
+
status: 'confirmed' | 'pending' | 'failed';
|
|
119
|
+
txId?: string;
|
|
120
|
+
leaseId?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface TransactionError {
|
|
124
|
+
code: number;
|
|
125
|
+
message: string;
|
|
126
|
+
userMessage: string;
|
|
127
|
+
}
|
package/src/util.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const bytesToHex: (b: Uint8Array) => string;
|
package/src/util.js
ADDED
package/src/util.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const bytesToHex = (b: Uint8Array) => "0x" + [...b].map(x => x.toString(16).padStart(2, "0")).join("").toUpperCase();
|
package/src/utils.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for byte array manipulation
|
|
3
|
+
*/
|
|
4
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
5
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
6
|
+
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
|
|
7
|
+
export declare function utf8ToBytes(str: string): Uint8Array;
|
|
8
|
+
export declare function bytesToUtf8(bytes: Uint8Array): string;
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for byte array manipulation
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.bytesToHex = bytesToHex;
|
|
7
|
+
exports.hexToBytes = hexToBytes;
|
|
8
|
+
exports.concatBytes = concatBytes;
|
|
9
|
+
exports.utf8ToBytes = utf8ToBytes;
|
|
10
|
+
exports.bytesToUtf8 = bytesToUtf8;
|
|
11
|
+
function bytesToHex(bytes) {
|
|
12
|
+
return Array.from(bytes)
|
|
13
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
14
|
+
.join('');
|
|
15
|
+
}
|
|
16
|
+
function hexToBytes(hex) {
|
|
17
|
+
if (hex.startsWith('0x'))
|
|
18
|
+
hex = hex.slice(2);
|
|
19
|
+
if (hex.length % 2 !== 0)
|
|
20
|
+
throw new Error('Invalid hex string');
|
|
21
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
22
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
23
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
24
|
+
}
|
|
25
|
+
return bytes;
|
|
26
|
+
}
|
|
27
|
+
function concatBytes(...arrays) {
|
|
28
|
+
const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
|
|
29
|
+
const result = new Uint8Array(totalLength);
|
|
30
|
+
let offset = 0;
|
|
31
|
+
for (const arr of arrays) {
|
|
32
|
+
result.set(arr, offset);
|
|
33
|
+
offset += arr.length;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
function utf8ToBytes(str) {
|
|
38
|
+
return new TextEncoder().encode(str);
|
|
39
|
+
}
|
|
40
|
+
function bytesToUtf8(bytes) {
|
|
41
|
+
return new TextDecoder().decode(bytes);
|
|
42
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for byte array manipulation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function bytesToHex(bytes: Uint8Array): string {
|
|
6
|
+
return Array.from(bytes)
|
|
7
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
8
|
+
.join('');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function hexToBytes(hex: string): Uint8Array {
|
|
12
|
+
if (hex.startsWith('0x')) hex = hex.slice(2);
|
|
13
|
+
if (hex.length % 2 !== 0) throw new Error('Invalid hex string');
|
|
14
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
15
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
16
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
17
|
+
}
|
|
18
|
+
return bytes;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
|
22
|
+
const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
|
|
23
|
+
const result = new Uint8Array(totalLength);
|
|
24
|
+
let offset = 0;
|
|
25
|
+
for (const arr of arrays) {
|
|
26
|
+
result.set(arr, offset);
|
|
27
|
+
offset += arr.length;
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function utf8ToBytes(str: string): Uint8Array {
|
|
33
|
+
return new TextEncoder().encode(str);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function bytesToUtf8(bytes: Uint8Array): string {
|
|
37
|
+
return new TextDecoder().decode(bytes);
|
|
38
|
+
}
|
package/src/verify.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { sha3_256 } from '@noble/hashes/sha3';
|
|
2
|
+
import { verifyTreeSignature, deserializeTreeSignature } from './treekey.js';
|
|
3
|
+
import { scriptToAddress, addressToRoot } from './derive.js';
|
|
4
|
+
import { scriptFromWotsPk } from './script.js';
|
|
5
|
+
import { getRootPublicKey } from './treekey.js';
|
|
6
|
+
import { wotsVerifyDigest } from './wots.js';
|
|
7
|
+
import { getParamSet } from './params.js';
|
|
8
|
+
import type { TreeSignature, SignatureProof } from './treekey.js';
|
|
9
|
+
import type { Bytes } from './mmr.js';
|
|
10
|
+
|
|
11
|
+
export interface VerificationResult {
|
|
12
|
+
valid: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function normalizeHex(hex: string): string {
|
|
17
|
+
const stripped = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
18
|
+
return stripped.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function hexToBytes(hex: string): Uint8Array {
|
|
22
|
+
const h = normalizeHex(hex);
|
|
23
|
+
if (h.length % 2 !== 0) throw new Error('Invalid hex string: odd length');
|
|
24
|
+
const bytes = new Uint8Array(h.length / 2);
|
|
25
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
26
|
+
bytes[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16);
|
|
27
|
+
}
|
|
28
|
+
return bytes;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function bytesToHex(bytes: Uint8Array): string {
|
|
32
|
+
return Array.from(bytes)
|
|
33
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
34
|
+
.join('');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function deriveAddressFromPublicKey(publicKeyHex: string): string {
|
|
38
|
+
const pkBytes = hexToBytes(publicKeyHex);
|
|
39
|
+
if (pkBytes.length !== 32) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Public key must be 32 bytes (64 hex chars), got ${pkBytes.length} bytes`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
const script = scriptFromWotsPk(pkBytes);
|
|
45
|
+
return scriptToAddress(script);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function verifyTreeSignatureDetailed(
|
|
49
|
+
expectedPubkey: Bytes,
|
|
50
|
+
data: Bytes,
|
|
51
|
+
signature: TreeSignature
|
|
52
|
+
): VerificationResult {
|
|
53
|
+
const { proofs } = signature;
|
|
54
|
+
|
|
55
|
+
if (proofs.length === 0) {
|
|
56
|
+
return { valid: false, error: 'Signature contains no proofs' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const paramSet = getParamSet();
|
|
60
|
+
|
|
61
|
+
for (let depth = 0; depth < proofs.length; depth++) {
|
|
62
|
+
const proof = proofs[depth];
|
|
63
|
+
|
|
64
|
+
const rootPubkey = getRootPublicKey(proof);
|
|
65
|
+
|
|
66
|
+
if (depth === 0) {
|
|
67
|
+
if (!timingSafeEqual(expectedPubkey, rootPubkey)) {
|
|
68
|
+
return {
|
|
69
|
+
valid: false,
|
|
70
|
+
error: 'Root public key does not match expected public key (wrong key or address)',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let signedData: Bytes;
|
|
76
|
+
if (depth === proofs.length - 1) {
|
|
77
|
+
signedData = data;
|
|
78
|
+
} else {
|
|
79
|
+
signedData = getRootPublicKey(proofs[depth + 1]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!wotsVerifyDigest(proof.signature, signedData, proof.leafPubkey, paramSet)) {
|
|
83
|
+
return {
|
|
84
|
+
valid: false,
|
|
85
|
+
error: `WOTS signature verification failed at depth ${depth} (digest mismatch or corrupted signature)`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { valid: true };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function verifySignature(
|
|
94
|
+
address: string,
|
|
95
|
+
message: string,
|
|
96
|
+
signatureHex: string,
|
|
97
|
+
publicKeyHex: string
|
|
98
|
+
): boolean {
|
|
99
|
+
const result = verifySignatureDetailed(address, message, signatureHex, publicKeyHex);
|
|
100
|
+
return result.valid;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function verifySignatureDetailed(
|
|
104
|
+
address: string,
|
|
105
|
+
message: string,
|
|
106
|
+
signatureHex: string,
|
|
107
|
+
publicKeyHex: string
|
|
108
|
+
): VerificationResult {
|
|
109
|
+
try {
|
|
110
|
+
const pkBytes = hexToBytes(publicKeyHex);
|
|
111
|
+
if (pkBytes.length !== 32) {
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
error: `Public key must be 32 bytes (64 hex chars), got ${pkBytes.length} bytes`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const derivedAddress = deriveAddressFromPublicKey(publicKeyHex);
|
|
119
|
+
if (derivedAddress.toLowerCase() !== address.toLowerCase()) {
|
|
120
|
+
return {
|
|
121
|
+
valid: false,
|
|
122
|
+
error: `Public key does not match claimed address. Expected ${address}, derived ${derivedAddress}`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const sigBytes = hexToBytes(signatureHex);
|
|
127
|
+
let treeSignature: TreeSignature;
|
|
128
|
+
try {
|
|
129
|
+
treeSignature = deserializeTreeSignature(sigBytes);
|
|
130
|
+
} catch (e: any) {
|
|
131
|
+
return {
|
|
132
|
+
valid: false,
|
|
133
|
+
error: `Malformed signature: ${e.message}`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const digest = sha3_256(new TextEncoder().encode(message));
|
|
138
|
+
|
|
139
|
+
return verifyTreeSignatureDetailed(pkBytes, digest, treeSignature);
|
|
140
|
+
} catch (e: any) {
|
|
141
|
+
return {
|
|
142
|
+
valid: false,
|
|
143
|
+
error: `Verification error: ${e.message}`,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
149
|
+
if (a.length !== b.length) return false;
|
|
150
|
+
let result = 0;
|
|
151
|
+
for (let i = 0; i < a.length; i++) {
|
|
152
|
+
result |= a[i] ^ b[i];
|
|
153
|
+
}
|
|
154
|
+
return result === 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function cryptoRandomBytes(length: number): Uint8Array {
|
|
158
|
+
const buf = new Uint8Array(length);
|
|
159
|
+
if (typeof globalThis.crypto !== 'undefined' && globalThis.crypto.getRandomValues) {
|
|
160
|
+
globalThis.crypto.getRandomValues(buf);
|
|
161
|
+
} else {
|
|
162
|
+
try {
|
|
163
|
+
const { randomFillSync } = require('crypto');
|
|
164
|
+
randomFillSync(buf);
|
|
165
|
+
} catch {
|
|
166
|
+
throw new Error('No cryptographically secure RNG available');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return buf;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function createChallenge(domain: string, nonce?: string): string {
|
|
173
|
+
const ts = Date.now();
|
|
174
|
+
const n = nonce || bytesToHex(
|
|
175
|
+
sha3_256(new Uint8Array([...new TextEncoder().encode(`${domain}:${ts}:`), ...cryptoRandomBytes(16)]))
|
|
176
|
+
).slice(0, 16);
|
|
177
|
+
return JSON.stringify({ domain, nonce: n, issuedAt: ts });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function validateChallenge(
|
|
181
|
+
challenge: string,
|
|
182
|
+
options?: { maxAgeMs?: number; expectedDomain?: string }
|
|
183
|
+
): VerificationResult {
|
|
184
|
+
try {
|
|
185
|
+
const parsed = JSON.parse(challenge);
|
|
186
|
+
const { domain, nonce, issuedAt } = parsed;
|
|
187
|
+
|
|
188
|
+
if (!domain || !nonce || !issuedAt) {
|
|
189
|
+
return { valid: false, error: 'Challenge missing required fields (domain, nonce, issuedAt)' };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (options?.expectedDomain && domain !== options.expectedDomain) {
|
|
193
|
+
return { valid: false, error: `Domain mismatch: expected ${options.expectedDomain}, got ${domain}` };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const maxAge = options?.maxAgeMs ?? 5 * 60 * 1000;
|
|
197
|
+
const age = Date.now() - issuedAt;
|
|
198
|
+
if (age > maxAge) {
|
|
199
|
+
return { valid: false, error: `Challenge expired: ${Math.round(age / 1000)}s old (max ${Math.round(maxAge / 1000)}s)` };
|
|
200
|
+
}
|
|
201
|
+
if (age < 0) {
|
|
202
|
+
return { valid: false, error: 'Challenge issued in the future' };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { valid: true };
|
|
206
|
+
} catch {
|
|
207
|
+
return { valid: false, error: 'Invalid challenge format (not valid JSON)' };
|
|
208
|
+
}
|
|
209
|
+
}
|
package/src/version.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK Core Version Information
|
|
3
|
+
*
|
|
4
|
+
* CORE_BUILD_ID is used to detect bundle duplication issues where
|
|
5
|
+
* the extension might bundle two different copies of sdk-core,
|
|
6
|
+
* computing addresses with one copy and signing with another.
|
|
7
|
+
*
|
|
8
|
+
* If you see different CORE_BUILD_IDs logged from wallet creation
|
|
9
|
+
* vs signing modules, there's a bundling issue.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export const CORE_BUILD_ID = '2026.02.05-v1';
|
|
13
|
+
export const CORE_VERSION = '1.0.0';
|
package/src/wots.d.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WOTS Implementation - BouncyCastle Compatible
|
|
3
|
+
*
|
|
4
|
+
* This implementation matches BouncyCastle's WinternitzOTSignature exactly,
|
|
5
|
+
* which is used by Minima's Winternitz.java class.
|
|
6
|
+
*
|
|
7
|
+
* Key differences from standard WOTS/WOTS+:
|
|
8
|
+
* - w=8 means 8 BITS per digit (not base-8), so each byte is one digit (0-255)
|
|
9
|
+
* - Stateful PRNG (GMSSRandom) for chain seed derivation, not stateless H(i||seed)
|
|
10
|
+
* - 34 total chains (32 message + 2 checksum), not 89
|
|
11
|
+
* - Each chain hashed up to 255 times (2^8 - 1)
|
|
12
|
+
*
|
|
13
|
+
* Source: org.bouncycastle.pqc.crypto.gmss.util.{WinternitzOTSignature, WinternitzOTSVerify, GMSSRandom}
|
|
14
|
+
*/
|
|
15
|
+
import { ParamSet } from "./params";
|
|
16
|
+
import type { LoggerAdapter } from "./adapters";
|
|
17
|
+
/**
|
|
18
|
+
* Enable WOTS debug logging with a custom logger
|
|
19
|
+
* Use this for parity testing to capture all intermediate values
|
|
20
|
+
*
|
|
21
|
+
* WARNING: Debug logging outputs sensitive cryptographic material including
|
|
22
|
+
* private key chains and seeds. NEVER enable in production builds.
|
|
23
|
+
* This is intended for development/testing parity verification only.
|
|
24
|
+
*/
|
|
25
|
+
export declare function setWotsLogger(logger: LoggerAdapter): void;
|
|
26
|
+
/**
|
|
27
|
+
* Disable WOTS debug logging
|
|
28
|
+
*/
|
|
29
|
+
export declare function disableWotsLogger(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Check if WOTS debug logging is enabled
|
|
32
|
+
*/
|
|
33
|
+
export declare function isWotsDebugEnabled(): boolean;
|
|
34
|
+
export declare const F: (x: Uint8Array) => Uint8Array<ArrayBufferLike>;
|
|
35
|
+
export declare const hex: (u: Uint8Array) => string;
|
|
36
|
+
export declare const fromHex: (h: string) => Uint8Array<ArrayBuffer>;
|
|
37
|
+
export declare const concatBytes: (...arrs: Uint8Array[]) => Uint8Array<ArrayBuffer>;
|
|
38
|
+
export declare const u16be: (n: number) => Uint8Array<ArrayBuffer>;
|
|
39
|
+
export declare const u32be: (n: number) => Uint8Array<ArrayBuffer>;
|
|
40
|
+
export declare function assert32(u: Uint8Array, label?: string): void;
|
|
41
|
+
export declare const h: (x: Uint8Array) => Uint8Array<ArrayBufferLike>;
|
|
42
|
+
/**
|
|
43
|
+
* GMSSRandom - BouncyCastle's stateful PRNG for WOTS chain seed derivation
|
|
44
|
+
*
|
|
45
|
+
* Algorithm from GMSSRandom.java:
|
|
46
|
+
* rand = H(state)
|
|
47
|
+
* state = state + rand + 1 (byte-wise addition with carry)
|
|
48
|
+
* return rand
|
|
49
|
+
*
|
|
50
|
+
* The state is MUTATED in place after each call.
|
|
51
|
+
*/
|
|
52
|
+
export declare class GMSSRandom {
|
|
53
|
+
/**
|
|
54
|
+
* Generate next random value and update state
|
|
55
|
+
*
|
|
56
|
+
* @param state - 32-byte state array (MUTATED in place)
|
|
57
|
+
* @returns 32-byte random value
|
|
58
|
+
*/
|
|
59
|
+
static nextSeed(state: Uint8Array): Uint8Array;
|
|
60
|
+
/**
|
|
61
|
+
* Add two byte arrays: a = a + b (little-endian, with carry)
|
|
62
|
+
* Matches GMSSRandom.addByteArrays()
|
|
63
|
+
*/
|
|
64
|
+
private static addByteArrays;
|
|
65
|
+
/**
|
|
66
|
+
* Add one to byte array: a = a + 1 (little-endian, with carry)
|
|
67
|
+
* Matches GMSSRandom.addOne()
|
|
68
|
+
*/
|
|
69
|
+
private static addOne;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* getLog - Calculate ceil(log2(x))
|
|
73
|
+
*
|
|
74
|
+
* Matches WinternitzOTSignature.getLog():
|
|
75
|
+
* int log = 1;
|
|
76
|
+
* int i = 2;
|
|
77
|
+
* while (i < intValue) { i <<= 1; log++; }
|
|
78
|
+
* return log;
|
|
79
|
+
*/
|
|
80
|
+
export declare function getLog(intValue: number): number;
|
|
81
|
+
/**
|
|
82
|
+
* Expand master seed into L private key chains using GMSSRandom
|
|
83
|
+
*
|
|
84
|
+
* Matches WinternitzOTSignature constructor:
|
|
85
|
+
* byte[] dummy = new byte[mdsize];
|
|
86
|
+
* System.arraycopy(seed0, 0, dummy, 0, dummy.length);
|
|
87
|
+
* for (int i = 0; i < keysize; i++) {
|
|
88
|
+
* privateKeyOTS[i] = gmssRandom.nextSeed(dummy);
|
|
89
|
+
* }
|
|
90
|
+
*/
|
|
91
|
+
export declare function expandPrivateKey(seed: Uint8Array, ps?: ParamSet): Uint8Array[];
|
|
92
|
+
/**
|
|
93
|
+
* Convert message hash to Winternitz digits with checksum
|
|
94
|
+
*
|
|
95
|
+
* For w=8 (8 bits per digit), since 8 % 8 == 0:
|
|
96
|
+
* - Each byte of the hash IS one digit (0-255)
|
|
97
|
+
* - messagesize = 32 digits
|
|
98
|
+
* - checksum = (messagesize << w) - sum = 8192 - sum
|
|
99
|
+
* - checksumsize = 14 bits, extracted as 2 digits
|
|
100
|
+
*
|
|
101
|
+
* Matches WinternitzOTSignature.getSignature() for w=8 case
|
|
102
|
+
*/
|
|
103
|
+
export declare function toWinternitzDigits(hash32: Uint8Array, ps?: ParamSet): {
|
|
104
|
+
digits: number[];
|
|
105
|
+
checksumDigits: number[];
|
|
106
|
+
total: number;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Decompose digest into base-w digits + checksum
|
|
110
|
+
* Returns flat array of all L digits
|
|
111
|
+
*/
|
|
112
|
+
export declare function baseWWithChecksum(msgHash: Uint8Array, paramSet?: ParamSet): number[];
|
|
113
|
+
/**
|
|
114
|
+
* Hash a value k times
|
|
115
|
+
*
|
|
116
|
+
* Matches WinternitzOTSignature.hashPrivateKeyBlock():
|
|
117
|
+
* - If rounds < 1: return input unchanged
|
|
118
|
+
* - Otherwise: hash `rounds` times
|
|
119
|
+
*/
|
|
120
|
+
export declare function hashChain(x: Uint8Array, rounds: number): Uint8Array;
|
|
121
|
+
export type WotsKeypair = {
|
|
122
|
+
seed: Uint8Array;
|
|
123
|
+
index: number;
|
|
124
|
+
pk: Uint8Array;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Derive unique seed for a specific key index
|
|
128
|
+
*
|
|
129
|
+
* DEPRECATED: Use deriveChainSeedJava for Java parity.
|
|
130
|
+
* This function is preserved for backward compatibility only.
|
|
131
|
+
*
|
|
132
|
+
* The correct derivation matching Minima's TreeKeyNode.java:
|
|
133
|
+
* MiniData seed = Crypto.getInstance().hashAllObjects(new MiniNumber(i), zPrivateSeed);
|
|
134
|
+
*
|
|
135
|
+
* This old implementation used H(seed || u32be(i)) which is NOT Java compatible.
|
|
136
|
+
* @deprecated Use deriveChainSeedJava from javaStreamables.ts
|
|
137
|
+
*/
|
|
138
|
+
export declare function deriveIndexedSeed(seed: Uint8Array, keyIndex: number): Uint8Array;
|
|
139
|
+
/**
|
|
140
|
+
* Derive WOTS public key digest from seed and key index
|
|
141
|
+
*
|
|
142
|
+
* The key index is mixed into the seed first (deriveIndexedSeed),
|
|
143
|
+
* then expanded using GMSSRandom to get unique chain seeds.
|
|
144
|
+
*
|
|
145
|
+
* Matches WinternitzOTSignature.getPublicKey():
|
|
146
|
+
* int rounds = (1 << w) - 1; // 255 for w=8
|
|
147
|
+
* for (int i = 0; i < keysize; i++) {
|
|
148
|
+
* hashPrivateKeyBlock(i, rounds, buf, pos);
|
|
149
|
+
* }
|
|
150
|
+
* return H(buf);
|
|
151
|
+
*/
|
|
152
|
+
export declare function derivePKdigest(seed: Uint8Array, i: number, ps?: ParamSet): Uint8Array;
|
|
153
|
+
/**
|
|
154
|
+
* Derive full WOTS public key (L × 32 bytes) without final hash
|
|
155
|
+
*
|
|
156
|
+
* Returns the concatenation of all chain tops BEFORE hashing.
|
|
157
|
+
* Required by Minima's SignatureProof which expects the full public key.
|
|
158
|
+
*/
|
|
159
|
+
export declare function deriveFullPublicKey(seed: Uint8Array, i: number, ps?: ParamSet): Uint8Array;
|
|
160
|
+
export declare function wotsKeypairFromSeed(seed: Uint8Array, index: number, paramSet?: ParamSet): WotsKeypair;
|
|
161
|
+
export type WotsSignature = {
|
|
162
|
+
index: number;
|
|
163
|
+
w: number;
|
|
164
|
+
sig: Uint8Array[];
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Sign a message hash using WOTS
|
|
168
|
+
*
|
|
169
|
+
* Matches WinternitzOTSignature.getSignature() for w=8:
|
|
170
|
+
* for each digit d[i]:
|
|
171
|
+
* sig[i] = hash(privateKey[i], d[i] times)
|
|
172
|
+
*
|
|
173
|
+
* @returns Flat signature (L × 32 = 1088 bytes)
|
|
174
|
+
*/
|
|
175
|
+
export declare function wotsSign(seed: Uint8Array, i: number, msgHash: Uint8Array, ps?: ParamSet): Uint8Array;
|
|
176
|
+
/**
|
|
177
|
+
* Legacy wrapper returning structured signature
|
|
178
|
+
*/
|
|
179
|
+
export declare function wotsSignLegacy(msgHash: Uint8Array, seed: Uint8Array, index: number, paramSet?: ParamSet): WotsSignature;
|
|
180
|
+
/**
|
|
181
|
+
* Recover public key from signature
|
|
182
|
+
*
|
|
183
|
+
* Matches WinternitzOTSVerify.Verify():
|
|
184
|
+
* 1. Hash the message: hashedMsg = SHA3-256(message)
|
|
185
|
+
* 2. for each digit d[i]:
|
|
186
|
+
* top[i] = hash(sig[i], (255 - d[i]) times)
|
|
187
|
+
* 3. return H(concat(tops))
|
|
188
|
+
*
|
|
189
|
+
* CRITICAL: This function hashes the message internally to match Java/BouncyCastle
|
|
190
|
+
* Winternitz behavior. Both wotsSign and wotsPkFromSig hash internally for parity.
|
|
191
|
+
*/
|
|
192
|
+
export declare function wotsPkFromSig(message: Uint8Array, signature: WotsSignature, paramSet?: ParamSet): Uint8Array;
|
|
193
|
+
/**
|
|
194
|
+
* Verify WOTS signature against a FULL 1088-byte public key
|
|
195
|
+
*
|
|
196
|
+
* Matches Java's Winternitz.verify():
|
|
197
|
+
* 1. Recover FULL public key from signature using the msgHash (1088 bytes)
|
|
198
|
+
* 2. Compare FULL reconstructed key to expected FULL public key
|
|
199
|
+
*
|
|
200
|
+
* CRITICAL: Java's Winternitz.verify() compares the FULL 1088-byte reconstructed
|
|
201
|
+
* public key against the FULL 1088-byte stored public key, NOT a 32-byte digest!
|
|
202
|
+
*
|
|
203
|
+
* IMPORTANT: This function expects a PRE-HASHED message (32-byte digest),
|
|
204
|
+
* just like wotsSign. The caller should hash the message before passing it.
|
|
205
|
+
*
|
|
206
|
+
* @param sig - The 1088-byte WOTS signature
|
|
207
|
+
* @param msgHash - The pre-hashed message (32-byte digest)
|
|
208
|
+
* @param pkFull - The FULL 1088-byte WOTS public key (L=34 × 32 bytes)
|
|
209
|
+
* @param ps - WOTS parameter set (default: minima)
|
|
210
|
+
*/
|
|
211
|
+
export declare function wotsVerify(sig: Uint8Array, message: Uint8Array, pkFull: Uint8Array, ps?: ParamSet): boolean;
|
|
212
|
+
/**
|
|
213
|
+
* Legacy verify function that accepts a 32-byte digest
|
|
214
|
+
* @deprecated Use wotsVerify with full 1088-byte public key instead
|
|
215
|
+
*/
|
|
216
|
+
export declare function wotsVerifyDigest(sig: Uint8Array, message: Uint8Array, pkDigest: Uint8Array, ps?: ParamSet): boolean;
|
|
217
|
+
/**
|
|
218
|
+
* Generate WOTS public key from seed (convenience wrapper)
|
|
219
|
+
*/
|
|
220
|
+
export declare function wotsPublicKeyFromSeed(seed: Uint8Array, index?: number, paramSet?: ParamSet): Uint8Array;
|
|
221
|
+
/**
|
|
222
|
+
* @deprecated Use expandPrivateKey instead
|
|
223
|
+
*/
|
|
224
|
+
export declare function prfChainSeed(seed: Uint8Array, i: number, j: number, _paramSet: ParamSet): Uint8Array;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wots.d.ts","sourceRoot":"","sources":["wots.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGjD,eAAO,MAAM,CAAC,GAAI,GAAG,UAAU,gCAAgB,CAAC;AAChD,eAAO,MAAM,GAAG,GAAI,GAAG,UAAU,WAAmC,CAAC;AACrE,eAAO,MAAM,OAAO,GAAI,GAAG,MAAM,4BAA8D,CAAC;AAChG,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,UAAU,EAAE,4BAKhD,CAAC;AACF,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,4BAAiD,CAAC;AACjF,eAAO,MAAM,KAAK,GAAI,GAAG,MAAM,4BAAoE,CAAC;AACpG,wBAAgB,QAAQ,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,SAAQ,QAGpD;AAID,eAAO,MAAM,CAAC,MAlBO,UAAU,gCAkBb,CAAC;AAMnB;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAKnG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA2D/H;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,EAAE,CAepF;AAeD,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,UAAU,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,GAAE,QAAwB,GAAG,UAAU,CAYpG;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,WAAW,CAiBrG;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,UAAU,EAAE,CAAC;CACnB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,GAAE,QAAwB,GAAG,UAAU,CAcnH;AAGD,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,aAAa,CAQvH;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAoB5G;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,GAAE,QAAwB,GAAG,OAAO,CAe5H;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAG5H;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,GAAE,MAAU,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAI1G"}
|