@totemsdk/wots-lease 0.1.3 → 0.1.4
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/axia.js +1 -0
- package/dist/journal.d.ts +1 -0
- package/dist/journal.js +45 -1
- package/dist/local.d.ts +4 -2
- package/dist/local.js +36 -9
- package/dist/stubs.d.ts +35 -12
- package/dist/stubs.js +84 -26
- package/dist/types.d.ts +4 -1
- package/package.json +17 -17
package/dist/axia.js
CHANGED
package/dist/journal.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class LeaseJournal {
|
|
|
12
12
|
constructor(storage: StorageAdapter, logger?: LoggerAdapter);
|
|
13
13
|
initialize(): Promise<void>;
|
|
14
14
|
append(entry: JournalEntry): Promise<void>;
|
|
15
|
+
private _verifyChain;
|
|
15
16
|
getAll(): JournalEntry[];
|
|
16
17
|
getByTree(treeId: string): JournalEntry[];
|
|
17
18
|
getByReservation(reservationId: string): JournalEntry | undefined;
|
package/dist/journal.js
CHANGED
|
@@ -5,8 +5,24 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.LeaseJournal = void 0;
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
8
9
|
const core_1 = require("@totemsdk/core");
|
|
9
10
|
const STORAGE_KEY = 'totem_wots_journal';
|
|
11
|
+
function hashEntry(entry) {
|
|
12
|
+
const encoder = new TextEncoder();
|
|
13
|
+
const data = {
|
|
14
|
+
t: entry.treeId,
|
|
15
|
+
b: entry.branchId,
|
|
16
|
+
i: entry.wotsIndex,
|
|
17
|
+
s: entry.status,
|
|
18
|
+
r: entry.reservationId,
|
|
19
|
+
p: entry.previousHash,
|
|
20
|
+
ts: entry.timestamp,
|
|
21
|
+
d: entry.deviceId,
|
|
22
|
+
};
|
|
23
|
+
const bytes = encoder.encode(JSON.stringify(data));
|
|
24
|
+
return (0, crypto_1.createHash)('sha256').update(bytes).digest('hex');
|
|
25
|
+
}
|
|
10
26
|
class LeaseJournal {
|
|
11
27
|
constructor(storage, logger = new core_1.NoopLogger()) {
|
|
12
28
|
this.storage = storage;
|
|
@@ -19,12 +35,40 @@ class LeaseJournal {
|
|
|
19
35
|
return;
|
|
20
36
|
const stored = await this.storage.get(STORAGE_KEY);
|
|
21
37
|
this.entries = stored ?? [];
|
|
38
|
+
if (this.entries.length > 0) {
|
|
39
|
+
const ok = this._verifyChain();
|
|
40
|
+
if (!ok) {
|
|
41
|
+
this.logger.error('[LeaseJournal] hash chain integrity check FAILED — journal may be tampered');
|
|
42
|
+
throw new Error('Journal hash chain integrity check failed');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
22
45
|
this._initialized = true;
|
|
23
46
|
}
|
|
24
47
|
async append(entry) {
|
|
48
|
+
const prev = this.entries[this.entries.length - 1];
|
|
49
|
+
entry.previousHash = prev?.hash;
|
|
50
|
+
entry.hash = hashEntry(entry);
|
|
25
51
|
this.entries.push(entry);
|
|
26
52
|
await this.storage.set(STORAGE_KEY, this.entries);
|
|
27
|
-
this.logger.debug(`[LeaseJournal] ${entry.status} treeId=${entry.treeId} idx=${entry.wotsIndex} txId=${entry.txId ?? '-'}`);
|
|
53
|
+
this.logger.debug(`[LeaseJournal] ${entry.status} treeId=${entry.treeId} idx=${entry.wotsIndex} txId=${entry.txId ?? '-'} hash=${entry.hash.slice(0, 8)}`);
|
|
54
|
+
}
|
|
55
|
+
_verifyChain() {
|
|
56
|
+
for (let i = 0; i < this.entries.length; i++) {
|
|
57
|
+
const e = this.entries[i];
|
|
58
|
+
const expectedHash = hashEntry(e);
|
|
59
|
+
if (e.hash !== expectedHash) {
|
|
60
|
+
this.logger.error(`[LeaseJournal] hash mismatch at entry ${i}: expected ${expectedHash}, stored ${e.hash}`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (i > 0) {
|
|
64
|
+
const prevHash = this.entries[i - 1].hash;
|
|
65
|
+
if (e.previousHash !== prevHash) {
|
|
66
|
+
this.logger.error(`[LeaseJournal] previousHash mismatch at entry ${i}: expected ${prevHash}, stored ${e.previousHash}`);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
28
72
|
}
|
|
29
73
|
getAll() {
|
|
30
74
|
return [...this.entries];
|
package/dist/local.d.ts
CHANGED
|
@@ -13,10 +13,12 @@ export declare class LocalLeaseProvider implements WotsLeaseProvider {
|
|
|
13
13
|
private readonly journal;
|
|
14
14
|
private _initialized;
|
|
15
15
|
private readonly deviceId;
|
|
16
|
-
/** In-memory map: reservationId → treeId (lost on restart; watermark survives). */
|
|
17
|
-
private readonly _treeIdByReservation;
|
|
18
16
|
constructor(storage: StorageAdapter, logger?: LoggerAdapter, deviceId?: string);
|
|
19
17
|
initialize(): Promise<void>;
|
|
18
|
+
/** Burn all active (non-expired) leases that have no matching treeId.
|
|
19
|
+
* On restart, leases with missing treeId (from before the treeId field was added)
|
|
20
|
+
* must be burned to prevent index reuse. */
|
|
21
|
+
private burnUnresolvedReservations;
|
|
20
22
|
private recoverExpiredReservations;
|
|
21
23
|
reserveKeyUse(params: ReserveParams): Promise<LeaseReservation>;
|
|
22
24
|
commitKeyUse(reservationId: string, txId: string): Promise<void>;
|
package/dist/local.js
CHANGED
|
@@ -22,8 +22,6 @@ class LocalLeaseProvider {
|
|
|
22
22
|
constructor(storage, logger = new core_1.NoopLogger(), deviceId = 'local') {
|
|
23
23
|
this.logger = logger;
|
|
24
24
|
this._initialized = false;
|
|
25
|
-
/** In-memory map: reservationId → treeId (lost on restart; watermark survives). */
|
|
26
|
-
this._treeIdByReservation = new Map();
|
|
27
25
|
this.watermark = new watermark_js_1.WotsWatermarkStore(storage, logger);
|
|
28
26
|
this.leaseStore = new core_2.LeaseStore(storage, logger);
|
|
29
27
|
this.journal = new journal_js_1.LeaseJournal(storage, logger);
|
|
@@ -36,13 +34,37 @@ class LocalLeaseProvider {
|
|
|
36
34
|
await this.leaseStore.initialize();
|
|
37
35
|
await this.journal.initialize();
|
|
38
36
|
await this.recoverExpiredReservations();
|
|
37
|
+
await this.burnUnresolvedReservations();
|
|
39
38
|
this._initialized = true;
|
|
40
39
|
}
|
|
40
|
+
/** Burn all active (non-expired) leases that have no matching treeId.
|
|
41
|
+
* On restart, leases with missing treeId (from before the treeId field was added)
|
|
42
|
+
* must be burned to prevent index reuse. */
|
|
43
|
+
async burnUnresolvedReservations() {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
for (const lease of this.leaseStore.getAll()) {
|
|
46
|
+
if (lease.status === 'active' && lease.expiresAt >= now && !lease.treeId) {
|
|
47
|
+
this.logger.warn(`[LocalLeaseProvider] Burning unresolved lease ${lease.leaseId} (no treeId)`);
|
|
48
|
+
await this.watermark.markUnavailable('default', lease.indices, 'burned');
|
|
49
|
+
await this.leaseStore.updateStatus(lease.leaseId, 'cancelled');
|
|
50
|
+
await this.journal.append({
|
|
51
|
+
treeId: 'default',
|
|
52
|
+
branchId: 'default',
|
|
53
|
+
wotsIndex: (0, watermark_js_1.flatIndex)(lease.indices),
|
|
54
|
+
indices: lease.indices,
|
|
55
|
+
status: 'burned',
|
|
56
|
+
reservationId: lease.leaseId,
|
|
57
|
+
timestamp: now,
|
|
58
|
+
deviceId: this.deviceId,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
41
63
|
async recoverExpiredReservations() {
|
|
42
64
|
const now = Date.now();
|
|
43
65
|
for (const lease of this.leaseStore.getAll()) {
|
|
44
66
|
if (lease.status === 'active' && lease.expiresAt < now) {
|
|
45
|
-
const treeId =
|
|
67
|
+
const treeId = lease.treeId ?? 'default';
|
|
46
68
|
await this.watermark.markUnavailable(treeId, lease.indices, 'reserved-expired');
|
|
47
69
|
await this.leaseStore.updateStatus(lease.leaseId, 'expired');
|
|
48
70
|
await this.journal.append({
|
|
@@ -51,6 +73,7 @@ class LocalLeaseProvider {
|
|
|
51
73
|
wotsIndex: (0, watermark_js_1.flatIndex)(lease.indices),
|
|
52
74
|
indices: lease.indices,
|
|
53
75
|
status: 'reserved-expired',
|
|
76
|
+
reservationId: lease.leaseId,
|
|
54
77
|
timestamp: now,
|
|
55
78
|
deviceId: this.deviceId,
|
|
56
79
|
});
|
|
@@ -62,7 +85,9 @@ class LocalLeaseProvider {
|
|
|
62
85
|
await this.initialize();
|
|
63
86
|
const treeId = params.treeId;
|
|
64
87
|
const ttlMs = params.ttlMs ?? 120000;
|
|
88
|
+
// Atomically get the next available index AND mark it reserved in the watermark
|
|
65
89
|
const indices = this.watermark.getNextIndices(treeId);
|
|
90
|
+
await this.watermark.markUnavailable(treeId, indices, 'reserved');
|
|
66
91
|
if (params.deviceId) {
|
|
67
92
|
const range = this.getDeviceRange(params.deviceId);
|
|
68
93
|
if (range && (indices.addressIndex < range.start || indices.addressIndex > range.end)) {
|
|
@@ -74,6 +99,7 @@ class LocalLeaseProvider {
|
|
|
74
99
|
const lease = {
|
|
75
100
|
leaseId: reservationId,
|
|
76
101
|
leaseToken: reservationId,
|
|
102
|
+
treeId,
|
|
77
103
|
indices,
|
|
78
104
|
expiresAt,
|
|
79
105
|
status: 'active',
|
|
@@ -81,14 +107,13 @@ class LocalLeaseProvider {
|
|
|
81
107
|
leaseTTL: ttlMs,
|
|
82
108
|
};
|
|
83
109
|
await this.leaseStore.save(lease);
|
|
84
|
-
// Track treeId in memory so commit/burn can reference the right watermark tree
|
|
85
|
-
this._treeIdByReservation.set(reservationId, treeId);
|
|
86
110
|
await this.journal.append({
|
|
87
111
|
treeId,
|
|
88
112
|
branchId: params.branchId ?? 'default',
|
|
89
113
|
wotsIndex: (0, watermark_js_1.flatIndex)(indices),
|
|
90
114
|
indices,
|
|
91
115
|
status: 'reserved',
|
|
116
|
+
reservationId,
|
|
92
117
|
payloadHash: params.payloadHash,
|
|
93
118
|
timestamp: Date.now(),
|
|
94
119
|
deviceId: params.deviceId ?? this.deviceId,
|
|
@@ -101,17 +126,19 @@ class LocalLeaseProvider {
|
|
|
101
126
|
const lease = this.leaseStore.get(reservationId);
|
|
102
127
|
if (!lease)
|
|
103
128
|
throw new errors_js_1.LeaseNotFoundError(reservationId);
|
|
104
|
-
|
|
129
|
+
if (lease.status === 'finalized')
|
|
130
|
+
return;
|
|
131
|
+
const treeId = lease.treeId ?? 'default';
|
|
105
132
|
const indices = lease.indices;
|
|
106
133
|
await this.watermark.markUnavailable(treeId, indices, 'committed');
|
|
107
134
|
await this.leaseStore.updateStatus(reservationId, 'finalized');
|
|
108
|
-
this._treeIdByReservation.delete(reservationId);
|
|
109
135
|
await this.journal.append({
|
|
110
136
|
treeId,
|
|
111
137
|
branchId: 'default',
|
|
112
138
|
wotsIndex: (0, watermark_js_1.flatIndex)(indices),
|
|
113
139
|
indices,
|
|
114
140
|
status: 'committed',
|
|
141
|
+
reservationId,
|
|
115
142
|
txId,
|
|
116
143
|
timestamp: Date.now(),
|
|
117
144
|
deviceId: this.deviceId,
|
|
@@ -123,11 +150,10 @@ class LocalLeaseProvider {
|
|
|
123
150
|
const lease = this.leaseStore.get(reservationId);
|
|
124
151
|
if (!lease)
|
|
125
152
|
throw new errors_js_1.LeaseNotFoundError(reservationId);
|
|
126
|
-
const treeId =
|
|
153
|
+
const treeId = lease.treeId ?? 'default';
|
|
127
154
|
const indices = lease.indices;
|
|
128
155
|
await this.watermark.markUnavailable(treeId, indices, 'burned');
|
|
129
156
|
await this.leaseStore.updateStatus(reservationId, 'cancelled');
|
|
130
|
-
this._treeIdByReservation.delete(reservationId);
|
|
131
157
|
this.logger.warn(`[LocalLeaseProvider] Burning reservation ${reservationId}: ${reason}`);
|
|
132
158
|
await this.journal.append({
|
|
133
159
|
treeId,
|
|
@@ -135,6 +161,7 @@ class LocalLeaseProvider {
|
|
|
135
161
|
wotsIndex: (0, watermark_js_1.flatIndex)(indices),
|
|
136
162
|
indices,
|
|
137
163
|
status: 'burned',
|
|
164
|
+
reservationId,
|
|
138
165
|
timestamp: Date.now(),
|
|
139
166
|
deviceId: this.deviceId,
|
|
140
167
|
});
|
package/dist/stubs.d.ts
CHANGED
|
@@ -1,23 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Implementations for Layers 3–5 of the WotsLeaseProvider hierarchy.
|
|
3
|
+
*
|
|
4
|
+
* Layer 3 (PersonalLeaseNodeProvider) — HTTP client for a personal @totemsdk/lookup-node
|
|
5
|
+
* that has lease coordination enabled ({ lease: { enabled: true } }).
|
|
6
|
+
* The node exposes REST endpoints under /v1/lease/*.
|
|
7
|
+
*
|
|
8
|
+
* Layers 4–5 (P2PQuorumLeaseProvider, OnchainWatermarkProvider) — require
|
|
9
|
+
* distributed infrastructure not yet available; they throw descriptive errors.
|
|
4
10
|
*/
|
|
5
11
|
import type { WotsLeaseProvider, ReserveParams, LeaseReservation, LeaseCertificate, LocalWatermark, SyncResult, PersonalLeaseNodeConfig } from './types.js';
|
|
6
12
|
/**
|
|
7
|
-
* Layer 3 — personal
|
|
13
|
+
* Layer 3 — personal lookup-node lease coordinator.
|
|
14
|
+
*
|
|
15
|
+
* Calls the HTTP REST API exposed by a running @totemsdk/lookup-node that has
|
|
16
|
+
* lease coordination enabled. All write operations are forwarded to the node's
|
|
17
|
+
* LeaseCoordinator so the node acts as the source-of-truth watermark journal
|
|
18
|
+
* for high-value transactions.
|
|
8
19
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
20
|
+
* Wrap with HybridLeaseProvider so local reservations always succeed even when
|
|
21
|
+
* the personal node is temporarily unreachable:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* const provider = new HybridLeaseProvider({
|
|
25
|
+
* local: new LocalLeaseProvider(storage),
|
|
26
|
+
* node: new PersonalLeaseNodeProvider({ nodeUrl, nodePubkey }),
|
|
27
|
+
* threshold: 10, // escalate to node for txns >= 10 MIN
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
12
30
|
*/
|
|
13
31
|
export declare class PersonalLeaseNodeProvider implements WotsLeaseProvider {
|
|
14
|
-
private readonly
|
|
32
|
+
private readonly baseUrl;
|
|
33
|
+
private readonly nodePubkey;
|
|
34
|
+
private readonly authToken?;
|
|
15
35
|
constructor(config: PersonalLeaseNodeConfig);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
36
|
+
private headers;
|
|
37
|
+
private post;
|
|
38
|
+
private get;
|
|
39
|
+
reserveKeyUse(params: ReserveParams): Promise<LeaseReservation>;
|
|
40
|
+
commitKeyUse(reservationId: string, txId: string): Promise<void>;
|
|
41
|
+
burnReservation(reservationId: string, reason: string): Promise<void>;
|
|
42
|
+
getLocalWatermark(treeId: string): Promise<LocalWatermark>;
|
|
43
|
+
publishWatermark(treeId: string): Promise<void>;
|
|
21
44
|
syncLeaseJournal(): Promise<SyncResult>;
|
|
22
45
|
verifyLeaseCertificate(cert?: LeaseCertificate): Promise<boolean>;
|
|
23
46
|
}
|
package/dist/stubs.js
CHANGED
|
@@ -1,44 +1,102 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Implementations for Layers 3–5 of the WotsLeaseProvider hierarchy.
|
|
4
|
+
*
|
|
5
|
+
* Layer 3 (PersonalLeaseNodeProvider) — HTTP client for a personal @totemsdk/lookup-node
|
|
6
|
+
* that has lease coordination enabled ({ lease: { enabled: true } }).
|
|
7
|
+
* The node exposes REST endpoints under /v1/lease/*.
|
|
8
|
+
*
|
|
9
|
+
* Layers 4–5 (P2PQuorumLeaseProvider, OnchainWatermarkProvider) — require
|
|
10
|
+
* distributed infrastructure not yet available; they throw descriptive errors.
|
|
5
11
|
*/
|
|
6
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
13
|
exports.OnchainWatermarkProvider = exports.P2PQuorumLeaseProvider = exports.PersonalLeaseNodeProvider = void 0;
|
|
8
14
|
const errors_js_1 = require("./errors.js");
|
|
9
15
|
/**
|
|
10
|
-
* Layer 3 — personal
|
|
16
|
+
* Layer 3 — personal lookup-node lease coordinator.
|
|
17
|
+
*
|
|
18
|
+
* Calls the HTTP REST API exposed by a running @totemsdk/lookup-node that has
|
|
19
|
+
* lease coordination enabled. All write operations are forwarded to the node's
|
|
20
|
+
* LeaseCoordinator so the node acts as the source-of-truth watermark journal
|
|
21
|
+
* for high-value transactions.
|
|
22
|
+
*
|
|
23
|
+
* Wrap with HybridLeaseProvider so local reservations always succeed even when
|
|
24
|
+
* the personal node is temporarily unreachable:
|
|
11
25
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* const provider = new HybridLeaseProvider({
|
|
28
|
+
* local: new LocalLeaseProvider(storage),
|
|
29
|
+
* node: new PersonalLeaseNodeProvider({ nodeUrl, nodePubkey }),
|
|
30
|
+
* threshold: 10, // escalate to node for txns >= 10 MIN
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
15
33
|
*/
|
|
16
34
|
class PersonalLeaseNodeProvider {
|
|
17
35
|
constructor(config) {
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
this.baseUrl = config.nodeUrl.replace(/\/$/, '');
|
|
37
|
+
this.nodePubkey = config.nodePubkey;
|
|
38
|
+
this.authToken = config.authToken;
|
|
39
|
+
}
|
|
40
|
+
headers() {
|
|
41
|
+
const h = { 'Content-Type': 'application/json' };
|
|
42
|
+
if (this.authToken)
|
|
43
|
+
h['Authorization'] = `Bearer ${this.authToken}`;
|
|
44
|
+
return h;
|
|
45
|
+
}
|
|
46
|
+
async post(path, body) {
|
|
47
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: this.headers(),
|
|
50
|
+
body: JSON.stringify(body),
|
|
51
|
+
});
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const text = await res.text().catch(() => '');
|
|
54
|
+
throw new Error(`PersonalLeaseNodeProvider: ${path} HTTP ${res.status} — ${text.slice(0, 200)}`);
|
|
55
|
+
}
|
|
56
|
+
return res.json();
|
|
57
|
+
}
|
|
58
|
+
async get(path) {
|
|
59
|
+
const res = await fetch(`${this.baseUrl}${path}`, { headers: this.headers() });
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const text = await res.text().catch(() => '');
|
|
62
|
+
throw new Error(`PersonalLeaseNodeProvider: GET ${path} HTTP ${res.status} — ${text.slice(0, 200)}`);
|
|
63
|
+
}
|
|
64
|
+
return res.json();
|
|
65
|
+
}
|
|
66
|
+
async reserveKeyUse(params) {
|
|
67
|
+
const data = await this.post('/v1/lease/reserve', {
|
|
68
|
+
treeId: params.treeId,
|
|
69
|
+
branchId: params.branchId,
|
|
70
|
+
deviceId: params.deviceId,
|
|
71
|
+
ttlMs: params.ttlMs,
|
|
72
|
+
payloadHash: params.payloadHash,
|
|
73
|
+
purpose: params.purpose,
|
|
74
|
+
});
|
|
75
|
+
return { ...data.reservation, certificate: data.certificate };
|
|
76
|
+
}
|
|
77
|
+
async commitKeyUse(reservationId, txId) {
|
|
78
|
+
const data = await this.post('/v1/lease/commit', { reservationId, txId });
|
|
79
|
+
if (!data.ok)
|
|
80
|
+
throw new Error(`PersonalLeaseNodeProvider: commit failed — ${data.error ?? 'unknown'}`);
|
|
81
|
+
}
|
|
82
|
+
async burnReservation(reservationId, reason) {
|
|
83
|
+
const data = await this.post('/v1/lease/burn', { reservationId, reason });
|
|
84
|
+
if (!data.ok)
|
|
85
|
+
throw new Error(`PersonalLeaseNodeProvider: burn failed — ${data.error ?? 'unknown'}`);
|
|
86
|
+
}
|
|
87
|
+
async getLocalWatermark(treeId) {
|
|
88
|
+
return this.get(`/v1/lease/watermark/${encodeURIComponent(treeId)}`);
|
|
89
|
+
}
|
|
90
|
+
async publishWatermark(treeId) {
|
|
91
|
+
await this.post(`/v1/lease/watermark/${encodeURIComponent(treeId)}/publish`, {});
|
|
92
|
+
}
|
|
93
|
+
async syncLeaseJournal() {
|
|
94
|
+
return this.get('/v1/lease/journal/sync');
|
|
37
95
|
}
|
|
38
96
|
async verifyLeaseCertificate(cert) {
|
|
39
97
|
if (!cert)
|
|
40
98
|
return false;
|
|
41
|
-
if (cert.issuedBy !== this.
|
|
99
|
+
if (cert.issuedBy !== this.nodePubkey)
|
|
42
100
|
return false;
|
|
43
101
|
if (cert.expiresAt <= Date.now())
|
|
44
102
|
return false;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @totemsdk/wots-lease — canonical v3 watermark types and WotsLeaseProvider interface
|
|
3
3
|
*/
|
|
4
|
-
export type UnavailableReason = 'committed' | 'burned' | 'reserved-expired';
|
|
4
|
+
export type UnavailableReason = 'reserved' | 'committed' | 'burned' | 'reserved-expired';
|
|
5
5
|
export interface SigningIndices {
|
|
6
6
|
addressIndex: number;
|
|
7
7
|
l1: number;
|
|
@@ -86,10 +86,13 @@ export interface JournalEntry {
|
|
|
86
86
|
wotsIndex: number;
|
|
87
87
|
indices: SigningIndices;
|
|
88
88
|
status: 'reserved' | 'committed' | 'burned' | 'reserved-expired';
|
|
89
|
+
reservationId?: string;
|
|
89
90
|
payloadHash?: string;
|
|
90
91
|
txId?: string;
|
|
91
92
|
timestamp: number;
|
|
92
93
|
deviceId: string;
|
|
94
|
+
previousHash?: string;
|
|
95
|
+
hash?: string;
|
|
93
96
|
}
|
|
94
97
|
export interface DeviceKeyRange {
|
|
95
98
|
deviceId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/wots-lease",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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,22 +11,27 @@
|
|
|
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
|
+
},
|
|
14
20
|
"files": [
|
|
15
21
|
"dist",
|
|
16
22
|
"README.md",
|
|
17
23
|
"LICENSE"
|
|
18
24
|
],
|
|
19
25
|
"dependencies": {
|
|
20
|
-
"@totemsdk/
|
|
21
|
-
"@totemsdk/
|
|
26
|
+
"@totemsdk/core": "^1.1.0",
|
|
27
|
+
"@totemsdk/lookup-protocol": "^0.1.3"
|
|
22
28
|
},
|
|
23
29
|
"devDependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@types/jest": "^29.0.0",
|
|
30
|
+
"@types/jest": "^30.0.0",
|
|
26
31
|
"@types/node": "^20.0.0",
|
|
27
|
-
"jest": "^
|
|
32
|
+
"jest": "^30.4.2",
|
|
28
33
|
"ts-jest": "^29.0.0",
|
|
29
|
-
"typescript": "^
|
|
34
|
+
"typescript": "^7.0.2"
|
|
30
35
|
},
|
|
31
36
|
"engines": {
|
|
32
37
|
"node": ">=18.0.0"
|
|
@@ -36,13 +41,13 @@
|
|
|
36
41
|
},
|
|
37
42
|
"license": "MIT",
|
|
38
43
|
"author": "Totem SDK",
|
|
39
|
-
"homepage": "https://
|
|
44
|
+
"homepage": "https://totem.ing",
|
|
40
45
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
46
|
+
"url": "https://github.com/totem-sdk/totem-sdk/issues"
|
|
42
47
|
},
|
|
43
48
|
"repository": {
|
|
44
49
|
"type": "git",
|
|
45
|
-
"url": "git+https://github.com/
|
|
50
|
+
"url": "git+https://github.com/totem-sdk/totem-sdk.git",
|
|
46
51
|
"directory": "packages/totem-sdk/packages/wots-lease"
|
|
47
52
|
},
|
|
48
53
|
"keywords": [
|
|
@@ -54,10 +59,5 @@
|
|
|
54
59
|
"wots",
|
|
55
60
|
"kissvm",
|
|
56
61
|
"utxo"
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
"build": "tsc",
|
|
60
|
-
"clean": "rm -rf dist",
|
|
61
|
-
"test": "jest --passWithNoTests"
|
|
62
|
-
}
|
|
63
|
-
}
|
|
62
|
+
]
|
|
63
|
+
}
|