@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.
- package/README.md +3 -3
- package/dist/core/src/index.d.ts +3 -3
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/index.js +3 -3
- package/dist/core/src/index.js.map +1 -1
- package/dist/core/src/interfaces/core.d.ts +7 -7
- package/dist/core/src/interfaces/core.d.ts.map +1 -1
- package/dist/core/src/logger.d.ts +1 -1
- package/dist/core/src/services/AnchorValidation.service.d.ts +10 -0
- package/dist/core/src/services/AnchorValidation.service.d.ts.map +1 -0
- package/dist/core/src/services/AnchorValidation.service.js +115 -0
- package/dist/core/src/services/AnchorValidation.service.js.map +1 -0
- package/dist/core/src/services/EventChain.service.d.ts +9 -10
- package/dist/core/src/services/EventChain.service.d.ts.map +1 -1
- package/dist/core/src/services/EventChain.service.js +16 -11
- package/dist/core/src/services/EventChain.service.js.map +1 -1
- package/dist/core/src/services/Ownable.service.d.ts +45 -19
- package/dist/core/src/services/Ownable.service.d.ts.map +1 -1
- package/dist/core/src/services/Ownable.service.js +278 -113
- package/dist/core/src/services/Ownable.service.js.map +1 -1
- package/dist/core/src/services/Polling.service.d.ts +3 -3
- package/dist/core/src/services/Polling.service.d.ts.map +1 -1
- package/dist/core/src/services/Polling.service.js +10 -10
- package/dist/core/src/services/Polling.service.js.map +1 -1
- package/dist/core/src/services/Progress.service.d.ts +7 -0
- package/dist/core/src/services/Progress.service.d.ts.map +1 -0
- package/dist/core/src/services/Progress.service.js +12 -0
- package/dist/core/src/services/Progress.service.js.map +1 -0
- package/dist/core/src/services/ReplayAuthority.service.d.ts +13 -6
- package/dist/core/src/services/ReplayAuthority.service.d.ts.map +1 -1
- package/dist/core/src/services/ReplayAuthority.service.js +140 -8
- package/dist/core/src/services/ReplayAuthority.service.js.map +1 -1
- package/dist/core/src/services/SIWE.service.d.ts +2 -2
- package/dist/core/src/services/SIWE.service.d.ts.map +1 -1
- package/dist/core/src/services/SIWE.service.js +26 -27
- package/dist/core/src/services/SIWE.service.js.map +1 -1
- package/dist/core/src/services/WorkerRPC.service.d.ts.map +1 -1
- package/dist/core/src/services/WorkerRPC.service.js +3 -1
- package/dist/core/src/services/WorkerRPC.service.js.map +1 -1
- package/dist/core/src/types/AnchorValidation.d.ts +55 -0
- package/dist/core/src/types/AnchorValidation.d.ts.map +1 -0
- package/dist/core/src/types/AnchorValidation.js +2 -0
- package/dist/core/src/types/AnchorValidation.js.map +1 -0
- package/dist/core/src/types/MessageInfo.d.ts +1 -1
- package/dist/core/src/types/OwnableRuntime.d.ts +7 -1
- package/dist/core/src/types/OwnableRuntime.d.ts.map +1 -1
- package/dist/core/src/types/Replay.d.ts +63 -6
- package/dist/core/src/types/Replay.d.ts.map +1 -1
- package/dist/core/src/utils/calculateOwnablePackageCid.d.ts +6 -0
- package/dist/core/src/utils/calculateOwnablePackageCid.d.ts.map +1 -0
- package/dist/core/src/{services/OwnablePackageCid.service.js → utils/calculateOwnablePackageCid.js} +8 -8
- package/dist/core/src/utils/calculateOwnablePackageCid.js.map +1 -0
- package/dist/core/src/utils.d.ts +2 -0
- package/dist/core/src/utils.d.ts.map +1 -0
- package/dist/core/src/utils.js +2 -0
- package/dist/core/src/utils.js.map +1 -0
- package/dist/platform-browser/src/assets/workerSource.d.ts.map +1 -0
- package/dist/platform-browser/src/assets/workerSource.js.map +1 -0
- package/dist/platform-browser/src/constants/hub.d.ts +2 -0
- package/dist/platform-browser/src/constants/hub.d.ts.map +1 -0
- package/dist/platform-browser/src/constants/hub.js +2 -0
- package/dist/platform-browser/src/constants/hub.js.map +1 -0
- package/dist/platform-browser/src/index.d.ts +10 -8
- package/dist/platform-browser/src/index.d.ts.map +1 -1
- package/dist/platform-browser/src/index.js +10 -8
- package/dist/platform-browser/src/index.js.map +1 -1
- package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.d.ts +5 -0
- package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.d.ts.map +1 -0
- package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.js +7 -0
- package/dist/platform-browser/src/services/BrowserRuntimeRpcProvider.service.js.map +1 -0
- package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.d.ts +7 -0
- package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.d.ts.map +1 -0
- package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.js +11 -0
- package/dist/platform-browser/src/services/BrowserRuntimeSourceProvider.service.js.map +1 -0
- package/dist/platform-browser/src/services/Hub.service.d.ts +28 -6
- package/dist/platform-browser/src/services/Hub.service.d.ts.map +1 -1
- package/dist/platform-browser/src/services/Hub.service.js +111 -49
- package/dist/platform-browser/src/services/Hub.service.js.map +1 -1
- package/dist/platform-browser/src/services/IDB.service.d.ts +1 -1
- package/dist/platform-browser/src/services/IDB.service.d.ts.map +1 -1
- package/dist/platform-browser/src/services/IDB.service.js +18 -19
- package/dist/platform-browser/src/services/IDB.service.js.map +1 -1
- package/dist/platform-browser/src/services/LocalStorage.service.d.ts.map +1 -1
- package/dist/platform-browser/src/services/LocalStorage.service.js +1 -1
- package/dist/platform-browser/src/services/LocalStorage.service.js.map +1 -1
- package/dist/platform-browser/src/services/Package.service.d.ts +8 -10
- package/dist/platform-browser/src/services/Package.service.d.ts.map +1 -1
- package/dist/platform-browser/src/services/Package.service.js +72 -77
- package/dist/platform-browser/src/services/Package.service.js.map +1 -1
- package/dist/platform-browser/src/services/Relay.service.d.ts +7 -7
- package/dist/platform-browser/src/services/Relay.service.d.ts.map +1 -1
- package/dist/platform-browser/src/services/Relay.service.js +25 -25
- package/dist/platform-browser/src/services/Relay.service.js.map +1 -1
- package/package.json +5 -3
- package/dist/core/src/ownableErrorMessage.d.ts +0 -2
- package/dist/core/src/ownableErrorMessage.d.ts.map +0 -1
- package/dist/core/src/ownableErrorMessage.js +0 -23
- package/dist/core/src/ownableErrorMessage.js.map +0 -1
- package/dist/core/src/progress.d.ts +0 -3
- package/dist/core/src/progress.d.ts.map +0 -1
- package/dist/core/src/progress.js +0 -8
- package/dist/core/src/progress.js.map +0 -1
- package/dist/core/src/services/OwnablePackageCid.service.d.ts +0 -6
- package/dist/core/src/services/OwnablePackageCid.service.d.ts.map +0 -1
- package/dist/core/src/services/OwnablePackageCid.service.js.map +0 -1
- package/dist/core/src/services/workerSource.d.ts.map +0 -1
- package/dist/core/src/services/workerSource.js.map +0 -1
- package/dist/platform-browser/src/utils/calculateCid.d.ts +0 -2
- package/dist/platform-browser/src/utils/calculateCid.d.ts.map +0 -1
- package/dist/platform-browser/src/utils/calculateCid.js +0 -8
- package/dist/platform-browser/src/utils/calculateCid.js.map +0 -1
- /package/dist/{core/src/services → platform-browser/src/assets}/workerSource.d.ts +0 -0
- /package/dist/{core/src/services → platform-browser/src/assets}/workerSource.js +0 -0
|
@@ -1,31 +1,26 @@
|
|
|
1
|
-
import { EventChain, Event, Binary } from
|
|
2
|
-
import { encode } from
|
|
3
|
-
import {
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
constructor(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
...
|
|
177
|
-
data: typeof
|
|
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 {
|
|
265
|
+
const { '@context': context, ...msg } = event.parsedData;
|
|
194
266
|
let result;
|
|
195
267
|
switch (context) {
|
|
196
|
-
case
|
|
268
|
+
case 'instantiate_msg.json':
|
|
197
269
|
result = await rpc.instantiate(msg, info);
|
|
198
270
|
break;
|
|
199
|
-
case
|
|
271
|
+
case 'execute_msg.json':
|
|
200
272
|
result = await rpc.execute(msg, info, stateDump);
|
|
201
273
|
break;
|
|
202
|
-
case
|
|
274
|
+
case 'register_msg.json':
|
|
203
275
|
result = await rpc.register(this.toRegisterRpcPayload(msg), info, stateDump);
|
|
204
276
|
break;
|
|
205
|
-
case
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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:
|
|
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[
|
|
310
|
-
const event = new Event({
|
|
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 ||
|
|
460
|
+
event.addAttachment(attachment.name, new Uint8Array(await attachment.file.arrayBuffer()), attachment.file.type || 'application/octet-stream');
|
|
313
461
|
}
|
|
314
|
-
await
|
|
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(
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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 ===
|
|
549
|
+
const consumeEvent = events.find((event) => event.type === 'consume');
|
|
383
550
|
if (!consumeEvent)
|
|
384
|
-
throw Error(
|
|
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
|
|
397
|
-
await
|
|
398
|
-
|
|
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}`,
|
|
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(
|
|
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,
|
|
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
|
-
(
|
|
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,
|
|
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}`,
|
|
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,
|
|
666
|
+
const verifyState = await this.stateStore.get(storeId, 'state');
|
|
502
667
|
if (verifyState !== chain.state.hex) {
|
|
503
|
-
throw new Error(
|
|
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(
|
|
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(
|
|
684
|
+
zip.file('chain.json', JSON.stringify(chain.toJSON()));
|
|
520
685
|
return zip;
|
|
521
686
|
}
|
|
522
687
|
}
|