@totemsdk/wots-lease 1.0.0 → 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.
Files changed (67) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +60 -0
  3. package/dist/axia.d.ts +1 -1
  4. package/dist/axia.js +41 -28
  5. package/dist/certificate.d.ts +37 -0
  6. package/dist/certificate.js +69 -0
  7. package/dist/cjs/axia.d.ts +33 -0
  8. package/dist/cjs/axia.js +145 -0
  9. package/dist/cjs/certificate.d.ts +37 -0
  10. package/dist/cjs/certificate.js +74 -0
  11. package/dist/cjs/device.d.ts +18 -0
  12. package/dist/cjs/device.js +34 -0
  13. package/dist/cjs/errors.d.ts +71 -0
  14. package/dist/cjs/errors.js +135 -0
  15. package/dist/cjs/hybrid.d.ts +37 -0
  16. package/dist/cjs/hybrid.js +145 -0
  17. package/dist/cjs/index.d.ts +17 -0
  18. package/dist/cjs/index.js +45 -0
  19. package/dist/cjs/journal.d.ts +30 -0
  20. package/dist/cjs/journal.js +369 -0
  21. package/dist/cjs/local.d.ts +83 -0
  22. package/dist/cjs/local.js +440 -0
  23. package/dist/cjs/onchain.d.ts +54 -0
  24. package/dist/cjs/onchain.js +314 -0
  25. package/dist/cjs/package.json +1 -0
  26. package/dist/cjs/quorum.d.ts +75 -0
  27. package/dist/cjs/quorum.js +307 -0
  28. package/dist/cjs/replicated-backup.d.ts +30 -0
  29. package/dist/cjs/replicated-backup.js +52 -0
  30. package/dist/cjs/seed.d.ts +41 -0
  31. package/dist/cjs/seed.js +84 -0
  32. package/dist/cjs/stubs.d.ts +50 -0
  33. package/dist/cjs/stubs.js +125 -0
  34. package/dist/cjs/types.d.ts +272 -0
  35. package/dist/cjs/types.js +5 -0
  36. package/dist/cjs/watermark.d.ts +51 -0
  37. package/dist/cjs/watermark.js +328 -0
  38. package/dist/device.js +2 -6
  39. package/dist/errors.d.ts +26 -0
  40. package/dist/errors.js +49 -25
  41. package/dist/hybrid.d.ts +4 -1
  42. package/dist/hybrid.js +72 -26
  43. package/dist/index.d.ts +6 -2
  44. package/dist/index.js +12 -38
  45. package/dist/journal.d.ts +14 -4
  46. package/dist/journal.js +327 -49
  47. package/dist/local.d.ts +31 -1
  48. package/dist/local.js +267 -68
  49. package/dist/onchain.d.ts +3 -1
  50. package/dist/onchain.js +63 -28
  51. package/dist/quorum.d.ts +15 -0
  52. package/dist/quorum.js +54 -15
  53. package/dist/replicated-backup.d.ts +30 -0
  54. package/dist/replicated-backup.js +48 -0
  55. package/dist/seed.d.ts +41 -0
  56. package/dist/seed.js +80 -0
  57. package/dist/stubs.d.ts +2 -0
  58. package/dist/stubs.js +20 -10
  59. package/dist/types.d.ts +74 -1
  60. package/dist/types.js +1 -2
  61. package/dist/watermark.d.ts +22 -1
  62. package/dist/watermark.js +133 -16
  63. package/package.json +25 -15
  64. package/rust/pkg/package.json +3 -3
  65. package/rust/pkg/wots_lease_wasm_bg.wasm +0 -0
  66. package/rust/pkg-node/package.json +3 -3
  67. package/rust/pkg-node/wots_lease_wasm_bg.wasm +0 -0
package/dist/hybrid.js CHANGED
@@ -1,19 +1,18 @@
1
- "use strict";
2
1
  /**
3
2
  * HybridLeaseProvider — recommended production default.
4
3
  *
5
4
  * Always uses local first, then gates on node cert and/or quorum attestation
6
5
  * for transactions above a configurable value threshold.
7
6
  */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.HybridLeaseProvider = void 0;
10
- class HybridLeaseProvider {
7
+ import { signCertificate, certificateSignatureVerified } from './certificate.js';
8
+ export class HybridLeaseProvider {
11
9
  constructor(config) {
12
10
  this.local = config.local;
13
11
  this.node = config.node;
14
12
  this.quorum = config.quorum;
15
13
  this.onchain = config.onchain;
16
14
  this.threshold = config.threshold ?? Infinity;
15
+ this.certificateSigner = config.certificateSigner;
17
16
  }
18
17
  isHighValue(params) {
19
18
  if (!params.valueHint)
@@ -22,29 +21,41 @@ class HybridLeaseProvider {
22
21
  return !isNaN(val) && val >= this.threshold;
23
22
  }
24
23
  async reserveKeyUse(params) {
24
+ // Cross-instance/restore safety (RFC-013 §8/§9): before a high-value
25
+ // reservation, anchor the local cursor to the on-chain watermark so a second
26
+ // instance of the same seed cannot be handed a leaf we already used.
27
+ if (this.isHighValue(params) && this.onchain) {
28
+ try {
29
+ await this.onchain.syncLeaseJournal();
30
+ }
31
+ catch {
32
+ // Chain unreachable — local monotonic watermark still guards reuse.
33
+ }
34
+ }
25
35
  const reservation = await this.local.reserveKeyUse(params);
26
36
  if (this.isHighValue(params)) {
27
37
  // Layer 4 — quorum attestation over the SAME local slot.
28
38
  if (this.quorum) {
29
39
  try {
30
40
  const attestations = await this.quorum.attestKeyUse(params, reservation.indices);
31
- return {
32
- ...reservation,
33
- certificate: {
34
- reservationId: reservation.reservationId,
35
- treeId: params.treeId,
36
- branchId: params.branchId,
37
- deviceId: params.deviceId,
38
- indices: reservation.indices,
39
- purpose: params.purpose,
40
- payloadHash: params.payloadHash,
41
- issuedBy: 'p2p-quorum',
42
- issuedAt: Date.now(),
43
- expiresAt: reservation.expiresAt,
44
- signature: '',
45
- attestations,
46
- },
41
+ const certificate = {
42
+ reservationId: reservation.reservationId,
43
+ treeId: params.treeId,
44
+ branchId: params.branchId,
45
+ deviceId: params.deviceId,
46
+ indices: reservation.indices,
47
+ purpose: params.purpose,
48
+ payloadHash: params.payloadHash,
49
+ issuedBy: this.certificateSigner?.name ?? 'p2p-quorum',
50
+ issuedAt: Date.now(),
51
+ expiresAt: reservation.expiresAt,
52
+ signature: '',
53
+ attestations,
47
54
  };
55
+ certificate.signature = this.certificateSigner
56
+ ? await signCertificate(this.certificateSigner, certificate)
57
+ : '';
58
+ return { ...reservation, certificate };
48
59
  }
49
60
  catch {
50
61
  // Quorum unavailable — fall through to node / local only.
@@ -72,24 +83,59 @@ class HybridLeaseProvider {
72
83
  getLocalWatermark(treeId) {
73
84
  return this.local.getLocalWatermark(treeId);
74
85
  }
75
- publishWatermark(treeId) {
86
+ async publishWatermark(treeId) {
87
+ // Anchor to the chain first (authoritative cross-instance cursor), then the
88
+ // personal node, then the local no-op.
89
+ if (this.onchain) {
90
+ try {
91
+ await this.onchain.publishWatermark(treeId);
92
+ return;
93
+ }
94
+ catch {
95
+ // fall through
96
+ }
97
+ }
76
98
  if (this.node) {
77
- return this.node.publishWatermark(treeId).catch(() => this.local.publishWatermark(treeId));
99
+ try {
100
+ await this.node.publishWatermark(treeId);
101
+ return;
102
+ }
103
+ catch {
104
+ // fall through
105
+ }
78
106
  }
79
- return this.local.publishWatermark(treeId);
107
+ await this.local.publishWatermark(treeId);
80
108
  }
81
- syncLeaseJournal() {
109
+ async syncLeaseJournal() {
110
+ // Read the authoritative on-chain cursor first so a restore/second instance
111
+ // advances past every leaf published by anyone holding this seed.
112
+ if (this.onchain) {
113
+ try {
114
+ return await this.onchain.syncLeaseJournal();
115
+ }
116
+ catch {
117
+ // fall through
118
+ }
119
+ }
82
120
  if (this.node) {
83
- return this.node.syncLeaseJournal().catch(() => this.local.syncLeaseJournal());
121
+ try {
122
+ return await this.node.syncLeaseJournal();
123
+ }
124
+ catch {
125
+ // fall through
126
+ }
84
127
  }
85
128
  return this.local.syncLeaseJournal();
86
129
  }
87
130
  async verifyLeaseCertificate(cert) {
88
131
  if (!cert)
89
132
  return this.local.verifyLeaseCertificate(cert);
133
+ // Locally-assembled quorum certificates authenticate against our issuer.
134
+ if (cert.issuedBy === (this.certificateSigner?.name ?? 'p2p-quorum')) {
135
+ return certificateSignatureVerified(cert, this.certificateSigner);
136
+ }
90
137
  if (this.node)
91
138
  return this.node.verifyLeaseCertificate(cert);
92
139
  return false;
93
140
  }
94
141
  }
95
- exports.HybridLeaseProvider = HybridLeaseProvider;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- export type { UnavailableReason, SigningIndices, LeaseStatus, TreeWatermark, WotsWatermarkState, LeaseCertificate, LeaseReservation, ReserveParams, LocalWatermark, ConflictRecord, SyncResult, WotsLeaseProvider, JournalEntry, DeviceKeyRange, PersonalLeaseNodeConfig, QuorumAttestation, QuorumPeer, P2PQuorumLeaseProviderConfig, OnchainWatermarkProviderConfig, } from './types.js';
1
+ export type { UnavailableReason, SigningIndices, LeaseStatus, TreeWatermark, WotsWatermarkState, LeaseCertificate, LeaseReservation, ReserveParams, LocalWatermark, WatermarkSeed, SeedWatermarkOptions, ConflictRecord, SyncResult, WotsLeaseProvider, JournalEntry, DeviceKeyRange, PersonalLeaseNodeConfig, QuorumAttestation, QuorumPeer, CertificateSigner, P2PQuorumLeaseProviderConfig, OnchainWatermarkProviderConfig, } from './types.js';
2
2
  export { WotsWatermarkStore, flatIndex, fromFlatIndex, } from './watermark.js';
3
3
  export { LeaseJournal } from './journal.js';
4
4
  export { LocalLeaseProvider } from './local.js';
5
+ export { seedLeaseWatermark, exportLeaseWatermark } from './seed.js';
6
+ export type { SeedLeaseWatermarkParams } from './seed.js';
5
7
  export { AxiaLeaseProvider } from './axia.js';
6
8
  export type { AxiaLeaseProviderConfig } from './axia.js';
7
9
  export { HybridLeaseProvider } from './hybrid.js';
@@ -10,4 +12,6 @@ export { PersonalLeaseNodeProvider, P2PQuorumLeaseProvider, OnchainWatermarkProv
10
12
  export { P2PQuorumLeaseProvider as QuorumLeaseProvider } from './quorum.js';
11
13
  export { OnchainWatermarkProvider as ChainWatermarkProvider } from './onchain.js';
12
14
  export { allocateDeviceRange, deviceSlotForAddressIndex } from './device.js';
13
- export { WatermarkMonotonicityError, WatermarkExhaustedError, LeaseNotFoundError, IndicesUnavailableError, PersonalLeaseNodeNotConfiguredError, P2PQuorumNotImplementedError, OnchainWatermarkNotImplementedError, QuorumUnavailableError, QuorumConflictError, OnchainWatermarkError, DeviceRangeViolationError, } from './errors.js';
15
+ export { WatermarkMonotonicityError, WatermarkExhaustedError, WatermarkSeedRegressionError, WatermarkSeedRequiredError, LeaseNotFoundError, IndicesUnavailableError, PersonalLeaseNodeNotConfiguredError, P2PQuorumNotImplementedError, OnchainWatermarkNotImplementedError, QuorumUnavailableError, QuorumConflictError, OnchainWatermarkError, DeviceRangeViolationError, } from './errors.js';
16
+ export { ReplicatedLeaseJournalBackup } from './replicated-backup.js';
17
+ export type { LeaseJournalRestoreTarget } from './replicated-backup.js';
package/dist/index.js CHANGED
@@ -1,38 +1,12 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DeviceRangeViolationError = exports.OnchainWatermarkError = exports.QuorumConflictError = exports.QuorumUnavailableError = exports.OnchainWatermarkNotImplementedError = exports.P2PQuorumNotImplementedError = exports.PersonalLeaseNodeNotConfiguredError = exports.IndicesUnavailableError = exports.LeaseNotFoundError = exports.WatermarkExhaustedError = exports.WatermarkMonotonicityError = exports.deviceSlotForAddressIndex = exports.allocateDeviceRange = exports.ChainWatermarkProvider = exports.QuorumLeaseProvider = exports.OnchainWatermarkProvider = exports.P2PQuorumLeaseProvider = exports.PersonalLeaseNodeProvider = exports.HybridLeaseProvider = exports.AxiaLeaseProvider = exports.LocalLeaseProvider = exports.LeaseJournal = exports.fromFlatIndex = exports.flatIndex = exports.WotsWatermarkStore = void 0;
4
- var watermark_js_1 = require("./watermark.js");
5
- Object.defineProperty(exports, "WotsWatermarkStore", { enumerable: true, get: function () { return watermark_js_1.WotsWatermarkStore; } });
6
- Object.defineProperty(exports, "flatIndex", { enumerable: true, get: function () { return watermark_js_1.flatIndex; } });
7
- Object.defineProperty(exports, "fromFlatIndex", { enumerable: true, get: function () { return watermark_js_1.fromFlatIndex; } });
8
- var journal_js_1 = require("./journal.js");
9
- Object.defineProperty(exports, "LeaseJournal", { enumerable: true, get: function () { return journal_js_1.LeaseJournal; } });
10
- var local_js_1 = require("./local.js");
11
- Object.defineProperty(exports, "LocalLeaseProvider", { enumerable: true, get: function () { return local_js_1.LocalLeaseProvider; } });
12
- var axia_js_1 = require("./axia.js");
13
- Object.defineProperty(exports, "AxiaLeaseProvider", { enumerable: true, get: function () { return axia_js_1.AxiaLeaseProvider; } });
14
- var hybrid_js_1 = require("./hybrid.js");
15
- Object.defineProperty(exports, "HybridLeaseProvider", { enumerable: true, get: function () { return hybrid_js_1.HybridLeaseProvider; } });
16
- var stubs_js_1 = require("./stubs.js");
17
- Object.defineProperty(exports, "PersonalLeaseNodeProvider", { enumerable: true, get: function () { return stubs_js_1.PersonalLeaseNodeProvider; } });
18
- Object.defineProperty(exports, "P2PQuorumLeaseProvider", { enumerable: true, get: function () { return stubs_js_1.P2PQuorumLeaseProvider; } });
19
- Object.defineProperty(exports, "OnchainWatermarkProvider", { enumerable: true, get: function () { return stubs_js_1.OnchainWatermarkProvider; } });
20
- var quorum_js_1 = require("./quorum.js");
21
- Object.defineProperty(exports, "QuorumLeaseProvider", { enumerable: true, get: function () { return quorum_js_1.P2PQuorumLeaseProvider; } });
22
- var onchain_js_1 = require("./onchain.js");
23
- Object.defineProperty(exports, "ChainWatermarkProvider", { enumerable: true, get: function () { return onchain_js_1.OnchainWatermarkProvider; } });
24
- var device_js_1 = require("./device.js");
25
- Object.defineProperty(exports, "allocateDeviceRange", { enumerable: true, get: function () { return device_js_1.allocateDeviceRange; } });
26
- Object.defineProperty(exports, "deviceSlotForAddressIndex", { enumerable: true, get: function () { return device_js_1.deviceSlotForAddressIndex; } });
27
- var errors_js_1 = require("./errors.js");
28
- Object.defineProperty(exports, "WatermarkMonotonicityError", { enumerable: true, get: function () { return errors_js_1.WatermarkMonotonicityError; } });
29
- Object.defineProperty(exports, "WatermarkExhaustedError", { enumerable: true, get: function () { return errors_js_1.WatermarkExhaustedError; } });
30
- Object.defineProperty(exports, "LeaseNotFoundError", { enumerable: true, get: function () { return errors_js_1.LeaseNotFoundError; } });
31
- Object.defineProperty(exports, "IndicesUnavailableError", { enumerable: true, get: function () { return errors_js_1.IndicesUnavailableError; } });
32
- Object.defineProperty(exports, "PersonalLeaseNodeNotConfiguredError", { enumerable: true, get: function () { return errors_js_1.PersonalLeaseNodeNotConfiguredError; } });
33
- Object.defineProperty(exports, "P2PQuorumNotImplementedError", { enumerable: true, get: function () { return errors_js_1.P2PQuorumNotImplementedError; } });
34
- Object.defineProperty(exports, "OnchainWatermarkNotImplementedError", { enumerable: true, get: function () { return errors_js_1.OnchainWatermarkNotImplementedError; } });
35
- Object.defineProperty(exports, "QuorumUnavailableError", { enumerable: true, get: function () { return errors_js_1.QuorumUnavailableError; } });
36
- Object.defineProperty(exports, "QuorumConflictError", { enumerable: true, get: function () { return errors_js_1.QuorumConflictError; } });
37
- Object.defineProperty(exports, "OnchainWatermarkError", { enumerable: true, get: function () { return errors_js_1.OnchainWatermarkError; } });
38
- Object.defineProperty(exports, "DeviceRangeViolationError", { enumerable: true, get: function () { return errors_js_1.DeviceRangeViolationError; } });
1
+ export { WotsWatermarkStore, flatIndex, fromFlatIndex, } from './watermark.js';
2
+ export { LeaseJournal } from './journal.js';
3
+ export { LocalLeaseProvider } from './local.js';
4
+ export { seedLeaseWatermark, exportLeaseWatermark } from './seed.js';
5
+ export { AxiaLeaseProvider } from './axia.js';
6
+ export { HybridLeaseProvider } from './hybrid.js';
7
+ export { PersonalLeaseNodeProvider, P2PQuorumLeaseProvider, OnchainWatermarkProvider, } from './stubs.js';
8
+ export { P2PQuorumLeaseProvider as QuorumLeaseProvider } from './quorum.js';
9
+ export { OnchainWatermarkProvider as ChainWatermarkProvider } from './onchain.js';
10
+ export { allocateDeviceRange, deviceSlotForAddressIndex } from './device.js';
11
+ export { WatermarkMonotonicityError, WatermarkExhaustedError, WatermarkSeedRegressionError, WatermarkSeedRequiredError, LeaseNotFoundError, IndicesUnavailableError, PersonalLeaseNodeNotConfiguredError, P2PQuorumNotImplementedError, OnchainWatermarkNotImplementedError, QuorumUnavailableError, QuorumConflictError, OnchainWatermarkError, DeviceRangeViolationError, } from './errors.js';
12
+ export { ReplicatedLeaseJournalBackup } from './replicated-backup.js';
package/dist/journal.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  /**
2
- * LeaseJournal — append-only audit log for WOTS key-use events.
3
- * Preserves the full history: reserved → committed | burned | reserved-expired.
2
+ * LeaseJournal — physically append-only audit log for WOTS key-use events.
3
+ *
4
+ * Layout v1 stores one immutable record per sequence. The legacy whole-array
5
+ * key is retained as migration input/forensic history but is never rewritten.
6
+ * The SHA-256 chain format is intentionally unchanged (RFC-007 OQ3).
4
7
  */
5
8
  import type { StorageAdapter, LoggerAdapter } from '@totemsdk/core';
6
9
  import type { JournalEntry } from './types.js';
@@ -8,11 +11,18 @@ export declare class LeaseJournal {
8
11
  private readonly storage;
9
12
  private readonly logger;
10
13
  private entries;
11
- private _initialized;
14
+ private initialized;
15
+ private initializePromise;
16
+ private appendTail;
12
17
  constructor(storage: StorageAdapter, logger?: LoggerAdapter);
13
18
  initialize(): Promise<void>;
19
+ private initializeOnce;
20
+ private readLegacyEntries;
21
+ private readPhysicalRecords;
22
+ private writeRecord;
14
23
  append(entry: JournalEntry): Promise<void>;
15
- private _verifyChain;
24
+ private appendOne;
25
+ private ensureInitialized;
16
26
  getAll(): JournalEntry[];
17
27
  getByTree(treeId: string): JournalEntry[];
18
28
  getByReservation(reservationId: string): JournalEntry | undefined;