@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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
function kissHex(hex) {
|
|
2
|
+
const raw = hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex;
|
|
3
|
+
return '0x' + raw.toUpperCase();
|
|
4
|
+
}
|
|
5
|
+
export function convertFlatChunkToSDK(chunk) {
|
|
6
|
+
return {
|
|
7
|
+
isLeft: chunk.isLeft,
|
|
8
|
+
mmrData: { data: chunk.data, value: 0n }
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function convertLegacyProofToSDK(legacy) {
|
|
12
|
+
return {
|
|
13
|
+
proof: {
|
|
14
|
+
chunks: legacy.proofChain.map(convertFlatChunkToSDK)
|
|
15
|
+
},
|
|
16
|
+
blockTime: legacy.blockTime
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function createEmptyMMRProof() {
|
|
20
|
+
return { chunks: [] };
|
|
21
|
+
}
|
|
22
|
+
export function createSignedByDescriptor(address, wotsRootPublicKey) {
|
|
23
|
+
return {
|
|
24
|
+
address,
|
|
25
|
+
scriptType: 'signedby',
|
|
26
|
+
script: `RETURN SIGNEDBY(${kissHex(wotsRootPublicKey)})`,
|
|
27
|
+
wotsRootPublicKey,
|
|
28
|
+
mastProof: createEmptyMMRProof(),
|
|
29
|
+
storeState: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function createMultisigDescriptor(address, publicKey1, publicKey2, ownPublicKey) {
|
|
33
|
+
return {
|
|
34
|
+
address,
|
|
35
|
+
scriptType: 'multisig',
|
|
36
|
+
script: `RETURN SIGNEDBY(${kissHex(publicKey1)}) AND SIGNEDBY(${kissHex(publicKey2)})`,
|
|
37
|
+
wotsRootPublicKey: ownPublicKey,
|
|
38
|
+
multisigKeys: [publicKey1, publicKey2],
|
|
39
|
+
multisigThreshold: 2,
|
|
40
|
+
mastProof: createEmptyMMRProof(),
|
|
41
|
+
storeState: false
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function createMofNMultisigDescriptor(address, threshold, publicKeys, ownPublicKey) {
|
|
45
|
+
const formattedKeys = publicKeys.map(pk => kissHex(pk)).join(' ');
|
|
46
|
+
return {
|
|
47
|
+
address,
|
|
48
|
+
scriptType: 'multisig_mofn',
|
|
49
|
+
script: `RETURN MULTISIG(${threshold} ${formattedKeys})`,
|
|
50
|
+
wotsRootPublicKey: ownPublicKey,
|
|
51
|
+
multisigKeys: publicKeys,
|
|
52
|
+
multisigThreshold: threshold,
|
|
53
|
+
mastProof: createEmptyMMRProof(),
|
|
54
|
+
storeState: false
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function createTimelockDescriptor(address, publicKey, unlockBlock) {
|
|
58
|
+
return {
|
|
59
|
+
address,
|
|
60
|
+
scriptType: 'timelock',
|
|
61
|
+
script: `RETURN SIGNEDBY(${kissHex(publicKey)}) AND @BLOCK GT ${unlockBlock}`,
|
|
62
|
+
wotsRootPublicKey: publicKey,
|
|
63
|
+
timelockBlock: unlockBlock,
|
|
64
|
+
mastProof: createEmptyMMRProof(),
|
|
65
|
+
storeState: false
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export function createHTLCDescriptor(address, ownerPublicKey, recipientPublicKey, hashLock, timeoutBlock, isOwner, preimage) {
|
|
69
|
+
const script = `IF @BLOCK GT ${timeoutBlock} AND SIGNEDBY(${kissHex(ownerPublicKey)}) THEN RETURN TRUE ENDIF RETURN (SIGNEDBY(${kissHex(recipientPublicKey)}) AND SHA3(STATE(1)) EQ ${kissHex(hashLock)})`;
|
|
70
|
+
const descriptor = {
|
|
71
|
+
address,
|
|
72
|
+
scriptType: 'htlc',
|
|
73
|
+
script,
|
|
74
|
+
wotsRootPublicKey: isOwner ? ownerPublicKey : recipientPublicKey,
|
|
75
|
+
htlcHash: hashLock,
|
|
76
|
+
timelockBlock: timeoutBlock,
|
|
77
|
+
mastProof: createEmptyMMRProof(),
|
|
78
|
+
storeState: false
|
|
79
|
+
};
|
|
80
|
+
if (preimage) {
|
|
81
|
+
descriptor.htlcPreimage = preimage;
|
|
82
|
+
descriptor.stateVariables = [{ port: 1, value: preimage, type: 'string' }];
|
|
83
|
+
}
|
|
84
|
+
return descriptor;
|
|
85
|
+
}
|
|
86
|
+
export function createMASTDescriptor(address, rootHash, branchScript, branchProof, wotsPublicKey) {
|
|
87
|
+
return {
|
|
88
|
+
address,
|
|
89
|
+
scriptType: 'mast',
|
|
90
|
+
script: `MAST ${kissHex(rootHash)}`,
|
|
91
|
+
wotsRootPublicKey: wotsPublicKey,
|
|
92
|
+
extraScripts: new Map([[branchScript, branchProof]]),
|
|
93
|
+
mastProof: createEmptyMMRProof(),
|
|
94
|
+
storeState: false
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function createExchangeDescriptor(address, ownerPublicKey, desiredAddress, desiredAmount, desiredTokenId) {
|
|
98
|
+
const ownerPk = kissHex(ownerPublicKey);
|
|
99
|
+
const script = `IF SIGNEDBY(PREVSTATE(0)) THEN RETURN TRUE ENDIF ASSERT VERIFYOUT(@INPUT PREVSTATE(1) PREVSTATE(2) PREVSTATE(3) TRUE) RETURN TRUE`;
|
|
100
|
+
return {
|
|
101
|
+
address,
|
|
102
|
+
scriptType: 'exchange',
|
|
103
|
+
script,
|
|
104
|
+
wotsRootPublicKey: ownerPublicKey,
|
|
105
|
+
stateVariables: [
|
|
106
|
+
{ port: 0, value: ownerPk, type: 'hex' },
|
|
107
|
+
{ port: 1, value: desiredAddress, type: 'hex' },
|
|
108
|
+
{ port: 2, value: desiredAmount, type: 'number' },
|
|
109
|
+
{ port: 3, value: desiredTokenId, type: 'hex' }
|
|
110
|
+
],
|
|
111
|
+
verifyOutExpectations: [{
|
|
112
|
+
inputIndex: '@INPUT',
|
|
113
|
+
outputAddress: desiredAddress,
|
|
114
|
+
amount: desiredAmount,
|
|
115
|
+
tokenId: desiredTokenId,
|
|
116
|
+
keepState: true
|
|
117
|
+
}],
|
|
118
|
+
mastProof: createEmptyMMRProof(),
|
|
119
|
+
storeState: true
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function createFlashCashDescriptor(address, ownerPublicKey, interestMultiplier = '1.01') {
|
|
123
|
+
const script = `IF SIGNEDBY(PREVSTATE(1)) THEN RETURN TRUE ENDIF ASSERT SAMESTATE(1 1) RETURN VERIFYOUT(@INPUT @ADDRESS @AMOUNT*${interestMultiplier} @TOKENID TRUE)`;
|
|
124
|
+
return {
|
|
125
|
+
address,
|
|
126
|
+
scriptType: 'flashcash',
|
|
127
|
+
script,
|
|
128
|
+
wotsRootPublicKey: ownerPublicKey,
|
|
129
|
+
stateVariables: [{ port: 1, value: ownerPublicKey, type: 'hex' }],
|
|
130
|
+
mastProof: createEmptyMMRProof(),
|
|
131
|
+
storeState: true
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function createSlowCashDescriptor(address, ownerPublicKey, withdrawalPercent = '0.9', cooldownBlocks = 10000n) {
|
|
135
|
+
const script = `IF @COINAGE LT ${cooldownBlocks} THEN RETURN FALSE ENDIF ASSERT SIGNEDBY(${kissHex(ownerPublicKey)}) AND VERIFYOUT(@INPUT @ADDRESS @AMOUNT*${withdrawalPercent} @TOKENID TRUE)`;
|
|
136
|
+
return {
|
|
137
|
+
address,
|
|
138
|
+
scriptType: 'slowcash',
|
|
139
|
+
script,
|
|
140
|
+
wotsRootPublicKey: ownerPublicKey,
|
|
141
|
+
timelockBlock: cooldownBlocks,
|
|
142
|
+
mastProof: createEmptyMMRProof(),
|
|
143
|
+
storeState: false
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MMRProof, MMRProofChunk } from '../mmr.js';
|
|
2
|
+
import type { ScriptDescriptor, StateValue, ExternalSignature, ScriptProofResult } from './types.js';
|
|
3
|
+
declare function concat(...arrays: Uint8Array[]): Uint8Array;
|
|
4
|
+
export declare function encodeMiniNumber(value: bigint, scale?: number): Uint8Array;
|
|
5
|
+
export declare function encodeMiniData(data: Uint8Array): Uint8Array;
|
|
6
|
+
export declare function encodeMiniString(str: string): Uint8Array;
|
|
7
|
+
export declare function serializeMMRProofChunk(chunk: MMRProofChunk): Uint8Array;
|
|
8
|
+
export declare function serializeRealMMRProof(proof: MMRProof, blockTime?: bigint): Uint8Array;
|
|
9
|
+
export declare function parseMMRProofFromHex(proofHex: string): {
|
|
10
|
+
proof: MMRProof;
|
|
11
|
+
blockTime: bigint;
|
|
12
|
+
};
|
|
13
|
+
export declare function serializeScriptProofWithProof(script: string, proof: MMRProof, blockTime?: bigint): Uint8Array;
|
|
14
|
+
export declare const STATETYPE_HEX = 1;
|
|
15
|
+
export declare const STATETYPE_NUMBER = 2;
|
|
16
|
+
export declare const STATETYPE_STRING = 4;
|
|
17
|
+
export declare const STATETYPE_BOOL = 8;
|
|
18
|
+
export declare function encodeStateValue(stateValue: StateValue): Uint8Array;
|
|
19
|
+
export declare function serializeStateVariables(stateValues: StateValue[]): Uint8Array;
|
|
20
|
+
export declare function buildScriptProofFromDescriptor(descriptor: ScriptDescriptor): ScriptProofResult;
|
|
21
|
+
export declare function deduplicateScriptDescriptors(descriptors: ScriptDescriptor[]): Map<string, ScriptDescriptor>;
|
|
22
|
+
export declare function serializeExtraScripts(extraScripts: Map<string, string>): Uint8Array;
|
|
23
|
+
export declare function aggregateSignatures(totemSignature: {
|
|
24
|
+
publicKey: Uint8Array;
|
|
25
|
+
signature: Uint8Array;
|
|
26
|
+
}, externalSignatures: ExternalSignature[]): Uint8Array[];
|
|
27
|
+
export declare function validateExternalSignature(signature: ExternalSignature, transactionDigest: Uint8Array): boolean;
|
|
28
|
+
export declare function computeScriptAddress(script: string): string;
|
|
29
|
+
export { concat };
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { sha3_256 } from '@noble/hashes/sha3';
|
|
2
|
+
import { serializeMMRProof } from '../mmr.js';
|
|
3
|
+
import { mxToHex } from '../minima32.js';
|
|
4
|
+
function hexToBytes(hex) {
|
|
5
|
+
const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
6
|
+
if (cleanHex.length % 2 !== 0) {
|
|
7
|
+
throw new Error('Invalid hex string');
|
|
8
|
+
}
|
|
9
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
10
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
11
|
+
bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
|
|
12
|
+
}
|
|
13
|
+
return bytes;
|
|
14
|
+
}
|
|
15
|
+
function normalizeHexValue(value) {
|
|
16
|
+
if (!value)
|
|
17
|
+
return '0x00';
|
|
18
|
+
const trimmed = value.trim();
|
|
19
|
+
if (trimmed.toLowerCase().startsWith('mx')) {
|
|
20
|
+
return mxToHex(trimmed);
|
|
21
|
+
}
|
|
22
|
+
return trimmed.startsWith('0x') ? trimmed : '0x' + trimmed;
|
|
23
|
+
}
|
|
24
|
+
function bytesToHex(bytes) {
|
|
25
|
+
return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
26
|
+
}
|
|
27
|
+
function concat(...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
|
+
export function encodeMiniNumber(value, scale = 0) {
|
|
38
|
+
if (typeof value !== 'bigint') {
|
|
39
|
+
throw new Error(`encodeMiniNumber requires bigint, got ${typeof value}`);
|
|
40
|
+
}
|
|
41
|
+
if (value === 0n) {
|
|
42
|
+
return new Uint8Array([scale & 0xff, 1, 0]);
|
|
43
|
+
}
|
|
44
|
+
if (value < 0n) {
|
|
45
|
+
throw new Error(`encodeMiniNumber does not support negative values: ${value}`);
|
|
46
|
+
}
|
|
47
|
+
let hex = value.toString(16);
|
|
48
|
+
if (hex.length % 2 !== 0) {
|
|
49
|
+
hex = '0' + hex;
|
|
50
|
+
}
|
|
51
|
+
let unscaledBytes = hexToBytes(hex);
|
|
52
|
+
if (unscaledBytes.length > 0 && (unscaledBytes[0] & 0x80) !== 0) {
|
|
53
|
+
const withLeadingZero = new Uint8Array(unscaledBytes.length + 1);
|
|
54
|
+
withLeadingZero[0] = 0x00;
|
|
55
|
+
withLeadingZero.set(unscaledBytes, 1);
|
|
56
|
+
unscaledBytes = withLeadingZero;
|
|
57
|
+
}
|
|
58
|
+
const length = unscaledBytes.length;
|
|
59
|
+
if (length > 32) {
|
|
60
|
+
throw new Error(`MiniNumber data too large: ${length} bytes (max 32)`);
|
|
61
|
+
}
|
|
62
|
+
const result = new Uint8Array(1 + 1 + length);
|
|
63
|
+
result[0] = scale & 0xff;
|
|
64
|
+
result[1] = length & 0xff;
|
|
65
|
+
result.set(unscaledBytes, 2);
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
export function encodeMiniData(data) {
|
|
69
|
+
const length = data.length;
|
|
70
|
+
const lengthBytes = new Uint8Array([
|
|
71
|
+
(length >> 24) & 0xff,
|
|
72
|
+
(length >> 16) & 0xff,
|
|
73
|
+
(length >> 8) & 0xff,
|
|
74
|
+
length & 0xff
|
|
75
|
+
]);
|
|
76
|
+
return concat(lengthBytes, data);
|
|
77
|
+
}
|
|
78
|
+
export function encodeMiniString(str) {
|
|
79
|
+
const utf8Bytes = new TextEncoder().encode(str);
|
|
80
|
+
return encodeMiniData(utf8Bytes);
|
|
81
|
+
}
|
|
82
|
+
function encodeMiniByte(value) {
|
|
83
|
+
return new Uint8Array([value ? 1 : 0]);
|
|
84
|
+
}
|
|
85
|
+
export function serializeMMRProofChunk(chunk) {
|
|
86
|
+
const parts = [];
|
|
87
|
+
parts.push(encodeMiniByte(chunk.isLeft));
|
|
88
|
+
parts.push(encodeMiniData(chunk.mmrData.data));
|
|
89
|
+
parts.push(encodeMiniNumber(chunk.mmrData.value));
|
|
90
|
+
return concat(...parts);
|
|
91
|
+
}
|
|
92
|
+
export function serializeRealMMRProof(proof, blockTime = 0n) {
|
|
93
|
+
return serializeMMRProof(proof, blockTime);
|
|
94
|
+
}
|
|
95
|
+
export function parseMMRProofFromHex(proofHex) {
|
|
96
|
+
const bytes = hexToBytes(proofHex);
|
|
97
|
+
let offset = 0;
|
|
98
|
+
const { value: blockTime, bytesRead: btRead } = readMiniNumber(bytes, offset);
|
|
99
|
+
offset += btRead;
|
|
100
|
+
const { value: chainLength, bytesRead: clRead } = readMiniNumber(bytes, offset);
|
|
101
|
+
offset += clRead;
|
|
102
|
+
const chunks = [];
|
|
103
|
+
for (let i = 0; i < Number(chainLength); i++) {
|
|
104
|
+
const { chunk, bytesRead } = readMMRProofChunk(bytes, offset);
|
|
105
|
+
chunks.push(chunk);
|
|
106
|
+
offset += bytesRead;
|
|
107
|
+
}
|
|
108
|
+
return { proof: { chunks }, blockTime };
|
|
109
|
+
}
|
|
110
|
+
function readMiniNumber(bytes, offset) {
|
|
111
|
+
const scale = bytes[offset];
|
|
112
|
+
const length = (bytes[offset + 1] << 24) | (bytes[offset + 2] << 16) |
|
|
113
|
+
(bytes[offset + 3] << 8) | bytes[offset + 4];
|
|
114
|
+
if (length === 0) {
|
|
115
|
+
return { value: 0n, bytesRead: 5 };
|
|
116
|
+
}
|
|
117
|
+
const dataBytes = bytes.slice(offset + 5, offset + 5 + length);
|
|
118
|
+
let value = 0n;
|
|
119
|
+
for (const b of dataBytes) {
|
|
120
|
+
value = (value << 8n) | BigInt(b);
|
|
121
|
+
}
|
|
122
|
+
return { value, bytesRead: 5 + length };
|
|
123
|
+
}
|
|
124
|
+
function readMMRProofChunk(bytes, offset) {
|
|
125
|
+
let pos = offset;
|
|
126
|
+
const isLeft = bytes[pos] === 1;
|
|
127
|
+
pos += 1;
|
|
128
|
+
const dataLength = (bytes[pos] << 24) | (bytes[pos + 1] << 16) |
|
|
129
|
+
(bytes[pos + 2] << 8) | bytes[pos + 3];
|
|
130
|
+
pos += 4;
|
|
131
|
+
const data = bytes.slice(pos, pos + dataLength);
|
|
132
|
+
pos += dataLength;
|
|
133
|
+
const { value, bytesRead: valueRead } = readMiniNumber(bytes, pos);
|
|
134
|
+
pos += valueRead;
|
|
135
|
+
return {
|
|
136
|
+
chunk: { isLeft, mmrData: { data, value } },
|
|
137
|
+
bytesRead: pos - offset
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export function serializeScriptProofWithProof(script, proof, blockTime = 0n) {
|
|
141
|
+
const scriptBytes = encodeMiniString(script);
|
|
142
|
+
const proofBytes = serializeRealMMRProof(proof, blockTime);
|
|
143
|
+
return concat(scriptBytes, proofBytes);
|
|
144
|
+
}
|
|
145
|
+
export const STATETYPE_HEX = 1;
|
|
146
|
+
export const STATETYPE_NUMBER = 2;
|
|
147
|
+
export const STATETYPE_STRING = 4;
|
|
148
|
+
export const STATETYPE_BOOL = 8;
|
|
149
|
+
export function encodeStateValue(stateValue) {
|
|
150
|
+
if (stateValue.port < 0 || stateValue.port > 255) {
|
|
151
|
+
throw new Error(`StateVariable port must be 0-255, got ${stateValue.port}`);
|
|
152
|
+
}
|
|
153
|
+
const portByte = new Uint8Array([stateValue.port]);
|
|
154
|
+
let typeByte;
|
|
155
|
+
let dataBytes;
|
|
156
|
+
switch (stateValue.type) {
|
|
157
|
+
case 'bool':
|
|
158
|
+
typeByte = new Uint8Array([STATETYPE_BOOL]);
|
|
159
|
+
if (typeof stateValue.value === 'boolean') {
|
|
160
|
+
dataBytes = new Uint8Array([stateValue.value ? 1 : 0]);
|
|
161
|
+
}
|
|
162
|
+
else if (typeof stateValue.value === 'string') {
|
|
163
|
+
const boolValue = stateValue.value.toUpperCase() === 'TRUE';
|
|
164
|
+
dataBytes = new Uint8Array([boolValue ? 1 : 0]);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
throw new Error(`Invalid bool state value: ${stateValue.value}`);
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
case 'number':
|
|
171
|
+
typeByte = new Uint8Array([STATETYPE_NUMBER]);
|
|
172
|
+
if (typeof stateValue.value === 'bigint') {
|
|
173
|
+
dataBytes = encodeMiniNumber(stateValue.value);
|
|
174
|
+
}
|
|
175
|
+
else if (typeof stateValue.value === 'string') {
|
|
176
|
+
const numValue = BigInt(stateValue.value);
|
|
177
|
+
dataBytes = encodeMiniNumber(numValue);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
throw new Error(`Invalid number state value: ${stateValue.value}`);
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
case 'hex':
|
|
184
|
+
typeByte = new Uint8Array([STATETYPE_HEX]);
|
|
185
|
+
if (typeof stateValue.value === 'string') {
|
|
186
|
+
const normalizedHex = normalizeHexValue(stateValue.value);
|
|
187
|
+
dataBytes = encodeMiniData(hexToBytes(normalizedHex));
|
|
188
|
+
}
|
|
189
|
+
else if (stateValue.value instanceof Uint8Array) {
|
|
190
|
+
dataBytes = encodeMiniData(stateValue.value);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
throw new Error(`Invalid hex state value: ${stateValue.value}`);
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
case 'string':
|
|
197
|
+
typeByte = new Uint8Array([STATETYPE_STRING]);
|
|
198
|
+
if (typeof stateValue.value === 'string') {
|
|
199
|
+
let bracketedValue = stateValue.value;
|
|
200
|
+
if (!stateValue.value.startsWith('[') || !stateValue.value.endsWith(']')) {
|
|
201
|
+
bracketedValue = `[${stateValue.value}]`;
|
|
202
|
+
}
|
|
203
|
+
dataBytes = encodeMiniString(bracketedValue);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
throw new Error(`Invalid string state value: ${stateValue.value}`);
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
default:
|
|
210
|
+
throw new Error(`Unknown state value type: ${stateValue.type}`);
|
|
211
|
+
}
|
|
212
|
+
return concat(portByte, typeByte, dataBytes);
|
|
213
|
+
}
|
|
214
|
+
export function serializeStateVariables(stateValues) {
|
|
215
|
+
const parts = [];
|
|
216
|
+
const countBytes = encodeMiniNumber(BigInt(stateValues.length));
|
|
217
|
+
parts.push(countBytes);
|
|
218
|
+
const sorted = [...stateValues].sort((a, b) => a.port - b.port);
|
|
219
|
+
for (const sv of sorted) {
|
|
220
|
+
parts.push(encodeStateValue(sv));
|
|
221
|
+
}
|
|
222
|
+
return concat(...parts);
|
|
223
|
+
}
|
|
224
|
+
export function buildScriptProofFromDescriptor(descriptor) {
|
|
225
|
+
const proof = descriptor.mastProof || { chunks: [] };
|
|
226
|
+
const serialized = serializeScriptProofWithProof(descriptor.script, proof);
|
|
227
|
+
return {
|
|
228
|
+
script: descriptor.script,
|
|
229
|
+
proof,
|
|
230
|
+
serialized
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
export function deduplicateScriptDescriptors(descriptors) {
|
|
234
|
+
const unique = new Map();
|
|
235
|
+
for (const desc of descriptors) {
|
|
236
|
+
const normalizedAddr = desc.address.toLowerCase();
|
|
237
|
+
if (!unique.has(normalizedAddr)) {
|
|
238
|
+
unique.set(normalizedAddr, desc);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return unique;
|
|
242
|
+
}
|
|
243
|
+
export function serializeExtraScripts(extraScripts) {
|
|
244
|
+
const parts = [];
|
|
245
|
+
const countBytes = encodeMiniNumber(BigInt(extraScripts.size));
|
|
246
|
+
parts.push(countBytes);
|
|
247
|
+
for (const [script, proofHex] of extraScripts) {
|
|
248
|
+
const { proof, blockTime } = proofHex
|
|
249
|
+
? parseMMRProofFromHex(proofHex)
|
|
250
|
+
: { proof: { chunks: [] }, blockTime: 0n };
|
|
251
|
+
parts.push(serializeScriptProofWithProof(script, proof, blockTime));
|
|
252
|
+
}
|
|
253
|
+
return concat(...parts);
|
|
254
|
+
}
|
|
255
|
+
export function aggregateSignatures(totemSignature, externalSignatures) {
|
|
256
|
+
const allSigProofs = [];
|
|
257
|
+
allSigProofs.push(serializeSignatureProof(totemSignature.publicKey, totemSignature.signature, { chunks: [] }));
|
|
258
|
+
for (const extSig of externalSignatures) {
|
|
259
|
+
if (!extSig.validated) {
|
|
260
|
+
console.warn(`[AggregateSignatures] Skipping unvalidated signature from ${extSig.publicKey}`);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
const proof = extSig.proof || { chunks: [] };
|
|
264
|
+
allSigProofs.push(serializeSignatureProof(hexToBytes(extSig.publicKey), hexToBytes(extSig.signature), proof));
|
|
265
|
+
}
|
|
266
|
+
return allSigProofs;
|
|
267
|
+
}
|
|
268
|
+
function serializeSignatureProof(publicKey, signature, proof) {
|
|
269
|
+
const parts = [];
|
|
270
|
+
parts.push(encodeMiniData(publicKey));
|
|
271
|
+
parts.push(encodeMiniData(signature));
|
|
272
|
+
parts.push(serializeRealMMRProof(proof));
|
|
273
|
+
return concat(...parts);
|
|
274
|
+
}
|
|
275
|
+
export function validateExternalSignature(signature, transactionDigest) {
|
|
276
|
+
if (!signature.publicKey || !signature.signature) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
console.log(`[ValidateSignature] Validating signature from ${signature.publicKey.substring(0, 16)}...`);
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
export function computeScriptAddress(script) {
|
|
283
|
+
const cleanScript = script.trim().toUpperCase();
|
|
284
|
+
const scriptBytes = new TextEncoder().encode(cleanScript);
|
|
285
|
+
const hashBytes = sha3_256(scriptBytes);
|
|
286
|
+
return bytesToHex(hashBytes);
|
|
287
|
+
}
|
|
288
|
+
export { concat };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction Serialization & Digest Computation
|
|
3
|
+
*
|
|
4
|
+
* Port of the extension's MinimaTransactionBuilder serialization logic
|
|
5
|
+
* to the SDK core for full parity with the Totem wallet extension.
|
|
6
|
+
*
|
|
7
|
+
* Matches Minima Java's Transaction.writeDataStream() and Coin.writeDataStream() exactly.
|
|
8
|
+
*
|
|
9
|
+
* CRITICAL: Before computing the transaction digest for signing, you MUST call
|
|
10
|
+
* precomputeTransactionCoinID() to set output coin IDs. Without this, the signed
|
|
11
|
+
* digest won't match what the Minima node verifies, causing allsignaturesvalid=false.
|
|
12
|
+
*/
|
|
13
|
+
export interface MinimaTransaction {
|
|
14
|
+
linkHash: Uint8Array;
|
|
15
|
+
inputs: MinimaCoin[];
|
|
16
|
+
outputs: MinimaCoin[];
|
|
17
|
+
state: StateVariable[];
|
|
18
|
+
}
|
|
19
|
+
export interface MinimaCoin {
|
|
20
|
+
coinId: Uint8Array;
|
|
21
|
+
address: Uint8Array;
|
|
22
|
+
amount: string;
|
|
23
|
+
tokenId: Uint8Array;
|
|
24
|
+
token: MinimaToken | null;
|
|
25
|
+
storeState: boolean;
|
|
26
|
+
state: StateVariable[] | RawStateVariable[];
|
|
27
|
+
mmrEntryNumber: bigint;
|
|
28
|
+
spent: boolean;
|
|
29
|
+
created: bigint;
|
|
30
|
+
rawAmountBytes?: Uint8Array;
|
|
31
|
+
rawMmrEntryBytes?: Uint8Array;
|
|
32
|
+
rawBlockCreatedBytes?: Uint8Array;
|
|
33
|
+
rawTokenData?: Uint8Array;
|
|
34
|
+
}
|
|
35
|
+
export interface ParsedMiniNumber {
|
|
36
|
+
scale: number;
|
|
37
|
+
unscaledValue: bigint;
|
|
38
|
+
}
|
|
39
|
+
export interface MinimaToken {
|
|
40
|
+
coinId: Uint8Array;
|
|
41
|
+
scale: number;
|
|
42
|
+
totalAmount: bigint;
|
|
43
|
+
name: Uint8Array;
|
|
44
|
+
script: Uint8Array;
|
|
45
|
+
created?: bigint;
|
|
46
|
+
}
|
|
47
|
+
export interface StateVariable {
|
|
48
|
+
port: number;
|
|
49
|
+
value: string | bigint | boolean | Uint8Array;
|
|
50
|
+
type: 'bool' | 'number' | 'hex' | 'string';
|
|
51
|
+
}
|
|
52
|
+
export interface RawStateVariable {
|
|
53
|
+
port: number;
|
|
54
|
+
type: number;
|
|
55
|
+
rawData: Uint8Array;
|
|
56
|
+
}
|
|
57
|
+
export interface CoinProofData {
|
|
58
|
+
coinId: Uint8Array;
|
|
59
|
+
address: Uint8Array;
|
|
60
|
+
rawAmountBytes: Uint8Array;
|
|
61
|
+
tokenId: Uint8Array;
|
|
62
|
+
storeState: boolean;
|
|
63
|
+
mmrEntryNumber: bigint;
|
|
64
|
+
rawMmrEntryBytes: Uint8Array;
|
|
65
|
+
spent: boolean;
|
|
66
|
+
blockCreated: bigint;
|
|
67
|
+
rawBlockCreatedBytes: Uint8Array;
|
|
68
|
+
state: RawStateVariable[];
|
|
69
|
+
rawTokenData?: Uint8Array;
|
|
70
|
+
}
|
|
71
|
+
export interface SpendableCoinInput {
|
|
72
|
+
coinId: string;
|
|
73
|
+
address: string;
|
|
74
|
+
amount: string;
|
|
75
|
+
tokenId: string;
|
|
76
|
+
rawAmountBytes?: Uint8Array;
|
|
77
|
+
coinProofData?: CoinProofData;
|
|
78
|
+
}
|
|
79
|
+
export interface TransactionBuildResult {
|
|
80
|
+
transaction: MinimaTransaction;
|
|
81
|
+
digestTx: Uint8Array;
|
|
82
|
+
digestTxHex: string;
|
|
83
|
+
serialized: Uint8Array;
|
|
84
|
+
serializedHex: string;
|
|
85
|
+
}
|
|
86
|
+
export declare function parseDecimalToMiniNumber(decimal: string): ParsedMiniNumber;
|
|
87
|
+
export declare function serializeCoin(coin: MinimaCoin): Uint8Array;
|
|
88
|
+
export declare function serializeTransaction(tx: MinimaTransaction): Uint8Array;
|
|
89
|
+
export declare function computeTransactionDigest(tx: MinimaTransaction): Uint8Array;
|
|
90
|
+
export declare function precomputeTransactionCoinID(tx: MinimaTransaction): void;
|
|
91
|
+
export declare function createDefaultTransaction(): MinimaTransaction;
|
|
92
|
+
export declare function buildMinimaCoin(opts: {
|
|
93
|
+
coinId?: Uint8Array;
|
|
94
|
+
address: Uint8Array;
|
|
95
|
+
amount: string;
|
|
96
|
+
tokenId?: Uint8Array;
|
|
97
|
+
storeState?: boolean;
|
|
98
|
+
state?: StateVariable[];
|
|
99
|
+
mmrEntryNumber?: bigint;
|
|
100
|
+
spent?: boolean;
|
|
101
|
+
created?: bigint;
|
|
102
|
+
rawAmountBytes?: Uint8Array;
|
|
103
|
+
rawMmrEntryBytes?: Uint8Array;
|
|
104
|
+
rawBlockCreatedBytes?: Uint8Array;
|
|
105
|
+
coinProofData?: CoinProofData;
|
|
106
|
+
}): MinimaCoin;
|