@totemsdk/wots-lease 0.1.5 → 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.
@@ -0,0 +1,60 @@
1
+ /**
2
+ * P2PQuorumLeaseProvider — Layer 4: multi-device quorum lease coordination.
3
+ *
4
+ * Safety model
5
+ * ────────────
6
+ * The local provider remains the authoritative watermark (it can only move
7
+ * forward). The quorum adds a second, independent check: before a reservation
8
+ * is handed to the caller, at least `minAttestations` peers must confirm that
9
+ * the same (treeId, indices) slot is free in *their* view of the world. This
10
+ * catches the failure mode where two devices share a seed and both believe
11
+ * they own the next slot — the peers see the double-reservation and refuse
12
+ * to attest.
13
+ *
14
+ * Wire protocol
15
+ * ─────────────
16
+ * Peers speak the same LEASE_RESERVE / LEASE_COMMIT / LEASE_BURN /
17
+ * LEASE_WATERMARK message shapes as the lookup protocol, so any peer that
18
+ * implements that protocol can participate: a personal lookup node, another
19
+ * device running this provider, or an in-memory test peer.
20
+ *
21
+ * Failure semantics
22
+ * ─────────────────
23
+ * - If quorum cannot be reached, the local reservation is burned and
24
+ * QuorumUnavailableError is thrown. The slot is NOT reusable (burned),
25
+ * which is the safe direction: a slot that might have been attested by
26
+ * some peers must never be handed out again.
27
+ * - If a peer reports the slot as taken, the local reservation is burned
28
+ * and QuorumConflictError is thrown.
29
+ */
30
+ import type { WotsLeaseProvider, ReserveParams, LeaseReservation, LeaseCertificate, LocalWatermark, SyncResult, QuorumAttestation, P2PQuorumLeaseProviderConfig } from './types.js';
31
+ export declare class P2PQuorumLeaseProvider implements WotsLeaseProvider {
32
+ private readonly peers;
33
+ private readonly minAttestations;
34
+ private readonly local;
35
+ private readonly requestTimeoutMs;
36
+ private readonly requireQuorumOnCommit;
37
+ private _initialized;
38
+ constructor(config: P2PQuorumLeaseProviderConfig);
39
+ initialize(): Promise<void>;
40
+ private ensureInit;
41
+ reserveKeyUse(params: ReserveParams): Promise<LeaseReservation>;
42
+ /**
43
+ * Collect quorum attestations for a reservation that was already made
44
+ * locally (used by HybridLeaseProvider so the local slot and the attested
45
+ * slot are the same). Throws QuorumUnavailableError / QuorumConflictError
46
+ * on failure — the caller owns the local reservation and must burn it.
47
+ */
48
+ attestKeyUse(params: ReserveParams, indices: {
49
+ addressIndex: number;
50
+ l1: number;
51
+ l2: number;
52
+ }): Promise<QuorumAttestation[]>;
53
+ commitKeyUse(reservationId: string, txId: string): Promise<void>;
54
+ burnReservation(reservationId: string, reason: string): Promise<void>;
55
+ private broadcastAck;
56
+ getLocalWatermark(treeId: string): Promise<LocalWatermark>;
57
+ publishWatermark(treeId: string): Promise<void>;
58
+ syncLeaseJournal(): Promise<SyncResult>;
59
+ verifyLeaseCertificate(cert?: LeaseCertificate): Promise<boolean>;
60
+ }
package/dist/quorum.js ADDED
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ /**
3
+ * P2PQuorumLeaseProvider — Layer 4: multi-device quorum lease coordination.
4
+ *
5
+ * Safety model
6
+ * ────────────
7
+ * The local provider remains the authoritative watermark (it can only move
8
+ * forward). The quorum adds a second, independent check: before a reservation
9
+ * is handed to the caller, at least `minAttestations` peers must confirm that
10
+ * the same (treeId, indices) slot is free in *their* view of the world. This
11
+ * catches the failure mode where two devices share a seed and both believe
12
+ * they own the next slot — the peers see the double-reservation and refuse
13
+ * to attest.
14
+ *
15
+ * Wire protocol
16
+ * ─────────────
17
+ * Peers speak the same LEASE_RESERVE / LEASE_COMMIT / LEASE_BURN /
18
+ * LEASE_WATERMARK message shapes as the lookup protocol, so any peer that
19
+ * implements that protocol can participate: a personal lookup node, another
20
+ * device running this provider, or an in-memory test peer.
21
+ *
22
+ * Failure semantics
23
+ * ─────────────────
24
+ * - If quorum cannot be reached, the local reservation is burned and
25
+ * QuorumUnavailableError is thrown. The slot is NOT reusable (burned),
26
+ * which is the safe direction: a slot that might have been attested by
27
+ * some peers must never be handed out again.
28
+ * - If a peer reports the slot as taken, the local reservation is burned
29
+ * and QuorumConflictError is thrown.
30
+ */
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.P2PQuorumLeaseProvider = void 0;
33
+ const errors_js_1 = require("./errors.js");
34
+ function sameIndices(a, b) {
35
+ return a.addressIndex === b.addressIndex && a.l1 === b.l1 && a.l2 === b.l2;
36
+ }
37
+ class P2PQuorumLeaseProvider {
38
+ constructor(config) {
39
+ this._initialized = false;
40
+ this.peers = config.peers;
41
+ this.minAttestations = config.minAttestations ?? 1;
42
+ this.local = config.local;
43
+ this.requestTimeoutMs = config.requestTimeoutMs ?? 5000;
44
+ this.requireQuorumOnCommit = config.requireQuorumOnCommit ?? false;
45
+ }
46
+ async initialize() {
47
+ if (this._initialized)
48
+ return;
49
+ await this.local.initialize();
50
+ this._initialized = true;
51
+ }
52
+ async ensureInit() {
53
+ if (!this._initialized)
54
+ await this.initialize();
55
+ }
56
+ // -------------------------------------------------------------------------
57
+ // Reserve — local first, then quorum attestation
58
+ // -------------------------------------------------------------------------
59
+ async reserveKeyUse(params) {
60
+ await this.ensureInit();
61
+ // 1. Local reservation (authoritative, monotonic watermark).
62
+ const reservation = await this.local.reserveKeyUse(params);
63
+ // 2. Collect attestations from peers for the SAME indices.
64
+ // On failure the local reservation is burned — the slot is never reused.
65
+ let attestations;
66
+ try {
67
+ attestations = await this.attestKeyUse(params, reservation.indices);
68
+ }
69
+ catch (err) {
70
+ const reason = err instanceof errors_js_1.QuorumConflictError
71
+ ? `quorum conflict: ${err.message}`
72
+ : 'quorum not reached';
73
+ await this.local.burnReservation(reservation.reservationId, reason);
74
+ throw err;
75
+ }
76
+ const certificate = {
77
+ reservationId: reservation.reservationId,
78
+ treeId: params.treeId,
79
+ branchId: params.branchId,
80
+ deviceId: params.deviceId,
81
+ indices: reservation.indices,
82
+ purpose: params.purpose,
83
+ payloadHash: params.payloadHash,
84
+ issuedBy: 'p2p-quorum',
85
+ issuedAt: Date.now(),
86
+ expiresAt: reservation.expiresAt,
87
+ signature: '',
88
+ attestations,
89
+ };
90
+ return { ...reservation, certificate };
91
+ }
92
+ /**
93
+ * Collect quorum attestations for a reservation that was already made
94
+ * locally (used by HybridLeaseProvider so the local slot and the attested
95
+ * slot are the same). Throws QuorumUnavailableError / QuorumConflictError
96
+ * on failure — the caller owns the local reservation and must burn it.
97
+ */
98
+ async attestKeyUse(params, indices) {
99
+ await this.ensureInit();
100
+ const attestations = [];
101
+ const conflicts = [];
102
+ await Promise.all(this.peers.map(async (peer) => {
103
+ try {
104
+ const resp = await peer.request({
105
+ type: 'LEASE_RESERVE',
106
+ payload: {
107
+ treeId: params.treeId,
108
+ branchId: params.branchId,
109
+ deviceId: params.deviceId,
110
+ ttlMs: params.ttlMs,
111
+ payloadHash: params.payloadHash,
112
+ purpose: params.purpose,
113
+ indices,
114
+ },
115
+ }, this.requestTimeoutMs);
116
+ if (resp.type === 'ERROR') {
117
+ conflicts.push(String(resp.payload.message ?? 'peer error'));
118
+ return;
119
+ }
120
+ const body = resp.payload;
121
+ const peerIndices = body.reservation?.indices;
122
+ if (!peerIndices || !sameIndices(peerIndices, indices)) {
123
+ conflicts.push('peer returned mismatched indices');
124
+ return;
125
+ }
126
+ attestations.push({
127
+ peerId: peer.peerId,
128
+ indices,
129
+ expiresAt: body.reservation?.expiresAt ?? Date.now() + (params.ttlMs ?? 120000),
130
+ });
131
+ }
132
+ catch {
133
+ // Peer unreachable — counted as no attestation.
134
+ }
135
+ }));
136
+ if (conflicts.length > 0) {
137
+ throw new errors_js_1.QuorumConflictError(params.treeId, indices);
138
+ }
139
+ if (attestations.length < this.minAttestations) {
140
+ throw new errors_js_1.QuorumUnavailableError(this.minAttestations, attestations.length);
141
+ }
142
+ return attestations;
143
+ }
144
+ // -------------------------------------------------------------------------
145
+ // Commit / burn — local authoritative, broadcast to peers
146
+ // -------------------------------------------------------------------------
147
+ async commitKeyUse(reservationId, txId) {
148
+ await this.ensureInit();
149
+ await this.local.commitKeyUse(reservationId, txId);
150
+ const acks = await this.broadcastAck('LEASE_COMMIT', { reservationId, txId });
151
+ if (this.requireQuorumOnCommit && acks < this.minAttestations) {
152
+ throw new errors_js_1.QuorumUnavailableError(this.minAttestations, acks);
153
+ }
154
+ }
155
+ async burnReservation(reservationId, reason) {
156
+ await this.ensureInit();
157
+ await this.local.burnReservation(reservationId, reason);
158
+ await this.broadcastAck('LEASE_BURN', { reservationId, reason });
159
+ }
160
+ async broadcastAck(type, payload) {
161
+ let acks = 0;
162
+ await Promise.all(this.peers.map(async (peer) => {
163
+ try {
164
+ const resp = await peer.request({ type, payload }, this.requestTimeoutMs);
165
+ if (resp.type !== 'ERROR')
166
+ acks++;
167
+ }
168
+ catch {
169
+ // Peer unreachable — no ack.
170
+ }
171
+ }));
172
+ return acks;
173
+ }
174
+ // -------------------------------------------------------------------------
175
+ // Watermark / journal
176
+ // -------------------------------------------------------------------------
177
+ async getLocalWatermark(treeId) {
178
+ await this.ensureInit();
179
+ return this.local.getLocalWatermark(treeId);
180
+ }
181
+ async publishWatermark(treeId) {
182
+ await this.ensureInit();
183
+ const wm = await this.local.getLocalWatermark(treeId);
184
+ await Promise.all(this.peers.map(async (peer) => {
185
+ try {
186
+ await peer.request({
187
+ type: 'LEASE_WATERMARK',
188
+ payload: {
189
+ treeId,
190
+ addressCursor: wm.addressCursor,
191
+ l1Cursor: wm.l1Cursor,
192
+ l2Cursor: wm.l2Cursor,
193
+ unavailableCount: wm.unavailableCount,
194
+ lastSyncTimestamp: wm.lastSyncTimestamp ?? Date.now(),
195
+ },
196
+ }, this.requestTimeoutMs);
197
+ }
198
+ catch {
199
+ // Peer unreachable — watermark publish is best-effort.
200
+ }
201
+ }));
202
+ }
203
+ async syncLeaseJournal() {
204
+ await this.ensureInit();
205
+ const conflicts = [];
206
+ let advancedTo;
207
+ for (const treeId of this.local.listTrees()) {
208
+ const localWm = await this.local.getLocalWatermark(treeId);
209
+ for (const peer of this.peers) {
210
+ try {
211
+ const resp = await peer.request({
212
+ type: 'LEASE_WATERMARK',
213
+ payload: { treeId, query: true },
214
+ }, this.requestTimeoutMs);
215
+ if (resp.type === 'ERROR')
216
+ continue;
217
+ const p = resp.payload;
218
+ if (typeof p.addressCursor !== 'number')
219
+ continue;
220
+ const remoteFlat = p.addressCursor * 64 * 64 + p.l1Cursor * 64 + p.l2Cursor;
221
+ const localFlat = localWm.addressCursor * 64 * 64 + localWm.l1Cursor * 64 + localWm.l2Cursor;
222
+ if (remoteFlat > localFlat) {
223
+ const advanced = await this.local.advanceToRemoteWatermark(treeId, {
224
+ addressCursor: p.addressCursor,
225
+ l1Cursor: p.l1Cursor,
226
+ l2Cursor: p.l2Cursor,
227
+ });
228
+ if (advanced) {
229
+ advancedTo = {
230
+ addressIndex: p.addressCursor,
231
+ l1: p.l1Cursor,
232
+ l2: p.l2Cursor,
233
+ };
234
+ }
235
+ }
236
+ else if (remoteFlat < localFlat) {
237
+ conflicts.push({
238
+ treeId,
239
+ localIndex: localFlat,
240
+ remoteIndex: remoteFlat,
241
+ timestamp: Date.now(),
242
+ });
243
+ }
244
+ }
245
+ catch {
246
+ // Peer unreachable — skip.
247
+ }
248
+ }
249
+ }
250
+ return { synced: conflicts.length === 0, conflicts, advancedTo };
251
+ }
252
+ async verifyLeaseCertificate(cert) {
253
+ if (!cert)
254
+ return false;
255
+ if (cert.expiresAt <= Date.now())
256
+ return false;
257
+ const attestations = cert.attestations ?? [];
258
+ if (attestations.length < this.minAttestations)
259
+ return false;
260
+ return attestations.every((a) => sameIndices(a.indices, cert.indices) &&
261
+ a.expiresAt > Date.now());
262
+ }
263
+ }
264
+ exports.P2PQuorumLeaseProvider = P2PQuorumLeaseProvider;
package/dist/stubs.d.ts CHANGED
@@ -5,10 +5,12 @@
5
5
  * that has lease coordination enabled ({ lease: { enabled: true } }).
6
6
  * The node exposes REST endpoints under /v1/lease/*.
7
7
  *
8
- * Layers 4–5 (P2PQuorumLeaseProvider, OnchainWatermarkProvider) — require
9
- * distributed infrastructure not yet available; they throw descriptive errors.
8
+ * Layers 4–5 (P2PQuorumLeaseProvider, OnchainWatermarkProvider) — implemented in
9
+ * quorum.ts and onchain.ts; re-exported here for backward compatibility.
10
10
  */
11
11
  import type { WotsLeaseProvider, ReserveParams, LeaseReservation, LeaseCertificate, LocalWatermark, SyncResult, PersonalLeaseNodeConfig } from './types.js';
12
+ export { P2PQuorumLeaseProvider } from './quorum.js';
13
+ export { OnchainWatermarkProvider } from './onchain.js';
12
14
  /**
13
15
  * Layer 3 — personal lookup-node lease coordinator.
14
16
  *
@@ -44,23 +46,3 @@ export declare class PersonalLeaseNodeProvider implements WotsLeaseProvider {
44
46
  syncLeaseJournal(): Promise<SyncResult>;
45
47
  verifyLeaseCertificate(cert?: LeaseCertificate): Promise<boolean>;
46
48
  }
47
- /** Layer 4 — p2p replicated lease witnesses. */
48
- export declare class P2PQuorumLeaseProvider implements WotsLeaseProvider {
49
- reserveKeyUse(_params: ReserveParams): Promise<LeaseReservation>;
50
- commitKeyUse(_reservationId: string, _txId: string): Promise<void>;
51
- burnReservation(_reservationId: string, _reason: string): Promise<void>;
52
- getLocalWatermark(_treeId: string): Promise<LocalWatermark>;
53
- publishWatermark(_treeId: string): Promise<void>;
54
- syncLeaseJournal(): Promise<SyncResult>;
55
- verifyLeaseCertificate(_cert?: LeaseCertificate): Promise<boolean>;
56
- }
57
- /** Layer 5 — on-chain watermark coin. */
58
- export declare class OnchainWatermarkProvider implements WotsLeaseProvider {
59
- reserveKeyUse(_params: ReserveParams): Promise<LeaseReservation>;
60
- commitKeyUse(_reservationId: string, _txId: string): Promise<void>;
61
- burnReservation(_reservationId: string, _reason: string): Promise<void>;
62
- getLocalWatermark(_treeId: string): Promise<LocalWatermark>;
63
- publishWatermark(_treeId: string): Promise<void>;
64
- syncLeaseJournal(): Promise<SyncResult>;
65
- verifyLeaseCertificate(_cert?: LeaseCertificate): Promise<boolean>;
66
- }
package/dist/stubs.js CHANGED
@@ -6,12 +6,15 @@
6
6
  * that has lease coordination enabled ({ lease: { enabled: true } }).
7
7
  * The node exposes REST endpoints under /v1/lease/*.
8
8
  *
9
- * Layers 4–5 (P2PQuorumLeaseProvider, OnchainWatermarkProvider) — require
10
- * distributed infrastructure not yet available; they throw descriptive errors.
9
+ * Layers 4–5 (P2PQuorumLeaseProvider, OnchainWatermarkProvider) — implemented in
10
+ * quorum.ts and onchain.ts; re-exported here for backward compatibility.
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.OnchainWatermarkProvider = exports.P2PQuorumLeaseProvider = exports.PersonalLeaseNodeProvider = void 0;
14
- const errors_js_1 = require("./errors.js");
13
+ exports.PersonalLeaseNodeProvider = exports.OnchainWatermarkProvider = exports.P2PQuorumLeaseProvider = void 0;
14
+ var quorum_js_1 = require("./quorum.js");
15
+ Object.defineProperty(exports, "P2PQuorumLeaseProvider", { enumerable: true, get: function () { return quorum_js_1.P2PQuorumLeaseProvider; } });
16
+ var onchain_js_1 = require("./onchain.js");
17
+ Object.defineProperty(exports, "OnchainWatermarkProvider", { enumerable: true, get: function () { return onchain_js_1.OnchainWatermarkProvider; } });
15
18
  /**
16
19
  * Layer 3 — personal lookup-node lease coordinator.
17
20
  *
@@ -104,53 +107,3 @@ class PersonalLeaseNodeProvider {
104
107
  }
105
108
  }
106
109
  exports.PersonalLeaseNodeProvider = PersonalLeaseNodeProvider;
107
- /** Layer 4 — p2p replicated lease witnesses. */
108
- class P2PQuorumLeaseProvider {
109
- reserveKeyUse(_params) {
110
- throw new errors_js_1.P2PQuorumNotImplementedError();
111
- }
112
- commitKeyUse(_reservationId, _txId) {
113
- throw new errors_js_1.P2PQuorumNotImplementedError();
114
- }
115
- burnReservation(_reservationId, _reason) {
116
- throw new errors_js_1.P2PQuorumNotImplementedError();
117
- }
118
- getLocalWatermark(_treeId) {
119
- throw new errors_js_1.P2PQuorumNotImplementedError();
120
- }
121
- publishWatermark(_treeId) {
122
- throw new errors_js_1.P2PQuorumNotImplementedError();
123
- }
124
- syncLeaseJournal() {
125
- throw new errors_js_1.P2PQuorumNotImplementedError();
126
- }
127
- verifyLeaseCertificate(_cert) {
128
- throw new errors_js_1.P2PQuorumNotImplementedError();
129
- }
130
- }
131
- exports.P2PQuorumLeaseProvider = P2PQuorumLeaseProvider;
132
- /** Layer 5 — on-chain watermark coin. */
133
- class OnchainWatermarkProvider {
134
- reserveKeyUse(_params) {
135
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
136
- }
137
- commitKeyUse(_reservationId, _txId) {
138
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
139
- }
140
- burnReservation(_reservationId, _reason) {
141
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
142
- }
143
- getLocalWatermark(_treeId) {
144
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
145
- }
146
- publishWatermark(_treeId) {
147
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
148
- }
149
- syncLeaseJournal() {
150
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
151
- }
152
- verifyLeaseCertificate(_cert) {
153
- throw new errors_js_1.OnchainWatermarkNotImplementedError();
154
- }
155
- }
156
- exports.OnchainWatermarkProvider = OnchainWatermarkProvider;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @totemsdk/wots-lease — canonical v3 watermark types and WotsLeaseProvider interface
3
3
  */
4
+ import type { LocalLeaseProvider } from './local.js';
4
5
  export type UnavailableReason = 'reserved' | 'committed' | 'burned' | 'reserved-expired';
5
6
  export interface SigningIndices {
6
7
  addressIndex: number;
@@ -22,6 +23,12 @@ export interface WotsWatermarkState {
22
23
  version: 3;
23
24
  trees: Record<string, TreeWatermark>;
24
25
  }
26
+ export interface QuorumAttestation {
27
+ peerId: string;
28
+ indices: SigningIndices;
29
+ expiresAt: number;
30
+ signature?: string;
31
+ }
25
32
  export interface LeaseCertificate {
26
33
  reservationId: string;
27
34
  treeId: string;
@@ -34,6 +41,10 @@ export interface LeaseCertificate {
34
41
  issuedAt: number;
35
42
  expiresAt: number;
36
43
  signature: string;
44
+ /** Layer 4 — quorum attestations collected from P2P peers. */
45
+ attestations?: QuorumAttestation[];
46
+ /** Layer 5 — content hash of the on-chain watermark TX (sha3-256 of TxPoW bytes). */
47
+ txpowid?: string;
37
48
  }
38
49
  export interface LeaseReservation {
39
50
  reservationId: string;
@@ -105,3 +116,84 @@ export interface PersonalLeaseNodeConfig {
105
116
  nodePubkey: string;
106
117
  authToken?: string;
107
118
  }
119
+ /**
120
+ * Layer 4 — P2P quorum lease coordination.
121
+ *
122
+ * `peers` is the set of quorum members this device coordinates with. Each
123
+ * entry is a transport-agnostic RPC handle: the provider sends the same
124
+ * LEASE_RESERVE / LEASE_COMMIT / LEASE_BURN wire messages used by the
125
+ * lookup protocol, so any peer that speaks that protocol can participate
126
+ * (lookup nodes, other devices, or in-memory test peers).
127
+ */
128
+ export interface QuorumPeer {
129
+ peerId: string;
130
+ request(message: {
131
+ type: 'LEASE_RESERVE' | 'LEASE_COMMIT' | 'LEASE_BURN' | 'LEASE_WATERMARK';
132
+ payload: Record<string, unknown>;
133
+ }, timeoutMs?: number): Promise<{
134
+ type: string;
135
+ payload: Record<string, unknown>;
136
+ }>;
137
+ }
138
+ export interface P2PQuorumLeaseProviderConfig {
139
+ /** Quorum members to coordinate with (excluding self). */
140
+ peers: QuorumPeer[];
141
+ /** Minimum attestations required for a reservation to be considered quorum-approved. Default: 1. */
142
+ minAttestations?: number;
143
+ /** Local provider used for the authoritative local watermark + journal. */
144
+ local: LocalLeaseProvider;
145
+ /** Timeout per peer request. Default: 5_000. */
146
+ requestTimeoutMs?: number;
147
+ /** Require quorum approval on commit as well as reserve. Default: false. */
148
+ requireQuorumOnCommit?: boolean;
149
+ }
150
+ /**
151
+ * Layer 5 — on-chain watermark anchoring.
152
+ *
153
+ * `chain` is any ChainStateProvider (hosted, Minima RPC, or lookup node).
154
+ * The provider spends a dedicated watermark coin whose STATE(0) holds the
155
+ * flat watermark cursor; every publish advances it on-chain so the watermark
156
+ * is verifiable by third parties without trusting this device.
157
+ */
158
+ export interface OnchainWatermarkProviderConfig {
159
+ /** Chain access for coin queries, proofs, and broadcasting. */
160
+ chain: {
161
+ getCoin(coinId: string): Promise<{
162
+ coinid: string;
163
+ address: string;
164
+ amount: string;
165
+ tokenid: string;
166
+ state?: unknown[];
167
+ } | null>;
168
+ getProof(coinId: string): Promise<{
169
+ data: unknown;
170
+ }>;
171
+ broadcastTxPoW(txpowHex: string): Promise<{
172
+ success: boolean;
173
+ txpowid?: string;
174
+ message?: string;
175
+ }>;
176
+ getTip?(): Promise<{
177
+ block: number;
178
+ }>;
179
+ };
180
+ /** Coin ID of the dedicated watermark coin. */
181
+ watermarkCoinId: string;
182
+ /** Address the watermark coin currently sits at (spending address). */
183
+ watermarkAddress: string;
184
+ /** Token ID of the watermark coin. Default: '0x00'. */
185
+ tokenId?: string;
186
+ /** Amount of the watermark coin in MIN base units. Default: '1'. */
187
+ amount?: string;
188
+ /** Local provider used for the authoritative local watermark + journal. */
189
+ local: LocalLeaseProvider;
190
+ /** Signer for the watermark coin's script (SIGNEDBY digest). */
191
+ signer: {
192
+ publicKeyDigest: string;
193
+ sign(message: Uint8Array): Promise<Uint8Array>;
194
+ };
195
+ /** Port holding the flat watermark cursor in the coin state. Default: 0. */
196
+ statePort?: number;
197
+ /** Minimum blocks between on-chain publishes (rate limit). Default: 1. */
198
+ minBlocksBetweenPublishes?: number;
199
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totemsdk/wots-lease",
3
- "version": "0.1.5",
3
+ "version": "1.0.0",
4
4
  "description": "WOTS key-use coordination — canonical v3 watermark, provider-based lease safety layers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,20 +11,17 @@
11
11
  "import": "./dist/index.js"
12
12
  }
13
13
  },
14
- "scripts": {
15
- "build": "tsc",
16
- "prepare": "tsc",
17
- "clean": "rm -rf dist",
18
- "test": "jest --passWithNoTests"
19
- },
20
14
  "files": [
21
15
  "dist",
16
+ "rust/pkg/",
17
+ "rust/pkg-node/",
22
18
  "README.md",
23
19
  "LICENSE"
24
20
  ],
25
21
  "dependencies": {
26
- "@totemsdk/core": "^1.1.0",
27
- "@totemsdk/lookup-protocol": "^0.1.3"
22
+ "@totemsdk/lookup-protocol": "^0.1.6",
23
+ "@totemsdk/core": "^1.2.9",
24
+ "@totemsdk/txpow": "^0.1.7"
28
25
  },
29
26
  "devDependencies": {
30
27
  "@types/jest": "^30.0.0",
@@ -48,7 +45,7 @@
48
45
  "repository": {
49
46
  "type": "git",
50
47
  "url": "git+https://github.com/totem-sdk/totem-sdk.git",
51
- "directory": "packages/totem-sdk/packages/wots-lease"
48
+ "directory": "packages/wots-lease"
52
49
  },
53
50
  "keywords": [
54
51
  "totem",
@@ -57,7 +54,12 @@
57
54
  "blockchain",
58
55
  "quantum-resistant",
59
56
  "wots",
60
- "kissvm",
61
57
  "utxo"
62
- ]
63
- }
58
+ ],
59
+ "scripts": {
60
+ "build": "npm run build:wasm && tsc && node ../../scripts/clean-wasm-output-ignores.mjs",
61
+ "build:wasm": "cd rust && wasm-pack build --target bundler --out-dir pkg && wasm-pack build --target nodejs --out-dir pkg-node",
62
+ "clean": "rm -rf dist rust/pkg rust/pkg-node rust/target",
63
+ "test": "jest"
64
+ }
65
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "wots-lease-wasm",
3
+ "type": "module",
4
+ "collaborators": [
5
+ "Totem SDK Contributors"
6
+ ],
7
+ "description": "WOTS key-use coordination — canonical v3 watermark, flat index computation, monotonicity enforcement, device keyspace partitioning (Rust/WASM)",
8
+ "version": "0.1.0",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/totem-sdk/totem-sdk"
13
+ },
14
+ "files": [
15
+ "wots_lease_wasm_bg.wasm",
16
+ "wots_lease_wasm.js",
17
+ "wots_lease_wasm_bg.js",
18
+ "wots_lease_wasm.d.ts"
19
+ ],
20
+ "main": "wots_lease_wasm.js",
21
+ "homepage": "https://totem.ing",
22
+ "types": "wots_lease_wasm.d.ts",
23
+ "sideEffects": [
24
+ "./wots_lease_wasm.js",
25
+ "./snippets/*"
26
+ ]
27
+ }
@@ -0,0 +1,24 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export function allocate_device_range_wasm(device_slot: number, device_id: string): any;
5
+
6
+ export function device_slot_for_address_index_wasm(address_index: number): number;
7
+
8
+ export function flat_index_wasm(address_index: number, l1: number, l2: number): number;
9
+
10
+ export function from_flat_index_wasm(flat: number): any;
11
+
12
+ export function get_capacity_per_tree_wasm(): number;
13
+
14
+ export function get_local_watermark_wasm(state_js: any, tree_id: string): any;
15
+
16
+ export function get_next_indices_wasm(state_js: any, tree_id: string): any;
17
+
18
+ export function init(): void;
19
+
20
+ export function is_unavailable_wasm(state_js: any, tree_id: string, indices_js: any): boolean;
21
+
22
+ export function mark_unavailable_wasm(state_js: any, tree_id: string, indices_js: any, reason: string): any;
23
+
24
+ export function new_watermark_state_wasm(): any;
@@ -0,0 +1,9 @@
1
+ /* @ts-self-types="./wots_lease_wasm.d.ts" */
2
+ import * as wasm from "./wots_lease_wasm_bg.wasm";
3
+ import { __wbg_set_wasm } from "./wots_lease_wasm_bg.js";
4
+
5
+ __wbg_set_wasm(wasm);
6
+ wasm.__wbindgen_start();
7
+ export {
8
+ allocate_device_range_wasm, device_slot_for_address_index_wasm, flat_index_wasm, from_flat_index_wasm, get_capacity_per_tree_wasm, get_local_watermark_wasm, get_next_indices_wasm, init, is_unavailable_wasm, mark_unavailable_wasm, new_watermark_state_wasm
9
+ } from "./wots_lease_wasm_bg.js";