@totemsdk/wots-lease 1.0.1 → 1.0.2
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/LICENSE +202 -21
- package/NOTICE +60 -0
- package/dist/axia.js +21 -25
- package/dist/certificate.js +7 -12
- package/dist/cjs/axia.d.ts +33 -0
- package/dist/cjs/axia.js +145 -0
- package/dist/cjs/certificate.d.ts +37 -0
- package/dist/cjs/certificate.js +74 -0
- package/dist/cjs/device.d.ts +18 -0
- package/dist/cjs/device.js +34 -0
- package/dist/cjs/errors.d.ts +71 -0
- package/dist/cjs/errors.js +135 -0
- package/dist/cjs/hybrid.d.ts +37 -0
- package/dist/cjs/hybrid.js +145 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/index.js +45 -0
- package/dist/cjs/journal.d.ts +30 -0
- package/dist/cjs/journal.js +369 -0
- package/dist/cjs/local.d.ts +83 -0
- package/dist/cjs/local.js +440 -0
- package/dist/cjs/onchain.d.ts +54 -0
- package/dist/cjs/onchain.js +314 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/quorum.d.ts +75 -0
- package/dist/cjs/quorum.js +307 -0
- package/dist/cjs/replicated-backup.d.ts +30 -0
- package/dist/cjs/replicated-backup.js +52 -0
- package/dist/cjs/seed.d.ts +41 -0
- package/dist/cjs/seed.js +84 -0
- package/dist/cjs/stubs.d.ts +50 -0
- package/dist/cjs/stubs.js +125 -0
- package/dist/cjs/types.d.ts +272 -0
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/watermark.d.ts +51 -0
- package/dist/cjs/watermark.js +328 -0
- package/dist/device.js +2 -6
- package/dist/errors.js +14 -31
- package/dist/hybrid.js +4 -8
- package/dist/index.d.ts +2 -0
- package/dist/index.js +12 -43
- package/dist/journal.js +35 -38
- package/dist/local.js +29 -33
- package/dist/onchain.js +23 -27
- package/dist/quorum.js +9 -13
- package/dist/replicated-backup.d.ts +30 -0
- package/dist/replicated-backup.js +48 -0
- package/dist/seed.js +7 -11
- package/dist/stubs.js +5 -11
- package/dist/types.js +1 -2
- package/dist/watermark.js +11 -18
- package/package.json +22 -13
- package/rust/pkg/package.json +2 -2
- package/rust/pkg/wots_lease_wasm_bg.wasm +0 -0
- package/rust/pkg-node/package.json +2 -2
- package/rust/pkg-node/wots_lease_wasm_bg.wasm +0 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OnchainWatermarkProvider — Layer 5: on-chain watermark anchoring.
|
|
4
|
+
*
|
|
5
|
+
* Safety model
|
|
6
|
+
* ────────────
|
|
7
|
+
* The local provider remains the authoritative watermark. This provider adds
|
|
8
|
+
* a verifiable public record: a dedicated watermark coin whose STATE(0) holds
|
|
9
|
+
* the flat watermark cursor. Every publish spends the coin back to itself
|
|
10
|
+
* with an advanced cursor, so any third party can read the on-chain state and
|
|
11
|
+
* prove that a given signing slot was consumed — without trusting this device.
|
|
12
|
+
*
|
|
13
|
+
* The watermark coin script is `RETURN SIGNEDBY(0x<digest>)` — the same
|
|
14
|
+
* single-sig pattern used by statechain reclaim addresses. The signer is the
|
|
15
|
+
* WOTS key owner; the digest is the 32-byte public key digest.
|
|
16
|
+
*
|
|
17
|
+
* Failure semantics
|
|
18
|
+
* ─────────────────
|
|
19
|
+
* - publishWatermark fails loudly (OnchainWatermarkError) when the chain
|
|
20
|
+
* rejects the TX — the local watermark is never rolled back.
|
|
21
|
+
* - syncLeaseJournal merges the on-chain cursor into the local watermark
|
|
22
|
+
* monotonically (remote ahead → advance; remote behind → conflict record).
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.OnchainWatermarkProvider = void 0;
|
|
26
|
+
const watermark_js_1 = require("./watermark.js");
|
|
27
|
+
const errors_js_1 = require("./errors.js");
|
|
28
|
+
const core_1 = require("@totemsdk/core");
|
|
29
|
+
const txpow_1 = require("@totemsdk/txpow");
|
|
30
|
+
const certificate_js_1 = require("./certificate.js");
|
|
31
|
+
const MAX_L = 64;
|
|
32
|
+
const WATERMARK_COIN_STORAGE_KEY = 'wots-lease:onchain:watermark-coin';
|
|
33
|
+
function multiConcat(parts) {
|
|
34
|
+
if (parts.length === 0)
|
|
35
|
+
return new Uint8Array(0);
|
|
36
|
+
let result = parts[0];
|
|
37
|
+
for (let i = 1; i < parts.length; i++) {
|
|
38
|
+
result = concatBytes(result, parts[i]);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
function concatBytes(a, b) {
|
|
43
|
+
const out = new Uint8Array(a.length + b.length);
|
|
44
|
+
out.set(a, 0);
|
|
45
|
+
out.set(b, a.length);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function addressToHex(address) {
|
|
49
|
+
const upper = address.startsWith('0x') || address.startsWith('0X') ? address.slice(2) : address;
|
|
50
|
+
return upper.toUpperCase().startsWith('MX')
|
|
51
|
+
? (0, core_1.bytesToHex)((0, core_1.parseMxAddress)(address))
|
|
52
|
+
: address.replace(/^0x/i, '');
|
|
53
|
+
}
|
|
54
|
+
function flatToHex(flat) {
|
|
55
|
+
let hex = BigInt(flat).toString(16);
|
|
56
|
+
if (hex.length % 2 !== 0)
|
|
57
|
+
hex = '0' + hex;
|
|
58
|
+
return hex || '00';
|
|
59
|
+
}
|
|
60
|
+
function hexToFlat(hex) {
|
|
61
|
+
const clean = (hex.startsWith('0x') ? hex.slice(2) : hex).replace(/^0+/, '') || '0';
|
|
62
|
+
return Number(BigInt('0x' + clean));
|
|
63
|
+
}
|
|
64
|
+
function readStateCursor(state, statePort) {
|
|
65
|
+
for (const raw of state) {
|
|
66
|
+
const entry = raw;
|
|
67
|
+
if (typeof entry !== 'object' || entry === null)
|
|
68
|
+
continue;
|
|
69
|
+
if (entry.port !== statePort)
|
|
70
|
+
continue;
|
|
71
|
+
if (typeof entry.data === 'string')
|
|
72
|
+
return hexToFlat(entry.data);
|
|
73
|
+
if (entry.value !== undefined)
|
|
74
|
+
return Number(entry.value);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function stateVarJson(port, hexValue) {
|
|
79
|
+
return { port, svtype: 'hex', data: hexValue };
|
|
80
|
+
}
|
|
81
|
+
function buildWitnessBytes(sigs) {
|
|
82
|
+
const parts = [(0, core_1.writeMiniNumber)(BigInt(sigs.length), 0)];
|
|
83
|
+
for (const sig of sigs)
|
|
84
|
+
parts.push(sig);
|
|
85
|
+
parts.push((0, core_1.writeMiniNumber)(0n, 0));
|
|
86
|
+
parts.push((0, core_1.writeMiniNumber)(0n, 0));
|
|
87
|
+
return multiConcat(parts);
|
|
88
|
+
}
|
|
89
|
+
class OnchainWatermarkProvider {
|
|
90
|
+
constructor(config) {
|
|
91
|
+
this._initialized = false;
|
|
92
|
+
this._lastPublishBlock = -1;
|
|
93
|
+
this.chain = config.chain;
|
|
94
|
+
this.initialWatermarkCoinId = config.watermarkCoinId;
|
|
95
|
+
this.currentCoinId = config.watermarkCoinId;
|
|
96
|
+
this.watermarkAddress = config.watermarkAddress;
|
|
97
|
+
this.tokenId = config.tokenId ?? '0x00';
|
|
98
|
+
this.amount = config.amount ?? '1';
|
|
99
|
+
this.local = config.local;
|
|
100
|
+
this.signer = config.signer;
|
|
101
|
+
this.statePort = config.statePort ?? 0;
|
|
102
|
+
this.minBlocksBetweenPublishes = config.minBlocksBetweenPublishes ?? 1;
|
|
103
|
+
this.storage = config.storage;
|
|
104
|
+
}
|
|
105
|
+
async initialize() {
|
|
106
|
+
if (this._initialized)
|
|
107
|
+
return;
|
|
108
|
+
// Recover the advanced watermark coin identity across restarts.
|
|
109
|
+
if (this.storage) {
|
|
110
|
+
try {
|
|
111
|
+
const saved = await this.storage.get(WATERMARK_COIN_STORAGE_KEY);
|
|
112
|
+
if (saved?.coinId)
|
|
113
|
+
this.currentCoinId = saved.coinId;
|
|
114
|
+
if (typeof saved?.lastPublishBlock === 'number') {
|
|
115
|
+
this._lastPublishBlock = saved.lastPublishBlock;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Storage unavailable — fall back to the configured coin.
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
await this.local.initialize();
|
|
123
|
+
this._initialized = true;
|
|
124
|
+
}
|
|
125
|
+
async ensureInit() {
|
|
126
|
+
if (!this._initialized)
|
|
127
|
+
await this.initialize();
|
|
128
|
+
}
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
// Reserve / commit / burn — local authoritative
|
|
131
|
+
// -------------------------------------------------------------------------
|
|
132
|
+
async reserveKeyUse(params) {
|
|
133
|
+
await this.ensureInit();
|
|
134
|
+
const reservation = await this.local.reserveKeyUse(params);
|
|
135
|
+
const certificate = {
|
|
136
|
+
reservationId: reservation.reservationId,
|
|
137
|
+
treeId: params.treeId,
|
|
138
|
+
branchId: params.branchId,
|
|
139
|
+
deviceId: params.deviceId,
|
|
140
|
+
indices: reservation.indices,
|
|
141
|
+
purpose: params.purpose,
|
|
142
|
+
payloadHash: params.payloadHash,
|
|
143
|
+
issuedBy: 'onchain-watermark',
|
|
144
|
+
issuedAt: Date.now(),
|
|
145
|
+
expiresAt: reservation.expiresAt,
|
|
146
|
+
signature: '',
|
|
147
|
+
};
|
|
148
|
+
certificate.signature = await (0, certificate_js_1.signCertificate)(this.signer, certificate);
|
|
149
|
+
return { ...reservation, certificate };
|
|
150
|
+
}
|
|
151
|
+
async commitKeyUse(reservationId, txId) {
|
|
152
|
+
await this.ensureInit();
|
|
153
|
+
await this.local.commitKeyUse(reservationId, txId);
|
|
154
|
+
}
|
|
155
|
+
async burnReservation(reservationId, reason) {
|
|
156
|
+
await this.ensureInit();
|
|
157
|
+
await this.local.burnReservation(reservationId, reason);
|
|
158
|
+
}
|
|
159
|
+
// -------------------------------------------------------------------------
|
|
160
|
+
// Watermark — local read, on-chain publish
|
|
161
|
+
// -------------------------------------------------------------------------
|
|
162
|
+
async getLocalWatermark(treeId) {
|
|
163
|
+
await this.ensureInit();
|
|
164
|
+
return this.local.getLocalWatermark(treeId);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Publish the local watermark cursor on-chain by spending the watermark
|
|
168
|
+
* coin back to itself with STATE(statePort) = flat cursor.
|
|
169
|
+
*
|
|
170
|
+
* Rate-limited by minBlocksBetweenPublishes using the chain tip.
|
|
171
|
+
*/
|
|
172
|
+
async publishWatermark(treeId) {
|
|
173
|
+
await this.ensureInit();
|
|
174
|
+
const wm = await this.local.getLocalWatermark(treeId);
|
|
175
|
+
const newFlat = (0, watermark_js_1.flatIndex)({
|
|
176
|
+
addressIndex: wm.addressCursor,
|
|
177
|
+
l1: wm.l1Cursor,
|
|
178
|
+
l2: wm.l2Cursor,
|
|
179
|
+
});
|
|
180
|
+
// Rate limit: skip when the last publish was too recent.
|
|
181
|
+
try {
|
|
182
|
+
const tip = await this.chain.getTip?.();
|
|
183
|
+
if (tip && this._lastPublishBlock >= 0 && tip.block - this._lastPublishBlock < this.minBlocksBetweenPublishes) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (tip)
|
|
187
|
+
this._lastPublishBlock = tip.block;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Tip unavailable — proceed without rate limiting.
|
|
191
|
+
}
|
|
192
|
+
// Read the current on-chain cursor so the input state matches the coin
|
|
193
|
+
// anchored by the CURRENT watermark coin (which advances on every spend).
|
|
194
|
+
const coin = await this.chain.getCoin(this.currentCoinId);
|
|
195
|
+
if (!coin) {
|
|
196
|
+
throw new errors_js_1.OnchainWatermarkError(`Watermark coin not found: ${this.currentCoinId}`);
|
|
197
|
+
}
|
|
198
|
+
const currentState = coin.state ?? [];
|
|
199
|
+
const currentFlat = readStateCursor(currentState, this.statePort);
|
|
200
|
+
if (currentFlat !== null && currentFlat >= newFlat) {
|
|
201
|
+
// On-chain cursor already at or ahead of local — nothing to publish.
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const inputCoin = {
|
|
205
|
+
coinid: this.currentCoinId,
|
|
206
|
+
address: addressToHex(this.watermarkAddress),
|
|
207
|
+
amount: this.amount,
|
|
208
|
+
tokenid: this.tokenId,
|
|
209
|
+
storestate: true,
|
|
210
|
+
state: currentState,
|
|
211
|
+
};
|
|
212
|
+
const outputCoin = {
|
|
213
|
+
address: addressToHex(this.watermarkAddress),
|
|
214
|
+
amount: this.amount,
|
|
215
|
+
tokenid: this.tokenId,
|
|
216
|
+
storestate: true,
|
|
217
|
+
state: [stateVarJson(this.statePort, flatToHex(newFlat))],
|
|
218
|
+
};
|
|
219
|
+
const rolledCoinId = (0, core_1.rollCoinId)((0, core_1.hexToBytes)(this.currentCoinId), 0);
|
|
220
|
+
const tx = {
|
|
221
|
+
linkhash: '0x00',
|
|
222
|
+
inputs: [inputCoin],
|
|
223
|
+
outputs: [{ ...outputCoin, coinid: '0x' + (0, core_1.bytesToHex)(rolledCoinId) }],
|
|
224
|
+
state: [],
|
|
225
|
+
};
|
|
226
|
+
const txBytes = (0, core_1.serializeTransaction)(JSON.stringify(tx));
|
|
227
|
+
const digest = (0, core_1.computeTransactionDigest)(txBytes);
|
|
228
|
+
const sig = await this.signer.sign(digest);
|
|
229
|
+
const witnessBytes = buildWitnessBytes([sig]);
|
|
230
|
+
const prng = (0, core_1.sha3_256)(new TextEncoder().encode(`watermark:${treeId}:${this.currentCoinId}:${newFlat}`));
|
|
231
|
+
const txHex = Buffer.from((0, txpow_1.serializeTxPoW)(txBytes, witnessBytes, { prng })).toString('hex');
|
|
232
|
+
const result = await this.chain.broadcastTxPoW(txHex);
|
|
233
|
+
if (!result.success) {
|
|
234
|
+
throw new errors_js_1.OnchainWatermarkError(`Watermark publish rejected: ${result.message ?? 'unknown error'}`);
|
|
235
|
+
}
|
|
236
|
+
// Rollover: the spent watermark coin is destroyed and replaced by a fresh
|
|
237
|
+
// coin with a new coin ID (output 0 of this spend). Track and persist the
|
|
238
|
+
// ADVANCED identity so the next publish spends the live coin, not the
|
|
239
|
+
// consumed one.
|
|
240
|
+
const newCoinId = '0x' + (0, core_1.bytesToHex)(rolledCoinId);
|
|
241
|
+
this.currentCoinId = newCoinId;
|
|
242
|
+
if (this.storage) {
|
|
243
|
+
try {
|
|
244
|
+
await this.storage.set(WATERMARK_COIN_STORAGE_KEY, {
|
|
245
|
+
coinId: newCoinId,
|
|
246
|
+
lastPublishBlock: this._lastPublishBlock,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// Storage unavailable — in-memory rollover still applies this session.
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// -------------------------------------------------------------------------
|
|
255
|
+
// Sync — merge on-chain cursor into local watermark
|
|
256
|
+
// -------------------------------------------------------------------------
|
|
257
|
+
async syncLeaseJournal() {
|
|
258
|
+
await this.ensureInit();
|
|
259
|
+
const conflicts = [];
|
|
260
|
+
let advancedTo;
|
|
261
|
+
for (const treeId of this.local.listTrees()) {
|
|
262
|
+
const localWm = await this.local.getLocalWatermark(treeId);
|
|
263
|
+
const localFlat = (0, watermark_js_1.flatIndex)({
|
|
264
|
+
addressIndex: localWm.addressCursor,
|
|
265
|
+
l1: localWm.l1Cursor,
|
|
266
|
+
l2: localWm.l2Cursor,
|
|
267
|
+
});
|
|
268
|
+
let onchainFlat = null;
|
|
269
|
+
try {
|
|
270
|
+
const coin = await this.chain.getCoin(this.currentCoinId);
|
|
271
|
+
if (coin)
|
|
272
|
+
onchainFlat = readStateCursor(coin.state ?? [], this.statePort);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Chain unreachable — skip.
|
|
276
|
+
}
|
|
277
|
+
if (onchainFlat === null)
|
|
278
|
+
continue;
|
|
279
|
+
if (onchainFlat > localFlat) {
|
|
280
|
+
const addressIndex = Math.floor(onchainFlat / (MAX_L * MAX_L));
|
|
281
|
+
const rem = onchainFlat % (MAX_L * MAX_L);
|
|
282
|
+
const l1 = Math.floor(rem / MAX_L);
|
|
283
|
+
const l2 = rem % MAX_L;
|
|
284
|
+
const advanced = await this.local.advanceToRemoteWatermark(treeId, {
|
|
285
|
+
addressCursor: addressIndex,
|
|
286
|
+
l1Cursor: l1,
|
|
287
|
+
l2Cursor: l2,
|
|
288
|
+
});
|
|
289
|
+
if (advanced) {
|
|
290
|
+
advancedTo = { addressIndex, l1, l2 };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
else if (onchainFlat < localFlat) {
|
|
294
|
+
conflicts.push({
|
|
295
|
+
treeId,
|
|
296
|
+
localIndex: localFlat,
|
|
297
|
+
remoteIndex: onchainFlat,
|
|
298
|
+
timestamp: Date.now(),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return { synced: conflicts.length === 0, conflicts, advancedTo };
|
|
303
|
+
}
|
|
304
|
+
async verifyLeaseCertificate(cert) {
|
|
305
|
+
if (!cert)
|
|
306
|
+
return false;
|
|
307
|
+
if (cert.expiresAt <= Date.now())
|
|
308
|
+
return false;
|
|
309
|
+
if (!(await (0, certificate_js_1.certificateSignatureVerified)(cert, this.signer)))
|
|
310
|
+
return false;
|
|
311
|
+
return cert.issuedBy === 'onchain-watermark';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
exports.OnchainWatermarkProvider = OnchainWatermarkProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,75 @@
|
|
|
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 readonly certificateSigner?;
|
|
38
|
+
/**
|
|
39
|
+
* localReservationId -> (peerId -> the reservation id that peer issued).
|
|
40
|
+
* Commit/burn must reference each peer's own id, because a peer only knows
|
|
41
|
+
* the reservation it issued, not the local provider's id (AUD-044).
|
|
42
|
+
*/
|
|
43
|
+
private readonly peerReservationIds;
|
|
44
|
+
private _initialized;
|
|
45
|
+
constructor(config: P2PQuorumLeaseProviderConfig);
|
|
46
|
+
initialize(): Promise<void>;
|
|
47
|
+
private ensureInit;
|
|
48
|
+
reserveKeyUse(params: ReserveParams): Promise<LeaseReservation>;
|
|
49
|
+
/**
|
|
50
|
+
* Collect quorum attestations for a reservation that was already made
|
|
51
|
+
* locally (used by HybridLeaseProvider so the local slot and the attested
|
|
52
|
+
* slot are the same). Throws QuorumUnavailableError / QuorumConflictError
|
|
53
|
+
* on failure — the caller owns the local reservation and must burn it.
|
|
54
|
+
*/
|
|
55
|
+
attestKeyUse(params: ReserveParams, indices: {
|
|
56
|
+
addressIndex: number;
|
|
57
|
+
l1: number;
|
|
58
|
+
l2: number;
|
|
59
|
+
}): Promise<QuorumAttestation[]>;
|
|
60
|
+
commitKeyUse(reservationId: string, txId: string): Promise<void>;
|
|
61
|
+
burnReservation(reservationId: string, reason: string): Promise<void>;
|
|
62
|
+
/** Record which peer issued which reservation id for a local reservation. */
|
|
63
|
+
private recordPeerReservationIds;
|
|
64
|
+
/**
|
|
65
|
+
* The id to send a peer for a local reservation. Peers only know the id they
|
|
66
|
+
* issued; fall back to the local id only when no peer id was recorded (e.g.
|
|
67
|
+
* attestations gathered outside `reserveKeyUse`).
|
|
68
|
+
*/
|
|
69
|
+
private peerReservationIdFor;
|
|
70
|
+
private broadcastAck;
|
|
71
|
+
getLocalWatermark(treeId: string): Promise<LocalWatermark>;
|
|
72
|
+
publishWatermark(treeId: string): Promise<void>;
|
|
73
|
+
syncLeaseJournal(): Promise<SyncResult>;
|
|
74
|
+
verifyLeaseCertificate(cert?: LeaseCertificate): Promise<boolean>;
|
|
75
|
+
}
|