@ibgib/space-gib 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/IMPLEMENTATION.md +9 -13
- package/dist/client/bootstrap.mjs +1 -1
- package/dist/client/bootstrap.mjs.map +1 -1
- package/dist/client/chunk-NCXKCVYS.mjs +42 -0
- package/dist/client/chunk-NCXKCVYS.mjs.map +7 -0
- package/dist/client/chunk-ZUEU37Z5.mjs +1920 -0
- package/dist/client/chunk-ZUEU37Z5.mjs.map +7 -0
- package/dist/client/index.html +108 -8
- package/dist/client/index.mjs +1 -1
- package/dist/client/script.mjs +1 -1
- package/dist/client/style.css +466 -61
- package/dist/respec-gib.node.mjs +5 -0
- package/dist/server/server.mjs +815 -316
- package/dist/server/server.mjs.map +4 -4
- package/package.json +6 -6
- package/src/client/AUTO-GENERATED-version.mts +1 -1
- package/src/client/api/space-gib-api-bridge.mts +35 -0
- package/src/client/components/identity-header/IMPLEMENTATION.md +45 -0
- package/src/client/components/identity-header/identity-header.css +74 -0
- package/src/client/components/identity-header/identity-header.html +10 -0
- package/src/client/components/identity-header/identity-header.mts +361 -0
- package/src/client/components/identity-manager/IMPLEMENTATION.md +100 -0
- package/src/client/components/identity-manager/identity-manager.css +467 -0
- package/src/client/components/identity-manager/identity-manager.html +113 -0
- package/src/client/components/identity-manager/identity-manager.mts +767 -0
- package/src/client/components/keystone-creator/keystone-creator.css +2 -76
- package/src/client/components/keystone-creator/keystone-creator.html +41 -26
- package/src/client/components/keystone-creator/keystone-creator.mts +178 -41
- package/src/client/dev-tools/base-tools.mts +252 -0
- package/src/client/dev-tools/common.mts +217 -0
- package/src/client/dev-tools/phase-1.mts +156 -0
- package/src/client/dev-tools/phase-2.mts +143 -0
- package/src/client/dev-tools/phase-3.mts +189 -0
- package/src/client/dev-tools/phase-4-1.mts +197 -0
- package/src/client/dev-tools/phase-4-10.mts +884 -0
- package/src/client/dev-tools/phase-4-2.mts +388 -0
- package/src/client/dev-tools/phase-4-3.mts +391 -0
- package/src/client/dev-tools/phase-4-4.mts +374 -0
- package/src/client/dev-tools/phase-4-5.mts +376 -0
- package/src/client/dev-tools/phase-4-6.mts +273 -0
- package/src/client/dev-tools/phase-4-7.mts +399 -0
- package/src/client/dev-tools/phase-4-8.mts +430 -0
- package/src/client/dev-tools/phase-4-9.mts +398 -0
- package/src/client/dev-tools/phase-4.mts +1302 -0
- package/src/client/dev-tools.mts +55 -1096
- package/src/client/index.html +108 -8
- package/src/client/style.css +466 -61
- package/src/client/ui/shell/space-gib-shell-constants.mts +0 -2
- package/src/client/ui/shell/space-gib-shell-service.mts +82 -10
- package/src/common/common-constants.mts +0 -0
- package/src/common/keystone-policies.json +40 -43
- package/src/common/keystone-policies.mts +4 -6
- package/src/server/path-helper.respec.mts +99 -94
- package/src/server/serve-gib/README.md +9 -0
- package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +1 -1
- package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +1 -1
- package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +4 -4
- package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +1 -1
- package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +37 -5
- package/src/server/serve-gib/handlers/ws/ws-helper.mts +73 -45
- package/dist/client/chunk-BL2SGXS4.mjs +0 -18994
- package/dist/client/chunk-RDTAT5G4.mjs +0 -235
- package/dist/client/chunk-RDTAT5G4.mjs.map +0 -7
- package/dist/client/chunk-RE7XSMHH.mjs +0 -31
- package/dist/client/chunk-RE7XSMHH.mjs.map +0 -7
- package/dist/client/chunk-YUSGN3J4.mjs +0 -23119
|
@@ -0,0 +1,1302 @@
|
|
|
1
|
+
import { extractErrorMsg, getUUID, getTimestamp } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
2
|
+
import { Factory_V1 as factory } from '@ibgib/ts-gib/dist/V1/factory.mjs';
|
|
3
|
+
import { ROOT } from '@ibgib/ts-gib/dist/V1/constants.mjs';
|
|
4
|
+
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
5
|
+
import { KeystoneIbGib_V1 } from '@ibgib/core-gib/dist/keystone/keystone-types.mjs';
|
|
6
|
+
import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
|
|
7
|
+
import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
|
|
8
|
+
import { mut8Timeline } from '@ibgib/core-gib/dist/timeline/timeline-api.mjs';
|
|
9
|
+
import { graphsAreEquivalent } from '@ibgib/core-gib/dist/common/other/graph-helper.mjs';
|
|
10
|
+
import { SyncPeerWebSocketSender_V1 } from '@ibgib/core-gib/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-sender/sync-peer-websocket-sender-v1.mjs';
|
|
11
|
+
import { SyncSagaCoordinator } from '@ibgib/core-gib/dist/sync/sync-saga-coordinator.mjs';
|
|
12
|
+
import { SyncConflictStrategy } from '@ibgib/core-gib/dist/sync/sync-constants.mjs';
|
|
13
|
+
import { SESSION_KEYSTONE_POLICY, getSpaceGibPoolConfig } from "../../common/keystone-policies.mjs";
|
|
14
|
+
import { SpaceGibApiBridge } from '../api/space-gib-api-bridge.mjs';
|
|
15
|
+
|
|
16
|
+
import { debugState, devLog, lc, performPhaseSetup, performPhaseSync } from './common.mjs';
|
|
17
|
+
|
|
18
|
+
export function init4_1bSetupButton(): void {
|
|
19
|
+
const btn = document.getElementById('btn-4-1b-setup') as HTMLButtonElement | null;
|
|
20
|
+
if (!btn) { return; }
|
|
21
|
+
btn.addEventListener('click', () => {
|
|
22
|
+
performPhaseSetup({
|
|
23
|
+
phaseText: '4.1B',
|
|
24
|
+
btnId: 'btn-4-1b-setup',
|
|
25
|
+
nextBtnId: 'btn-4-1b-sync',
|
|
26
|
+
masterSecret: 'test-sender-secret-phase4-1',
|
|
27
|
+
syncSalt: 'senderidentitysyncsaltphase4-1',
|
|
28
|
+
manageSalt: 'senderidentitymanagesaltphase4-1',
|
|
29
|
+
ib: 'test data 4.1b',
|
|
30
|
+
data: { hello: 'world4_1', random: Math.random() }
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function init4_1bSyncButton(): void {
|
|
36
|
+
const btn = document.getElementById('btn-4-1b-sync') as HTMLButtonElement | null;
|
|
37
|
+
if (!btn) { return; }
|
|
38
|
+
btn.addEventListener('click', () => {
|
|
39
|
+
performPhaseSync({
|
|
40
|
+
phaseText: '4.1B',
|
|
41
|
+
btnId: 'btn-4-1b-sync',
|
|
42
|
+
nextBtnId: 'btn-4-1b-check'
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function init4_1bCheckButton(): void {
|
|
48
|
+
const lc_fn = `${lc}[init4_1bCheckButton]`;
|
|
49
|
+
const btn = document.getElementById('btn-4-1b-check') as HTMLButtonElement | null;
|
|
50
|
+
if (!btn) { return; }
|
|
51
|
+
|
|
52
|
+
btn.addEventListener('click', async () => {
|
|
53
|
+
try {
|
|
54
|
+
devLog('4.1B Check: Asserting cryptographic and WebSocket state expectations...');
|
|
55
|
+
|
|
56
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
57
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
58
|
+
if (!space) { throw new Error("No default space."); }
|
|
59
|
+
|
|
60
|
+
const domainI = debugState.domainI!;
|
|
61
|
+
const targetX = debugState.targetX!;
|
|
62
|
+
const xAddr = getIbGibAddr({ ibGib: targetX });
|
|
63
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
64
|
+
|
|
65
|
+
// 1. Assert Target X exists locally (with evolved timeline) and get latest local graph
|
|
66
|
+
const latestLocalXAddr = await metaspace.getLatestAddr({
|
|
67
|
+
addr: xAddr,
|
|
68
|
+
space
|
|
69
|
+
});
|
|
70
|
+
if (!latestLocalXAddr) {
|
|
71
|
+
devLog('✗ Check: Evolved target X NOT found in local space!');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
devLog(`✓ Check: Evolved target X found locally at: ${latestLocalXAddr}`);
|
|
75
|
+
|
|
76
|
+
const localXGraph = await metaspace.getDependencyGraph({
|
|
77
|
+
ibGibAddr: latestLocalXAddr,
|
|
78
|
+
space
|
|
79
|
+
});
|
|
80
|
+
if (!localXGraph || Object.keys(localXGraph).length === 0) {
|
|
81
|
+
devLog('✗ Check: Failed to load target X dependency graph locally.');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
devLog(`✓ Check: Target X local graph loaded (${Object.keys(localXGraph).length} nodes).`);
|
|
85
|
+
|
|
86
|
+
// 2. Fetch server's Target X graph via API bridge
|
|
87
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
88
|
+
const serverGetRes = await apiBridge.getIbGibGraph(domainAddr, xAddr, true);
|
|
89
|
+
if (!serverGetRes.success || !serverGetRes.graph) {
|
|
90
|
+
devLog(`✗ Check: Failed to fetch target X graph from server: ${serverGetRes.message}`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const serverXGraph = serverGetRes.graph;
|
|
94
|
+
devLog(`✓ Check: Target X server graph loaded (${Object.keys(serverXGraph).length} nodes).`);
|
|
95
|
+
|
|
96
|
+
// 3. Verify graphs are equivalent using graphsAreEquivalent
|
|
97
|
+
const equal = graphsAreEquivalent({ graphA: localXGraph, graphB: serverXGraph });
|
|
98
|
+
if (equal) {
|
|
99
|
+
devLog('✓ Check: Target X dependency graphs are equivalent on client and server.');
|
|
100
|
+
} else {
|
|
101
|
+
devLog('✗ Check: Target X dependency graphs mismatch between client and server!');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 4. Assert evolved sender identity I1 exists locally and on server
|
|
106
|
+
const latestLocalIAddr = await metaspace.getLatestAddr({
|
|
107
|
+
addr: domainAddr,
|
|
108
|
+
space
|
|
109
|
+
});
|
|
110
|
+
if (!latestLocalIAddr) {
|
|
111
|
+
devLog('✗ Check: Evolved domain keystone tip I1 NOT found in local space!');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
devLog(`✓ Check: Evolved Domain Keystone (I1) exists locally.`);
|
|
115
|
+
|
|
116
|
+
const getRes = await metaspace.get({ addrs: [latestLocalIAddr], space });
|
|
117
|
+
const localI = getRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
118
|
+
if (!localI) {
|
|
119
|
+
devLog('✗ Check: Failed to load I1 from local space.');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const syncProof = localI.data?.proofs?.find(p => p.claim?.verb === 'sync');
|
|
124
|
+
const sessionIdentityTjpAddr = syncProof?.claim?.target;
|
|
125
|
+
if (!sessionIdentityTjpAddr) {
|
|
126
|
+
devLog('✗ Check: I1 sync claim does not target a session keystone.');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
|
|
131
|
+
if (!serverIGetRes.success || !serverIGetRes.ibGib) {
|
|
132
|
+
devLog(`✗ Check: Evolved Domain Keystone (I1) NOT found on server!`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
devLog(`✓ Check: Evolved Domain Keystone (I1) exists on server.`);
|
|
136
|
+
|
|
137
|
+
// 5. Assert session identity S tip exists locally and on server
|
|
138
|
+
const latestLocalSAddr = await metaspace.getLatestAddr({
|
|
139
|
+
addr: sessionIdentityTjpAddr,
|
|
140
|
+
space
|
|
141
|
+
});
|
|
142
|
+
if (!latestLocalSAddr) {
|
|
143
|
+
devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
devLog(`✓ Check: Found latest session keystone (S) locally: ${latestLocalSAddr}`);
|
|
147
|
+
|
|
148
|
+
const localSRes = await metaspace.get({ addrs: [latestLocalSAddr], space });
|
|
149
|
+
const sessionS = localSRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
150
|
+
if (!sessionS) {
|
|
151
|
+
devLog('✗ Check: Failed to load latest Session Keystone (S) locally.');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Verify S evolved to correct n (n === 3 or 4)
|
|
156
|
+
const n = sessionS.data?.n;
|
|
157
|
+
devLog(`✓ Check: S tip evolved to n = ${n}.`);
|
|
158
|
+
if (n !== 3 && n !== 4) {
|
|
159
|
+
devLog(`⚠ Check: Expected S to evolve to n = 3 or 4, got n = ${n}. Continuing anyway...`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const serverSGetRes = await apiBridge.getIbGib(domainAddr, latestLocalSAddr);
|
|
163
|
+
if (!serverSGetRes.success || !serverSGetRes.ibGib) {
|
|
164
|
+
devLog(`✗ Check: Latest Session Keystone (S) NOT found on server!`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
devLog(`✓ Check: Latest Session Keystone (S) exists on server.`);
|
|
168
|
+
|
|
169
|
+
// 6. Assert S graph is identical on client and server
|
|
170
|
+
const localSGraph = await metaspace.getDependencyGraph({
|
|
171
|
+
ibGibAddr: latestLocalSAddr,
|
|
172
|
+
space
|
|
173
|
+
});
|
|
174
|
+
const serverSGetGraphRes = await apiBridge.getIbGibGraph(domainAddr, sessionIdentityTjpAddr, true);
|
|
175
|
+
if (!serverSGetGraphRes.success || !serverSGetGraphRes.graph) {
|
|
176
|
+
devLog(`✗ Check: Failed to fetch session S graph from server: ${serverSGetGraphRes.message}`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const serverSGraph = serverSGetGraphRes.graph;
|
|
180
|
+
|
|
181
|
+
const sGraphsEqual = graphsAreEquivalent({ graphA: localSGraph, graphB: serverSGraph });
|
|
182
|
+
if (sGraphsEqual) {
|
|
183
|
+
devLog('✓ Check: Session Keystone (S) dependency graphs are equivalent on client and server.');
|
|
184
|
+
} else {
|
|
185
|
+
devLog('✗ Check: Session Keystone (S) dependency graphs mismatch between client and server!');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
devLog('🎉 ALL PHASE 4.1B TRANSACTION SYNC CHECKS PASSED FLAWLESSLY! ✓');
|
|
190
|
+
btn.textContent = '✓ 4.1B All Passed';
|
|
191
|
+
|
|
192
|
+
} catch (error) {
|
|
193
|
+
devLog(`✗ 4.1B Check FAILED: ${extractErrorMsg(error)}`);
|
|
194
|
+
console.error(`${lc_fn} 4.1B Check error:`, error);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function init4_2bSetupButton(): void {
|
|
200
|
+
const btn = document.getElementById('btn-4-2b-setup') as HTMLButtonElement | null;
|
|
201
|
+
if (!btn) { return; }
|
|
202
|
+
btn.addEventListener('click', () => {
|
|
203
|
+
performPhaseSetup({
|
|
204
|
+
phaseText: '4.2B',
|
|
205
|
+
btnId: 'btn-4-2b-setup',
|
|
206
|
+
nextBtnId: 'btn-4-2b-sync',
|
|
207
|
+
masterSecret: 'test-sender-secret-phase4-2',
|
|
208
|
+
syncSalt: 'senderidentitysyncsaltphase4-2',
|
|
209
|
+
manageSalt: 'senderidentitymanagesaltphase4-2',
|
|
210
|
+
ib: 'dummy target 4.2b',
|
|
211
|
+
data: { hello: 'dummy' }
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function performPhaseSync4_2b(): Promise<void> {
|
|
217
|
+
const lc_fn = `${lc}[performPhaseSync4_2b]`;
|
|
218
|
+
const btn = document.getElementById('btn-4-2b-sync') as HTMLButtonElement | null;
|
|
219
|
+
if (!btn) { return; }
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
btn.disabled = true;
|
|
223
|
+
devLog(`4.2B Sync: Creating Constants and executing three-pass WebSocket Sync...`);
|
|
224
|
+
|
|
225
|
+
const domainI = debugState.domainI;
|
|
226
|
+
if (!domainI) {
|
|
227
|
+
devLog(`⚠ 4.2B Sync: Missing setup state. Please run Setup first.`);
|
|
228
|
+
btn.disabled = false;
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
233
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
234
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
235
|
+
if (!space) { throw new Error("No default space."); }
|
|
236
|
+
|
|
237
|
+
// 1. Create Constant C1 locally
|
|
238
|
+
const c1 = await factory.constant({
|
|
239
|
+
parentPrimitiveIb: 'root',
|
|
240
|
+
ib: 'constant_c1_' + (await getUUID()).slice(0, 8),
|
|
241
|
+
data: { some: 'data_4_2b', random: Math.random() },
|
|
242
|
+
});
|
|
243
|
+
const addrC1 = getIbGibAddr({ ibGib: c1 });
|
|
244
|
+
await metaspace.put({ ibGib: c1, space });
|
|
245
|
+
await metaspace.registerNewIbGib({ ibGib: c1 });
|
|
246
|
+
debugState.targetC1 = c1;
|
|
247
|
+
devLog(`4.2B Sync: ✓ Constant C1 created: ${addrC1}`);
|
|
248
|
+
|
|
249
|
+
// --- Sync 1: C1 First Pass ---
|
|
250
|
+
devLog(`4.2B Sync: Starting Pass 1 (Sync C1)...`);
|
|
251
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
252
|
+
let senderPeer = new SyncPeerWebSocketSender_V1({
|
|
253
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
254
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
255
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
let coordinator = new SyncSagaCoordinator();
|
|
259
|
+
let sagaId = await getUUID();
|
|
260
|
+
debugState.sagaId = sagaId;
|
|
261
|
+
debugState.senderPeer = senderPeer;
|
|
262
|
+
|
|
263
|
+
await senderPeer.initializeOpts({
|
|
264
|
+
localMetaspace: metaspace,
|
|
265
|
+
localSpace: space,
|
|
266
|
+
senderIdentity: domainI,
|
|
267
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
268
|
+
sagaId,
|
|
269
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
270
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
271
|
+
targetAddrs: [domainAddr]
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
let syncSaga = await coordinator.sync({
|
|
275
|
+
domainIbGibs: [c1],
|
|
276
|
+
senderIdentity: domainI,
|
|
277
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
278
|
+
peer: senderPeer,
|
|
279
|
+
localSpace: space,
|
|
280
|
+
metaspace,
|
|
281
|
+
conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
|
|
282
|
+
});
|
|
283
|
+
await syncSaga.done;
|
|
284
|
+
devLog(`4.2B Sync: ✓ Pass 1 Complete.`);
|
|
285
|
+
|
|
286
|
+
const latestSenderAddr1 = await metaspace.getLatestAddr({
|
|
287
|
+
addr: getIbGibAddr({ ibGib: domainI }),
|
|
288
|
+
space,
|
|
289
|
+
});
|
|
290
|
+
const resGetLatestI1 = await metaspace.get({ addrs: [latestSenderAddr1!], space });
|
|
291
|
+
const senderIdentityEvolved1 = resGetLatestI1.ibGibs![0] as KeystoneIbGib_V1;
|
|
292
|
+
|
|
293
|
+
// --- Sync 2: C1 Second Pass (Idempotent check) ---
|
|
294
|
+
devLog(`4.2B Sync: Starting Pass 2 (Sync C1 - Idempotent)...`);
|
|
295
|
+
senderPeer = new SyncPeerWebSocketSender_V1({
|
|
296
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
297
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
298
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
299
|
+
});
|
|
300
|
+
coordinator = new SyncSagaCoordinator();
|
|
301
|
+
sagaId = await getUUID();
|
|
302
|
+
debugState.sagaId = sagaId;
|
|
303
|
+
debugState.senderPeer = senderPeer;
|
|
304
|
+
|
|
305
|
+
await senderPeer.initializeOpts({
|
|
306
|
+
localMetaspace: metaspace,
|
|
307
|
+
localSpace: space,
|
|
308
|
+
senderIdentity: senderIdentityEvolved1,
|
|
309
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
310
|
+
sagaId,
|
|
311
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
312
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
313
|
+
targetAddrs: [domainAddr]
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
syncSaga = await coordinator.sync({
|
|
317
|
+
domainIbGibs: [c1],
|
|
318
|
+
senderIdentity: senderIdentityEvolved1,
|
|
319
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
320
|
+
peer: senderPeer,
|
|
321
|
+
localSpace: space,
|
|
322
|
+
metaspace,
|
|
323
|
+
conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
|
|
324
|
+
});
|
|
325
|
+
await syncSaga.done;
|
|
326
|
+
devLog(`4.2B Sync: ✓ Pass 2 Complete.`);
|
|
327
|
+
|
|
328
|
+
const latestSenderAddr2 = await metaspace.getLatestAddr({
|
|
329
|
+
addr: getIbGibAddr({ ibGib: domainI }),
|
|
330
|
+
space,
|
|
331
|
+
});
|
|
332
|
+
const resGetLatestI2 = await metaspace.get({ addrs: [latestSenderAddr2!], space });
|
|
333
|
+
const senderIdentityEvolved2 = resGetLatestI2.ibGibs![0] as KeystoneIbGib_V1;
|
|
334
|
+
|
|
335
|
+
// 2. Create Constant C2 locally linking to C1
|
|
336
|
+
const c2 = await factory.constant({
|
|
337
|
+
ib: 'constant_c2_' + (await getUUID()).slice(0, 8),
|
|
338
|
+
parentPrimitiveIb: 'root',
|
|
339
|
+
rel8ns: {
|
|
340
|
+
link: [addrC1],
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
const addrC2 = getIbGibAddr({ ibGib: c2 });
|
|
344
|
+
await metaspace.put({ ibGib: c2, space });
|
|
345
|
+
await metaspace.registerNewIbGib({ ibGib: c2 });
|
|
346
|
+
debugState.targetC2 = c2;
|
|
347
|
+
devLog(`4.2B Sync: ✓ Constant C2 created linking to C1: ${addrC2}`);
|
|
348
|
+
|
|
349
|
+
// --- Sync 3: C2 Pass ---
|
|
350
|
+
devLog(`4.2B Sync: Starting Pass 3 (Sync C2)...`);
|
|
351
|
+
senderPeer = new SyncPeerWebSocketSender_V1({
|
|
352
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
353
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
354
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
355
|
+
});
|
|
356
|
+
coordinator = new SyncSagaCoordinator();
|
|
357
|
+
sagaId = await getUUID();
|
|
358
|
+
debugState.sagaId = sagaId;
|
|
359
|
+
debugState.senderPeer = senderPeer;
|
|
360
|
+
|
|
361
|
+
await senderPeer.initializeOpts({
|
|
362
|
+
localMetaspace: metaspace,
|
|
363
|
+
localSpace: space,
|
|
364
|
+
senderIdentity: senderIdentityEvolved2,
|
|
365
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
366
|
+
sagaId,
|
|
367
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
368
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
369
|
+
targetAddrs: [domainAddr]
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
syncSaga = await coordinator.sync({
|
|
373
|
+
domainIbGibs: [c2],
|
|
374
|
+
senderIdentity: senderIdentityEvolved2,
|
|
375
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
376
|
+
peer: senderPeer,
|
|
377
|
+
localSpace: space,
|
|
378
|
+
metaspace,
|
|
379
|
+
conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
|
|
380
|
+
});
|
|
381
|
+
await syncSaga.done;
|
|
382
|
+
devLog(`4.2B Sync: ✓ Pass 3 Complete.`);
|
|
383
|
+
|
|
384
|
+
devLog(`✓ 4.2B Sync Sequence Complete! Ready for State Checks.`);
|
|
385
|
+
btn.textContent = `✓ 4.2B Sync Run`;
|
|
386
|
+
|
|
387
|
+
const checkBtn = document.getElementById('btn-4-2b-check') as HTMLButtonElement | null;
|
|
388
|
+
if (checkBtn) { checkBtn.disabled = false; }
|
|
389
|
+
|
|
390
|
+
} catch (error) {
|
|
391
|
+
devLog(`✗ 4.2B Sync FAILED: ${extractErrorMsg(error)}`);
|
|
392
|
+
console.error(`${lc_fn} 4.2B Sync error:`, error);
|
|
393
|
+
btn.disabled = false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function init4_2bSyncButton(): void {
|
|
398
|
+
const btn = document.getElementById('btn-4-2b-sync') as HTMLButtonElement | null;
|
|
399
|
+
if (!btn) { return; }
|
|
400
|
+
btn.addEventListener('click', () => {
|
|
401
|
+
performPhaseSync4_2b();
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function init4_2bCheckButton(): void {
|
|
406
|
+
const lc_fn = `${lc}[init4_2bCheckButton]`;
|
|
407
|
+
const btn = document.getElementById('btn-4-2b-check') as HTMLButtonElement | null;
|
|
408
|
+
if (!btn) { return; }
|
|
409
|
+
|
|
410
|
+
btn.addEventListener('click', async () => {
|
|
411
|
+
try {
|
|
412
|
+
devLog('4.2B Check: Asserting cryptographic and WebSocket state expectations...');
|
|
413
|
+
|
|
414
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
415
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
416
|
+
if (!space) { throw new Error("No default space."); }
|
|
417
|
+
|
|
418
|
+
const domainI = debugState.domainI!;
|
|
419
|
+
const targetC1 = debugState.targetC1!;
|
|
420
|
+
const targetC2 = debugState.targetC2!;
|
|
421
|
+
const c1Addr = getIbGibAddr({ ibGib: targetC1 });
|
|
422
|
+
const c2Addr = getIbGibAddr({ ibGib: targetC2 });
|
|
423
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
424
|
+
|
|
425
|
+
// 1. Verify C1 and C2 exist locally
|
|
426
|
+
const localC1Res = await metaspace.get({ addrs: [c1Addr], space });
|
|
427
|
+
const localC2Res = await metaspace.get({ addrs: [c2Addr], space });
|
|
428
|
+
if (!localC1Res.success || !localC1Res.ibGibs?.[0]) {
|
|
429
|
+
devLog('✗ Check: Constant C1 NOT found locally.');
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (!localC2Res.success || !localC2Res.ibGibs?.[0]) {
|
|
433
|
+
devLog('✗ Check: Constant C2 NOT found locally.');
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
devLog(`✓ Check: Constants C1 and C2 exist locally.`);
|
|
437
|
+
|
|
438
|
+
// 2. Fetch server's graphs for C1 and C2
|
|
439
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
440
|
+
const serverC1Res = await apiBridge.getIbGib(domainAddr, c1Addr);
|
|
441
|
+
const serverC2Res = await apiBridge.getIbGib(domainAddr, c2Addr);
|
|
442
|
+
if (!serverC1Res.success || !serverC1Res.ibGib) {
|
|
443
|
+
devLog(`✗ Check: Constant C1 NOT found on server: ${serverC1Res.message}`);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (!serverC2Res.success || !serverC2Res.ibGib) {
|
|
447
|
+
devLog(`✗ Check: Constant C2 NOT found on server: ${serverC2Res.message}`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
devLog(`✓ Check: Constants C1 and C2 exist on server.`);
|
|
451
|
+
|
|
452
|
+
const localC2Graph = await metaspace.getDependencyGraph({
|
|
453
|
+
ibGibAddr: c2Addr,
|
|
454
|
+
space
|
|
455
|
+
});
|
|
456
|
+
const serverC2GetGraphRes = await apiBridge.getIbGibGraph(domainAddr, c2Addr, true);
|
|
457
|
+
if (!serverC2GetGraphRes.success || !serverC2GetGraphRes.graph) {
|
|
458
|
+
devLog(`✗ Check: Failed to fetch C2 graph from server: ${serverC2GetGraphRes.message}`);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const serverC2Graph = serverC2GetGraphRes.graph;
|
|
462
|
+
|
|
463
|
+
const c2GraphsEqual = graphsAreEquivalent({ graphA: localC2Graph, graphB: serverC2Graph });
|
|
464
|
+
if (c2GraphsEqual) {
|
|
465
|
+
devLog('✓ Check: C2 dependency graphs are equivalent on client and server.');
|
|
466
|
+
} else {
|
|
467
|
+
devLog('✗ Check: C2 dependency graphs mismatch between client and server!');
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// 3. Verify evolved sender identity I exists locally and on server, n = 3
|
|
472
|
+
const latestLocalIAddr = await metaspace.getLatestAddr({
|
|
473
|
+
addr: domainAddr,
|
|
474
|
+
space
|
|
475
|
+
});
|
|
476
|
+
if (!latestLocalIAddr) {
|
|
477
|
+
devLog('✗ Check: Evolved domain keystone tip I3 NOT found in local space!');
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const localIRes = await metaspace.get({ addrs: [latestLocalIAddr], space });
|
|
481
|
+
const localI = localIRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
482
|
+
if (!localI) {
|
|
483
|
+
devLog('✗ Check: Failed to load evolved I from local space.');
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
devLog(`✓ Check: Evolved Domain Keystone exists locally: ${latestLocalIAddr}`);
|
|
487
|
+
|
|
488
|
+
const nI = localI.data?.n;
|
|
489
|
+
devLog(`✓ Check: Evolved I tip is at n = ${nI}.`);
|
|
490
|
+
if (nI !== 3) {
|
|
491
|
+
devLog(`✗ Check: Expected I to evolve to n = 3, got n = ${nI}.`);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
|
|
496
|
+
if (!serverIGetRes.success || !serverIGetRes.ibGib) {
|
|
497
|
+
devLog(`✗ Check: Evolved Domain Keystone tip NOT found on server!`);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
devLog(`✓ Check: Evolved Domain Keystone tip exists on server.`);
|
|
501
|
+
|
|
502
|
+
// 4. Verify session identity S tip exists locally and on server
|
|
503
|
+
const syncProof = localI.data?.proofs?.find(p => p.claim?.verb === 'sync');
|
|
504
|
+
const sessionIdentityTjpAddr = syncProof?.claim?.target;
|
|
505
|
+
if (!sessionIdentityTjpAddr) {
|
|
506
|
+
devLog('✗ Check: Evolved I sync claim does not target a session keystone.');
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const latestLocalSAddr = await metaspace.getLatestAddr({
|
|
511
|
+
addr: sessionIdentityTjpAddr,
|
|
512
|
+
space
|
|
513
|
+
});
|
|
514
|
+
if (!latestLocalSAddr) {
|
|
515
|
+
devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
devLog(`✓ Check: Found latest session keystone (S) locally: ${latestLocalSAddr}`);
|
|
519
|
+
|
|
520
|
+
const localSRes = await metaspace.get({ addrs: [latestLocalSAddr], space });
|
|
521
|
+
const sessionS = localSRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
522
|
+
if (!sessionS) {
|
|
523
|
+
devLog('✗ Check: Failed to load latest Session Keystone (S) locally.');
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const nS = sessionS.data?.n;
|
|
528
|
+
devLog(`✓ Check: Latest session S tip evolved to n = ${nS}.`);
|
|
529
|
+
if (nS !== 3) {
|
|
530
|
+
devLog(`✗ Check: Expected latest session S to evolve to n = 3, got n = ${nS}.`);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const serverSGetRes = await apiBridge.getIbGib(domainAddr, latestLocalSAddr);
|
|
535
|
+
if (!serverSGetRes.success || !serverSGetRes.ibGib) {
|
|
536
|
+
devLog(`✗ Check: Latest Session Keystone (S) NOT found on server!`);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
devLog(`✓ Check: Latest Session Keystone (S) exists on server.`);
|
|
540
|
+
|
|
541
|
+
// 5. Assert S graph is identical on client and server
|
|
542
|
+
const localSGraph = await metaspace.getDependencyGraph({
|
|
543
|
+
ibGibAddr: latestLocalSAddr,
|
|
544
|
+
space
|
|
545
|
+
});
|
|
546
|
+
const serverSGetGraphRes = await apiBridge.getIbGibGraph(domainAddr, sessionIdentityTjpAddr, true);
|
|
547
|
+
if (!serverSGetGraphRes.success || !serverSGetGraphRes.graph) {
|
|
548
|
+
devLog(`✗ Check: Failed to fetch session S graph from server: ${serverSGetGraphRes.message}`);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const serverSGraph = serverSGetGraphRes.graph;
|
|
552
|
+
|
|
553
|
+
const sGraphsEqual = graphsAreEquivalent({ graphA: localSGraph, graphB: serverSGraph });
|
|
554
|
+
if (sGraphsEqual) {
|
|
555
|
+
devLog('✓ Check: Session Keystone (S) dependency graphs are equivalent on client and server.');
|
|
556
|
+
} else {
|
|
557
|
+
devLog('✗ Check: Session Keystone (S) dependency graphs mismatch between client and server!');
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
devLog('🎉 ALL PHASE 4.2B TRANSACTION SYNC CHECKS PASSED FLAWLESSLY! ✓');
|
|
562
|
+
btn.textContent = '✓ 4.2B All Passed';
|
|
563
|
+
|
|
564
|
+
} catch (error) {
|
|
565
|
+
devLog(`✗ 4.2B Check FAILED: ${extractErrorMsg(error)}`);
|
|
566
|
+
console.error(`${lc_fn} 4.2B Check error:`, error);
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export function init4_3bSetupButton(): void {
|
|
572
|
+
const btn = document.getElementById('btn-4-3b-setup') as HTMLButtonElement | null;
|
|
573
|
+
if (!btn) { return; }
|
|
574
|
+
btn.addEventListener('click', async () => {
|
|
575
|
+
try {
|
|
576
|
+
btn.disabled = true;
|
|
577
|
+
devLog('4.3B Setup: Generating Domain Keystone (I) and Target timeline V0 -> V1...');
|
|
578
|
+
|
|
579
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
580
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
581
|
+
if (!space) { throw new Error("No default space."); }
|
|
582
|
+
|
|
583
|
+
const keystoneService = new KeystoneService_V1();
|
|
584
|
+
|
|
585
|
+
const domainI = await keystoneService.genesis({
|
|
586
|
+
masterSecret: 'test-sender-secret-phase4-3',
|
|
587
|
+
configs: [
|
|
588
|
+
getSpaceGibPoolConfig('sync', 'senderidentitysyncsaltphase4-3'),
|
|
589
|
+
getSpaceGibPoolConfig('manage', 'senderidentitymanagesaltphase4-3'),
|
|
590
|
+
],
|
|
591
|
+
metaspace,
|
|
592
|
+
space,
|
|
593
|
+
frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
debugState.domainI = domainI;
|
|
597
|
+
debugState.domainIMasterSecret = 'test-sender-secret-phase4-3';
|
|
598
|
+
|
|
599
|
+
devLog(`4.3B Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
|
|
600
|
+
|
|
601
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
602
|
+
const resGenesis = await apiBridge.postGenesisKeystone(domainI);
|
|
603
|
+
if (resGenesis.success) {
|
|
604
|
+
devLog(`4.3B Setup: ✓ Domain Keystone registered on server.`);
|
|
605
|
+
} else {
|
|
606
|
+
throw new Error(`Server rejected genesis domain keystone: ${resGenesis.message}`);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const resV0 = await factory.firstGen({
|
|
610
|
+
parentIbGib: ROOT,
|
|
611
|
+
ib: 'timeline_root_partial_4_3b',
|
|
612
|
+
data: { type: 'root', label: 'Root4_3b', random: Math.random() },
|
|
613
|
+
dna: true,
|
|
614
|
+
nCounter: true,
|
|
615
|
+
tjp: { uuid: true, timestamp: true }
|
|
616
|
+
});
|
|
617
|
+
const r1_v0 = resV0.newIbGib;
|
|
618
|
+
await metaspace.persistTransformResult({ resTransform: resV0, space });
|
|
619
|
+
await metaspace.registerNewIbGib({ ibGib: r1_v0 });
|
|
620
|
+
|
|
621
|
+
const r1_v1 = await mut8Timeline({
|
|
622
|
+
timeline: r1_v0,
|
|
623
|
+
mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1_4_3b' } },
|
|
624
|
+
metaspace,
|
|
625
|
+
space,
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
debugState.targetC1 = r1_v1;
|
|
629
|
+
debugState.targetX = r1_v1;
|
|
630
|
+
devLog(`4.3B Setup: ✓ Timeline V0 -> V1 created and stored locally. V1: ${getIbGibAddr({ ibGib: r1_v1 })}`);
|
|
631
|
+
|
|
632
|
+
devLog('✓ 4.3B Setup Complete! Ready for 4.3B Sync.');
|
|
633
|
+
btn.textContent = '✓ 4.3B Setup Complete';
|
|
634
|
+
|
|
635
|
+
const syncBtn = document.getElementById('btn-4-3b-sync') as HTMLButtonElement | null;
|
|
636
|
+
if (syncBtn) { syncBtn.disabled = false; }
|
|
637
|
+
|
|
638
|
+
} catch (error) {
|
|
639
|
+
devLog(`✗ 4.3B Setup FAILED: ${extractErrorMsg(error)}`);
|
|
640
|
+
console.error(error);
|
|
641
|
+
btn.disabled = false;
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
async function performPhaseSync4_3b(): Promise<void> {
|
|
647
|
+
const lc_fn = `${lc}[performPhaseSync4_3b]`;
|
|
648
|
+
const btn = document.getElementById('btn-4-3b-sync') as HTMLButtonElement | null;
|
|
649
|
+
if (!btn) { return; }
|
|
650
|
+
|
|
651
|
+
try {
|
|
652
|
+
btn.disabled = true;
|
|
653
|
+
devLog(`4.3B Sync: Starting Pass 1 (Sync V1 to server)...`);
|
|
654
|
+
|
|
655
|
+
const domainI = debugState.domainI;
|
|
656
|
+
const targetV1 = debugState.targetX;
|
|
657
|
+
if (!domainI || !targetV1) {
|
|
658
|
+
devLog(`⚠ 4.3B Sync: Missing setup state. Please run Setup first.`);
|
|
659
|
+
btn.disabled = false;
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
664
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
665
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
666
|
+
if (!space) { throw new Error("No default space."); }
|
|
667
|
+
|
|
668
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
669
|
+
|
|
670
|
+
let senderPeer = new SyncPeerWebSocketSender_V1({
|
|
671
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
672
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
673
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
let coordinator = new SyncSagaCoordinator();
|
|
677
|
+
let sagaId = await getUUID();
|
|
678
|
+
debugState.sagaId = sagaId;
|
|
679
|
+
debugState.senderPeer = senderPeer;
|
|
680
|
+
|
|
681
|
+
await senderPeer.initializeOpts({
|
|
682
|
+
localMetaspace: metaspace,
|
|
683
|
+
localSpace: space,
|
|
684
|
+
senderIdentity: domainI,
|
|
685
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
686
|
+
sagaId,
|
|
687
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
688
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
689
|
+
targetAddrs: [domainAddr]
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
let syncSaga = await coordinator.sync({
|
|
693
|
+
domainIbGibs: [targetV1],
|
|
694
|
+
senderIdentity: domainI,
|
|
695
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
696
|
+
peer: senderPeer,
|
|
697
|
+
localSpace: space,
|
|
698
|
+
metaspace,
|
|
699
|
+
conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
|
|
700
|
+
});
|
|
701
|
+
await syncSaga.done;
|
|
702
|
+
devLog(`4.3B Sync: ✓ Pass 1 Complete (V1 synced).`);
|
|
703
|
+
|
|
704
|
+
const latestSenderAddr1 = await metaspace.getLatestAddr({
|
|
705
|
+
addr: getIbGibAddr({ ibGib: domainI }),
|
|
706
|
+
space,
|
|
707
|
+
});
|
|
708
|
+
const resGetLatestI1 = await metaspace.get({ addrs: [latestSenderAddr1!], space });
|
|
709
|
+
const senderIdentityEvolved1 = resGetLatestI1.ibGibs![0] as KeystoneIbGib_V1;
|
|
710
|
+
|
|
711
|
+
devLog(`4.3B Sync: Mutating V1 to V2 locally...`);
|
|
712
|
+
const r1_v2 = await mut8Timeline({
|
|
713
|
+
timeline: targetV1,
|
|
714
|
+
mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V2_4_3b' } },
|
|
715
|
+
metaspace,
|
|
716
|
+
space,
|
|
717
|
+
});
|
|
718
|
+
debugState.targetX = r1_v2;
|
|
719
|
+
devLog(`4.3B Sync: ✓ V2 created locally: ${getIbGibAddr({ ibGib: r1_v2 })}`);
|
|
720
|
+
|
|
721
|
+
devLog(`4.3B Sync: Starting Pass 2 (Sync V2 delta to server)...`);
|
|
722
|
+
senderPeer = new SyncPeerWebSocketSender_V1({
|
|
723
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
724
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
725
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
726
|
+
});
|
|
727
|
+
coordinator = new SyncSagaCoordinator();
|
|
728
|
+
sagaId = await getUUID();
|
|
729
|
+
debugState.sagaId = sagaId;
|
|
730
|
+
debugState.senderPeer = senderPeer;
|
|
731
|
+
|
|
732
|
+
await senderPeer.initializeOpts({
|
|
733
|
+
localMetaspace: metaspace,
|
|
734
|
+
localSpace: space,
|
|
735
|
+
senderIdentity: senderIdentityEvolved1,
|
|
736
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
737
|
+
sagaId,
|
|
738
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
739
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
740
|
+
targetAddrs: [domainAddr]
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
syncSaga = await coordinator.sync({
|
|
744
|
+
domainIbGibs: [r1_v2],
|
|
745
|
+
senderIdentity: senderIdentityEvolved1,
|
|
746
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
747
|
+
peer: senderPeer,
|
|
748
|
+
localSpace: space,
|
|
749
|
+
metaspace,
|
|
750
|
+
conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
|
|
751
|
+
});
|
|
752
|
+
await syncSaga.done;
|
|
753
|
+
devLog(`4.3B Sync: ✓ Pass 2 Complete (V2 synced).`);
|
|
754
|
+
|
|
755
|
+
devLog(`✓ 4.3B Sync Sequence Complete! Ready for State Checks.`);
|
|
756
|
+
btn.textContent = `✓ 4.3B Sync Run`;
|
|
757
|
+
|
|
758
|
+
const checkBtn = document.getElementById('btn-4-3b-check') as HTMLButtonElement | null;
|
|
759
|
+
if (checkBtn) { checkBtn.disabled = false; }
|
|
760
|
+
|
|
761
|
+
} catch (error) {
|
|
762
|
+
devLog(`✗ 4.3B Sync FAILED: ${extractErrorMsg(error)}`);
|
|
763
|
+
console.error(error);
|
|
764
|
+
btn.disabled = false;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
export function init4_3bSyncButton(): void {
|
|
769
|
+
const btn = document.getElementById('btn-4-3b-sync') as HTMLButtonElement | null;
|
|
770
|
+
if (!btn) { return; }
|
|
771
|
+
btn.addEventListener('click', () => {
|
|
772
|
+
performPhaseSync4_3b();
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export function init4_3bCheckButton(): void {
|
|
777
|
+
const lc_fn = `${lc}[init4_3bCheckButton]`;
|
|
778
|
+
const btn = document.getElementById('btn-4-3b-check') as HTMLButtonElement | null;
|
|
779
|
+
if (!btn) { return; }
|
|
780
|
+
|
|
781
|
+
btn.addEventListener('click', async () => {
|
|
782
|
+
try {
|
|
783
|
+
devLog('4.3B Check: Asserting cryptographic and WebSocket state expectations...');
|
|
784
|
+
|
|
785
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
786
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
787
|
+
if (!space) { throw new Error("No default space."); }
|
|
788
|
+
|
|
789
|
+
const domainI = debugState.domainI!;
|
|
790
|
+
const targetV1 = debugState.targetC1!;
|
|
791
|
+
const targetV2 = debugState.targetX!;
|
|
792
|
+
const v1Addr = getIbGibAddr({ ibGib: targetV1 });
|
|
793
|
+
const v2Addr = getIbGibAddr({ ibGib: targetV2 });
|
|
794
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
795
|
+
|
|
796
|
+
// 1. Verify V1 and V2 exist locally
|
|
797
|
+
const localV1Res = await metaspace.get({ addrs: [v1Addr], space });
|
|
798
|
+
const localV2Res = await metaspace.get({ addrs: [v2Addr], space });
|
|
799
|
+
if (!localV1Res.success || !localV1Res.ibGibs?.[0]) {
|
|
800
|
+
devLog('✗ Check: Timeline frame V1 NOT found locally.');
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
if (!localV2Res.success || !localV2Res.ibGibs?.[0]) {
|
|
804
|
+
devLog('✗ Check: Timeline frame V2 NOT found locally.');
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
devLog(`✓ Check: Timeline frames V1 and V2 exist locally.`);
|
|
808
|
+
|
|
809
|
+
// 2. Fetch server's graphs for V2
|
|
810
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
811
|
+
const serverV2Res = await apiBridge.getIbGib(domainAddr, v2Addr);
|
|
812
|
+
if (!serverV2Res.success || !serverV2Res.ibGib) {
|
|
813
|
+
devLog(`✗ Check: Timeline frame V2 NOT found on server: ${serverV2Res.message}`);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
devLog(`✓ Check: Timeline frame V2 exists on server.`);
|
|
817
|
+
|
|
818
|
+
const serverV2IbGib = serverV2Res.ibGib;
|
|
819
|
+
const pastAddr = serverV2IbGib.rel8ns?.past?.at(-1);
|
|
820
|
+
if (pastAddr === v1Addr) {
|
|
821
|
+
devLog('✓ Check: Server V2 correctly references V1 in its past relation.');
|
|
822
|
+
} else {
|
|
823
|
+
devLog(`✗ Check: Server V2 does not reference V1 in past relation! got: ${pastAddr}`);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const localV2Graph = await metaspace.getDependencyGraph({
|
|
828
|
+
ibGibAddr: v2Addr,
|
|
829
|
+
space
|
|
830
|
+
});
|
|
831
|
+
const serverV2GetGraphRes = await apiBridge.getIbGibGraph(domainAddr, v2Addr, true);
|
|
832
|
+
if (!serverV2GetGraphRes.success || !serverV2GetGraphRes.graph) {
|
|
833
|
+
devLog(`✗ Check: Failed to fetch V2 graph from server: ${serverV2GetGraphRes.message}`);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const serverV2Graph = serverV2GetGraphRes.graph;
|
|
837
|
+
|
|
838
|
+
const v2GraphsEqual = graphsAreEquivalent({ graphA: localV2Graph, graphB: serverV2Graph });
|
|
839
|
+
if (v2GraphsEqual) {
|
|
840
|
+
devLog('✓ Check: V2 timeline dependency graphs are equivalent on client and server.');
|
|
841
|
+
} else {
|
|
842
|
+
devLog('✗ Check: V2 timeline dependency graphs mismatch between client and server!');
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// 3. Verify evolved sender identity I exists locally and on server, n = 2
|
|
847
|
+
const latestLocalIAddr = await metaspace.getLatestAddr({
|
|
848
|
+
addr: domainAddr,
|
|
849
|
+
space
|
|
850
|
+
});
|
|
851
|
+
if (!latestLocalIAddr) {
|
|
852
|
+
devLog('✗ Check: Evolved domain keystone tip NOT found in local space!');
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const localIRes = await metaspace.get({ addrs: [latestLocalIAddr], space });
|
|
856
|
+
const localI = localIRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
857
|
+
if (!localI) {
|
|
858
|
+
devLog('✗ Check: Failed to load evolved I from local space.');
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
devLog(`✓ Check: Evolved Domain Keystone exists locally: ${latestLocalIAddr}`);
|
|
862
|
+
|
|
863
|
+
const nI = localI.data?.n;
|
|
864
|
+
devLog(`✓ Check: Evolved I tip is at n = ${nI}.`);
|
|
865
|
+
if (nI !== 2) {
|
|
866
|
+
devLog(`✗ Check: Expected I to evolve to n = 2, got n = ${nI}.`);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
|
|
871
|
+
if (!serverIGetRes.success || !serverIGetRes.ibGib) {
|
|
872
|
+
devLog(`✗ Check: Evolved Domain Keystone tip NOT found on server!`);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
devLog(`✓ Check: Evolved Domain Keystone tip exists on server.`);
|
|
876
|
+
|
|
877
|
+
// 4. Verify session identity S tip exists locally and on server, n = 3
|
|
878
|
+
const syncProof = localI.data?.proofs?.find(p => p.claim?.verb === 'sync');
|
|
879
|
+
const sessionIdentityTjpAddr = syncProof?.claim?.target;
|
|
880
|
+
if (!sessionIdentityTjpAddr) {
|
|
881
|
+
devLog('✗ Check: Evolved I sync claim does not target a session keystone.');
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const latestLocalSAddr = await metaspace.getLatestAddr({
|
|
886
|
+
addr: sessionIdentityTjpAddr,
|
|
887
|
+
space
|
|
888
|
+
});
|
|
889
|
+
if (!latestLocalSAddr) {
|
|
890
|
+
devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
devLog(`✓ Check: Found latest session keystone (S) locally: ${latestLocalSAddr}`);
|
|
894
|
+
|
|
895
|
+
const localSRes = await metaspace.get({ addrs: [latestLocalSAddr], space });
|
|
896
|
+
const sessionS = localSRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
897
|
+
if (!sessionS) {
|
|
898
|
+
devLog('✗ Check: Failed to load latest Session Keystone (S) locally.');
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
const nS = sessionS.data?.n;
|
|
903
|
+
devLog(`✓ Check: Latest session S tip evolved to n = ${nS}.`);
|
|
904
|
+
if (nS !== 3) {
|
|
905
|
+
devLog(`✗ Check: Expected latest session S to evolve to n = 3, got n = ${nS}.`);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const serverSGetRes = await apiBridge.getIbGib(domainAddr, latestLocalSAddr);
|
|
910
|
+
if (!serverSGetRes.success || !serverSGetRes.ibGib) {
|
|
911
|
+
devLog(`✗ Check: Latest Session Keystone (S) NOT found on server!`);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
devLog(`✓ Check: Latest Session Keystone (S) exists on server.`);
|
|
915
|
+
|
|
916
|
+
// 5. Assert S graph is identical on client and server
|
|
917
|
+
const localSGraph = await metaspace.getDependencyGraph({
|
|
918
|
+
ibGibAddr: latestLocalSAddr,
|
|
919
|
+
space
|
|
920
|
+
});
|
|
921
|
+
const serverSGetGraphRes = await apiBridge.getIbGibGraph(domainAddr, sessionIdentityTjpAddr, true);
|
|
922
|
+
if (!serverSGetGraphRes.success || !serverSGetGraphRes.graph) {
|
|
923
|
+
devLog(`✗ Check: Failed to fetch session S graph from server: ${serverSGetGraphRes.message}`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const serverSGraph = serverSGetGraphRes.graph;
|
|
927
|
+
|
|
928
|
+
const sGraphsEqual = graphsAreEquivalent({ graphA: localSGraph, graphB: serverSGraph });
|
|
929
|
+
if (sGraphsEqual) {
|
|
930
|
+
devLog('✓ Check: Session Keystone (S) dependency graphs are equivalent on client and server.');
|
|
931
|
+
} else {
|
|
932
|
+
devLog('✗ Check: Session Keystone (S) dependency graphs mismatch between client and server!');
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
devLog('🎉 ALL PHASE 4.3B TRANSACTION SYNC CHECKS PASSED FLAWLESSLY! ✓');
|
|
937
|
+
btn.textContent = '✓ 4.3B All Passed';
|
|
938
|
+
|
|
939
|
+
} catch (error) {
|
|
940
|
+
devLog(`✗ 4.3B Check FAILED: ${extractErrorMsg(error)}`);
|
|
941
|
+
console.error(`${lc_fn} 4.3B Check error:`, error);
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
export function init4_4bSetupButton(): void {
|
|
947
|
+
const btn = document.getElementById('btn-4-4b-setup') as HTMLButtonElement | null;
|
|
948
|
+
if (!btn) { return; }
|
|
949
|
+
btn.addEventListener('click', async () => {
|
|
950
|
+
try {
|
|
951
|
+
btn.disabled = true;
|
|
952
|
+
devLog('4.4B Setup: Generating Domain Keystone (I) and Target timeline V0 -> V1 -> V2...');
|
|
953
|
+
|
|
954
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
955
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
956
|
+
if (!space) { throw new Error("No default space."); }
|
|
957
|
+
|
|
958
|
+
const keystoneService = new KeystoneService_V1();
|
|
959
|
+
|
|
960
|
+
const domainI = await keystoneService.genesis({
|
|
961
|
+
masterSecret: 'test-sender-secret-phase4-4',
|
|
962
|
+
configs: [
|
|
963
|
+
getSpaceGibPoolConfig('sync', 'senderidentitysyncsaltphase4-4'),
|
|
964
|
+
getSpaceGibPoolConfig('manage', 'senderidentitymanagesaltphase4-4'),
|
|
965
|
+
],
|
|
966
|
+
metaspace,
|
|
967
|
+
space,
|
|
968
|
+
frameDetails: { client: 'space-gib-web-dev', timestamp: getTimestamp() }
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
debugState.domainI = domainI;
|
|
972
|
+
debugState.domainIMasterSecret = 'test-sender-secret-phase4-4';
|
|
973
|
+
|
|
974
|
+
devLog(`4.4B Setup: ✓ Domain Keystone (I) created locally: ${getIbGibAddr({ ibGib: domainI })}`);
|
|
975
|
+
|
|
976
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
977
|
+
const resGenesis = await apiBridge.postGenesisKeystone(domainI);
|
|
978
|
+
if (resGenesis.success) {
|
|
979
|
+
devLog(`4.4B Setup: ✓ Domain Keystone registered on server.`);
|
|
980
|
+
} else {
|
|
981
|
+
throw new Error(`Server rejected genesis domain keystone: ${resGenesis.message}`);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// Create V0 (Root)
|
|
985
|
+
const resV0 = await factory.firstGen({
|
|
986
|
+
parentIbGib: ROOT,
|
|
987
|
+
ib: 'timeline_root_deep_4_4b',
|
|
988
|
+
data: { type: 'root', label: 'Root4_4b', random: Math.random() },
|
|
989
|
+
dna: true,
|
|
990
|
+
nCounter: true,
|
|
991
|
+
tjp: { uuid: true, timestamp: true }
|
|
992
|
+
});
|
|
993
|
+
const r1_v0 = resV0.newIbGib;
|
|
994
|
+
await metaspace.persistTransformResult({ resTransform: resV0, space });
|
|
995
|
+
await metaspace.registerNewIbGib({ ibGib: r1_v0 });
|
|
996
|
+
debugState.targetC1 = r1_v0; // V0
|
|
997
|
+
|
|
998
|
+
// Create V1
|
|
999
|
+
const r1_v1 = await mut8Timeline({
|
|
1000
|
+
timeline: r1_v0,
|
|
1001
|
+
mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V1_4_4b' } },
|
|
1002
|
+
metaspace,
|
|
1003
|
+
space,
|
|
1004
|
+
});
|
|
1005
|
+
debugState.targetC2 = r1_v1; // V1
|
|
1006
|
+
|
|
1007
|
+
// Create V2
|
|
1008
|
+
const r1_v2 = await mut8Timeline({
|
|
1009
|
+
timeline: r1_v1,
|
|
1010
|
+
mut8Opts: { dataToAddOrPatch: { type: 'comment', label: 'V2_4_4b' } },
|
|
1011
|
+
metaspace,
|
|
1012
|
+
space,
|
|
1013
|
+
});
|
|
1014
|
+
debugState.targetX = r1_v2; // V2 (Tip)
|
|
1015
|
+
|
|
1016
|
+
devLog(`4.4B Setup: ✓ Timeline V0 -> V1 -> V2 created locally. V2 Tip: ${getIbGibAddr({ ibGib: r1_v2 })}`);
|
|
1017
|
+
|
|
1018
|
+
devLog('✓ 4.4B Setup Complete! Ready for 4.4B Sync.');
|
|
1019
|
+
btn.textContent = '✓ 4.4B Setup Complete';
|
|
1020
|
+
|
|
1021
|
+
const syncBtn = document.getElementById('btn-4-4b-sync') as HTMLButtonElement | null;
|
|
1022
|
+
if (syncBtn) { syncBtn.disabled = false; }
|
|
1023
|
+
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
devLog(`✗ 4.4B Setup FAILED: ${extractErrorMsg(error)}`);
|
|
1026
|
+
console.error(error);
|
|
1027
|
+
btn.disabled = false;
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
async function performPhaseSync4_4b(): Promise<void> {
|
|
1033
|
+
const lc_fn = `${lc}[performPhaseSync4_4b]`;
|
|
1034
|
+
const btn = document.getElementById('btn-4-4b-sync') as HTMLButtonElement | null;
|
|
1035
|
+
if (!btn) { return; }
|
|
1036
|
+
|
|
1037
|
+
try {
|
|
1038
|
+
btn.disabled = true;
|
|
1039
|
+
devLog(`4.4B Sync: Initiating WebSocket Sync of V2 Tip...`);
|
|
1040
|
+
|
|
1041
|
+
const domainI = debugState.domainI;
|
|
1042
|
+
const targetV2 = debugState.targetX;
|
|
1043
|
+
if (!domainI || !targetV2) {
|
|
1044
|
+
devLog(`⚠ 4.4B Sync: Missing setup state. Please run Setup first.`);
|
|
1045
|
+
btn.disabled = false;
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
1050
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
1051
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
1052
|
+
if (!space) { throw new Error("No default space."); }
|
|
1053
|
+
|
|
1054
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
1055
|
+
|
|
1056
|
+
const senderPeer = new SyncPeerWebSocketSender_V1({
|
|
1057
|
+
classname: 'SyncPeerWebSocketSender_V1',
|
|
1058
|
+
httpEvolveUrl: `${location.protocol}//${location.host}/api/keystone/evolve/${encodeURIComponent(domainAddr)}`,
|
|
1059
|
+
wsUrl: `${protocol}//${location.host}/api/sync/ws/${encodeURIComponent(domainAddr)}`
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
const coordinator = new SyncSagaCoordinator();
|
|
1063
|
+
const sagaId = await getUUID();
|
|
1064
|
+
debugState.sagaId = sagaId;
|
|
1065
|
+
debugState.senderPeer = senderPeer;
|
|
1066
|
+
|
|
1067
|
+
await senderPeer.initializeOpts({
|
|
1068
|
+
localMetaspace: metaspace,
|
|
1069
|
+
localSpace: space,
|
|
1070
|
+
senderIdentity: domainI,
|
|
1071
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
1072
|
+
sagaId,
|
|
1073
|
+
sessionConnectPoolConfig: SESSION_KEYSTONE_POLICY.CONNECT_POOL as any,
|
|
1074
|
+
sessionSyncPoolConfig: SESSION_KEYSTONE_POLICY.DEFAULT_POOL as any,
|
|
1075
|
+
targetAddrs: [domainAddr]
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
const syncSaga = await coordinator.sync({
|
|
1079
|
+
domainIbGibs: [targetV2],
|
|
1080
|
+
senderIdentity: domainI,
|
|
1081
|
+
fnSenderSecret: async () => debugState.domainIMasterSecret!,
|
|
1082
|
+
peer: senderPeer,
|
|
1083
|
+
localSpace: space,
|
|
1084
|
+
metaspace,
|
|
1085
|
+
conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
|
|
1086
|
+
});
|
|
1087
|
+
await syncSaga.done;
|
|
1088
|
+
devLog(`4.4B Sync: ✓ WebSocket Sync completed.`);
|
|
1089
|
+
|
|
1090
|
+
devLog(`✓ 4.4B Sync Sequence Complete! Ready for State Checks.`);
|
|
1091
|
+
btn.textContent = `✓ 4.4B Sync Run`;
|
|
1092
|
+
|
|
1093
|
+
const checkBtn = document.getElementById('btn-4-4b-check') as HTMLButtonElement | null;
|
|
1094
|
+
if (checkBtn) { checkBtn.disabled = false; }
|
|
1095
|
+
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
devLog(`✗ 4.4B Sync FAILED: ${extractErrorMsg(error)}`);
|
|
1098
|
+
console.error(error);
|
|
1099
|
+
btn.disabled = false;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
export function init4_4bSyncButton(): void {
|
|
1104
|
+
const btn = document.getElementById('btn-4-4b-sync') as HTMLButtonElement | null;
|
|
1105
|
+
if (!btn) { return; }
|
|
1106
|
+
btn.addEventListener('click', () => {
|
|
1107
|
+
performPhaseSync4_4b();
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
export function init4_4bCheckButton(): void {
|
|
1112
|
+
const lc_fn = `${lc}[init4_4bCheckButton]`;
|
|
1113
|
+
const btn = document.getElementById('btn-4-4b-check') as HTMLButtonElement | null;
|
|
1114
|
+
if (!btn) { return; }
|
|
1115
|
+
|
|
1116
|
+
btn.addEventListener('click', async () => {
|
|
1117
|
+
try {
|
|
1118
|
+
devLog('4.4B Check: Asserting cryptographic and WebSocket state expectations...');
|
|
1119
|
+
|
|
1120
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
1121
|
+
const space = await metaspace.getLocalUserSpace({});
|
|
1122
|
+
if (!space) { throw new Error("No default space."); }
|
|
1123
|
+
|
|
1124
|
+
const domainI = debugState.domainI!;
|
|
1125
|
+
const targetV0 = debugState.targetC1!;
|
|
1126
|
+
const targetV1 = debugState.targetC2!;
|
|
1127
|
+
const targetV2 = debugState.targetX!;
|
|
1128
|
+
const v0Addr = getIbGibAddr({ ibGib: targetV0 });
|
|
1129
|
+
const v1Addr = getIbGibAddr({ ibGib: targetV1 });
|
|
1130
|
+
const v2Addr = getIbGibAddr({ ibGib: targetV2 });
|
|
1131
|
+
const domainAddr = getIbGibAddr({ ibGib: domainI });
|
|
1132
|
+
|
|
1133
|
+
// 1. Verify V0, V1, and V2 exist locally
|
|
1134
|
+
const localV0Res = await metaspace.get({ addrs: [v0Addr], space });
|
|
1135
|
+
const localV1Res = await metaspace.get({ addrs: [v1Addr], space });
|
|
1136
|
+
const localV2Res = await metaspace.get({ addrs: [v2Addr], space });
|
|
1137
|
+
if (!localV0Res.success || !localV0Res.ibGibs?.[0]) {
|
|
1138
|
+
devLog('✗ Check: Timeline frame V0 NOT found locally.');
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (!localV1Res.success || !localV1Res.ibGibs?.[0]) {
|
|
1142
|
+
devLog('✗ Check: Timeline frame V1 NOT found locally.');
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
if (!localV2Res.success || !localV2Res.ibGibs?.[0]) {
|
|
1146
|
+
devLog('✗ Check: Timeline frame V2 NOT found locally.');
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
devLog(`✓ Check: Timeline frames V0, V1, and V2 exist locally.`);
|
|
1150
|
+
|
|
1151
|
+
// 2. Fetch server's V2, V1, and V0
|
|
1152
|
+
const apiBridge = new SpaceGibApiBridge();
|
|
1153
|
+
const serverV2Res = await apiBridge.getIbGib(domainAddr, v2Addr);
|
|
1154
|
+
if (!serverV2Res.success || !serverV2Res.ibGib) {
|
|
1155
|
+
devLog(`✗ Check: Timeline frame V2 NOT found on server: ${serverV2Res.message}`);
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
devLog(`✓ Check: Timeline frame V2 exists on server.`);
|
|
1159
|
+
|
|
1160
|
+
const serverV2IbGib = serverV2Res.ibGib;
|
|
1161
|
+
const pastAddrV2 = serverV2IbGib.rel8ns?.past?.at(-1);
|
|
1162
|
+
if (pastAddrV2 === v1Addr) {
|
|
1163
|
+
devLog('✓ Check: Server V2 correctly references V1 in its past relation.');
|
|
1164
|
+
} else {
|
|
1165
|
+
devLog(`✗ Check: Server V2 does not reference V1 in past relation! got: ${pastAddrV2}`);
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
const serverV1Res = await apiBridge.getIbGib(domainAddr, v1Addr);
|
|
1170
|
+
if (!serverV1Res.success || !serverV1Res.ibGib) {
|
|
1171
|
+
devLog(`✗ Check: Timeline frame V1 NOT found on server: ${serverV1Res.message}`);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
devLog(`✓ Check: Timeline frame V1 exists on server.`);
|
|
1175
|
+
|
|
1176
|
+
const serverV1IbGib = serverV1Res.ibGib;
|
|
1177
|
+
const pastAddrV1 = serverV1IbGib.rel8ns?.past?.at(-1);
|
|
1178
|
+
if (pastAddrV1 === v0Addr) {
|
|
1179
|
+
devLog('✓ Check: Server V1 correctly references V0 in its past relation.');
|
|
1180
|
+
} else {
|
|
1181
|
+
devLog(`✗ Check: Server V1 does not reference V0 in past relation! got: ${pastAddrV1}`);
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
const localV2Graph = await metaspace.getDependencyGraph({
|
|
1186
|
+
ibGibAddr: v2Addr,
|
|
1187
|
+
space
|
|
1188
|
+
});
|
|
1189
|
+
const serverV2GetGraphRes = await apiBridge.getIbGibGraph(domainAddr, v2Addr, true);
|
|
1190
|
+
if (!serverV2GetGraphRes.success || !serverV2GetGraphRes.graph) {
|
|
1191
|
+
devLog(`✗ Check: Failed to fetch V2 graph from server: ${serverV2GetGraphRes.message}`);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
const serverV2Graph = serverV2GetGraphRes.graph;
|
|
1195
|
+
|
|
1196
|
+
const v2GraphsEqual = graphsAreEquivalent({ graphA: localV2Graph, graphB: serverV2Graph });
|
|
1197
|
+
if (v2GraphsEqual) {
|
|
1198
|
+
devLog('✓ Check: V2 timeline dependency graphs are equivalent on client and server.');
|
|
1199
|
+
} else {
|
|
1200
|
+
devLog('✗ Check: V2 timeline dependency graphs mismatch between client and server!');
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// 3. Verify evolved sender identity I exists locally and on server, n = 1 (1 connect/sync handshake)
|
|
1205
|
+
const latestLocalIAddr = await metaspace.getLatestAddr({
|
|
1206
|
+
addr: domainAddr,
|
|
1207
|
+
space
|
|
1208
|
+
});
|
|
1209
|
+
if (!latestLocalIAddr) {
|
|
1210
|
+
devLog('✗ Check: Evolved domain keystone tip NOT found in local space!');
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
const localIRes = await metaspace.get({ addrs: [latestLocalIAddr], space });
|
|
1214
|
+
const localI = localIRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
1215
|
+
if (!localI) {
|
|
1216
|
+
devLog('✗ Check: Failed to load evolved I from local space.');
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
devLog(`✓ Check: Evolved Domain Keystone exists locally: ${latestLocalIAddr}`);
|
|
1220
|
+
|
|
1221
|
+
const nI = localI.data?.n;
|
|
1222
|
+
devLog(`✓ Check: Evolved I tip is at n = ${nI}.`);
|
|
1223
|
+
if (nI !== 1) {
|
|
1224
|
+
devLog(`✗ Check: Expected I to evolve to n = 1, got n = ${nI}.`);
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
const serverIGetRes = await apiBridge.getIbGib(domainAddr, latestLocalIAddr);
|
|
1229
|
+
if (!serverIGetRes.success || !serverIGetRes.ibGib) {
|
|
1230
|
+
devLog(`✗ Check: Evolved Domain Keystone tip NOT found on server!`);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
devLog(`✓ Check: Evolved Domain Keystone tip exists on server.`);
|
|
1234
|
+
|
|
1235
|
+
// 4. Verify session identity S tip exists locally and on server, n = 2
|
|
1236
|
+
const syncProof = localI.data?.proofs?.find(p => p.claim?.verb === 'sync');
|
|
1237
|
+
const sessionIdentityTjpAddr = syncProof?.claim?.target;
|
|
1238
|
+
if (!sessionIdentityTjpAddr) {
|
|
1239
|
+
devLog('✗ Check: Evolved I sync claim does not target a session keystone.');
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
const latestLocalSAddr = await metaspace.getLatestAddr({
|
|
1244
|
+
addr: sessionIdentityTjpAddr,
|
|
1245
|
+
space
|
|
1246
|
+
});
|
|
1247
|
+
if (!latestLocalSAddr) {
|
|
1248
|
+
devLog('✗ Check: Latest Session Keystone (S) NOT found in local space.');
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
devLog(`✓ Check: Found latest session keystone (S) locally: ${latestLocalSAddr}`);
|
|
1252
|
+
|
|
1253
|
+
const localSRes = await metaspace.get({ addrs: [latestLocalSAddr], space });
|
|
1254
|
+
const sessionS = localSRes.ibGibs?.[0] as KeystoneIbGib_V1 | undefined;
|
|
1255
|
+
if (!sessionS) {
|
|
1256
|
+
devLog('✗ Check: Failed to load latest Session Keystone (S) locally.');
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
const nS = sessionS.data?.n;
|
|
1261
|
+
devLog(`✓ Check: Latest session S tip evolved to n = ${nS}.`);
|
|
1262
|
+
if (nS !== 2) {
|
|
1263
|
+
devLog(`✗ Check: Expected latest session S to evolve to n = 2, got n = ${nS}.`);
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
const serverSGetRes = await apiBridge.getIbGib(domainAddr, latestLocalSAddr);
|
|
1268
|
+
if (!serverSGetRes.success || !serverSGetRes.ibGib) {
|
|
1269
|
+
devLog(`✗ Check: Latest Session Keystone (S) NOT found on server!`);
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
devLog(`✓ Check: Latest Session Keystone (S) exists on server.`);
|
|
1273
|
+
|
|
1274
|
+
// 5. Assert S graph is identical on client and server
|
|
1275
|
+
const localSGraph = await metaspace.getDependencyGraph({
|
|
1276
|
+
ibGibAddr: latestLocalSAddr,
|
|
1277
|
+
space
|
|
1278
|
+
});
|
|
1279
|
+
const serverSGetGraphRes = await apiBridge.getIbGibGraph(domainAddr, sessionIdentityTjpAddr, true);
|
|
1280
|
+
if (!serverSGetGraphRes.success || !serverSGetGraphRes.graph) {
|
|
1281
|
+
devLog(`✗ Check: Failed to fetch session S graph from server: ${serverSGetGraphRes.message}`);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const serverSGraph = serverSGetGraphRes.graph;
|
|
1285
|
+
|
|
1286
|
+
const sGraphsEqual = graphsAreEquivalent({ graphA: localSGraph, graphB: serverSGraph });
|
|
1287
|
+
if (sGraphsEqual) {
|
|
1288
|
+
devLog('✓ Check: Session Keystone (S) dependency graphs are equivalent on client and server.');
|
|
1289
|
+
} else {
|
|
1290
|
+
devLog('✗ Check: Session Keystone (S) dependency graphs mismatch between client and server!');
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
devLog('🎉 ALL PHASE 4.4B TRANSACTION SYNC CHECKS PASSED FLAWLESSLY! ✓');
|
|
1295
|
+
btn.textContent = '✓ 4.4B All Passed';
|
|
1296
|
+
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
devLog(`✗ 4.4B Check FAILED: ${extractErrorMsg(error)}`);
|
|
1299
|
+
console.error(`${lc_fn} 4.4B Check error:`, error);
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
}
|