@ibgib/core-gib 0.1.60 → 0.1.62
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 +16 -0
- package/dist/common/other/graph-helper.d.mts.map +1 -1
- package/dist/common/other/graph-helper.mjs +7 -6
- package/dist/common/other/graph-helper.mjs.map +1 -1
- package/dist/keystone/keystone-config-builder.d.mts +1 -2
- package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
- package/dist/keystone/keystone-config-builder.mjs +4 -19
- package/dist/keystone/keystone-config-builder.mjs.map +1 -1
- package/dist/keystone/keystone-constants.d.mts +2 -1
- package/dist/keystone/keystone-constants.d.mts.map +1 -1
- package/dist/keystone/keystone-constants.mjs +2 -1
- package/dist/keystone/keystone-constants.mjs.map +1 -1
- package/dist/keystone/keystone-helpers.d.mts +28 -16
- package/dist/keystone/keystone-helpers.d.mts.map +1 -1
- package/dist/keystone/keystone-helpers.mjs +155 -76
- package/dist/keystone/keystone-helpers.mjs.map +1 -1
- package/dist/keystone/keystone-policy-types.d.mts +6 -4
- package/dist/keystone/keystone-policy-types.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.d.mts +38 -2
- package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
- package/dist/keystone/keystone-service-v1.mjs +177 -11
- package/dist/keystone/keystone-service-v1.mjs.map +1 -1
- package/dist/keystone/keystone-service-v1.respec.mjs +527 -41
- package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
- package/dist/keystone/keystone-types.d.mts +53 -11
- package/dist/keystone/keystone-types.d.mts.map +1 -1
- package/dist/keystone/policy/keystone-profile-builder.d.mts +25 -0
- package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -0
- package/dist/keystone/policy/keystone-profile-builder.mjs +117 -0
- package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -0
- package/dist/keystone/policy/profiles/profile-high.json +84 -0
- package/dist/keystone/policy/profiles/profile-low.json +84 -0
- package/dist/keystone/policy/profiles/profile-medium.json +84 -0
- package/dist/keystone/policy/profiles/profile-session.json +84 -0
- package/dist/keystone/policy/profiles/profile-test.json +84 -0
- package/dist/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
- package/dist/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
- package/dist/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
- package/dist/keystone/policy/schemas/keystone.high.schema.json +26 -0
- package/dist/keystone/policy/schemas/keystone.low.schema.json +26 -0
- package/dist/keystone/policy/schemas/keystone.medium.schema.json +26 -0
- package/dist/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
- package/dist/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
- package/dist/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
- package/dist/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
- package/dist/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
- package/dist/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
- package/dist/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
- package/dist/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
- package/dist/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
- package/dist/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
- package/dist/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
- package/dist/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts +8 -0
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +582 -0
- package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts +7 -0
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +274 -0
- package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts +8 -0
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +322 -0
- package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts +7 -0
- package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +307 -0
- package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-innerspace-constants.withid.respec.d.mts +8 -0
- package/dist/sync/sync-innerspace-constants.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace-constants.withid.respec.mjs +233 -0
- package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts +7 -0
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +233 -0
- package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts +7 -0
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +225 -0
- package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts +7 -0
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +235 -0
- package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts +7 -0
- package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +211 -0
- package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-innerspace.withid.respec.d.mts +8 -0
- package/dist/sync/sync-innerspace.withid.respec.d.mts.map +1 -0
- package/dist/sync/sync-innerspace.withid.respec.mjs +260 -0
- package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +1 -0
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +13 -4
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +5 -3
- package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
- package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
- package/dist/sync/sync-saga-coordinator.mjs +165 -145
- package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
- package/dist/sync/sync-withid.pingpong.respec.mjs +8 -8
- package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
- package/dist/timeline/timeline-api.d.mts.map +1 -1
- package/dist/timeline/timeline-api.mjs +1 -14
- package/dist/timeline/timeline-api.mjs.map +1 -1
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts.map +1 -1
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs +0 -40
- package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs.map +1 -1
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts.map +1 -1
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +6 -6
- package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -1
- package/ibgib-foundations.md +2 -1
- package/package.json +1 -1
- package/src/common/other/graph-helper.mts +7 -6
- package/src/keystone/README.md +34 -17
- package/src/keystone/docs/CRYPTANALYSIS.md +185 -0
- package/src/keystone/docs/architecture.md +45 -3
- package/src/keystone/docs/delegation.md +57 -0
- package/src/keystone/docs/profiles.md +124 -0
- package/src/keystone/keystone-config-builder.mts +3 -21
- package/src/keystone/keystone-constants.mts +2 -1
- package/src/keystone/keystone-helpers.mts +204 -81
- package/src/keystone/keystone-policy-types.mts +6 -4
- package/src/keystone/keystone-service-v1.mts +223 -11
- package/src/keystone/keystone-service-v1.respec.mts +596 -39
- package/src/keystone/keystone-types.mts +55 -9
- package/src/keystone/policy/IMPLEMENTATION.md +60 -0
- package/src/keystone/policy/README.md +94 -0
- package/src/keystone/policy/keystone-profile-builder.mts +131 -0
- package/src/keystone/policy/profiles/profile-high.json +84 -0
- package/src/keystone/policy/profiles/profile-low.json +84 -0
- package/src/keystone/policy/profiles/profile-medium.json +84 -0
- package/src/keystone/policy/profiles/profile-session.json +84 -0
- package/src/keystone/policy/profiles/profile-test.json +84 -0
- package/src/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
- package/src/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
- package/src/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
- package/src/keystone/policy/schemas/keystone.high.schema.json +26 -0
- package/src/keystone/policy/schemas/keystone.low.schema.json +26 -0
- package/src/keystone/policy/schemas/keystone.medium.schema.json +26 -0
- package/src/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
- package/src/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
- package/src/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
- package/src/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
- package/src/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
- package/src/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
- package/src/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
- package/src/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
- package/src/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
- package/src/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
- package/src/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
- package/src/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
- package/src/sync/docs/security.md +54 -21
- package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +706 -0
- package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +321 -0
- package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +374 -0
- package/src/sync/sync-conflict-text-merge.withid.respec.mts +347 -0
- package/src/sync/sync-innerspace-constants.withid.respec.mts +264 -0
- package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +272 -0
- package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +266 -0
- package/src/sync/sync-innerspace-multiple-timelines.respec.mts +0 -1
- package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +273 -0
- package/src/sync/sync-innerspace-partial-update.withid.respec.mts +249 -0
- package/src/sync/sync-innerspace.withid.respec.mts +265 -0
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +12 -4
- package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +5 -3
- package/src/sync/sync-saga-coordinator.mts +159 -143
- package/src/sync/sync-withid.pingpong.respec.mts +8 -8
- package/src/timeline/timeline-api.mts +1 -15
- package/src/timeline/timeline-api.respec.mts +3 -3
- package/src/witness/space/reconciliation-space/reconciliation-space-base.mts +0 -46
- package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +6 -5
- package/tsconfig.json +6 -1
- package/src/keystone/keystone-policy.schema.json +0 -51
- package/src/witness/space/reconciliation-space/reconciliation-space-base.mts.OLD.md +0 -884
- package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts.OLD.md +0 -125
|
@@ -10,9 +10,11 @@ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
|
|
|
10
10
|
import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
|
|
11
11
|
import { KeystoneChallengePool, KeystoneClaim, KeystoneIbGib_V1, KeystonePoolConfig_HashV1 } from './keystone-types.mjs';
|
|
12
12
|
import { createRevocationPoolConfig, createStandardPoolConfig } from './keystone-config-builder.mjs';
|
|
13
|
-
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE } from './keystone-constants.mjs';
|
|
14
|
-
import { KeystoneService_V1 } from './keystone-service-v1.mjs';
|
|
15
|
-
import {
|
|
13
|
+
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE, POOL_ID_MANAGE } from './keystone-constants.mjs';
|
|
14
|
+
import { KeystoneService_V1, } from './keystone-service-v1.mjs';
|
|
15
|
+
import { selectChallengeIds, validateChallengePool, validateKeystoneIb, validateKeystoneTransition, validateGenesisKeystone } from './keystone-helpers.mjs';
|
|
16
|
+
import { KeystoneProfileBuilder } from './policy/keystone-profile-builder.mjs';
|
|
17
|
+
import { getTjpAddr } from '../common/other/ibgib-helper.mjs';
|
|
16
18
|
|
|
17
19
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
18
20
|
|
|
@@ -36,16 +38,55 @@ class MockIbGibSpace {
|
|
|
36
38
|
return data ? JSON.parse(JSON.stringify(data)) : null;
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
async argy({ argData, ibGibs }: { argData: any; ibGibs?: IbGib_V1[] }): Promise<any> {
|
|
42
|
+
return {
|
|
43
|
+
ib: 'arg^gib',
|
|
44
|
+
gib: 'arg_gib',
|
|
45
|
+
data: argData,
|
|
46
|
+
ibGibs,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
39
50
|
async witness(arg: any): Promise<any> {
|
|
40
51
|
const cmd = arg.data?.cmd;
|
|
52
|
+
const cmdModifiers = arg.data?.cmdModifiers || [];
|
|
41
53
|
if (cmd === 'get') {
|
|
42
54
|
const addrs = arg.data.ibGibAddrs || [];
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
55
|
+
if (cmdModifiers.includes('latest') && cmdModifiers.includes('addrs')) {
|
|
56
|
+
const latestAddrsMap: { [addr: string]: string } = {};
|
|
57
|
+
for (const queryAddr of addrs) {
|
|
58
|
+
const queryTjpGib = queryAddr.includes('.') ? queryAddr.split('.')[1] : queryAddr.split('^')[1];
|
|
59
|
+
let latestAddr = queryAddr;
|
|
60
|
+
let maxN = -1;
|
|
61
|
+
for (const [addr, ibGib] of this.store.entries()) {
|
|
62
|
+
const tjpGib = addr.includes('.') ? addr.split('.')[1] : addr.split('^')[1];
|
|
63
|
+
if (tjpGib === queryTjpGib) {
|
|
64
|
+
const n = ibGib.data?.n ?? 0;
|
|
65
|
+
if (n > maxN) {
|
|
66
|
+
maxN = n;
|
|
67
|
+
latestAddr = addr;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
latestAddrsMap[queryAddr] = latestAddr;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
data: {
|
|
75
|
+
success: true,
|
|
76
|
+
latestAddrsMap,
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
const ibGibs: IbGib_V1[] = [];
|
|
81
|
+
for (const addr of addrs) {
|
|
82
|
+
const x = await this.get({ addr });
|
|
83
|
+
if (x) { ibGibs.push(x); }
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
data: { success: true },
|
|
87
|
+
ibGibs,
|
|
88
|
+
};
|
|
47
89
|
}
|
|
48
|
-
return { ibGibs };
|
|
49
90
|
}
|
|
50
91
|
return undefined;
|
|
51
92
|
}
|
|
@@ -80,6 +121,15 @@ class MockMetaspaceService {
|
|
|
80
121
|
return target.put(args);
|
|
81
122
|
}
|
|
82
123
|
|
|
124
|
+
async get(args: { addr: string, space?: any }): Promise<{ success: boolean, ibGibs?: IbGib_V1[] }> {
|
|
125
|
+
const target = args.space || this.space;
|
|
126
|
+
const x = await target.get({ addr: args.addr });
|
|
127
|
+
return {
|
|
128
|
+
success: !!x,
|
|
129
|
+
ibGibs: x ? [x] : [],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
83
133
|
/**
|
|
84
134
|
* Tracks the latest version of an ibGib timeline.
|
|
85
135
|
*/
|
|
@@ -131,7 +181,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
131
181
|
|
|
132
182
|
await respecfully(sir, 'Derivation Logic', async () => {
|
|
133
183
|
|
|
134
|
-
await
|
|
184
|
+
await ifWeMight(sir, 'derivePoolSecret with same inputs returns same output', async () => {
|
|
135
185
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
136
186
|
|
|
137
187
|
const secretA = await strategy.derivePoolSecret({ masterSecret });
|
|
@@ -141,7 +191,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
141
191
|
iReckon(sir, secretA).asTo('secret length').isGonnaBeTruthy();
|
|
142
192
|
});
|
|
143
193
|
|
|
144
|
-
await
|
|
194
|
+
await ifWeMight(sir, 'derivePoolSecret with different master secret returns different output', async () => {
|
|
145
195
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
146
196
|
|
|
147
197
|
const secretA = await strategy.derivePoolSecret({ masterSecret });
|
|
@@ -150,7 +200,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
150
200
|
iReckon(sir, secretA).asTo('secrets differ').not.willEqual(secretB);
|
|
151
201
|
});
|
|
152
202
|
|
|
153
|
-
await
|
|
203
|
+
await ifWeMight(sir, 'derivePoolSecret with different salt returns different output', async () => {
|
|
154
204
|
// Modify salt in a copy of config
|
|
155
205
|
const configB = { ...config, salt: "OtherPool" };
|
|
156
206
|
const strategyA = KeystoneStrategyFactory.create({ config });
|
|
@@ -165,7 +215,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
165
215
|
|
|
166
216
|
await respecfully(sir, 'Challenge/Solution Logic', async () => {
|
|
167
217
|
|
|
168
|
-
await
|
|
218
|
+
await ifWeMight(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
|
|
169
219
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
170
220
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
171
221
|
const challengeId = "a3ff7843552870fc28bef2b"; // arbitrary random challengeId
|
|
@@ -184,7 +234,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
184
234
|
iReckon(sir, isValid).asTo('valid pair should pass').isGonnaBeTrue();
|
|
185
235
|
});
|
|
186
236
|
|
|
187
|
-
await
|
|
237
|
+
await ifWeMight(sir, 'validateSolution fails for mismatched values', async () => {
|
|
188
238
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
189
239
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
190
240
|
const challengeId = "8c994f3ed598f150e25513"; // arbitrary random challengeId
|
|
@@ -200,7 +250,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
200
250
|
iReckon(sir, isValid).asTo('tampered solution should fail').isGonnaBeFalse();
|
|
201
251
|
});
|
|
202
252
|
|
|
203
|
-
await
|
|
253
|
+
await ifWeMight(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
|
|
204
254
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
205
255
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
206
256
|
|
|
@@ -241,7 +291,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
241
291
|
});
|
|
242
292
|
|
|
243
293
|
await respecfully(sir, 'Genesis', async () => {
|
|
244
|
-
await
|
|
294
|
+
await ifWeMight(sir, 'creates a valid genesis frame and persists it', async () => {
|
|
245
295
|
const salt = (await getUUID()).substring(0, 16);
|
|
246
296
|
const config = createStandardPoolConfig({
|
|
247
297
|
id: POOL_ID_DEFAULT,
|
|
@@ -273,7 +323,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
273
323
|
});
|
|
274
324
|
|
|
275
325
|
await respecfully(sir, 'Signing (Evolution)', async () => {
|
|
276
|
-
await
|
|
326
|
+
await ifWeMight(sir, 'evolves the keystone with a valid proof', async () => {
|
|
277
327
|
const claim: Partial<KeystoneClaim> = {
|
|
278
328
|
target: "comment 123^gib",
|
|
279
329
|
verb: "post"
|
|
@@ -303,7 +353,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
303
353
|
});
|
|
304
354
|
|
|
305
355
|
await respecfully(sir, 'Validation', async () => {
|
|
306
|
-
await
|
|
356
|
+
await ifWeMight(sir, 'validates the genesis->signed transition', async () => {
|
|
307
357
|
const errors = await service.validate({
|
|
308
358
|
prevIbGib: genesisKeystone,
|
|
309
359
|
currentIbGib: signedKeystone,
|
|
@@ -352,7 +402,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
352
402
|
});
|
|
353
403
|
|
|
354
404
|
await respecfully(sir, 'Wrong Secret (Forgery)', async () => {
|
|
355
|
-
await
|
|
405
|
+
await ifWeMight(sir, 'prevents creation of forged frames', async () => {
|
|
356
406
|
const claim: Partial<KeystoneClaim> = { target: "comment 123^gib", verb: "post" };
|
|
357
407
|
|
|
358
408
|
let errorCaught = false;
|
|
@@ -381,7 +431,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
381
431
|
});
|
|
382
432
|
|
|
383
433
|
await respecfully(sir, 'Policy Violation (Restricted Verbs)', async () => {
|
|
384
|
-
await
|
|
434
|
+
await ifWeMight(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
|
|
385
435
|
// Create a specific restricted pool config manually
|
|
386
436
|
const restrictedPoolId = "read_only_pool";
|
|
387
437
|
const restrictedConfig = createStandardPoolConfig({
|
|
@@ -461,7 +511,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
461
511
|
await respecfully(sir, 'Revoke Lifecycle', async () => {
|
|
462
512
|
let revokedKeystone: KeystoneIbGib_V1;
|
|
463
513
|
|
|
464
|
-
await
|
|
514
|
+
await ifWeMight(sir, 'successfully creates a revocation frame', async () => {
|
|
465
515
|
revokedKeystone = await service.revoke({
|
|
466
516
|
latestKeystone: genesisKeystone,
|
|
467
517
|
masterSecret,
|
|
@@ -479,7 +529,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
479
529
|
iReckon(sir, data.revocationInfo!.proof.claim.verb).willEqual(KEYSTONE_VERB_REVOKE);
|
|
480
530
|
});
|
|
481
531
|
|
|
482
|
-
await
|
|
532
|
+
await ifWeMight(sir, 'validates the revocation frame', async () => {
|
|
483
533
|
const errors = await service.validate({
|
|
484
534
|
prevIbGib: genesisKeystone,
|
|
485
535
|
currentIbGib: revokedKeystone!,
|
|
@@ -490,7 +540,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
490
540
|
iReckon(sir, errors.length).asTo('no validation errors').willEqual(0);
|
|
491
541
|
});
|
|
492
542
|
|
|
493
|
-
await
|
|
543
|
+
await ifWeMight(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
|
|
494
544
|
const data = revokedKeystone!.data!;
|
|
495
545
|
const revokePool = data.challengePools.find(p => p.id === POOL_ID_REVOKE);
|
|
496
546
|
|
|
@@ -528,7 +578,6 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
528
578
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
529
579
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret: bobSecret });
|
|
530
580
|
const challenges: { [id: string]: any } = {};
|
|
531
|
-
const bindingMap: { [char: string]: string[] } = {};
|
|
532
581
|
|
|
533
582
|
for (let i = 0; i < 10; i++) {
|
|
534
583
|
// Manually replicate ID gen for test
|
|
@@ -540,14 +589,12 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
540
589
|
});
|
|
541
590
|
const challenge = await strategy.generateChallenge({ solution });
|
|
542
591
|
challenges[challengeId] = challenge;
|
|
543
|
-
addToBindingMap(bindingMap, challengeId);
|
|
544
592
|
}
|
|
545
593
|
|
|
546
594
|
return {
|
|
547
595
|
id,
|
|
548
596
|
config,
|
|
549
597
|
challenges,
|
|
550
|
-
bindingMap,
|
|
551
598
|
isForeign: true,
|
|
552
599
|
metadata: { owner: 'Bob' }
|
|
553
600
|
};
|
|
@@ -569,7 +616,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
569
616
|
});
|
|
570
617
|
|
|
571
618
|
await respecfully(sir, 'Happy Path', async () => {
|
|
572
|
-
await
|
|
619
|
+
await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
|
|
573
620
|
const bobPool = await createForeignPool("pool_bob", ["post"]);
|
|
574
621
|
|
|
575
622
|
const updatedKeystone = await service.addPools({
|
|
@@ -608,7 +655,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
608
655
|
});
|
|
609
656
|
|
|
610
657
|
await respecfully(sir, 'Permissions & Logic', async () => {
|
|
611
|
-
await
|
|
658
|
+
await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
|
|
612
659
|
// 1. Create a restricted keystone
|
|
613
660
|
let id = "read_only";
|
|
614
661
|
const restrictedConfig = createStandardPoolConfig({ id, salt: id });
|
|
@@ -641,7 +688,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
641
688
|
iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
|
|
642
689
|
});
|
|
643
690
|
|
|
644
|
-
await
|
|
691
|
+
await ifWeMight(sir, 'fails on ID collision', async () => {
|
|
645
692
|
// Try to add "pool_bob" again (it was added in Happy Path)
|
|
646
693
|
const duplicatePool = await createForeignPool("pool_bob");
|
|
647
694
|
|
|
@@ -688,7 +735,6 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
688
735
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
689
736
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret: bobSecret });
|
|
690
737
|
const challenges: { [id: string]: any } = {};
|
|
691
|
-
const bindingMap: { [char: string]: string[] } = {};
|
|
692
738
|
|
|
693
739
|
// Generate a small set of challenges
|
|
694
740
|
for (let i = 0; i < 10; i++) {
|
|
@@ -700,14 +746,12 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
700
746
|
});
|
|
701
747
|
const challenge = await strategy.generateChallenge({ solution });
|
|
702
748
|
challenges[challengeId] = challenge;
|
|
703
|
-
addToBindingMap(bindingMap, challengeId);
|
|
704
749
|
}
|
|
705
750
|
|
|
706
751
|
return {
|
|
707
752
|
id,
|
|
708
753
|
config,
|
|
709
754
|
challenges,
|
|
710
|
-
bindingMap,
|
|
711
755
|
isForeign: true,
|
|
712
756
|
metadata: { owner: 'Bob', role: 'Delegate' }
|
|
713
757
|
};
|
|
@@ -732,7 +776,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
732
776
|
});
|
|
733
777
|
|
|
734
778
|
await respecfully(sir, 'Happy Path', async () => {
|
|
735
|
-
await
|
|
779
|
+
await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
|
|
736
780
|
const bobPool = await createForeignPool("pool_bob", ["post"]);
|
|
737
781
|
|
|
738
782
|
const updatedKeystone = await service.addPools({
|
|
@@ -771,7 +815,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
771
815
|
});
|
|
772
816
|
|
|
773
817
|
await respecfully(sir, 'Permissions & Logic', async () => {
|
|
774
|
-
await
|
|
818
|
+
await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
|
|
775
819
|
// 1. Create a restricted keystone (read-only)
|
|
776
820
|
let id = "read_only";
|
|
777
821
|
const restrictedConfig = createStandardPoolConfig({ id, salt: id });
|
|
@@ -804,7 +848,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
804
848
|
iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
|
|
805
849
|
});
|
|
806
850
|
|
|
807
|
-
await
|
|
851
|
+
await ifWeMight(sir, 'fails on ID collision', async () => {
|
|
808
852
|
// Try to add "pool_bob" again (it was added in Happy Path)
|
|
809
853
|
const duplicatePool = await createForeignPool("pool_bob");
|
|
810
854
|
|
|
@@ -866,7 +910,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
866
910
|
|
|
867
911
|
await respecfully(sir, 'Proof Granularity & Math', async () => {
|
|
868
912
|
|
|
869
|
-
await
|
|
913
|
+
await ifWeMight(sir, 'generates exactly the expected number of solutions', async () => {
|
|
870
914
|
signedKeystone = await service.sign({
|
|
871
915
|
latestKeystone: genesisKeystone,
|
|
872
916
|
masterSecret: aliceSecret,
|
|
@@ -883,7 +927,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
883
927
|
iReckon(sir, solutions.length).asTo('solution count').willEqual(4);
|
|
884
928
|
});
|
|
885
929
|
|
|
886
|
-
await
|
|
930
|
+
await ifWeMight(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
|
|
887
931
|
const proof = signedKeystone.data!.proofs[0];
|
|
888
932
|
const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === poolId)!;
|
|
889
933
|
|
|
@@ -912,7 +956,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
912
956
|
}
|
|
913
957
|
});
|
|
914
958
|
|
|
915
|
-
await
|
|
959
|
+
await ifWeMight(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
|
|
916
960
|
const proof = signedKeystone.data!.proofs[0];
|
|
917
961
|
const poolSnapshot = genesisKeystone.data!.challengePools.find(p => p.id === poolId)!;
|
|
918
962
|
|
|
@@ -930,11 +974,75 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
930
974
|
iReckon(sir, hasFirst).asTo(`Solution includes 1st FIFO ID (${expectedFifoIds[0]})`).isGonnaBeTrue();
|
|
931
975
|
iReckon(sir, hasSecond).asTo(`Solution includes 2nd FIFO ID (${expectedFifoIds[1]})`).isGonnaBeTrue();
|
|
932
976
|
});
|
|
977
|
+
|
|
978
|
+
await ifWeMight(sir, 'verifies Next-Gen Hash-Based Target Binding Selection', async () => {
|
|
979
|
+
const bindingConfig = createStandardPoolConfig({
|
|
980
|
+
id: "binding_pool",
|
|
981
|
+
salt: "binding_salt",
|
|
982
|
+
sequential: 0,
|
|
983
|
+
random: 0,
|
|
984
|
+
targetBinding: 3,
|
|
985
|
+
size: 20
|
|
986
|
+
}) as KeystonePoolConfig_HashV1;
|
|
987
|
+
|
|
988
|
+
const tempGenesis = await service.genesis({
|
|
989
|
+
masterSecret: aliceSecret,
|
|
990
|
+
configs: [bindingConfig],
|
|
991
|
+
metaspace: mockMetaspace,
|
|
992
|
+
space: mockSpace as any,
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
// Evolve using the binding pool
|
|
996
|
+
const evolved = await service.sign({
|
|
997
|
+
latestKeystone: tempGenesis,
|
|
998
|
+
masterSecret: aliceSecret,
|
|
999
|
+
poolId: "binding_pool",
|
|
1000
|
+
claim: {
|
|
1001
|
+
verb: "sign",
|
|
1002
|
+
target: "some_target_address^gib"
|
|
1003
|
+
},
|
|
1004
|
+
metaspace: mockMetaspace,
|
|
1005
|
+
space: mockSpace as any,
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
const proof = evolved.data!.proofs[0];
|
|
1009
|
+
iReckon(sir, proof.solutions.length).asTo('proof solutions count').willEqual(3);
|
|
1010
|
+
|
|
1011
|
+
// Now, let's verify that the selection is perfectly deterministic.
|
|
1012
|
+
// If we run selectChallengeIds manually with the same pool and target, it must return the same IDs.
|
|
1013
|
+
const poolSnapshot = tempGenesis.data!.challengePools.find(p => p.id === "binding_pool")!;
|
|
1014
|
+
const selectedIds = await selectChallengeIds({
|
|
1015
|
+
pool: poolSnapshot,
|
|
1016
|
+
targetAddr: "some_target_address^gib"
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
const proofIds = proof.solutions.map(s => s.challengeId);
|
|
1020
|
+
iReckon(sir, selectedIds.length).asTo('selected IDs count').willEqual(3);
|
|
1021
|
+
for (const id of selectedIds) {
|
|
1022
|
+
iReckon(sir, proofIds.includes(id)).asTo(`proof includes selected ID ${id}`).isGonnaBeTrue();
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
await ifWeMight(sir, 'fails validation if total requested challenges >= size', async () => {
|
|
1027
|
+
const invalidConfig = createStandardPoolConfig({
|
|
1028
|
+
id: "invalid_pool",
|
|
1029
|
+
salt: "invalid_salt",
|
|
1030
|
+
sequential: 5,
|
|
1031
|
+
random: 5,
|
|
1032
|
+
targetBinding: 6,
|
|
1033
|
+
size: 15 // total requested is 5+5+6 = 16 >= 15
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
const errors = await validateChallengePool({ pool: { id: "invalid_pool", config: invalidConfig, challenges: {} } });
|
|
1037
|
+
iReckon(sir, errors.length > 0).asTo('errors length is positive').isGonnaBeTrue();
|
|
1038
|
+
const hasCorrectError = errors.some(e => e.includes("Total requested challenges"));
|
|
1039
|
+
iReckon(sir, hasCorrectError).asTo('validation error content').isGonnaBeTrue();
|
|
1040
|
+
});
|
|
933
1041
|
});
|
|
934
1042
|
|
|
935
1043
|
await respecfully(sir, 'DTO & Serialization', async () => {
|
|
936
1044
|
|
|
937
|
-
await
|
|
1045
|
+
await ifWeMight(sir, 'survives a clone/JSON-cycle without corruption', async () => {
|
|
938
1046
|
// 1. Create a DTO (simulate network transmission/storage)
|
|
939
1047
|
// 'clone' does a JSON stringify/parse under the hood (usually) or structured clone.
|
|
940
1048
|
const dto = clone(signedKeystone);
|
|
@@ -954,7 +1062,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
954
1062
|
iReckon(sir, errors.length).asTo('DTO validation errors').willEqual(0);
|
|
955
1063
|
});
|
|
956
1064
|
|
|
957
|
-
await
|
|
1065
|
+
await ifWeMight(sir, 'ensures data contains no functions or circular refs', async () => {
|
|
958
1066
|
// A crude but effective test: ensure JSON.stringify doesn't throw
|
|
959
1067
|
// and the result is equal to the object (if we parsed it back).
|
|
960
1068
|
|
|
@@ -978,4 +1086,453 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
978
1086
|
});
|
|
979
1087
|
|
|
980
1088
|
});
|
|
1089
|
+
|
|
1090
|
+
await respecfully(sir, 'KeystoneProfileBuilder and Modular Schemas', async () => {
|
|
1091
|
+
|
|
1092
|
+
await ifWeMight(sir, 'compiles configs successfully with medium profile', async () => {
|
|
1093
|
+
const builder = KeystoneProfileBuilder.buildKeystone('medium')
|
|
1094
|
+
.withUsername('alice')
|
|
1095
|
+
.withEmail('alice@example.com')
|
|
1096
|
+
.withDescription('Alice test keystone')
|
|
1097
|
+
.withDetails({ extraInfo: 'abc' });
|
|
1098
|
+
|
|
1099
|
+
const configs = await builder.compileConfigs();
|
|
1100
|
+
iReckon(sir, configs.length).asTo('number of pools').willEqual(5);
|
|
1101
|
+
|
|
1102
|
+
// Verify unique salt derivation per pool
|
|
1103
|
+
const salts = configs.map(c => c.salt);
|
|
1104
|
+
const uniqueSalts = new Set(salts);
|
|
1105
|
+
iReckon(sir, uniqueSalts.size).asTo('unique salts count').willEqual(5);
|
|
1106
|
+
|
|
1107
|
+
// Verify frameDetails unification
|
|
1108
|
+
const details = builder.getFrameDetails();
|
|
1109
|
+
iReckon(sir, details.username).asTo('username').willEqual('alice');
|
|
1110
|
+
iReckon(sir, details.email).asTo('email').willEqual('alice@example.com');
|
|
1111
|
+
iReckon(sir, details.description).asTo('description').willEqual('Alice test keystone');
|
|
1112
|
+
iReckon(sir, details.extraInfo).asTo('extraInfo').willEqual('abc');
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
await ifWeMight(sir, 'supports customized overrides', async () => {
|
|
1116
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
1117
|
+
.customizePool('sync', {
|
|
1118
|
+
behavior: {
|
|
1119
|
+
size: 15,
|
|
1120
|
+
targetBindingCount: 4
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
const configs = await builder.compileConfigs();
|
|
1125
|
+
const syncPool = configs.find(c => c.id === 'sync')!;
|
|
1126
|
+
iReckon(sir, syncPool.behavior.size).asTo('customized size').willEqual(15);
|
|
1127
|
+
iReckon(sir, syncPool.behavior.targetBindingCount).asTo('customized target binding').willEqual(4);
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
await respecfully(sir, 'Keystone Metadata Validation', async () => {
|
|
1133
|
+
|
|
1134
|
+
await ifWeMight(sir, 'validates correct username and description', async () => {
|
|
1135
|
+
const service = new KeystoneService_V1();
|
|
1136
|
+
const space = new MockIbGibSpace();
|
|
1137
|
+
const metaspace = new MockMetaspaceService(space);
|
|
1138
|
+
|
|
1139
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
1140
|
+
.withUsername('alice_123.test')
|
|
1141
|
+
.withDescription('Valid description: yes, it is!')
|
|
1142
|
+
.withDetails({ client: 'respec' });
|
|
1143
|
+
|
|
1144
|
+
const configs = await builder.compileConfigs();
|
|
1145
|
+
const frameDetails = builder.getFrameDetails();
|
|
1146
|
+
|
|
1147
|
+
// Should succeed without error
|
|
1148
|
+
const keystoneIbGib = await service.genesis({
|
|
1149
|
+
masterSecret: 'mysecret',
|
|
1150
|
+
configs,
|
|
1151
|
+
metaspace: metaspace as any,
|
|
1152
|
+
space: space as any,
|
|
1153
|
+
frameDetails
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
const errors = await service.validateGenesisKeystone({ keystoneIbGib });
|
|
1157
|
+
iReckon(sir, errors.length).asTo('errors count').willEqual(0);
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
await ifWeMight(sir, 'fails validation for invalid username length or character', async () => {
|
|
1161
|
+
const service = new KeystoneService_V1();
|
|
1162
|
+
const space = new MockIbGibSpace();
|
|
1163
|
+
const metaspace = new MockMetaspaceService(space);
|
|
1164
|
+
|
|
1165
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
1166
|
+
.withUsername('invalid_user_name_that_is_way_too_long_for_the_sixty_three_char_limit_and_will_fail_regex_check')
|
|
1167
|
+
.withDetails({ client: 'respec' });
|
|
1168
|
+
|
|
1169
|
+
const configs = await builder.compileConfigs();
|
|
1170
|
+
const frameDetails = builder.getFrameDetails();
|
|
1171
|
+
|
|
1172
|
+
const keystoneIbGib = await service.genesis({
|
|
1173
|
+
masterSecret: 'mysecret',
|
|
1174
|
+
configs,
|
|
1175
|
+
metaspace: metaspace as any,
|
|
1176
|
+
space: space as any,
|
|
1177
|
+
frameDetails
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
const errors = await service.validateGenesisKeystone({ keystoneIbGib });
|
|
1181
|
+
iReckon(sir, errors.length).asTo('errors count').willEqual(1);
|
|
1182
|
+
iReckon(sir, errors[0]).includes('invalid username');
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
await ifWeMight(sir, 'fails validation for invalid description characters', async () => {
|
|
1186
|
+
const service = new KeystoneService_V1();
|
|
1187
|
+
const space = new MockIbGibSpace();
|
|
1188
|
+
const metaspace = new MockMetaspaceService(space);
|
|
1189
|
+
|
|
1190
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
1191
|
+
.withUsername('alice')
|
|
1192
|
+
.withDescription('Invalid desc with weird emoji 🚀')
|
|
1193
|
+
.withDetails({ client: 'respec' });
|
|
1194
|
+
|
|
1195
|
+
const configs = await builder.compileConfigs();
|
|
1196
|
+
const frameDetails = builder.getFrameDetails();
|
|
1197
|
+
|
|
1198
|
+
const keystoneIbGib = await service.genesis({
|
|
1199
|
+
masterSecret: 'mysecret',
|
|
1200
|
+
configs,
|
|
1201
|
+
metaspace: metaspace as any,
|
|
1202
|
+
space: space as any,
|
|
1203
|
+
frameDetails
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
const errors = await service.validateGenesisKeystone({ keystoneIbGib });
|
|
1207
|
+
iReckon(sir, errors.length).asTo('errors count').willEqual(1);
|
|
1208
|
+
iReckon(sir, errors[0]).includes('invalid description');
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
});
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
// ===========================================================================
|
|
1216
|
+
// SUITE E: DELEGATION (Register/Unregister)
|
|
1217
|
+
// ===========================================================================
|
|
1218
|
+
|
|
1219
|
+
await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () => {
|
|
1220
|
+
|
|
1221
|
+
const service = new KeystoneService_V1();
|
|
1222
|
+
const parentSecret = "ParentMasterSecret_123";
|
|
1223
|
+
const delegateSecret = "DelegateMasterSecret_456";
|
|
1224
|
+
|
|
1225
|
+
let mockSpace: MockIbGibSpace;
|
|
1226
|
+
let mockMetaspace: any;
|
|
1227
|
+
|
|
1228
|
+
let parentKeystone: KeystoneIbGib_V1;
|
|
1229
|
+
let delegateKeystone: KeystoneIbGib_V1;
|
|
1230
|
+
|
|
1231
|
+
firstOfEach(sir, async () => {
|
|
1232
|
+
mockSpace = new MockIbGibSpace();
|
|
1233
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1234
|
+
|
|
1235
|
+
const salt = (await getUUID()).substring(0, 16);
|
|
1236
|
+
const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
|
|
1237
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt + "_manage" });
|
|
1238
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
|
|
1239
|
+
|
|
1240
|
+
// 1. Create Parent Keystone (with manage pool)
|
|
1241
|
+
parentKeystone = await service.genesis({
|
|
1242
|
+
masterSecret: parentSecret,
|
|
1243
|
+
configs: [defaultConfig, manageConfig],
|
|
1244
|
+
metaspace: mockMetaspace,
|
|
1245
|
+
space: mockSpace as any,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
// 2. Create Delegate Keystone
|
|
1249
|
+
const delegateConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt + "_delegate" });
|
|
1250
|
+
delegateKeystone = await service.genesis({
|
|
1251
|
+
masterSecret: delegateSecret,
|
|
1252
|
+
configs: [delegateConfig],
|
|
1253
|
+
metaspace: mockMetaspace,
|
|
1254
|
+
space: mockSpace as any,
|
|
1255
|
+
});
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
await ifWeMight(sir, 'registers a delegate keystone and evolves parent using manage pool', async () => {
|
|
1259
|
+
// Register the delegate
|
|
1260
|
+
const evolvedParent = await service.registerDelegate({
|
|
1261
|
+
parentKeystone,
|
|
1262
|
+
delegateKeystone,
|
|
1263
|
+
masterSecret: parentSecret,
|
|
1264
|
+
metaspace: mockMetaspace,
|
|
1265
|
+
space: mockSpace as any,
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
iReckon(sir, evolvedParent).asTo('evolved parent exists').isGonnaBeTruthy();
|
|
1269
|
+
iReckon(sir, evolvedParent.data?.delegates).asTo('delegates map exists').isGonnaBeTruthy();
|
|
1270
|
+
|
|
1271
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1272
|
+
if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 312e986ce4b2ed6c084c03e8f45fa826)`); }
|
|
1273
|
+
const delegateInfo = evolvedParent.data?.delegates?.[delegateTjpAddr];
|
|
1274
|
+
iReckon(sir, delegateInfo).asTo('delegate info registered').isGonnaBeTruthy();
|
|
1275
|
+
iReckon(sir, delegateInfo?.delegateTjpAddr).asTo('delegate tjp matches').willEqual(delegateTjpAddr);
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
await ifWeMight(sir, 'gets correct delegate status via parent TJP address', async () => {
|
|
1279
|
+
const parentTjpAddr = getTjpAddr({ ibGib: parentKeystone });
|
|
1280
|
+
if (!parentTjpAddr) { throw new Error(`(UNEXPECTED) parentTjpAddr falsy? (E: b7ce2835ccd951d8c99257328be3b826)`); }
|
|
1281
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1282
|
+
if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 3e4878ea19dd8034e827466a8bf09526)`); }
|
|
1283
|
+
|
|
1284
|
+
// Before registration
|
|
1285
|
+
const statusBefore = await service.getDelegateStatus({
|
|
1286
|
+
parentTjpAddr: parentTjpAddr,
|
|
1287
|
+
delegateTjpAddr: delegateTjpAddr,
|
|
1288
|
+
metaspace: mockMetaspace,
|
|
1289
|
+
space: mockSpace as any,
|
|
1290
|
+
});
|
|
1291
|
+
iReckon(sir, statusBefore.registered).asTo('initially unregistered').isGonnaBeFalse();
|
|
1292
|
+
|
|
1293
|
+
// Register
|
|
1294
|
+
const evolvedParent = await service.registerDelegate({
|
|
1295
|
+
parentKeystone,
|
|
1296
|
+
delegateKeystone,
|
|
1297
|
+
masterSecret: parentSecret,
|
|
1298
|
+
metaspace: mockMetaspace,
|
|
1299
|
+
space: mockSpace as any,
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
// After registration
|
|
1303
|
+
const statusAfter = await service.getDelegateStatus({
|
|
1304
|
+
parentTjpAddr: parentTjpAddr,
|
|
1305
|
+
delegateTjpAddr: delegateTjpAddr,
|
|
1306
|
+
metaspace: mockMetaspace,
|
|
1307
|
+
space: mockSpace as any,
|
|
1308
|
+
});
|
|
1309
|
+
iReckon(sir, statusAfter.registered).asTo('registered status').isGonnaBeTrue();
|
|
1310
|
+
iReckon(sir, statusAfter.delegateInfo?.delegateTjpAddr).asTo('status tjp matches').willEqual(delegateTjpAddr);
|
|
1311
|
+
});
|
|
1312
|
+
|
|
1313
|
+
await ifWeMight(sir, 'unregisters a registered delegate keystone', async () => {
|
|
1314
|
+
// Register first
|
|
1315
|
+
const evolvedParent = await service.registerDelegate({
|
|
1316
|
+
parentKeystone,
|
|
1317
|
+
delegateKeystone,
|
|
1318
|
+
masterSecret: parentSecret,
|
|
1319
|
+
metaspace: mockMetaspace,
|
|
1320
|
+
space: mockSpace as any,
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1324
|
+
if (!delegateTjpAddr) { throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 486b486a8c984a52b8d2b22418c20d26)`); }
|
|
1325
|
+
|
|
1326
|
+
// Unregister
|
|
1327
|
+
const finalParent = await service.unregisterDelegate({
|
|
1328
|
+
parentKeystone: evolvedParent,
|
|
1329
|
+
delegateTjpAddr,
|
|
1330
|
+
masterSecret: parentSecret,
|
|
1331
|
+
metaspace: mockMetaspace,
|
|
1332
|
+
space: mockSpace as any,
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
iReckon(sir, finalParent).asTo('final evolved parent exists').isGonnaBeTruthy();
|
|
1336
|
+
const info = finalParent.data?.delegates?.[delegateTjpAddr];
|
|
1337
|
+
iReckon(sir, info).asTo('delegate info removed').isGonnaBeUndefined();
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
await ifWeMight(sir, 'throws if parent does not have a manage pool', async () => {
|
|
1341
|
+
// Create a parent WITHOUT a manage pool
|
|
1342
|
+
const salt = (await getUUID()).substring(0, 16);
|
|
1343
|
+
const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
|
|
1344
|
+
const restrictedParent = await service.genesis({
|
|
1345
|
+
masterSecret: parentSecret,
|
|
1346
|
+
configs: [defaultConfig],
|
|
1347
|
+
metaspace: mockMetaspace,
|
|
1348
|
+
space: mockSpace as any,
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
let errorCaught = false;
|
|
1352
|
+
try {
|
|
1353
|
+
await service.registerDelegate({
|
|
1354
|
+
parentKeystone: restrictedParent,
|
|
1355
|
+
delegateKeystone,
|
|
1356
|
+
masterSecret: parentSecret,
|
|
1357
|
+
metaspace: mockMetaspace,
|
|
1358
|
+
space: mockSpace as any,
|
|
1359
|
+
});
|
|
1360
|
+
} catch (e: any) {
|
|
1361
|
+
errorCaught = true;
|
|
1362
|
+
iReckon(sir, e.message).includes('Manage pool is required');
|
|
1363
|
+
}
|
|
1364
|
+
iReckon(sir, errorCaught).asTo('delegation rejected').isGonnaBeTrue();
|
|
1365
|
+
});
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
1369
|
+
const service = new KeystoneService_V1();
|
|
1370
|
+
const parentSecret = "ParentMasterSecret_SuiteG";
|
|
1371
|
+
const delegateSecret = "DelegateMasterSecret_SuiteG";
|
|
1372
|
+
|
|
1373
|
+
let mockSpace: MockIbGibSpace;
|
|
1374
|
+
let mockMetaspace: any;
|
|
1375
|
+
|
|
1376
|
+
let parentKeystone: KeystoneIbGib_V1;
|
|
1377
|
+
let delegateKeystone: KeystoneIbGib_V1;
|
|
1378
|
+
|
|
1379
|
+
firstOfEach(sir, async () => {
|
|
1380
|
+
mockSpace = new MockIbGibSpace();
|
|
1381
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1382
|
+
|
|
1383
|
+
const salt1 = (await getUUID()).substring(0, 16);
|
|
1384
|
+
const salt2 = (await getUUID()).substring(0, 16);
|
|
1385
|
+
|
|
1386
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt2 });
|
|
1387
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
|
|
1388
|
+
|
|
1389
|
+
const configs = [
|
|
1390
|
+
createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 }),
|
|
1391
|
+
manageConfig
|
|
1392
|
+
];
|
|
1393
|
+
|
|
1394
|
+
parentKeystone = await service.genesis({
|
|
1395
|
+
masterSecret: parentSecret,
|
|
1396
|
+
configs,
|
|
1397
|
+
metaspace: mockMetaspace,
|
|
1398
|
+
space: mockSpace as any,
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
delegateKeystone = await service.genesis({
|
|
1402
|
+
masterSecret: delegateSecret,
|
|
1403
|
+
configs: [createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 })],
|
|
1404
|
+
metaspace: mockMetaspace,
|
|
1405
|
+
space: mockSpace as any,
|
|
1406
|
+
});
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
await ifWeMight(sir, 'validateKeystoneIb returns true for valid keystone ib and false for others', async () => {
|
|
1410
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'keystone' })).isGonnaBeTrue();
|
|
1411
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'keystone user info' })).isGonnaBeTrue();
|
|
1412
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'invalidAtom' })).isGonnaBeFalse();
|
|
1413
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'not_keystone sub_atom' })).isGonnaBeFalse();
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
await ifWeMight(sir, 'validateGenesisKeystone fails validation if ib is invalid', async () => {
|
|
1417
|
+
const invalidGenesis = {
|
|
1418
|
+
...parentKeystone,
|
|
1419
|
+
ib: 'not_a_keystone^' + parentKeystone.gib,
|
|
1420
|
+
};
|
|
1421
|
+
const errors = await validateGenesisKeystone({ keystoneIbGib: invalidGenesis });
|
|
1422
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1423
|
+
iReckon(sir, errors[0]).includes('invalid keystone ib');
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
await ifWeMight(sir, 'validateKeystoneTransition passes for valid delegate registration', async () => {
|
|
1427
|
+
const evolvedParent = await service.registerDelegate({
|
|
1428
|
+
parentKeystone,
|
|
1429
|
+
delegateKeystone,
|
|
1430
|
+
masterSecret: parentSecret,
|
|
1431
|
+
metaspace: mockMetaspace,
|
|
1432
|
+
space: mockSpace as any,
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
const errors = await validateKeystoneTransition({
|
|
1436
|
+
currentIbGib: evolvedParent,
|
|
1437
|
+
prevIbGib: parentKeystone,
|
|
1438
|
+
});
|
|
1439
|
+
iReckon(sir, errors.length).willEqual(0);
|
|
1440
|
+
});
|
|
1441
|
+
|
|
1442
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegates map is mutated without manage proof', async () => {
|
|
1443
|
+
const evolvedParent = await service.registerDelegate({
|
|
1444
|
+
parentKeystone,
|
|
1445
|
+
delegateKeystone,
|
|
1446
|
+
masterSecret: parentSecret,
|
|
1447
|
+
metaspace: mockMetaspace,
|
|
1448
|
+
space: mockSpace as any,
|
|
1449
|
+
});
|
|
1450
|
+
|
|
1451
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1452
|
+
|
|
1453
|
+
// Remove the manage verb proof by converting it to sign
|
|
1454
|
+
for (const proof of tamperedParent.data.proofs) {
|
|
1455
|
+
if (proof.claim) {
|
|
1456
|
+
proof.claim.verb = 'sign';
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
const errors = await validateKeystoneTransition({
|
|
1461
|
+
currentIbGib: tamperedParent,
|
|
1462
|
+
prevIbGib: parentKeystone,
|
|
1463
|
+
});
|
|
1464
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1465
|
+
iReckon(sir, errors.some(e => e.includes('Policy Violation: Delegates map was mutated'))).isGonnaBeTrue();
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegate key does not match delegateTjpAddr', async () => {
|
|
1469
|
+
const evolvedParent = await service.registerDelegate({
|
|
1470
|
+
parentKeystone,
|
|
1471
|
+
delegateKeystone,
|
|
1472
|
+
masterSecret: parentSecret,
|
|
1473
|
+
metaspace: mockMetaspace,
|
|
1474
|
+
space: mockSpace as any,
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1478
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1479
|
+
if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
|
|
1480
|
+
|
|
1481
|
+
const info = tamperedParent.data.delegates![delegateTjpAddr];
|
|
1482
|
+
delete tamperedParent.data.delegates![delegateTjpAddr];
|
|
1483
|
+
tamperedParent.data.delegates!['mismatched_key'] = info;
|
|
1484
|
+
|
|
1485
|
+
const errors = await validateKeystoneTransition({
|
|
1486
|
+
currentIbGib: tamperedParent,
|
|
1487
|
+
prevIbGib: parentKeystone,
|
|
1488
|
+
});
|
|
1489
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1490
|
+
iReckon(sir, errors.some(e => e.includes('does not match delegateTjpAddr'))).isGonnaBeTrue();
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1493
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegate addresses have invalid keystone ib', async () => {
|
|
1494
|
+
const evolvedParent = await service.registerDelegate({
|
|
1495
|
+
parentKeystone,
|
|
1496
|
+
delegateKeystone,
|
|
1497
|
+
masterSecret: parentSecret,
|
|
1498
|
+
metaspace: mockMetaspace,
|
|
1499
|
+
space: mockSpace as any,
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1503
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1504
|
+
if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
|
|
1505
|
+
|
|
1506
|
+
tamperedParent.data.delegates![delegateTjpAddr].delegateAddr = 'invalid_ib^12345';
|
|
1507
|
+
|
|
1508
|
+
const errors = await validateKeystoneTransition({
|
|
1509
|
+
currentIbGib: tamperedParent,
|
|
1510
|
+
prevIbGib: parentKeystone,
|
|
1511
|
+
});
|
|
1512
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1513
|
+
iReckon(sir, errors.some(e => e.includes('not a valid keystone address'))).isGonnaBeTrue();
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if thisAddr is not in the parent timeline history', async () => {
|
|
1517
|
+
const evolvedParent = await service.registerDelegate({
|
|
1518
|
+
parentKeystone,
|
|
1519
|
+
delegateKeystone,
|
|
1520
|
+
masterSecret: parentSecret,
|
|
1521
|
+
metaspace: mockMetaspace,
|
|
1522
|
+
space: mockSpace as any,
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent)) as KeystoneIbGib_V1;
|
|
1526
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1527
|
+
if (!delegateTjpAddr) { throw new Error(`delegateTjpAddr falsy?`); }
|
|
1528
|
+
|
|
1529
|
+
tamperedParent.data.delegates![delegateTjpAddr].thisAddr = 'keystone^randomgib';
|
|
1530
|
+
|
|
1531
|
+
const errors = await validateKeystoneTransition({
|
|
1532
|
+
currentIbGib: tamperedParent,
|
|
1533
|
+
prevIbGib: parentKeystone,
|
|
1534
|
+
});
|
|
1535
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1536
|
+
iReckon(sir, errors.some(e => e.includes('not a valid historical address in the parent keystone timeline'))).isGonnaBeTrue();
|
|
1537
|
+
});
|
|
981
1538
|
});
|