@totemsdk/core 1.2.9 → 1.2.11
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/cjs/index.d.ts +5 -4
- package/dist/cjs/index.js +25 -13
- package/dist/cjs/lease/LeaseStore.d.ts +8 -1
- package/dist/cjs/lease/LeaseStore.js +92 -22
- package/dist/cjs/lease/WatermarkStore.js +6 -12
- package/dist/cjs/script.js +1 -1
- package/dist/cjs/scripts/witness-serializer.d.ts +1 -1
- package/dist/cjs/scripts/witness-serializer.js +28 -5
- package/dist/cjs/transaction.d.ts +22 -0
- package/dist/cjs/transaction.js +157 -6
- package/dist/cjs/tx/TransactionReceiptStore.js +30 -21
- package/dist/index.d.ts +5 -4
- package/dist/index.js +11 -9
- package/dist/lease/LeaseStore.d.ts +8 -1
- package/dist/lease/LeaseStore.js +92 -22
- package/dist/lease/WatermarkStore.js +6 -12
- package/dist/script.js +1 -1
- package/dist/scripts/witness-serializer.d.ts +1 -1
- package/dist/scripts/witness-serializer.js +28 -5
- package/dist/transaction.d.ts +22 -0
- package/dist/transaction.js +154 -6
- package/dist/tx/TransactionReceiptStore.js +30 -21
- package/package.json +5 -5
package/dist/transaction.d.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface MinimaToken {
|
|
|
43
43
|
name: Uint8Array;
|
|
44
44
|
script: Uint8Array;
|
|
45
45
|
created?: bigint;
|
|
46
|
+
/**
|
|
47
|
+
* MiniNumber scale of `totalAmount` in the serialized Token. Minima's
|
|
48
|
+
* `tokencreate` builds the minima amount from `MINI_UNIT` (1E-44) times
|
|
49
|
+
* integers, so its scale is always 44; default accordingly.
|
|
50
|
+
*/
|
|
51
|
+
totalAmountScale?: number;
|
|
46
52
|
}
|
|
47
53
|
export interface StateVariable {
|
|
48
54
|
port: number;
|
|
@@ -82,11 +88,27 @@ export interface TransactionBuildResult {
|
|
|
82
88
|
digestTxHex: string;
|
|
83
89
|
serialized: Uint8Array;
|
|
84
90
|
serializedHex: string;
|
|
91
|
+
/** Set when the transaction creates a token (RFC-005 #2/#11). */
|
|
92
|
+
tokenId?: string;
|
|
85
93
|
}
|
|
86
94
|
export declare function parseDecimalToMiniNumber(decimal: string): ParsedMiniNumber;
|
|
87
95
|
export declare function serializeCoin(coin: MinimaCoin): Uint8Array;
|
|
88
96
|
export declare function serializeTransaction(tx: MinimaTransaction): Uint8Array;
|
|
89
97
|
export declare function computeTransactionDigest(tx: MinimaTransaction): Uint8Array;
|
|
98
|
+
/**
|
|
99
|
+
* Compute a Minima **token id** — SHA3-256 of the MiniData-wrapped serialized
|
|
100
|
+
* Token, matching Java `Token.calculateTokenID()`. A newly created token's coins
|
|
101
|
+
* carry this value as their `tokenId`; token creation returns it.
|
|
102
|
+
*/
|
|
103
|
+
export declare function computeTokenId(token: MinimaToken): Uint8Array;
|
|
104
|
+
/** Serialize a token descriptor (Java `Token.writeDataStream()` order). */
|
|
105
|
+
export declare function serializeTokenDescriptor(token: MinimaToken): Uint8Array;
|
|
106
|
+
/**
|
|
107
|
+
* Deserialize a transaction produced by {@link serializeTransaction}. The
|
|
108
|
+
* inverse is byte-exact for canonically-encoded transactions (outputs/inputs
|
|
109
|
+
* written from decimal amounts, MMR entry numbers and tokens).
|
|
110
|
+
*/
|
|
111
|
+
export declare function deserializeTransaction(bytes: Uint8Array): MinimaTransaction;
|
|
90
112
|
export declare function precomputeTransactionCoinID(tx: MinimaTransaction): void;
|
|
91
113
|
export declare function createDefaultTransaction(): MinimaTransaction;
|
|
92
114
|
export declare function buildMinimaCoin(opts: {
|
package/dist/transaction.js
CHANGED
|
@@ -116,11 +116,11 @@ function serializeStateVariable(sv) {
|
|
|
116
116
|
case 'string':
|
|
117
117
|
typeByte = new Uint8Array([STATETYPE_STRING]);
|
|
118
118
|
if (typeof sv.value === 'string') {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const utf8Bytes = new TextEncoder().encode(
|
|
119
|
+
// Minima stores a string state variable as MiniString: MiniData of the
|
|
120
|
+
// raw UTF-8 bytes. The `[ … ]` form is KISSVM literal syntax, NOT part
|
|
121
|
+
// of the stored value (the C++ StateVariable::writeDataStream writes
|
|
122
|
+
// mData directly; the deserializer likewise strips no brackets).
|
|
123
|
+
const utf8Bytes = new TextEncoder().encode(sv.value);
|
|
124
124
|
dataBytes = writeMiniData(utf8Bytes);
|
|
125
125
|
}
|
|
126
126
|
else {
|
|
@@ -138,7 +138,10 @@ function serializeToken(token) {
|
|
|
138
138
|
parts.push(writeHashToStream(token.coinId));
|
|
139
139
|
parts.push(writeMiniData(token.script));
|
|
140
140
|
parts.push(writeMiniNumber(BigInt(token.scale), 0));
|
|
141
|
-
|
|
141
|
+
// The minima amount is a MiniNumber carrying its own scale (Java
|
|
142
|
+
// Token.writeDataStream → mTokenMinimaAmount.writeDataStream). Minima's
|
|
143
|
+
// tokencreate always builds it from MINI_UNIT (scale 44).
|
|
144
|
+
parts.push(writeMiniNumber(token.totalAmount, token.totalAmountScale ?? 44));
|
|
142
145
|
parts.push(writeMiniData(token.name));
|
|
143
146
|
parts.push(writeMiniNumber(token.created ?? 0n, 0));
|
|
144
147
|
return concat(...parts);
|
|
@@ -224,6 +227,151 @@ export function computeTransactionDigest(tx) {
|
|
|
224
227
|
const serialized = serializeTransaction(tx);
|
|
225
228
|
return sha3_256(serialized);
|
|
226
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Compute a Minima **token id** — SHA3-256 of the MiniData-wrapped serialized
|
|
232
|
+
* Token, matching Java `Token.calculateTokenID()`. A newly created token's coins
|
|
233
|
+
* carry this value as their `tokenId`; token creation returns it.
|
|
234
|
+
*/
|
|
235
|
+
export function computeTokenId(token) {
|
|
236
|
+
return sha3_256(writeMiniData(serializeToken(token)));
|
|
237
|
+
}
|
|
238
|
+
/** Serialize a token descriptor (Java `Token.writeDataStream()` order). */
|
|
239
|
+
export function serializeTokenDescriptor(token) {
|
|
240
|
+
return serializeToken(token);
|
|
241
|
+
}
|
|
242
|
+
function txReadBytes(data, c, n) {
|
|
243
|
+
if (n < 0 || c.offset + n > data.length) {
|
|
244
|
+
throw new Error(`deserializeTransaction: unexpected end of input at offset ${c.offset} (need ${n})`);
|
|
245
|
+
}
|
|
246
|
+
const out = data.slice(c.offset, c.offset + n);
|
|
247
|
+
c.offset += n;
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
function txReadMiniByte(data, c) {
|
|
251
|
+
return txReadBytes(data, c, 1)[0];
|
|
252
|
+
}
|
|
253
|
+
/** MiniData / hash: 4-byte big-endian length + bytes. */
|
|
254
|
+
function txReadMiniData(data, c) {
|
|
255
|
+
const lenBytes = txReadBytes(data, c, 4);
|
|
256
|
+
const len = ((lenBytes[0] << 24) | (lenBytes[1] << 16) | (lenBytes[2] << 8) | lenBytes[3]) >>> 0;
|
|
257
|
+
return txReadBytes(data, c, len);
|
|
258
|
+
}
|
|
259
|
+
/** MiniNumber: 1-byte scale + 1-byte length + two's-complement unscaled bytes. */
|
|
260
|
+
function txReadMiniNumber(data, c) {
|
|
261
|
+
const scale = txReadBytes(data, c, 1)[0];
|
|
262
|
+
const len = txReadBytes(data, c, 1)[0];
|
|
263
|
+
const bytes = txReadBytes(data, c, len);
|
|
264
|
+
let value = 0n;
|
|
265
|
+
for (const b of bytes)
|
|
266
|
+
value = (value << 8n) | BigInt(b);
|
|
267
|
+
// RFC-020 P2-10 (TXB-SERIAL-003): Java's BigInteger.toByteArray() (and the
|
|
268
|
+
// canonical writers here) use two's-complement big-endian; a set high bit
|
|
269
|
+
// means the value is negative. Previously this was read as unsigned.
|
|
270
|
+
if (bytes.length > 0 && (bytes[0] & 0x80) !== 0) {
|
|
271
|
+
value -= 1n << BigInt(8 * bytes.length);
|
|
272
|
+
}
|
|
273
|
+
return { value, scale };
|
|
274
|
+
}
|
|
275
|
+
function txBytesToBigInt(bytes) {
|
|
276
|
+
let value = 0n;
|
|
277
|
+
for (const b of bytes)
|
|
278
|
+
value = (value << 8n) | BigInt(b);
|
|
279
|
+
return value;
|
|
280
|
+
}
|
|
281
|
+
/** MMREntryNumber: MiniNumber(scale) followed by MiniData(unscaled). */
|
|
282
|
+
function txReadMMREntryNumber(data, c) {
|
|
283
|
+
txReadMiniNumber(data, c); // scale (always 0 for integer entry numbers)
|
|
284
|
+
return txBytesToBigInt(txReadMiniData(data, c));
|
|
285
|
+
}
|
|
286
|
+
function miniNumberToDecimal(value, scale) {
|
|
287
|
+
if (scale === 0)
|
|
288
|
+
return value.toString();
|
|
289
|
+
const digits = value.toString().padStart(scale + 1, '0');
|
|
290
|
+
const whole = digits.slice(0, digits.length - scale);
|
|
291
|
+
const frac = digits.slice(digits.length - scale);
|
|
292
|
+
return `${whole}.${frac}`;
|
|
293
|
+
}
|
|
294
|
+
function txReadStateVariable(data, c) {
|
|
295
|
+
const port = txReadBytes(data, c, 1)[0];
|
|
296
|
+
const type = txReadBytes(data, c, 1)[0];
|
|
297
|
+
switch (type) {
|
|
298
|
+
case STATETYPE_HEX:
|
|
299
|
+
return { port, type: 'hex', value: txReadMiniData(data, c) };
|
|
300
|
+
case STATETYPE_NUMBER:
|
|
301
|
+
return { port, type: 'number', value: txReadMiniNumber(data, c).value };
|
|
302
|
+
case STATETYPE_STRING:
|
|
303
|
+
return { port, type: 'string', value: new TextDecoder().decode(txReadMiniData(data, c)) };
|
|
304
|
+
case STATETYPE_BOOL:
|
|
305
|
+
return { port, type: 'bool', value: txReadMiniByte(data, c) === 1 };
|
|
306
|
+
default:
|
|
307
|
+
throw new Error(`deserializeTransaction: unknown StateVariable type ${type}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function txReadToken(data, c) {
|
|
311
|
+
const coinId = txReadMiniData(data, c);
|
|
312
|
+
const script = txReadMiniData(data, c);
|
|
313
|
+
const { value: scale } = txReadMiniNumber(data, c);
|
|
314
|
+
// RFC-020 P2-10 (TXB-SERIAL-002): carry the totalAmount's own scale so a
|
|
315
|
+
// serialize→deserialize→serialize round-trip is byte-exact (previously the
|
|
316
|
+
// scale was dropped and re-defaulted to 44).
|
|
317
|
+
const { value: totalAmount, scale: totalAmountScale } = txReadMiniNumber(data, c);
|
|
318
|
+
const name = txReadMiniData(data, c);
|
|
319
|
+
const { value: created } = txReadMiniNumber(data, c);
|
|
320
|
+
return { coinId, script, scale: Number(scale), totalAmount, totalAmountScale, name, created };
|
|
321
|
+
}
|
|
322
|
+
function txReadCoin(data, c) {
|
|
323
|
+
const coinId = txReadMiniData(data, c);
|
|
324
|
+
const address = txReadMiniData(data, c);
|
|
325
|
+
const { value: amountUnscaled, scale } = txReadMiniNumber(data, c);
|
|
326
|
+
const tokenId = txReadMiniData(data, c);
|
|
327
|
+
const storeState = txReadMiniByte(data, c) === 1;
|
|
328
|
+
const mmrEntryNumber = txReadMMREntryNumber(data, c);
|
|
329
|
+
const spent = txReadMiniByte(data, c) === 1;
|
|
330
|
+
const { value: created } = txReadMiniNumber(data, c);
|
|
331
|
+
const stateCount = Number(txReadMiniNumber(data, c).value);
|
|
332
|
+
const state = [];
|
|
333
|
+
for (let i = 0; i < stateCount; i++)
|
|
334
|
+
state.push(txReadStateVariable(data, c));
|
|
335
|
+
const hasToken = txReadMiniByte(data, c) === 1;
|
|
336
|
+
const token = hasToken ? txReadToken(data, c) : null;
|
|
337
|
+
return {
|
|
338
|
+
coinId,
|
|
339
|
+
address,
|
|
340
|
+
amount: miniNumberToDecimal(amountUnscaled, scale),
|
|
341
|
+
tokenId,
|
|
342
|
+
token,
|
|
343
|
+
storeState,
|
|
344
|
+
state,
|
|
345
|
+
mmrEntryNumber,
|
|
346
|
+
spent,
|
|
347
|
+
created,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Deserialize a transaction produced by {@link serializeTransaction}. The
|
|
352
|
+
* inverse is byte-exact for canonically-encoded transactions (outputs/inputs
|
|
353
|
+
* written from decimal amounts, MMR entry numbers and tokens).
|
|
354
|
+
*/
|
|
355
|
+
export function deserializeTransaction(bytes) {
|
|
356
|
+
const c = { offset: 0 };
|
|
357
|
+
const inputCount = Number(txReadMiniNumber(bytes, c).value);
|
|
358
|
+
const inputs = [];
|
|
359
|
+
for (let i = 0; i < inputCount; i++)
|
|
360
|
+
inputs.push(txReadCoin(bytes, c));
|
|
361
|
+
const outputCount = Number(txReadMiniNumber(bytes, c).value);
|
|
362
|
+
const outputs = [];
|
|
363
|
+
for (let i = 0; i < outputCount; i++)
|
|
364
|
+
outputs.push(txReadCoin(bytes, c));
|
|
365
|
+
const stateCount = Number(txReadMiniNumber(bytes, c).value);
|
|
366
|
+
const state = [];
|
|
367
|
+
for (let i = 0; i < stateCount; i++)
|
|
368
|
+
state.push(txReadStateVariable(bytes, c));
|
|
369
|
+
const linkHash = txReadMiniData(bytes, c);
|
|
370
|
+
if (c.offset !== bytes.length) {
|
|
371
|
+
throw new Error(`deserializeTransaction: ${bytes.length - c.offset} trailing byte(s) after transaction`);
|
|
372
|
+
}
|
|
373
|
+
return { linkHash, inputs, outputs, state };
|
|
374
|
+
}
|
|
227
375
|
export function precomputeTransactionCoinID(tx) {
|
|
228
376
|
if (tx.inputs.length === 0)
|
|
229
377
|
return;
|
|
@@ -34,33 +34,28 @@ export class TransactionReceiptStore {
|
|
|
34
34
|
return this._initialized;
|
|
35
35
|
}
|
|
36
36
|
async load() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.logger.debug(`Loaded ${this.receipts.length} transaction receipts`);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
this.logger.error('Failed to load receipts:', error);
|
|
37
|
+
const receipts = await this.storage.get(this.storageKey);
|
|
38
|
+
if (receipts) {
|
|
39
|
+
this.receipts = receipts;
|
|
40
|
+
this.logger.debug(`Loaded ${this.receipts.length} transaction receipts`);
|
|
46
41
|
}
|
|
47
42
|
}
|
|
48
43
|
async persist() {
|
|
44
|
+
const trimmedReceipts = this.receipts.slice(-this.maxReceipts);
|
|
45
|
+
await this.storage.set(this.storageKey, trimmedReceipts);
|
|
46
|
+
this.receipts = trimmedReceipts;
|
|
47
|
+
}
|
|
48
|
+
async add(receipt) {
|
|
49
|
+
this.receipts.push(receipt);
|
|
50
|
+
this.logger.debug(`Added receipt: ${receipt.txpowid}`);
|
|
49
51
|
try {
|
|
50
|
-
|
|
51
|
-
await this.storage.set(this.storageKey, trimmedReceipts);
|
|
52
|
-
this.receipts = trimmedReceipts;
|
|
52
|
+
await this.persist();
|
|
53
53
|
}
|
|
54
54
|
catch (error) {
|
|
55
|
-
this.
|
|
55
|
+
this.receipts.pop();
|
|
56
56
|
throw error;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
async add(receipt) {
|
|
60
|
-
this.receipts.push(receipt);
|
|
61
|
-
this.logger.debug(`Added receipt: ${receipt.txpowid}`);
|
|
62
|
-
await this.persist();
|
|
63
|
-
}
|
|
64
59
|
getAll() {
|
|
65
60
|
return [...this.receipts].reverse();
|
|
66
61
|
}
|
|
@@ -73,15 +68,29 @@ export class TransactionReceiptStore {
|
|
|
73
68
|
async updateStatus(txpowid, status) {
|
|
74
69
|
const receipt = this.receipts.find(r => r.txpowid === txpowid);
|
|
75
70
|
if (receipt) {
|
|
71
|
+
const previousStatus = receipt.status;
|
|
76
72
|
receipt.status = status;
|
|
77
73
|
this.logger.debug(`Updated receipt status: ${txpowid} → ${status}`);
|
|
78
|
-
|
|
74
|
+
try {
|
|
75
|
+
await this.persist();
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
receipt.status = previousStatus;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
async clear() {
|
|
84
|
+
const previous = this.receipts;
|
|
82
85
|
this.receipts = [];
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
try {
|
|
87
|
+
await this.storage.remove(this.storageKey);
|
|
88
|
+
this.logger.debug('Cleared all receipts');
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
this.receipts = previous;
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
85
94
|
}
|
|
86
95
|
count() {
|
|
87
96
|
return this.receipts.length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.11",
|
|
4
4
|
"description": "Core cryptographic primitives for Totem SDK — backed by Rust/WASM",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"LICENSE"
|
|
120
120
|
],
|
|
121
121
|
"dependencies": {
|
|
122
|
-
"@totemsdk/core-wasm": "^1.0.
|
|
122
|
+
"@totemsdk/core-wasm": "^1.0.1"
|
|
123
123
|
},
|
|
124
124
|
"engines": {
|
|
125
125
|
"node": ">=16.0.0"
|
|
@@ -136,13 +136,13 @@
|
|
|
136
136
|
},
|
|
137
137
|
"license": "MIT",
|
|
138
138
|
"author": "Totem SDK",
|
|
139
|
-
"homepage": "https://totem.ing",
|
|
139
|
+
"homepage": "https://docs.totem.ing",
|
|
140
140
|
"bugs": {
|
|
141
|
-
"url": "https://github.com/
|
|
141
|
+
"url": "https://github.com/Totem-Edge/totem-sdk/issues"
|
|
142
142
|
},
|
|
143
143
|
"repository": {
|
|
144
144
|
"type": "git",
|
|
145
|
-
"url": "git+https://github.com/
|
|
145
|
+
"url": "git+https://github.com/Totem-Edge/totem-sdk.git",
|
|
146
146
|
"directory": "packages/core"
|
|
147
147
|
},
|
|
148
148
|
"keywords": [
|