@totemsdk/core 1.2.10 → 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.
@@ -20,6 +20,7 @@ export { mmrRootFromPublicKeys, verifyMMRProof, } from './wasm-sync.js';
20
20
  export { phraseToSeed, generateWordList, validatePhrase, cleanSeedPhrase, } from './bip39.js';
21
21
  export { makeMxAddress, parseMxAddress, wotsAddressFromKeypair, } from './wasm-sync.js';
22
22
  export { serializeTransaction, computeTransactionDigest, } from './wasm-sync.js';
23
+ export { deserializeTransaction } from './transaction.js';
23
24
  export { timingSafeEqual, createChallenge, validateChallenge, } from './wasm-sync.js';
24
25
  export { scriptFromWotsPk } from './script.js';
25
26
  export { makeMxAddress as encodeMx, parseMxAddress as decodeMx } from './wasm-sync.js';
@@ -30,13 +31,13 @@ export * from './scripts/index.js';
30
31
  export { scriptToAddress, addressToRoot } from './derive.js';
31
32
  export { WORD_LIST, convertStringToSeed, convertWordListToSeed, generateSeedPhrase, } from './bip39.js';
32
33
  export { MiniNumber } from './MiniNumber.js';
33
- export { type MinimaTransaction, type MinimaCoin, type ParsedMiniNumber, type MinimaToken, type StateVariable, type RawStateVariable, type CoinProofData, type SpendableCoinInput, type TransactionBuildResult, parseDecimalToMiniNumber, serializeCoin, createDefaultTransaction, buildMinimaCoin, } from './transaction.js';
34
+ export { type MinimaTransaction, type MinimaCoin, type ParsedMiniNumber, type MinimaToken, type StateVariable, type RawStateVariable, type CoinProofData, type SpendableCoinInput, type TransactionBuildResult, parseDecimalToMiniNumber, serializeCoin, serializeTransaction as serializeTransactionObject, computeTokenId, serializeTokenDescriptor, createDefaultTransaction, buildMinimaCoin, } from './transaction.js';
34
35
  export { verifySignature, verifySignatureDetailed, verifyTreeSignatureDetailed, deriveAddressFromPublicKey, normalizeHex, type VerificationResult, } from './verify.js';
35
- export { writeMiniByte, writeMMREntryNumber, concat, bigIntToByteArray, type Bytes, } from './Streamable.js';
36
+ export { writeMiniByte, writeMMREntryNumber, writeSignature, writeSignatureProof, concat, bigIntToByteArray, type Bytes, } from './Streamable.js';
36
37
  export { serializeMiniNumber, serializeMiniData, hashAllObjects, deriveChildTreeSeedJava, hashObject, serializeMiniNumberZERO, serializeMiniNumberONE, writeHashToStream, javaHashAllObjects, indexToMiniDataBytes, deriveUnifiedChildSeed, createMMREntryNumber, serializeMMREntryNumber, serializeMMRData, serializeMMREntry, type MMREntryNumber as JavaMMREntryNumber, type MMRData as JavaMMRData, type MMREntry as JavaMMREntry, } from './javaStreamables.js';
37
- export { TreeKey, TreeKeyNode, serializeTreeSignature, deserializeTreeSignature, getRootPublicKey, DEFAULT_KEYS_PER_LEVEL, DEFAULT_LEVELS, createUnifiedChildTreeKeyAsync, createPerAddressTreeKey, createPerAddressTreeKeyAsync, type KeyGenProgress, type ProgressCallback, type SignatureProof, type TreeSignature, } from './treekey.js';
38
+ export { TreeKey, TreeKeyNode, serializeTreeSignature, deserializeTreeSignature, getRootPublicKey, DEFAULT_KEYS_PER_LEVEL, DEFAULT_LEVELS, createUnifiedChildTreeKeyAsync, createPerAddressTreeKey, createPerAddressTreeKeyAsync, setTreeKeyLogger, disableTreeKeyLogger, type KeyGenProgress, type ProgressCallback, type SignatureProof, type TreeSignature, } from './treekey.js';
38
39
  export type { WotsKeypair, WotsSignature } from './wots.js';
39
- export { F, hex, fromHex, u16be, u32be, assert32, h, prfChainSeed, toWinternitzDigits, baseWWithChecksum, wotsSignLegacy, } from './wots.js';
40
+ export { F, hex, fromHex, setWotsLogger, disableWotsLogger, u16be, u32be, assert32, h, prfChainSeed, toWinternitzDigits, baseWWithChecksum, wotsSignLegacy, } from './wots.js';
40
41
  export { toHex, canonicalJson, hashCanonical } from './canonical.js';
41
42
  export { utf8ToBytes, bytesToUtf8 } from './utils.js';
42
43
  export declare const MINIMA_CONSTANTS: {
package/dist/cjs/index.js CHANGED
@@ -21,9 +21,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
21
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.validateChallenge = exports.createChallenge = exports.timingSafeEqual = exports.computeTransactionDigest = exports.serializeTransaction = exports.wotsAddressFromKeypair = exports.parseMxAddress = exports.makeMxAddress = exports.cleanSeedPhrase = exports.validatePhrase = exports.generateWordList = exports.phraseToSeed = exports.verifyMMRProof = exports.mmrRootFromPublicKeys = exports.verifyTreeSignature = exports.deriveUnifiedAddressPublicKey = exports.createUnifiedRootTreeKey = exports.createUnifiedChildTreeKey = exports.precomputeTransactionCoinID = exports.writeMiniString = exports.writeMiniData = exports.writeMiniNumber = exports.deriveRootPrivSeed = exports.derivePerAddressSeed = exports.deriveChainSeedJava = exports.wotsKeypairFromSeed = exports.wotsPublicKeyFromSeed = exports.wotsPkFromSig = exports.wotsVerifyDigest = exports.wotsVerify = exports.wotsSign = exports.deriveFullPublicKey = exports.derivePKdigest = exports.hashChain = exports.expandPrivateKey = exports.sha3_256 = exports.concatBytes = exports.hexToBytes = exports.bytesToHex = exports.TransactionReceiptStore = exports.WatermarkExhaustedError = exports.TransactionLifecycleError = exports.TransactionLifecycle = exports.TransactionService = exports.flatIndexFromLanes = exports.finalizeLease = exports.prepareLease = exports.LeaseMonitor = exports.WatermarkStore = exports.LeaseStore = void 0;
25
- exports.hex = exports.F = exports.createPerAddressTreeKeyAsync = exports.createPerAddressTreeKey = exports.createUnifiedChildTreeKeyAsync = exports.DEFAULT_LEVELS = exports.DEFAULT_KEYS_PER_LEVEL = exports.getRootPublicKey = exports.deserializeTreeSignature = exports.serializeTreeSignature = exports.TreeKeyNode = exports.TreeKey = exports.serializeMMREntry = exports.serializeMMRData = exports.serializeMMREntryNumber = exports.createMMREntryNumber = exports.deriveUnifiedChildSeed = exports.indexToMiniDataBytes = exports.javaHashAllObjects = exports.writeHashToStream = exports.serializeMiniNumberONE = exports.serializeMiniNumberZERO = exports.hashObject = exports.deriveChildTreeSeedJava = exports.hashAllObjects = exports.serializeMiniData = exports.serializeMiniNumber = exports.bigIntToByteArray = exports.concat = exports.writeMMREntryNumber = exports.writeMiniByte = exports.normalizeHex = exports.deriveAddressFromPublicKey = exports.verifyTreeSignatureDetailed = exports.verifySignatureDetailed = exports.verifySignature = exports.buildMinimaCoin = exports.createDefaultTransaction = exports.serializeCoin = exports.parseDecimalToMiniNumber = exports.MiniNumber = exports.generateSeedPhrase = exports.convertWordListToSeed = exports.convertStringToSeed = exports.WORD_LIST = exports.addressToRoot = exports.scriptToAddress = exports.decodeMx = exports.encodeMx = exports.scriptFromWotsPk = void 0;
26
- exports.MINIMA_CONSTANTS = exports.bytesToUtf8 = exports.utf8ToBytes = exports.hashCanonical = exports.canonicalJson = exports.toHex = exports.wotsSignLegacy = exports.baseWWithChecksum = exports.toWinternitzDigits = exports.prfChainSeed = exports.h = exports.assert32 = exports.u32be = exports.u16be = exports.fromHex = void 0;
24
+ exports.createChallenge = exports.timingSafeEqual = exports.deserializeTransaction = exports.computeTransactionDigest = exports.serializeTransaction = exports.wotsAddressFromKeypair = exports.parseMxAddress = exports.makeMxAddress = exports.cleanSeedPhrase = exports.validatePhrase = exports.generateWordList = exports.phraseToSeed = exports.verifyMMRProof = exports.mmrRootFromPublicKeys = exports.verifyTreeSignature = exports.deriveUnifiedAddressPublicKey = exports.createUnifiedRootTreeKey = exports.createUnifiedChildTreeKey = exports.precomputeTransactionCoinID = exports.writeMiniString = exports.writeMiniData = exports.writeMiniNumber = exports.deriveRootPrivSeed = exports.derivePerAddressSeed = exports.deriveChainSeedJava = exports.wotsKeypairFromSeed = exports.wotsPublicKeyFromSeed = exports.wotsPkFromSig = exports.wotsVerifyDigest = exports.wotsVerify = exports.wotsSign = exports.deriveFullPublicKey = exports.derivePKdigest = exports.hashChain = exports.expandPrivateKey = exports.sha3_256 = exports.concatBytes = exports.hexToBytes = exports.bytesToHex = exports.TransactionReceiptStore = exports.WatermarkExhaustedError = exports.TransactionLifecycleError = exports.TransactionLifecycle = exports.TransactionService = exports.flatIndexFromLanes = exports.finalizeLease = exports.prepareLease = exports.LeaseMonitor = exports.WatermarkStore = exports.LeaseStore = void 0;
25
+ exports.DEFAULT_KEYS_PER_LEVEL = exports.getRootPublicKey = exports.deserializeTreeSignature = exports.serializeTreeSignature = exports.TreeKeyNode = exports.TreeKey = exports.serializeMMREntry = exports.serializeMMRData = exports.serializeMMREntryNumber = exports.createMMREntryNumber = exports.deriveUnifiedChildSeed = exports.indexToMiniDataBytes = exports.javaHashAllObjects = exports.writeHashToStream = exports.serializeMiniNumberONE = exports.serializeMiniNumberZERO = exports.hashObject = exports.deriveChildTreeSeedJava = exports.hashAllObjects = exports.serializeMiniData = exports.serializeMiniNumber = exports.bigIntToByteArray = exports.concat = exports.writeSignatureProof = exports.writeSignature = exports.writeMMREntryNumber = exports.writeMiniByte = exports.normalizeHex = exports.deriveAddressFromPublicKey = exports.verifyTreeSignatureDetailed = exports.verifySignatureDetailed = exports.verifySignature = exports.buildMinimaCoin = exports.createDefaultTransaction = exports.serializeTokenDescriptor = exports.computeTokenId = exports.serializeTransactionObject = exports.serializeCoin = exports.parseDecimalToMiniNumber = exports.MiniNumber = exports.generateSeedPhrase = exports.convertWordListToSeed = exports.convertStringToSeed = exports.WORD_LIST = exports.addressToRoot = exports.scriptToAddress = exports.decodeMx = exports.encodeMx = exports.scriptFromWotsPk = exports.validateChallenge = void 0;
26
+ exports.MINIMA_CONSTANTS = exports.bytesToUtf8 = exports.utf8ToBytes = exports.hashCanonical = exports.canonicalJson = exports.toHex = exports.wotsSignLegacy = exports.baseWWithChecksum = exports.toWinternitzDigits = exports.prfChainSeed = exports.h = exports.assert32 = exports.u32be = exports.u16be = exports.disableWotsLogger = exports.setWotsLogger = exports.fromHex = exports.hex = exports.F = exports.disableTreeKeyLogger = exports.setTreeKeyLogger = exports.createPerAddressTreeKeyAsync = exports.createPerAddressTreeKey = exports.createUnifiedChildTreeKeyAsync = exports.DEFAULT_LEVELS = void 0;
27
27
  exports.mxToHex = mxToHex;
28
28
  // Version information for bundle duplication detection
29
29
  __exportStar(require("./version.js"), exports);
@@ -112,6 +112,9 @@ Object.defineProperty(exports, "wotsAddressFromKeypair", { enumerable: true, get
112
112
  var wasm_sync_js_8 = require("./wasm-sync.js");
113
113
  Object.defineProperty(exports, "serializeTransaction", { enumerable: true, get: function () { return wasm_sync_js_8.serializeTransaction; } });
114
114
  Object.defineProperty(exports, "computeTransactionDigest", { enumerable: true, get: function () { return wasm_sync_js_8.computeTransactionDigest; } });
115
+ // Transaction deserialization (byte-exact inverse of the TS serializer)
116
+ var transaction_js_1 = require("./transaction.js");
117
+ Object.defineProperty(exports, "deserializeTransaction", { enumerable: true, get: function () { return transaction_js_1.deserializeTransaction; } });
115
118
  // Verification
116
119
  var wasm_sync_js_9 = require("./wasm-sync.js");
117
120
  Object.defineProperty(exports, "timingSafeEqual", { enumerable: true, get: function () { return wasm_sync_js_9.timingSafeEqual; } });
@@ -152,11 +155,14 @@ Object.defineProperty(exports, "generateSeedPhrase", { enumerable: true, get: fu
152
155
  var MiniNumber_js_1 = require("./MiniNumber.js");
153
156
  Object.defineProperty(exports, "MiniNumber", { enumerable: true, get: function () { return MiniNumber_js_1.MiniNumber; } });
154
157
  // Transaction types (from JS)
155
- var transaction_js_1 = require("./transaction.js");
156
- Object.defineProperty(exports, "parseDecimalToMiniNumber", { enumerable: true, get: function () { return transaction_js_1.parseDecimalToMiniNumber; } });
157
- Object.defineProperty(exports, "serializeCoin", { enumerable: true, get: function () { return transaction_js_1.serializeCoin; } });
158
- Object.defineProperty(exports, "createDefaultTransaction", { enumerable: true, get: function () { return transaction_js_1.createDefaultTransaction; } });
159
- Object.defineProperty(exports, "buildMinimaCoin", { enumerable: true, get: function () { return transaction_js_1.buildMinimaCoin; } });
158
+ var transaction_js_2 = require("./transaction.js");
159
+ Object.defineProperty(exports, "parseDecimalToMiniNumber", { enumerable: true, get: function () { return transaction_js_2.parseDecimalToMiniNumber; } });
160
+ Object.defineProperty(exports, "serializeCoin", { enumerable: true, get: function () { return transaction_js_2.serializeCoin; } });
161
+ Object.defineProperty(exports, "serializeTransactionObject", { enumerable: true, get: function () { return transaction_js_2.serializeTransaction; } });
162
+ Object.defineProperty(exports, "computeTokenId", { enumerable: true, get: function () { return transaction_js_2.computeTokenId; } });
163
+ Object.defineProperty(exports, "serializeTokenDescriptor", { enumerable: true, get: function () { return transaction_js_2.serializeTokenDescriptor; } });
164
+ Object.defineProperty(exports, "createDefaultTransaction", { enumerable: true, get: function () { return transaction_js_2.createDefaultTransaction; } });
165
+ Object.defineProperty(exports, "buildMinimaCoin", { enumerable: true, get: function () { return transaction_js_2.buildMinimaCoin; } });
160
166
  // High-level Verification API (convenience functions for integrators)
161
167
  var verify_js_1 = require("./verify.js");
162
168
  Object.defineProperty(exports, "verifySignature", { enumerable: true, get: function () { return verify_js_1.verifySignature; } });
@@ -168,6 +174,8 @@ Object.defineProperty(exports, "normalizeHex", { enumerable: true, get: function
168
174
  var Streamable_js_1 = require("./Streamable.js");
169
175
  Object.defineProperty(exports, "writeMiniByte", { enumerable: true, get: function () { return Streamable_js_1.writeMiniByte; } });
170
176
  Object.defineProperty(exports, "writeMMREntryNumber", { enumerable: true, get: function () { return Streamable_js_1.writeMMREntryNumber; } });
177
+ Object.defineProperty(exports, "writeSignature", { enumerable: true, get: function () { return Streamable_js_1.writeSignature; } });
178
+ Object.defineProperty(exports, "writeSignatureProof", { enumerable: true, get: function () { return Streamable_js_1.writeSignatureProof; } });
171
179
  Object.defineProperty(exports, "concat", { enumerable: true, get: function () { return Streamable_js_1.concat; } });
172
180
  Object.defineProperty(exports, "bigIntToByteArray", { enumerable: true, get: function () { return Streamable_js_1.bigIntToByteArray; } });
173
181
  // Java-Compatible Serialization Helpers (non-crypto)
@@ -199,10 +207,14 @@ Object.defineProperty(exports, "DEFAULT_LEVELS", { enumerable: true, get: functi
199
207
  Object.defineProperty(exports, "createUnifiedChildTreeKeyAsync", { enumerable: true, get: function () { return treekey_js_2.createUnifiedChildTreeKeyAsync; } });
200
208
  Object.defineProperty(exports, "createPerAddressTreeKey", { enumerable: true, get: function () { return treekey_js_2.createPerAddressTreeKey; } });
201
209
  Object.defineProperty(exports, "createPerAddressTreeKeyAsync", { enumerable: true, get: function () { return treekey_js_2.createPerAddressTreeKeyAsync; } });
210
+ Object.defineProperty(exports, "setTreeKeyLogger", { enumerable: true, get: function () { return treekey_js_2.setTreeKeyLogger; } });
211
+ Object.defineProperty(exports, "disableTreeKeyLogger", { enumerable: true, get: function () { return treekey_js_2.disableTreeKeyLogger; } });
202
212
  var wots_js_1 = require("./wots.js");
203
213
  Object.defineProperty(exports, "F", { enumerable: true, get: function () { return wots_js_1.F; } });
204
214
  Object.defineProperty(exports, "hex", { enumerable: true, get: function () { return wots_js_1.hex; } });
205
215
  Object.defineProperty(exports, "fromHex", { enumerable: true, get: function () { return wots_js_1.fromHex; } });
216
+ Object.defineProperty(exports, "setWotsLogger", { enumerable: true, get: function () { return wots_js_1.setWotsLogger; } });
217
+ Object.defineProperty(exports, "disableWotsLogger", { enumerable: true, get: function () { return wots_js_1.disableWotsLogger; } });
206
218
  Object.defineProperty(exports, "u16be", { enumerable: true, get: function () { return wots_js_1.u16be; } });
207
219
  Object.defineProperty(exports, "u32be", { enumerable: true, get: function () { return wots_js_1.u32be; } });
208
220
  Object.defineProperty(exports, "assert32", { enumerable: true, get: function () { return wots_js_1.assert32; } });
@@ -33,6 +33,7 @@ export interface LeaseStoreConfig {
33
33
  export declare class LeaseStore {
34
34
  private leases;
35
35
  private _initialized;
36
+ private mutationTail;
36
37
  private readonly storageKey;
37
38
  private readonly storage;
38
39
  private readonly logger;
@@ -42,16 +43,22 @@ export declare class LeaseStore {
42
43
  private load;
43
44
  private persist;
44
45
  save(lease: StoredLease): Promise<void>;
46
+ private saveUnlocked;
47
+ delete(leaseId: string): Promise<boolean>;
48
+ private deleteUnlocked;
45
49
  get(leaseId: string): StoredLease | undefined;
46
50
  getByToken(leaseToken: string): StoredLease | undefined;
47
51
  getAll(): StoredLease[];
48
52
  getActive(): StoredLease[];
49
53
  getExpiringSoon(thresholdMs?: number): StoredLease[];
50
- delete(leaseId: string): Promise<boolean>;
51
54
  deleteByToken(leaseToken: string): Promise<boolean>;
52
55
  updateStatus(leaseId: string, status: LeaseStatus): Promise<void>;
56
+ private updateStatusUnlocked;
53
57
  cleanupExpired(): Promise<number>;
58
+ private cleanupExpiredUnlocked;
54
59
  clear(): Promise<void>;
60
+ private clearUnlocked;
61
+ private mutate;
55
62
  getMinimumTTL(): number | null;
56
63
  calculateMonitoringInterval(): number;
57
64
  private flattenIndex;
@@ -14,9 +14,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.LeaseStore = void 0;
15
15
  const index_js_1 = require("../adapters/index.js");
16
16
  const DEFAULT_STORAGE_KEY = 'totem_wots_leases';
17
+ /** Drop fields whose value is `undefined` (strict adapters reject `undefined`). */
18
+ function stripUndefined(value) {
19
+ const out = {};
20
+ for (const [k, v] of Object.entries(value)) {
21
+ if (v !== undefined)
22
+ out[k] = v;
23
+ }
24
+ return out;
25
+ }
17
26
  class LeaseStore {
18
27
  leases = new Map();
19
28
  _initialized = false;
29
+ mutationTail = Promise.resolve();
20
30
  storageKey;
21
31
  storage;
22
32
  logger;
@@ -45,11 +55,12 @@ class LeaseStore {
45
55
  }
46
56
  catch (error) {
47
57
  this.logger.error('Failed to load leases:', error);
58
+ throw error;
48
59
  }
49
60
  }
50
61
  async persist() {
51
62
  try {
52
- const leasesArray = Array.from(this.leases.values());
63
+ const leasesArray = Array.from(this.leases.values()).map((lease) => stripUndefined(lease));
53
64
  await this.storage.set(this.storageKey, leasesArray);
54
65
  }
55
66
  catch (error) {
@@ -58,12 +69,45 @@ class LeaseStore {
58
69
  }
59
70
  }
60
71
  async save(lease) {
72
+ return this.mutate(() => this.saveUnlocked(lease));
73
+ }
74
+ async saveUnlocked(lease) {
61
75
  const flatIndex = this.flattenIndex(lease.indices);
62
76
  this.logger.debug(`Storing lease: ${lease.leaseId}`);
63
77
  this.logger.debug(` indices: (addressIndex=${lease.indices.addressIndex}, l1=${lease.indices.l1}, l2=${lease.indices.l2}) [${flatIndex}/262,144]`);
64
78
  this.logger.debug(` status: ${lease.status}, TTL: ${lease.leaseTTL}ms`);
79
+ const previous = this.leases.get(lease.leaseId);
65
80
  this.leases.set(lease.leaseId, lease);
66
- await this.persist();
81
+ try {
82
+ await this.persist();
83
+ }
84
+ catch (error) {
85
+ if (previous)
86
+ this.leases.set(lease.leaseId, previous);
87
+ else
88
+ this.leases.delete(lease.leaseId);
89
+ throw error;
90
+ }
91
+ }
92
+ async delete(leaseId) {
93
+ return this.mutate(() => this.deleteUnlocked(leaseId));
94
+ }
95
+ async deleteUnlocked(leaseId) {
96
+ const lease = this.leases.get(leaseId);
97
+ if (lease) {
98
+ this.logger.debug(`Deleting lease: ${leaseId}, final status: ${lease.status}`);
99
+ }
100
+ const existed = this.leases.delete(leaseId);
101
+ if (existed) {
102
+ try {
103
+ await this.persist();
104
+ }
105
+ catch (error) {
106
+ this.leases.set(leaseId, lease);
107
+ throw error;
108
+ }
109
+ }
110
+ return existed;
67
111
  }
68
112
  get(leaseId) {
69
113
  return this.leases.get(leaseId);
@@ -84,17 +128,6 @@ class LeaseStore {
84
128
  lease.expiresAt > now &&
85
129
  lease.expiresAt <= now + thresholdMs);
86
130
  }
87
- async delete(leaseId) {
88
- const lease = this.leases.get(leaseId);
89
- if (lease) {
90
- this.logger.debug(`Deleting lease: ${leaseId}, final status: ${lease.status}`);
91
- }
92
- const existed = this.leases.delete(leaseId);
93
- if (existed) {
94
- await this.persist();
95
- }
96
- return existed;
97
- }
98
131
  async deleteByToken(leaseToken) {
99
132
  const lease = this.getByToken(leaseToken);
100
133
  if (lease) {
@@ -103,34 +136,71 @@ class LeaseStore {
103
136
  return false;
104
137
  }
105
138
  async updateStatus(leaseId, status) {
139
+ return this.mutate(() => this.updateStatusUnlocked(leaseId, status));
140
+ }
141
+ async updateStatusUnlocked(leaseId, status) {
106
142
  const lease = this.leases.get(leaseId);
107
143
  if (lease) {
108
144
  const oldStatus = lease.status;
109
145
  this.logger.debug(`Lease status change: ${leaseId} ${oldStatus} → ${status}`);
110
146
  lease.status = status;
111
- await this.persist();
147
+ try {
148
+ await this.persist();
149
+ }
150
+ catch (error) {
151
+ lease.status = oldStatus;
152
+ throw error;
153
+ }
112
154
  }
113
155
  }
114
156
  async cleanupExpired() {
157
+ return this.mutate(() => this.cleanupExpiredUnlocked());
158
+ }
159
+ async cleanupExpiredUnlocked() {
115
160
  const now = Date.now();
116
- const expiredLeaseIds = [];
161
+ const expiredLeases = [];
117
162
  for (const [leaseId, lease] of this.leases.entries()) {
118
- if (lease.expiresAt <= now && lease.status !== 'finalized') {
163
+ if (lease.expiresAt <= now && (lease.status === 'active' || lease.status === 'pending')) {
164
+ expiredLeases.push({ leaseId, status: lease.status });
119
165
  lease.status = 'expired';
120
- expiredLeaseIds.push(leaseId);
121
166
  }
122
167
  }
123
- if (expiredLeaseIds.length > 0) {
124
- this.logger.debug(`Marked ${expiredLeaseIds.length} leases as expired`);
125
- await this.persist();
168
+ if (expiredLeases.length > 0) {
169
+ this.logger.debug(`Marked ${expiredLeases.length} leases as expired`);
170
+ try {
171
+ await this.persist();
172
+ }
173
+ catch (error) {
174
+ for (const { leaseId, status } of expiredLeases)
175
+ this.leases.get(leaseId).status = status;
176
+ throw error;
177
+ }
126
178
  }
127
- return expiredLeaseIds.length;
179
+ return expiredLeases.length;
128
180
  }
129
181
  async clear() {
182
+ await this.mutate(() => this.clearUnlocked());
183
+ }
184
+ async clearUnlocked() {
185
+ const previous = new Map(this.leases);
130
186
  this.leases.clear();
131
- await this.storage.remove(this.storageKey);
187
+ try {
188
+ await this.storage.remove(this.storageKey);
189
+ }
190
+ catch (error) {
191
+ this.leases = previous;
192
+ throw error;
193
+ }
132
194
  this.logger.debug('Cleared all leases');
133
195
  }
196
+ mutate(operation) {
197
+ const prior = this.mutationTail;
198
+ let release;
199
+ const gate = new Promise((resolve) => { release = resolve; });
200
+ const run = prior.then(() => operation()).finally(() => release());
201
+ this.mutationTail = run.then(() => undefined, () => undefined);
202
+ return run;
203
+ }
134
204
  getMinimumTTL() {
135
205
  const activeLeases = this.getActive();
136
206
  if (activeLeases.length === 0)
@@ -50,19 +50,13 @@ class WatermarkStore {
50
50
  return this._initialized;
51
51
  }
52
52
  async load() {
53
- try {
54
- const watermark = await this.storage.get(this.storageKey);
55
- if (watermark) {
56
- this.state = watermark;
57
- this.logger.debug('Loaded watermark from storage');
58
- return this.state;
59
- }
60
- return null;
61
- }
62
- catch (error) {
63
- this.logger.error('Failed to load watermark:', error);
64
- return null;
53
+ const watermark = await this.storage.get(this.storageKey);
54
+ if (watermark) {
55
+ this.state = watermark;
56
+ this.logger.debug('Loaded watermark from storage');
57
+ return this.state;
65
58
  }
59
+ return null;
66
60
  }
67
61
  async save(watermark) {
68
62
  try {
@@ -23,7 +23,7 @@ export declare function serializeExtraScripts(extraScripts: Map<string, string>)
23
23
  export declare function aggregateSignatures(totemSignature: {
24
24
  publicKey: Uint8Array;
25
25
  signature: Uint8Array;
26
- }, externalSignatures: ExternalSignature[]): Uint8Array[];
26
+ }, externalSignatures: ExternalSignature[], transactionDigest: Uint8Array): Uint8Array[];
27
27
  export declare function validateExternalSignature(signature: ExternalSignature, transactionDigest: Uint8Array): boolean;
28
28
  export declare function computeScriptAddress(script: string): string;
29
29
  export { concat };
@@ -18,6 +18,7 @@ exports.validateExternalSignature = validateExternalSignature;
18
18
  exports.computeScriptAddress = computeScriptAddress;
19
19
  exports.concat = concat;
20
20
  const wasm_sync_js_1 = require("../wasm-sync.js");
21
+ const wots_js_1 = require("../wots.js");
21
22
  const mmr_js_1 = require("../mmr.js");
22
23
  const minima32_js_1 = require("../minima32.js");
23
24
  function hexToBytes(hex) {
@@ -271,12 +272,26 @@ function serializeExtraScripts(extraScripts) {
271
272
  }
272
273
  return concat(...parts);
273
274
  }
274
- function aggregateSignatures(totemSignature, externalSignatures) {
275
+ function aggregateSignatures(totemSignature, externalSignatures, transactionDigest) {
276
+ // RFC-020 C5: the aggregate must never include an unverified signature.
277
+ // The totem signature is verified against the transaction digest; external
278
+ // signatures are verified here as well as gated on their `validated` flag.
279
+ const totemValid = (() => {
280
+ try {
281
+ return (0, wots_js_1.wotsVerifyDigest)(totemSignature.signature, transactionDigest, totemSignature.publicKey);
282
+ }
283
+ catch {
284
+ return false;
285
+ }
286
+ })();
287
+ if (!totemValid) {
288
+ throw new Error('aggregateSignatures: the totem signature is invalid over the transaction digest');
289
+ }
275
290
  const allSigProofs = [];
276
291
  allSigProofs.push(serializeSignatureProof(totemSignature.publicKey, totemSignature.signature, { chunks: [] }));
277
292
  for (const extSig of externalSignatures) {
278
- if (!extSig.validated) {
279
- console.warn(`[AggregateSignatures] Skipping unvalidated signature from ${extSig.publicKey}`);
293
+ if (!extSig.validated || !validateExternalSignature(extSig, transactionDigest)) {
294
+ console.warn(`[AggregateSignatures] Skipping unverified signature from ${extSig.publicKey}`);
280
295
  continue;
281
296
  }
282
297
  const proof = extSig.proof || { chunks: [] };
@@ -295,8 +310,16 @@ function validateExternalSignature(signature, transactionDigest) {
295
310
  if (!signature.publicKey || !signature.signature) {
296
311
  return false;
297
312
  }
298
- console.log(`[ValidateSignature] Validating signature from ${signature.publicKey.substring(0, 16)}...`);
299
- return true;
313
+ // RFC-020 C5: real WOTS verification (was an unconditional `true` stub).
314
+ if (signature.signatureType && signature.signatureType !== 'wots') {
315
+ return false;
316
+ }
317
+ try {
318
+ return (0, wots_js_1.wotsVerifyDigest)(hexToBytes(signature.signature), transactionDigest, hexToBytes(signature.publicKey));
319
+ }
320
+ catch {
321
+ return false;
322
+ }
300
323
  }
301
324
  function computeScriptAddress(script) {
302
325
  const cleanScript = script.trim().toUpperCase();
@@ -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: {
@@ -16,6 +16,9 @@ exports.parseDecimalToMiniNumber = parseDecimalToMiniNumber;
16
16
  exports.serializeCoin = serializeCoin;
17
17
  exports.serializeTransaction = serializeTransaction;
18
18
  exports.computeTransactionDigest = computeTransactionDigest;
19
+ exports.computeTokenId = computeTokenId;
20
+ exports.serializeTokenDescriptor = serializeTokenDescriptor;
21
+ exports.deserializeTransaction = deserializeTransaction;
19
22
  exports.precomputeTransactionCoinID = precomputeTransactionCoinID;
20
23
  exports.createDefaultTransaction = createDefaultTransaction;
21
24
  exports.buildMinimaCoin = buildMinimaCoin;
@@ -125,11 +128,11 @@ function serializeStateVariable(sv) {
125
128
  case 'string':
126
129
  typeByte = new Uint8Array([STATETYPE_STRING]);
127
130
  if (typeof sv.value === 'string') {
128
- let bracketedValue = sv.value;
129
- if (!sv.value.startsWith('[') || !sv.value.endsWith(']')) {
130
- bracketedValue = `[${sv.value}]`;
131
- }
132
- const utf8Bytes = new TextEncoder().encode(bracketedValue);
131
+ // Minima stores a string state variable as MiniString: MiniData of the
132
+ // raw UTF-8 bytes. The `[ … ]` form is KISSVM literal syntax, NOT part
133
+ // of the stored value (the C++ StateVariable::writeDataStream writes
134
+ // mData directly; the deserializer likewise strips no brackets).
135
+ const utf8Bytes = new TextEncoder().encode(sv.value);
133
136
  dataBytes = (0, Streamable_js_1.writeMiniData)(utf8Bytes);
134
137
  }
135
138
  else {
@@ -147,7 +150,10 @@ function serializeToken(token) {
147
150
  parts.push((0, Streamable_js_1.writeHashToStream)(token.coinId));
148
151
  parts.push((0, Streamable_js_1.writeMiniData)(token.script));
149
152
  parts.push((0, Streamable_js_1.writeMiniNumber)(BigInt(token.scale), 0));
150
- parts.push((0, Streamable_js_1.writeMiniNumber)(token.totalAmount, 0));
153
+ // The minima amount is a MiniNumber carrying its own scale (Java
154
+ // Token.writeDataStream → mTokenMinimaAmount.writeDataStream). Minima's
155
+ // tokencreate always builds it from MINI_UNIT (scale 44).
156
+ parts.push((0, Streamable_js_1.writeMiniNumber)(token.totalAmount, token.totalAmountScale ?? 44));
151
157
  parts.push((0, Streamable_js_1.writeMiniData)(token.name));
152
158
  parts.push((0, Streamable_js_1.writeMiniNumber)(token.created ?? 0n, 0));
153
159
  return (0, Streamable_js_1.concat)(...parts);
@@ -233,6 +239,151 @@ function computeTransactionDigest(tx) {
233
239
  const serialized = serializeTransaction(tx);
234
240
  return (0, wasm_sync_js_1.sha3_256)(serialized);
235
241
  }
242
+ /**
243
+ * Compute a Minima **token id** — SHA3-256 of the MiniData-wrapped serialized
244
+ * Token, matching Java `Token.calculateTokenID()`. A newly created token's coins
245
+ * carry this value as their `tokenId`; token creation returns it.
246
+ */
247
+ function computeTokenId(token) {
248
+ return (0, wasm_sync_js_1.sha3_256)((0, Streamable_js_1.writeMiniData)(serializeToken(token)));
249
+ }
250
+ /** Serialize a token descriptor (Java `Token.writeDataStream()` order). */
251
+ function serializeTokenDescriptor(token) {
252
+ return serializeToken(token);
253
+ }
254
+ function txReadBytes(data, c, n) {
255
+ if (n < 0 || c.offset + n > data.length) {
256
+ throw new Error(`deserializeTransaction: unexpected end of input at offset ${c.offset} (need ${n})`);
257
+ }
258
+ const out = data.slice(c.offset, c.offset + n);
259
+ c.offset += n;
260
+ return out;
261
+ }
262
+ function txReadMiniByte(data, c) {
263
+ return txReadBytes(data, c, 1)[0];
264
+ }
265
+ /** MiniData / hash: 4-byte big-endian length + bytes. */
266
+ function txReadMiniData(data, c) {
267
+ const lenBytes = txReadBytes(data, c, 4);
268
+ const len = ((lenBytes[0] << 24) | (lenBytes[1] << 16) | (lenBytes[2] << 8) | lenBytes[3]) >>> 0;
269
+ return txReadBytes(data, c, len);
270
+ }
271
+ /** MiniNumber: 1-byte scale + 1-byte length + two's-complement unscaled bytes. */
272
+ function txReadMiniNumber(data, c) {
273
+ const scale = txReadBytes(data, c, 1)[0];
274
+ const len = txReadBytes(data, c, 1)[0];
275
+ const bytes = txReadBytes(data, c, len);
276
+ let value = 0n;
277
+ for (const b of bytes)
278
+ value = (value << 8n) | BigInt(b);
279
+ // RFC-020 P2-10 (TXB-SERIAL-003): Java's BigInteger.toByteArray() (and the
280
+ // canonical writers here) use two's-complement big-endian; a set high bit
281
+ // means the value is negative. Previously this was read as unsigned.
282
+ if (bytes.length > 0 && (bytes[0] & 0x80) !== 0) {
283
+ value -= 1n << BigInt(8 * bytes.length);
284
+ }
285
+ return { value, scale };
286
+ }
287
+ function txBytesToBigInt(bytes) {
288
+ let value = 0n;
289
+ for (const b of bytes)
290
+ value = (value << 8n) | BigInt(b);
291
+ return value;
292
+ }
293
+ /** MMREntryNumber: MiniNumber(scale) followed by MiniData(unscaled). */
294
+ function txReadMMREntryNumber(data, c) {
295
+ txReadMiniNumber(data, c); // scale (always 0 for integer entry numbers)
296
+ return txBytesToBigInt(txReadMiniData(data, c));
297
+ }
298
+ function miniNumberToDecimal(value, scale) {
299
+ if (scale === 0)
300
+ return value.toString();
301
+ const digits = value.toString().padStart(scale + 1, '0');
302
+ const whole = digits.slice(0, digits.length - scale);
303
+ const frac = digits.slice(digits.length - scale);
304
+ return `${whole}.${frac}`;
305
+ }
306
+ function txReadStateVariable(data, c) {
307
+ const port = txReadBytes(data, c, 1)[0];
308
+ const type = txReadBytes(data, c, 1)[0];
309
+ switch (type) {
310
+ case STATETYPE_HEX:
311
+ return { port, type: 'hex', value: txReadMiniData(data, c) };
312
+ case STATETYPE_NUMBER:
313
+ return { port, type: 'number', value: txReadMiniNumber(data, c).value };
314
+ case STATETYPE_STRING:
315
+ return { port, type: 'string', value: new TextDecoder().decode(txReadMiniData(data, c)) };
316
+ case STATETYPE_BOOL:
317
+ return { port, type: 'bool', value: txReadMiniByte(data, c) === 1 };
318
+ default:
319
+ throw new Error(`deserializeTransaction: unknown StateVariable type ${type}`);
320
+ }
321
+ }
322
+ function txReadToken(data, c) {
323
+ const coinId = txReadMiniData(data, c);
324
+ const script = txReadMiniData(data, c);
325
+ const { value: scale } = txReadMiniNumber(data, c);
326
+ // RFC-020 P2-10 (TXB-SERIAL-002): carry the totalAmount's own scale so a
327
+ // serialize→deserialize→serialize round-trip is byte-exact (previously the
328
+ // scale was dropped and re-defaulted to 44).
329
+ const { value: totalAmount, scale: totalAmountScale } = txReadMiniNumber(data, c);
330
+ const name = txReadMiniData(data, c);
331
+ const { value: created } = txReadMiniNumber(data, c);
332
+ return { coinId, script, scale: Number(scale), totalAmount, totalAmountScale, name, created };
333
+ }
334
+ function txReadCoin(data, c) {
335
+ const coinId = txReadMiniData(data, c);
336
+ const address = txReadMiniData(data, c);
337
+ const { value: amountUnscaled, scale } = txReadMiniNumber(data, c);
338
+ const tokenId = txReadMiniData(data, c);
339
+ const storeState = txReadMiniByte(data, c) === 1;
340
+ const mmrEntryNumber = txReadMMREntryNumber(data, c);
341
+ const spent = txReadMiniByte(data, c) === 1;
342
+ const { value: created } = txReadMiniNumber(data, c);
343
+ const stateCount = Number(txReadMiniNumber(data, c).value);
344
+ const state = [];
345
+ for (let i = 0; i < stateCount; i++)
346
+ state.push(txReadStateVariable(data, c));
347
+ const hasToken = txReadMiniByte(data, c) === 1;
348
+ const token = hasToken ? txReadToken(data, c) : null;
349
+ return {
350
+ coinId,
351
+ address,
352
+ amount: miniNumberToDecimal(amountUnscaled, scale),
353
+ tokenId,
354
+ token,
355
+ storeState,
356
+ state,
357
+ mmrEntryNumber,
358
+ spent,
359
+ created,
360
+ };
361
+ }
362
+ /**
363
+ * Deserialize a transaction produced by {@link serializeTransaction}. The
364
+ * inverse is byte-exact for canonically-encoded transactions (outputs/inputs
365
+ * written from decimal amounts, MMR entry numbers and tokens).
366
+ */
367
+ function deserializeTransaction(bytes) {
368
+ const c = { offset: 0 };
369
+ const inputCount = Number(txReadMiniNumber(bytes, c).value);
370
+ const inputs = [];
371
+ for (let i = 0; i < inputCount; i++)
372
+ inputs.push(txReadCoin(bytes, c));
373
+ const outputCount = Number(txReadMiniNumber(bytes, c).value);
374
+ const outputs = [];
375
+ for (let i = 0; i < outputCount; i++)
376
+ outputs.push(txReadCoin(bytes, c));
377
+ const stateCount = Number(txReadMiniNumber(bytes, c).value);
378
+ const state = [];
379
+ for (let i = 0; i < stateCount; i++)
380
+ state.push(txReadStateVariable(bytes, c));
381
+ const linkHash = txReadMiniData(bytes, c);
382
+ if (c.offset !== bytes.length) {
383
+ throw new Error(`deserializeTransaction: ${bytes.length - c.offset} trailing byte(s) after transaction`);
384
+ }
385
+ return { linkHash, inputs, outputs, state };
386
+ }
236
387
  function precomputeTransactionCoinID(tx) {
237
388
  if (tx.inputs.length === 0)
238
389
  return;