@ownables/platform-browser 0.8.1 → 0.9.1

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 (113) hide show
  1. package/README.md +3 -3
  2. package/dist/core/src/index.d.ts +3 -3
  3. package/dist/core/src/index.d.ts.map +1 -1
  4. package/dist/core/src/index.js +3 -3
  5. package/dist/core/src/index.js.map +1 -1
  6. package/dist/core/src/interfaces/core.d.ts +7 -7
  7. package/dist/core/src/interfaces/core.d.ts.map +1 -1
  8. package/dist/core/src/logger.d.ts +1 -1
  9. package/dist/core/src/services/AnchorValidation.service.d.ts +10 -0
  10. package/dist/core/src/services/AnchorValidation.service.d.ts.map +1 -0
  11. package/dist/core/src/services/AnchorValidation.service.js +115 -0
  12. package/dist/core/src/services/AnchorValidation.service.js.map +1 -0
  13. package/dist/core/src/services/EventChain.service.d.ts +9 -10
  14. package/dist/core/src/services/EventChain.service.d.ts.map +1 -1
  15. package/dist/core/src/services/EventChain.service.js +16 -11
  16. package/dist/core/src/services/EventChain.service.js.map +1 -1
  17. package/dist/core/src/services/Ownable.service.d.ts +45 -19
  18. package/dist/core/src/services/Ownable.service.d.ts.map +1 -1
  19. package/dist/core/src/services/Ownable.service.js +278 -113
  20. package/dist/core/src/services/Ownable.service.js.map +1 -1
  21. package/dist/core/src/services/Polling.service.d.ts +3 -3
  22. package/dist/core/src/services/Polling.service.d.ts.map +1 -1
  23. package/dist/core/src/services/Polling.service.js +10 -10
  24. package/dist/core/src/services/Polling.service.js.map +1 -1
  25. package/dist/core/src/services/Progress.service.d.ts +7 -0
  26. package/dist/core/src/services/Progress.service.d.ts.map +1 -0
  27. package/dist/core/src/services/Progress.service.js +12 -0
  28. package/dist/core/src/services/Progress.service.js.map +1 -0
  29. package/dist/core/src/services/ReplayAuthority.service.d.ts +13 -6
  30. package/dist/core/src/services/ReplayAuthority.service.d.ts.map +1 -1
  31. package/dist/core/src/services/ReplayAuthority.service.js +140 -8
  32. package/dist/core/src/services/ReplayAuthority.service.js.map +1 -1
  33. package/dist/core/src/services/SIWE.service.d.ts +2 -2
  34. package/dist/core/src/services/SIWE.service.d.ts.map +1 -1
  35. package/dist/core/src/services/SIWE.service.js +26 -27
  36. package/dist/core/src/services/SIWE.service.js.map +1 -1
  37. package/dist/core/src/services/WorkerRPC.service.d.ts.map +1 -1
  38. package/dist/core/src/services/WorkerRPC.service.js +3 -1
  39. package/dist/core/src/services/WorkerRPC.service.js.map +1 -1
  40. package/dist/core/src/types/AnchorValidation.d.ts +55 -0
  41. package/dist/core/src/types/AnchorValidation.d.ts.map +1 -0
  42. package/dist/core/src/types/AnchorValidation.js +2 -0
  43. package/dist/core/src/types/AnchorValidation.js.map +1 -0
  44. package/dist/core/src/types/MessageInfo.d.ts +1 -1
  45. package/dist/core/src/types/OwnableRuntime.d.ts +7 -1
  46. package/dist/core/src/types/OwnableRuntime.d.ts.map +1 -1
  47. package/dist/core/src/types/Replay.d.ts +63 -6
  48. package/dist/core/src/types/Replay.d.ts.map +1 -1
  49. package/dist/core/src/utils/calculateOwnablePackageCid.d.ts +6 -0
  50. package/dist/core/src/utils/calculateOwnablePackageCid.d.ts.map +1 -0
  51. package/dist/core/src/{services/OwnablePackageCid.service.js → utils/calculateOwnablePackageCid.js} +8 -8
  52. package/dist/core/src/utils/calculateOwnablePackageCid.js.map +1 -0
  53. package/dist/core/src/utils.d.ts +2 -0
  54. package/dist/core/src/utils.d.ts.map +1 -0
  55. package/dist/core/src/utils.js +2 -0
  56. package/dist/core/src/utils.js.map +1 -0
  57. package/dist/platform-browser/src/assets/workerSource.d.ts.map +1 -0
  58. package/dist/platform-browser/src/assets/workerSource.js.map +1 -0
  59. package/dist/platform-browser/src/constants/hub.d.ts +2 -0
  60. package/dist/platform-browser/src/constants/hub.d.ts.map +1 -0
  61. package/dist/platform-browser/src/constants/hub.js +2 -0
  62. package/dist/platform-browser/src/constants/hub.js.map +1 -0
  63. package/dist/platform-browser/src/index.d.ts +10 -8
  64. package/dist/platform-browser/src/index.d.ts.map +1 -1
  65. package/dist/platform-browser/src/index.js +10 -8
  66. package/dist/platform-browser/src/index.js.map +1 -1
  67. package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.d.ts +5 -0
  68. package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.d.ts.map +1 -0
  69. package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.js +7 -0
  70. package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.js.map +1 -0
  71. package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.d.ts +7 -0
  72. package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.d.ts.map +1 -0
  73. package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.js +11 -0
  74. package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.js.map +1 -0
  75. package/dist/platform-browser/src/services/Hub.service.d.ts +28 -6
  76. package/dist/platform-browser/src/services/Hub.service.d.ts.map +1 -1
  77. package/dist/platform-browser/src/services/Hub.service.js +111 -49
  78. package/dist/platform-browser/src/services/Hub.service.js.map +1 -1
  79. package/dist/platform-browser/src/services/IDB.service.d.ts +1 -1
  80. package/dist/platform-browser/src/services/IDB.service.d.ts.map +1 -1
  81. package/dist/platform-browser/src/services/IDB.service.js +18 -19
  82. package/dist/platform-browser/src/services/IDB.service.js.map +1 -1
  83. package/dist/platform-browser/src/services/LocalStorage.service.d.ts.map +1 -1
  84. package/dist/platform-browser/src/services/LocalStorage.service.js +1 -1
  85. package/dist/platform-browser/src/services/LocalStorage.service.js.map +1 -1
  86. package/dist/platform-browser/src/services/Package.service.d.ts +8 -10
  87. package/dist/platform-browser/src/services/Package.service.d.ts.map +1 -1
  88. package/dist/platform-browser/src/services/Package.service.js +72 -77
  89. package/dist/platform-browser/src/services/Package.service.js.map +1 -1
  90. package/dist/platform-browser/src/services/Relay.service.d.ts +7 -7
  91. package/dist/platform-browser/src/services/Relay.service.d.ts.map +1 -1
  92. package/dist/platform-browser/src/services/Relay.service.js +25 -25
  93. package/dist/platform-browser/src/services/Relay.service.js.map +1 -1
  94. package/package.json +5 -3
  95. package/dist/core/src/ownableErrorMessage.d.ts +0 -2
  96. package/dist/core/src/ownableErrorMessage.d.ts.map +0 -1
  97. package/dist/core/src/ownableErrorMessage.js +0 -23
  98. package/dist/core/src/ownableErrorMessage.js.map +0 -1
  99. package/dist/core/src/progress.d.ts +0 -3
  100. package/dist/core/src/progress.d.ts.map +0 -1
  101. package/dist/core/src/progress.js +0 -8
  102. package/dist/core/src/progress.js.map +0 -1
  103. package/dist/core/src/services/OwnablePackageCid.service.d.ts +0 -6
  104. package/dist/core/src/services/OwnablePackageCid.service.d.ts.map +0 -1
  105. package/dist/core/src/services/OwnablePackageCid.service.js.map +0 -1
  106. package/dist/core/src/services/workerSource.d.ts.map +0 -1
  107. package/dist/core/src/services/workerSource.js.map +0 -1
  108. package/dist/platform-browser/src/utils/calculateCid.d.ts +0 -2
  109. package/dist/platform-browser/src/utils/calculateCid.d.ts.map +0 -1
  110. package/dist/platform-browser/src/utils/calculateCid.js +0 -8
  111. package/dist/platform-browser/src/utils/calculateCid.js.map +0 -1
  112. /package/dist/{core/src/services → platform-browser/src/assets}/workerSource.d.ts +0 -0
  113. /package/dist/{core/src/services → platform-browser/src/assets}/workerSource.js +0 -0
@@ -1,31 +1,26 @@
1
- import { EventChain, Event, Binary } from "eqty-core";
2
- import { encode } from "cbor-x";
3
- import { withProgress } from "../progress.js";
4
- import WorkerRPC from "./WorkerRPC.service.js";
5
- import { DEFAULT_WORKER_SOURCE } from "./workerSource.js";
6
- import { dedupeIndexedPublicEvents, publicEventReplayKey } from "./ReplayAuthority.service.js";
1
+ import { EventChain, Event, Binary } from 'eqty-core';
2
+ import { encode } from 'cbor-x';
3
+ import { ProgressService } from './Progress.service.js';
7
4
  export default class OwnableService {
5
+ SNAPSHOT_INTERVAL = 50;
6
+ PUBLIC_EVENT_REPLAY_STORE_SUFFIX = '.public-event-replays';
8
7
  stateStore;
9
8
  eventChains;
10
9
  eqty;
11
10
  packages;
12
11
  runtimeSource;
13
- logger;
14
12
  runtimeRpc;
15
- SNAPSHOT_INTERVAL = 50;
16
- PUBLIC_EVENT_REPLAY_STORE_SUFFIX = ".public-event-replays";
17
- constructor(stateStore, eventChains, eqty, packages, runtimeSource = {
18
- getWorkerSource: () => DEFAULT_WORKER_SOURCE,
19
- }, logger = console, runtimeRpc = {
20
- create: (id) => new WorkerRPC(id),
21
- }) {
22
- this.stateStore = stateStore;
23
- this.eventChains = eventChains;
24
- this.eqty = eqty;
25
- this.packages = packages;
26
- this.runtimeSource = runtimeSource;
27
- this.logger = logger;
28
- this.runtimeRpc = runtimeRpc;
13
+ replay;
14
+ logger;
15
+ constructor(deps) {
16
+ this.stateStore = deps.stateStore;
17
+ this.eventChains = deps.eventChains;
18
+ this.eqty = deps.anchorProvider;
19
+ this.packages = deps.packages;
20
+ this.runtimeSource = deps.runtimeSource;
21
+ this.runtimeRpc = deps.runtimeRpc;
22
+ this.replay = deps.replay;
23
+ this.logger = deps.logger ?? console;
29
24
  }
30
25
  _rpc = new Map();
31
26
  get anchoring() {
@@ -59,19 +54,20 @@ export default class OwnableService {
59
54
  if (this._rpc.has(id))
60
55
  return;
61
56
  const js = this.runtimeSource.getWorkerSource();
62
- const wasm = (await this.packages.getAsset(cid, "ownable_bg.wasm", (fr, file) => fr.readAsArrayBuffer(file)));
57
+ const wasm = (await this.packages.getAsset(cid, 'ownable_bg.wasm', (fr, file) => fr.readAsArrayBuffer(file)));
63
58
  const rpc = this.runtimeRpc.create(id);
64
59
  await rpc.initialize(js, new Uint8Array(wasm));
65
60
  this._rpc.set(id, rpc);
66
61
  }
67
62
  async create(pkg, onProgress) {
63
+ const progress = new ProgressService(onProgress);
68
64
  const address = this.eqty.address;
69
65
  const networkId = this.eqty.chainId;
70
66
  const chain = EventChain.create(address, networkId);
71
67
  const anchors = [];
72
68
  if (pkg.isDynamic || this.anchoring) {
73
69
  const msg = {
74
- "@context": "instantiate_msg.json",
70
+ '@context': 'instantiate_msg.json',
75
71
  ownable_id: chain.id,
76
72
  package: pkg.cid,
77
73
  network_id: networkId,
@@ -79,7 +75,7 @@ export default class OwnableService {
79
75
  ...(pkg.title ? { name: pkg.title } : {}),
80
76
  ...(pkg.description ? { description: pkg.description } : {}),
81
77
  };
82
- await withProgress(onProgress)("signEvent", () => this.eqty.sign(new Event(msg).addTo(chain)));
78
+ await progress.step('signEvent', () => this.eqty.sign(new Event(msg).addTo(chain)));
83
79
  }
84
80
  if (this.anchoring) {
85
81
  const hash = chain.latestHash.hex;
@@ -88,7 +84,7 @@ export default class OwnableService {
88
84
  if (anchors.length > 0) {
89
85
  // Queue anchors and submit as single tx
90
86
  await this.eqty.anchor(...anchors);
91
- const txHash = await withProgress(onProgress)("anchorEvent", () => this.eqty.submitAnchors());
87
+ const txHash = await progress.step('anchorEvent', () => this.eqty.submitAnchors());
92
88
  return txHash ? { chain, txHash } : { chain };
93
89
  }
94
90
  return { chain };
@@ -119,19 +115,17 @@ export default class OwnableService {
119
115
  const keys = await this.stateStore.keys(snapshotStoreId);
120
116
  if (keys.length > 3) {
121
117
  const sortedKeys = keys
122
- .map((key) => parseInt(key.replace("snapshot_", "")))
118
+ .map((key) => parseInt(key.replace('snapshot_', '')))
123
119
  .sort((a, b) => b - a);
124
120
  // Delete oldest snapshots, keep the 3 most recent
125
- const keysToDelete = sortedKeys
126
- .slice(3)
127
- .map((index) => `snapshot_${index}`);
121
+ const keysToDelete = sortedKeys.slice(3).map((index) => `snapshot_${index}`);
128
122
  for (const key of keysToDelete) {
129
123
  await this.stateStore.delete(snapshotStoreId, key);
130
124
  }
131
125
  }
132
126
  }
133
127
  catch (error) {
134
- this.logger.error("Error creating snapshot:", error);
128
+ this.logger.error('Error creating snapshot:', error);
135
129
  }
136
130
  }
137
131
  async getLatestSnapshot(chainId) {
@@ -145,7 +139,7 @@ export default class OwnableService {
145
139
  if (snapshots.length === 0)
146
140
  return null;
147
141
  const latestKey = snapshots
148
- .map((key) => parseInt(key.replace("snapshot_", "")))
142
+ .map((key) => parseInt(key.replace('snapshot_', '')))
149
143
  .sort((a, b) => b - a)[0];
150
144
  return await this.stateStore.get(snapshotStoreId, `snapshot_${latestKey}`);
151
145
  }
@@ -157,7 +151,7 @@ export default class OwnableService {
157
151
  }
158
152
  const snapshots = await this.stateStore.keys(snapshotStoreId);
159
153
  const sortedKeys = snapshots
160
- .map((key) => parseInt(key.replace("snapshot_", "")))
154
+ .map((key) => parseInt(key.replace('snapshot_', '')))
161
155
  .sort((a, b) => a - b);
162
156
  return Promise.all(sortedKeys.map((index) => this.stateStore.get(snapshotStoreId, `snapshot_${index}`)));
163
157
  }
@@ -171,18 +165,96 @@ export default class OwnableService {
171
165
  publicEventReplayStoreId(chainId) {
172
166
  return `ownable:${chainId}${this.PUBLIC_EVENT_REPLAY_STORE_SUFFIX}`;
173
167
  }
168
+ replayRecord(event, status, sources) {
169
+ return {
170
+ replayKey: this.replay.key(event),
171
+ event,
172
+ status,
173
+ sources: [...new Set(sources)],
174
+ };
175
+ }
176
+ mergeReplayRecordSources(current, next) {
177
+ return current.includes(next) ? current : [...current, next];
178
+ }
179
+ async ensureReplayStore(chainId) {
180
+ const replayStoreId = this.publicEventReplayStoreId(chainId);
181
+ if (!(await this.stateStore.hasStore(replayStoreId))) {
182
+ await this.stateStore.createStore(replayStoreId);
183
+ }
184
+ return replayStoreId;
185
+ }
186
+ async listReplayRecords(chainId) {
187
+ const replayStoreId = this.publicEventReplayStoreId(chainId);
188
+ if (!(await this.stateStore.hasStore(replayStoreId))) {
189
+ return [];
190
+ }
191
+ const records = (await this.stateStore.getAll(replayStoreId));
192
+ return [...records].sort((left, right) => {
193
+ if (left.event.blockNumber !== right.event.blockNumber) {
194
+ return left.event.blockNumber - right.event.blockNumber;
195
+ }
196
+ if (left.event.transactionIndex !== right.event.transactionIndex) {
197
+ return left.event.transactionIndex - right.event.transactionIndex;
198
+ }
199
+ return left.event.logIndex - right.event.logIndex;
200
+ });
201
+ }
202
+ async listTrackedPublicEvents(chainId) {
203
+ return this.listReplayRecords(chainId);
204
+ }
205
+ emptyReplayAttemptResult(stateDump) {
206
+ return {
207
+ complete: true,
208
+ stateDump,
209
+ appliedEvents: [],
210
+ appliedReplayKeys: [],
211
+ duplicateReplayKeys: [],
212
+ appliedPublicEvents: [],
213
+ duplicatePublicEvents: [],
214
+ ignoredPublicEvents: [],
215
+ pendingPublicEvents: [],
216
+ confirmedPendingPublicEvents: [],
217
+ };
218
+ }
219
+ publicEventSubjectId(chain) {
220
+ return Binary.fromHex(chain.id).hash().hex;
221
+ }
222
+ provisionalPublicEventReplayKey(chain, eventType, data) {
223
+ return `pending:${this.publicEventSubjectId(chain)}:${eventType}:${new Binary(data).hash().hex}`;
224
+ }
225
+ provisionalPublicEventRecord(chain, eventType, data) {
226
+ const replayKey = this.provisionalPublicEventReplayKey(chain, eventType, data);
227
+ const payloadHex = new Binary(data).hex;
228
+ return {
229
+ replayKey,
230
+ event: {
231
+ source: this.eqty.address,
232
+ eventType,
233
+ data: payloadHex,
234
+ blockNumber: 0,
235
+ transactionHash: payloadHex,
236
+ transactionIndex: 0,
237
+ logIndex: 0,
238
+ },
239
+ status: 'pending',
240
+ sources: ['local'],
241
+ };
242
+ }
174
243
  toRegisterRuntimeEvent(event) {
244
+ const { subjectId: _subjectId, timestamp: _timestamp, ...publicEvent } = event;
175
245
  return {
176
- ...event,
177
- data: typeof event.data === "string"
178
- ? event.data
179
- : new Binary(event.data).hex,
246
+ ...publicEvent,
247
+ data: typeof publicEvent.data === 'string' ? publicEvent.data : new Binary(event.data).hex,
180
248
  };
181
249
  }
250
+ publicEventSubjectMatches(chain, event) {
251
+ return event.subjectId.toLowerCase() === this.publicEventSubjectId(chain).toLowerCase();
252
+ }
182
253
  toRegisterRpcPayload(event) {
183
254
  return {
184
255
  ...event,
185
256
  data: Binary.fromHex(event.data),
257
+ transactionHash: Binary.fromHex(event.transactionHash),
186
258
  };
187
259
  }
188
260
  async applyEvent(rpc, event, stateDump, chain, eventIndex) {
@@ -190,19 +262,19 @@ export default class OwnableService {
190
262
  sender: event.signerAddress || this.eqty.address,
191
263
  funds: [],
192
264
  };
193
- const { "@context": context, ...msg } = event.parsedData;
265
+ const { '@context': context, ...msg } = event.parsedData;
194
266
  let result;
195
267
  switch (context) {
196
- case "instantiate_msg.json":
268
+ case 'instantiate_msg.json':
197
269
  result = await rpc.instantiate(msg, info);
198
270
  break;
199
- case "execute_msg.json":
271
+ case 'execute_msg.json':
200
272
  result = await rpc.execute(msg, info, stateDump);
201
273
  break;
202
- case "register_msg.json":
274
+ case 'register_msg.json':
203
275
  result = await rpc.register(this.toRegisterRpcPayload(msg), info, stateDump);
204
276
  break;
205
- case "ingest_msg.json":
277
+ case 'ingest_msg.json':
206
278
  result = await rpc.ingest(msg, info, stateDump);
207
279
  break;
208
280
  default:
@@ -252,101 +324,195 @@ export default class OwnableService {
252
324
  }
253
325
  return stateDump;
254
326
  }
255
- async replayIndexedPublicEvents(chainId, stateDump, indexedPublicEvents) {
256
- const replay = await this.attemptReplayIndexedPublicEvents(chainId, stateDump, indexedPublicEvents);
257
- if (!replay.complete) {
258
- if (replay.failure?.cause instanceof Error) {
259
- throw replay.failure.cause;
260
- }
261
- throw new Error(`Failed to replay indexed public event ${replay.failure?.replayKey ?? "unknown"}`);
262
- }
263
- return replay;
327
+ async replayIndexedPublicEvents(chainId, stateDump, indexedPublicEvents, options) {
328
+ return this.attemptReplayIndexedPublicEvents(chainId, stateDump, indexedPublicEvents, options);
264
329
  }
265
- async attemptReplayIndexedPublicEvents(chainId, stateDump, indexedPublicEvents) {
330
+ async attemptReplayIndexedPublicEvents(chainId, stateDump, indexedPublicEvents, options) {
266
331
  const info = {
267
332
  sender: this.eqty.address,
268
333
  funds: [],
269
334
  };
270
- const { events, duplicateReplayKeys } = dedupeIndexedPublicEvents(indexedPublicEvents);
335
+ const deduped = this.replay.dedupe(indexedPublicEvents);
336
+ const selected = options
337
+ ? this.replay.selectReplayable(indexedPublicEvents, options)
338
+ : { events: deduped.events, ignoredPublicEvents: [] };
339
+ const events = selected.events;
340
+ const duplicateReplayKeys = deduped.duplicateReplayKeys;
271
341
  const appliedEvents = [];
272
342
  const appliedReplayKeys = [];
343
+ const appliedPublicEvents = [];
344
+ const duplicatePublicEvents = deduped.duplicateEvents.map((event) => ({
345
+ replayKey: this.replay.key(event),
346
+ event,
347
+ }));
348
+ const ignoredPublicEvents = [...selected.ignoredPublicEvents];
273
349
  let nextState = stateDump;
274
350
  for (const indexedEvent of events) {
275
351
  const runtimeEvent = this.toRegisterRuntimeEvent(indexedEvent);
276
- const replayKey = publicEventReplayKey(indexedEvent);
352
+ const replayKey = this.replay.key(indexedEvent);
277
353
  try {
278
354
  const { state } = await this.rpc(chainId).register(this.toRegisterRpcPayload(runtimeEvent), info, nextState);
279
355
  nextState = state;
280
356
  appliedEvents.push(indexedEvent);
281
357
  appliedReplayKeys.push(replayKey);
358
+ appliedPublicEvents.push({
359
+ replayKey,
360
+ event: indexedEvent,
361
+ });
282
362
  }
283
363
  catch (cause) {
284
- return {
285
- complete: false,
286
- stateDump: nextState,
287
- appliedEvents,
288
- appliedReplayKeys,
289
- duplicateReplayKeys,
290
- failure: {
291
- replayKey,
292
- event: indexedEvent,
293
- cause,
294
- },
295
- };
364
+ ignoredPublicEvents.push({
365
+ replayKey,
366
+ event: indexedEvent,
367
+ reason: 'register_failed',
368
+ cause,
369
+ });
296
370
  }
297
371
  }
298
372
  return {
299
- complete: true,
373
+ complete: ignoredPublicEvents.length === 0,
300
374
  stateDump: nextState,
301
375
  appliedEvents,
302
376
  appliedReplayKeys,
303
377
  duplicateReplayKeys,
378
+ appliedPublicEvents,
379
+ duplicatePublicEvents,
380
+ ignoredPublicEvents,
381
+ pendingPublicEvents: [],
382
+ confirmedPendingPublicEvents: [],
383
+ };
384
+ }
385
+ async applyIndexedPublicEventSnapshot(chain, indexedPublicEvents, onProgress) {
386
+ return this.reconcileIndexedPublicEvents(chain, indexedPublicEvents, 'snapshot', onProgress);
387
+ }
388
+ async applyIndexedPublicEventStream(chain, indexedPublicEvents, onProgress) {
389
+ return this.reconcileIndexedPublicEvents(chain, indexedPublicEvents, 'stream', onProgress);
390
+ }
391
+ async reconcileIndexedPublicEvents(chain, indexedPublicEvents, source, onProgress) {
392
+ const progress = new ProgressService(onProgress);
393
+ const stateDump = await this.eventChains.getStateDump(chain.id, chain.state.hex);
394
+ if (!stateDump)
395
+ throw Error('State mismatch for register public event');
396
+ const replayStoreId = await this.ensureReplayStore(chain.id);
397
+ const base = this.emptyReplayAttemptResult(stateDump);
398
+ const deduped = this.replay.dedupe(indexedPublicEvents);
399
+ const info = {
400
+ sender: this.eqty.address,
401
+ funds: [],
402
+ };
403
+ base.duplicateReplayKeys.push(...deduped.duplicateReplayKeys);
404
+ base.duplicatePublicEvents.push(...deduped.duplicateEvents.map((event) => this.replayRecord(event, 'confirmed', [source])));
405
+ let nextState = stateDump;
406
+ for (const indexedEvent of deduped.events) {
407
+ const replayKey = this.replay.key(indexedEvent);
408
+ const existing = (await this.stateStore
409
+ .get(replayStoreId, replayKey)
410
+ .catch(() => undefined));
411
+ if (existing?.status === 'confirmed') {
412
+ const updated = this.replayRecord(existing.event, 'confirmed', this.mergeReplayRecordSources(existing.sources, source));
413
+ await this.stateStore.set(replayStoreId, replayKey, updated);
414
+ base.duplicateReplayKeys.push(replayKey);
415
+ base.duplicatePublicEvents.push(updated);
416
+ continue;
417
+ }
418
+ const runtimeEvent = this.toRegisterRuntimeEvent(indexedEvent);
419
+ try {
420
+ const { state } = await this.rpc(chain.id).register(this.toRegisterRpcPayload(runtimeEvent), info, nextState);
421
+ nextState = state;
422
+ await progress.step('signPublicEvent', () => this.eqty.sign(new Event({
423
+ '@context': 'register_msg.json',
424
+ ...runtimeEvent,
425
+ }).addTo(chain)));
426
+ await this.store(chain, nextState);
427
+ const confirmedRecord = this.replayRecord(indexedEvent, 'confirmed', existing?.status === 'pending'
428
+ ? this.mergeReplayRecordSources(existing.sources, source)
429
+ : [source]);
430
+ await this.stateStore.set(replayStoreId, replayKey, confirmedRecord);
431
+ base.appliedEvents.push(indexedEvent);
432
+ base.appliedReplayKeys.push(replayKey);
433
+ base.appliedPublicEvents.push({ replayKey, event: indexedEvent });
434
+ if (existing?.status === 'pending') {
435
+ base.confirmedPendingPublicEvents.push(confirmedRecord);
436
+ }
437
+ }
438
+ catch (cause) {
439
+ base.ignoredPublicEvents.push({
440
+ replayKey,
441
+ event: indexedEvent,
442
+ reason: 'register_failed',
443
+ cause,
444
+ });
445
+ }
446
+ }
447
+ return {
448
+ ...base,
449
+ complete: base.ignoredPublicEvents.length === 0,
450
+ stateDump: nextState,
304
451
  };
305
452
  }
306
453
  async execute(chain, msg, stateDump, onProgress, attachments = []) {
454
+ const progress = new ProgressService(onProgress);
307
455
  const info = { sender: this.eqty.address, funds: [] };
308
456
  const { state: newStateDump } = await this.rpc(chain.id).execute(msg, info, stateDump);
309
- delete msg["@context"]; // Shouldn't be set
310
- const event = new Event({ "@context": "execute_msg.json", ...msg }).addTo(chain);
457
+ delete msg['@context']; // Shouldn't be set
458
+ const event = new Event({ '@context': 'execute_msg.json', ...msg }).addTo(chain);
311
459
  for (const attachment of attachments) {
312
- event.addAttachment(attachment.name, new Uint8Array(await attachment.file.arrayBuffer()), attachment.file.type || "application/octet-stream");
460
+ event.addAttachment(attachment.name, new Uint8Array(await attachment.file.arrayBuffer()), attachment.file.type || 'application/octet-stream');
313
461
  }
314
- await withProgress(onProgress)("signEvent", () => this.eqty.sign(event));
462
+ await progress.step('signEvent', () => this.eqty.sign(event));
315
463
  // Store without submitting anchors yet; submission is controlled by caller
316
464
  await this.store(chain, newStateDump);
317
465
  return newStateDump;
318
466
  }
319
467
  async registerPublicEvent(chain, event, onProgress) {
468
+ const publicEvent = this.toRegisterRuntimeEvent(event);
469
+ return this.applyIndexedPublicEventStream(chain, [publicEvent], onProgress);
470
+ }
471
+ async emitPublicEvent(chain, eventType, payload, onProgress) {
472
+ const progress = new ProgressService(onProgress);
320
473
  const stateDump = await this.eventChains.getStateDump(chain.id, chain.state.hex);
321
474
  if (!stateDump)
322
- throw Error("State mismatch for register public event");
323
- const publicEvent = this.toRegisterRuntimeEvent(event);
324
- const replayStoreId = this.publicEventReplayStoreId(chain.id);
325
- const replayKey = publicEventReplayKey(publicEvent);
326
- if ((await this.stateStore.hasStore(replayStoreId)) && (await this.stateStore.get(replayStoreId, replayKey))) {
327
- return;
475
+ throw Error('State mismatch for emit public event');
476
+ const encodedPayload = await progress.step('encodePublicEvent', () => this.rpc(chain.id).encodePublicEvent(eventType, encode(payload)));
477
+ const subjectId = this.publicEventSubjectId(chain);
478
+ const replayStoreId = await this.ensureReplayStore(chain.id);
479
+ const provisionalPendingRecord = this.provisionalPublicEventRecord(chain, eventType, encodedPayload);
480
+ await this.stateStore.set(replayStoreId, provisionalPendingRecord.replayKey, provisionalPendingRecord);
481
+ let publicEvent;
482
+ try {
483
+ publicEvent = await progress.step('emitPublicEvent', () => this.eqty.emitPublicEvent(subjectId, eventType, encodedPayload));
328
484
  }
329
- const replay = await this.replayIndexedPublicEvents(chain.id, stateDump, [publicEvent]);
330
- const newStateDump = replay.stateDump;
331
- await withProgress(onProgress)("signPublicEvent", () => this.eqty.sign(new Event({
332
- "@context": "register_msg.json",
333
- ...publicEvent,
334
- }).addTo(chain)));
335
- await this.store(chain, newStateDump);
336
- if (!(await this.stateStore.hasStore(replayStoreId))) {
337
- await this.stateStore.createStore(replayStoreId);
485
+ catch (cause) {
486
+ await this.stateStore.delete(replayStoreId, provisionalPendingRecord.replayKey);
487
+ throw cause;
338
488
  }
339
- await this.stateStore.set(replayStoreId, replayKey, true);
340
- }
341
- async emitPublicEvent(chain, eventType, payload, onProgress) {
342
- const encodedPayload = await withProgress(onProgress)("encodePublicEvent", () => this.rpc(chain.id).encodePublicEvent(eventType, encode(payload)));
343
- const publicEvent = await withProgress(onProgress)("emitPublicEvent", () => this.eqty.emitPublicEvent(chain.id, eventType, encodedPayload));
344
- await this.registerPublicEvent(chain, publicEvent, onProgress);
489
+ if (!this.publicEventSubjectMatches(chain, publicEvent)) {
490
+ await this.stateStore.delete(replayStoreId, provisionalPendingRecord.replayKey);
491
+ const result = this.emptyReplayAttemptResult(stateDump);
492
+ result.complete = false;
493
+ result.ignoredPublicEvents.push({
494
+ replayKey: this.replay.key(this.toRegisterRuntimeEvent(publicEvent)),
495
+ event: this.toRegisterRuntimeEvent(publicEvent),
496
+ reason: 'invalid_subject_id',
497
+ cause: {
498
+ expectedSubjectId: subjectId,
499
+ receivedSubjectId: publicEvent.subjectId,
500
+ },
501
+ });
502
+ return result;
503
+ }
504
+ const runtimeEvent = this.toRegisterRuntimeEvent(publicEvent);
505
+ const pendingRecord = this.replayRecord(runtimeEvent, 'pending', ['local']);
506
+ await this.stateStore.delete(replayStoreId, provisionalPendingRecord.replayKey);
507
+ await this.stateStore.set(replayStoreId, pendingRecord.replayKey, pendingRecord);
508
+ const result = this.emptyReplayAttemptResult(stateDump);
509
+ result.pendingPublicEvents.push(pendingRecord);
510
+ return result;
345
511
  }
346
512
  async submitAnchors(onProgress) {
347
513
  if (!this.anchoring)
348
514
  return undefined;
349
- return await withProgress(onProgress)("anchor", () => this.eqty.submitAnchors());
515
+ return await new ProgressService(onProgress).step('anchor', () => this.eqty.submitAnchors());
350
516
  }
351
517
  async canConsume(consumer, info) {
352
518
  if (!this.packages.info(consumer.package).isConsumer)
@@ -364,11 +530,12 @@ export default class OwnableService {
364
530
  return result === true;
365
531
  }
366
532
  catch (error) {
367
- this.logger.warn("Error checking canConsume:", error);
533
+ this.logger.warn('Error checking canConsume:', error);
368
534
  return false;
369
535
  }
370
536
  }
371
537
  async consume(consumer, consumable, onProgress) {
538
+ const progress = new ProgressService(onProgress);
372
539
  const info = {
373
540
  sender: this.eqty.address,
374
541
  funds: [],
@@ -377,11 +544,11 @@ export default class OwnableService {
377
544
  const consumerState = await this.eventChains.getStateDump(consumer.id, consumer.state.hex);
378
545
  const consumableState = await this.eventChains.getStateDump(consumable.id, consumable.state.hex);
379
546
  if (!consumerState || !consumableState)
380
- throw Error("State mismatch for consume");
547
+ throw Error('State mismatch for consume');
381
548
  const { events, state: consumableStateDump } = await this.rpc(consumable.id).execute(consumeMessage, info, consumableState);
382
- const consumeEvent = events.find((event) => event.type === "consume");
549
+ const consumeEvent = events.find((event) => event.type === 'consume');
383
550
  if (!consumeEvent)
384
- throw Error("No consume event emitted");
551
+ throw Error('No consume event emitted');
385
552
  const consumableInfo = (await this.rpc(consumable.id).query({ get_info: {} }, consumableStateDump));
386
553
  const ingestEvent = {
387
554
  source: {
@@ -393,15 +560,15 @@ export default class OwnableService {
393
560
  eventType: consumeEvent.type,
394
561
  };
395
562
  const { state: consumerStateDump } = await this.rpc(consumer.id).ingest(ingestEvent, info, consumerState);
396
- await withProgress(onProgress)("signConsumableEvent", () => this.eqty.sign(new Event({ "@context": "execute_msg.json", ...consumeMessage }).addTo(consumable)));
397
- await withProgress(onProgress)("signConsumerEvent", () => this.eqty.sign(new Event({
398
- "@context": "ingest_msg.json",
563
+ await progress.step('signConsumableEvent', () => this.eqty.sign(new Event({ '@context': 'execute_msg.json', ...consumeMessage }).addTo(consumable)));
564
+ await progress.step('signConsumerEvent', () => this.eqty.sign(new Event({
565
+ '@context': 'ingest_msg.json',
399
566
  ...ingestEvent,
400
567
  }).addTo(consumer)));
401
568
  // Store both chains; emit anchor progress only once to represent anchoring both
402
569
  // Queue anchors for both chains without submitting yet
403
570
  await this.store(consumable, consumableStateDump);
404
- await this.stateStore.set(`ownable:${consumable.id}`, "isConsumed", true);
571
+ await this.stateStore.set(`ownable:${consumable.id}`, 'isConsumed', true);
405
572
  await this.store(consumer, consumerStateDump);
406
573
  // Submit a single anchor tx for both
407
574
  await this.submitAnchors(onProgress);
@@ -431,8 +598,7 @@ export default class OwnableService {
431
598
  created: new Date(),
432
599
  latestHash: chain.latestHash.hex,
433
600
  keywords: this.packages.info(pkg).keywords,
434
- uniqueMessageHash: this.packages.info(pkg, uniqueMessageHash)
435
- .uniqueMessageHash,
601
+ uniqueMessageHash: this.packages.info(pkg, uniqueMessageHash).uniqueMessageHash,
436
602
  };
437
603
  const stores = [storeId];
438
604
  if (stateDump)
@@ -454,17 +620,16 @@ export default class OwnableService {
454
620
  }
455
621
  catch (error) {
456
622
  // If setAll fails, attempt to clean up
457
- this.logger.error("Failed to set data, cleaning up stores...");
623
+ this.logger.error('Failed to set data, cleaning up stores...');
458
624
  await Promise.all(stores.map((store) => this.stateStore.deleteStore(store).catch(() => { })));
459
625
  throw error;
460
626
  }
461
627
  const verifyData = await Promise.all([
462
- this.stateStore.get(storeId, "state"),
628
+ this.stateStore.get(storeId, 'state'),
463
629
  stateDump ? this.stateStore.getAll(stateStoreId) : Promise.resolve(null),
464
630
  ]);
465
- if (verifyData[0] !== chainData.state ||
466
- (stateDump && !verifyData[1]?.length)) {
467
- throw new Error("Data verification failed after write");
631
+ if (verifyData[0] !== chainData.state || (stateDump && !verifyData[1]?.length)) {
632
+ throw new Error('Data verification failed after write');
468
633
  }
469
634
  });
470
635
  }
@@ -473,7 +638,7 @@ export default class OwnableService {
473
638
  const storeId = `ownable:${chain.id}`;
474
639
  const stateStoreId = `${storeId}.state`;
475
640
  await this.retryOperation(async () => {
476
- const storedState = await this.stateStore.get(storeId, "state");
641
+ const storedState = await this.stateStore.get(storeId, 'state');
477
642
  if (storedState === chain.state)
478
643
  return;
479
644
  const data = {
@@ -485,7 +650,7 @@ export default class OwnableService {
485
650
  [stateStoreId]: new Map(stateDump),
486
651
  };
487
652
  if (this.anchoring) {
488
- const previousHash = await this.stateStore.get(`ownable:${chain.id}`, "latestHash");
653
+ const previousHash = await this.stateStore.get(`ownable:${chain.id}`, 'latestHash');
489
654
  anchors.push(...chain.startingAfter(Binary.fromHex(previousHash)).anchorMap);
490
655
  }
491
656
  if (anchors.length > 0) {
@@ -498,9 +663,9 @@ export default class OwnableService {
498
663
  await this.createSnapshot(chain, stateDump, eventCount - 1);
499
664
  }
500
665
  // Verify write
501
- const verifyState = await this.stateStore.get(storeId, "state");
666
+ const verifyState = await this.stateStore.get(storeId, 'state');
502
667
  if (verifyState !== chain.state.hex) {
503
- throw new Error("State verification failed after write");
668
+ throw new Error('State verification failed after write');
504
669
  }
505
670
  });
506
671
  }
@@ -513,10 +678,10 @@ export default class OwnableService {
513
678
  async zip(chain) {
514
679
  const firstEvent = chain.events[0];
515
680
  if (!firstEvent)
516
- throw new Error("Cannot zip an empty ownable chain");
681
+ throw new Error('Cannot zip an empty ownable chain');
517
682
  const packageCid = firstEvent.parsedData.package;
518
683
  const zip = await this.packages.zip(packageCid);
519
- zip.file("chain.json", JSON.stringify(chain.toJSON()));
684
+ zip.file('chain.json', JSON.stringify(chain.toJSON()));
520
685
  return zip;
521
686
  }
522
687
  }