@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
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { respecfully, iReckon,
|
|
1
|
+
import { respecfully, iReckon, firstOfAll, firstOfEach, ifWeMight } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
|
|
2
2
|
const maam = `[${import.meta.url}]`, sir = maam;
|
|
3
3
|
import { clone, hash, getUUID } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
|
|
4
4
|
import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
|
|
5
5
|
import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
|
|
6
6
|
import { KeystoneStrategyFactory } from './strategy/keystone-strategy-factory.mjs';
|
|
7
7
|
import { createRevocationPoolConfig, createStandardPoolConfig } from './keystone-config-builder.mjs';
|
|
8
|
-
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE } from './keystone-constants.mjs';
|
|
9
|
-
import { KeystoneService_V1 } from './keystone-service-v1.mjs';
|
|
10
|
-
import {
|
|
8
|
+
import { POOL_ID_DEFAULT, POOL_ID_REVOKE, KEYSTONE_VERB_REVOKE, KEYSTONE_VERB_MANAGE, POOL_ID_MANAGE } from './keystone-constants.mjs';
|
|
9
|
+
import { KeystoneService_V1, } from './keystone-service-v1.mjs';
|
|
10
|
+
import { selectChallengeIds, validateChallengePool, validateKeystoneIb, validateKeystoneTransition, validateGenesisKeystone } from './keystone-helpers.mjs';
|
|
11
|
+
import { KeystoneProfileBuilder } from './policy/keystone-profile-builder.mjs';
|
|
12
|
+
import { getTjpAddr } from '../common/other/ibgib-helper.mjs';
|
|
11
13
|
const logalot = GLOBAL_LOG_A_LOT;
|
|
12
14
|
/**
|
|
13
15
|
* A simple in-memory map acting as a Space.
|
|
@@ -27,18 +29,57 @@ class MockIbGibSpace {
|
|
|
27
29
|
const data = this.store.get(addr);
|
|
28
30
|
return data ? JSON.parse(JSON.stringify(data)) : null;
|
|
29
31
|
}
|
|
32
|
+
async argy({ argData, ibGibs }) {
|
|
33
|
+
return {
|
|
34
|
+
ib: 'arg^gib',
|
|
35
|
+
gib: 'arg_gib',
|
|
36
|
+
data: argData,
|
|
37
|
+
ibGibs,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
30
40
|
async witness(arg) {
|
|
31
41
|
const cmd = arg.data?.cmd;
|
|
42
|
+
const cmdModifiers = arg.data?.cmdModifiers || [];
|
|
32
43
|
if (cmd === 'get') {
|
|
33
44
|
const addrs = arg.data.ibGibAddrs || [];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
if (cmdModifiers.includes('latest') && cmdModifiers.includes('addrs')) {
|
|
46
|
+
const latestAddrsMap = {};
|
|
47
|
+
for (const queryAddr of addrs) {
|
|
48
|
+
const queryTjpGib = queryAddr.includes('.') ? queryAddr.split('.')[1] : queryAddr.split('^')[1];
|
|
49
|
+
let latestAddr = queryAddr;
|
|
50
|
+
let maxN = -1;
|
|
51
|
+
for (const [addr, ibGib] of this.store.entries()) {
|
|
52
|
+
const tjpGib = addr.includes('.') ? addr.split('.')[1] : addr.split('^')[1];
|
|
53
|
+
if (tjpGib === queryTjpGib) {
|
|
54
|
+
const n = ibGib.data?.n ?? 0;
|
|
55
|
+
if (n > maxN) {
|
|
56
|
+
maxN = n;
|
|
57
|
+
latestAddr = addr;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
latestAddrsMap[queryAddr] = latestAddr;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
data: {
|
|
65
|
+
success: true,
|
|
66
|
+
latestAddrsMap,
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const ibGibs = [];
|
|
72
|
+
for (const addr of addrs) {
|
|
73
|
+
const x = await this.get({ addr });
|
|
74
|
+
if (x) {
|
|
75
|
+
ibGibs.push(x);
|
|
76
|
+
}
|
|
39
77
|
}
|
|
78
|
+
return {
|
|
79
|
+
data: { success: true },
|
|
80
|
+
ibGibs,
|
|
81
|
+
};
|
|
40
82
|
}
|
|
41
|
-
return { ibGibs };
|
|
42
83
|
}
|
|
43
84
|
return undefined;
|
|
44
85
|
}
|
|
@@ -69,6 +110,14 @@ class MockMetaspaceService {
|
|
|
69
110
|
const target = args.space || this.space;
|
|
70
111
|
return target.put(args);
|
|
71
112
|
}
|
|
113
|
+
async get(args) {
|
|
114
|
+
const target = args.space || this.space;
|
|
115
|
+
const x = await target.get({ addr: args.addr });
|
|
116
|
+
return {
|
|
117
|
+
success: !!x,
|
|
118
|
+
ibGibs: x ? [x] : [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
72
121
|
/**
|
|
73
122
|
* Tracks the latest version of an ibGib timeline.
|
|
74
123
|
*/
|
|
@@ -110,20 +159,20 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
110
159
|
});
|
|
111
160
|
});
|
|
112
161
|
await respecfully(sir, 'Derivation Logic', async () => {
|
|
113
|
-
await
|
|
162
|
+
await ifWeMight(sir, 'derivePoolSecret with same inputs returns same output', async () => {
|
|
114
163
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
115
164
|
const secretA = await strategy.derivePoolSecret({ masterSecret });
|
|
116
165
|
const secretB = await strategy.derivePoolSecret({ masterSecret });
|
|
117
166
|
iReckon(sir, secretA).asTo('secret consistency').willEqual(secretB);
|
|
118
167
|
iReckon(sir, secretA).asTo('secret length').isGonnaBeTruthy();
|
|
119
168
|
});
|
|
120
|
-
await
|
|
169
|
+
await ifWeMight(sir, 'derivePoolSecret with different master secret returns different output', async () => {
|
|
121
170
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
122
171
|
const secretA = await strategy.derivePoolSecret({ masterSecret });
|
|
123
172
|
const secretB = await strategy.derivePoolSecret({ masterSecret: masterSecret + "_diff" });
|
|
124
173
|
iReckon(sir, secretA).asTo('secrets differ').not.willEqual(secretB);
|
|
125
174
|
});
|
|
126
|
-
await
|
|
175
|
+
await ifWeMight(sir, 'derivePoolSecret with different salt returns different output', async () => {
|
|
127
176
|
// Modify salt in a copy of config
|
|
128
177
|
const configB = { ...config, salt: "OtherPool" };
|
|
129
178
|
const strategyA = KeystoneStrategyFactory.create({ config });
|
|
@@ -134,7 +183,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
134
183
|
});
|
|
135
184
|
});
|
|
136
185
|
await respecfully(sir, 'Challenge/Solution Logic', async () => {
|
|
137
|
-
await
|
|
186
|
+
await ifWeMight(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
|
|
138
187
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
139
188
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
140
189
|
const challengeId = "a3ff7843552870fc28bef2b"; // arbitrary random challengeId
|
|
@@ -149,7 +198,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
149
198
|
const isValid = await strategy.validateSolution({ solution, challenge });
|
|
150
199
|
iReckon(sir, isValid).asTo('valid pair should pass').isGonnaBeTrue();
|
|
151
200
|
});
|
|
152
|
-
await
|
|
201
|
+
await ifWeMight(sir, 'validateSolution fails for mismatched values', async () => {
|
|
153
202
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
154
203
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
155
204
|
const challengeId = "8c994f3ed598f150e25513"; // arbitrary random challengeId
|
|
@@ -161,7 +210,7 @@ await respecfully(sir, 'Suite A: Strategy Vectors (HashRevealV1)', async () => {
|
|
|
161
210
|
const isValid = await strategy.validateSolution({ solution: badSolution, challenge });
|
|
162
211
|
iReckon(sir, isValid).asTo('tampered solution should fail').isGonnaBeFalse();
|
|
163
212
|
});
|
|
164
|
-
await
|
|
213
|
+
await ifWeMight(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
|
|
165
214
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
166
215
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret });
|
|
167
216
|
// Generate pair A
|
|
@@ -192,7 +241,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
192
241
|
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
193
242
|
});
|
|
194
243
|
await respecfully(sir, 'Genesis', async () => {
|
|
195
|
-
await
|
|
244
|
+
await ifWeMight(sir, 'creates a valid genesis frame and persists it', async () => {
|
|
196
245
|
const salt = (await getUUID()).substring(0, 16);
|
|
197
246
|
const config = createStandardPoolConfig({
|
|
198
247
|
id: POOL_ID_DEFAULT,
|
|
@@ -218,7 +267,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
218
267
|
});
|
|
219
268
|
});
|
|
220
269
|
await respecfully(sir, 'Signing (Evolution)', async () => {
|
|
221
|
-
await
|
|
270
|
+
await ifWeMight(sir, 'evolves the keystone with a valid proof', async () => {
|
|
222
271
|
const claim = {
|
|
223
272
|
target: "comment 123^gib",
|
|
224
273
|
verb: "post"
|
|
@@ -243,7 +292,7 @@ await respecfully(sir, 'Suite B: Service Lifecycle', async () => {
|
|
|
243
292
|
});
|
|
244
293
|
});
|
|
245
294
|
await respecfully(sir, 'Validation', async () => {
|
|
246
|
-
await
|
|
295
|
+
await ifWeMight(sir, 'validates the genesis->signed transition', async () => {
|
|
247
296
|
const errors = await service.validate({
|
|
248
297
|
prevIbGib: genesisKeystone,
|
|
249
298
|
currentIbGib: signedKeystone,
|
|
@@ -284,7 +333,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
284
333
|
});
|
|
285
334
|
});
|
|
286
335
|
await respecfully(sir, 'Wrong Secret (Forgery)', async () => {
|
|
287
|
-
await
|
|
336
|
+
await ifWeMight(sir, 'prevents creation of forged frames', async () => {
|
|
288
337
|
const claim = { target: "comment 123^gib", verb: "post" };
|
|
289
338
|
let errorCaught = false;
|
|
290
339
|
let errorMsg = "";
|
|
@@ -310,7 +359,7 @@ await respecfully(sir, 'Suite C: Security Vectors', async () => {
|
|
|
310
359
|
});
|
|
311
360
|
});
|
|
312
361
|
await respecfully(sir, 'Policy Violation (Restricted Verbs)', async () => {
|
|
313
|
-
await
|
|
362
|
+
await ifWeMight(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
|
|
314
363
|
// Create a specific restricted pool config manually
|
|
315
364
|
const restrictedPoolId = "read_only_pool";
|
|
316
365
|
const restrictedConfig = createStandardPoolConfig({
|
|
@@ -378,7 +427,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
378
427
|
});
|
|
379
428
|
await respecfully(sir, 'Revoke Lifecycle', async () => {
|
|
380
429
|
let revokedKeystone;
|
|
381
|
-
await
|
|
430
|
+
await ifWeMight(sir, 'successfully creates a revocation frame', async () => {
|
|
382
431
|
revokedKeystone = await service.revoke({
|
|
383
432
|
latestKeystone: genesisKeystone,
|
|
384
433
|
masterSecret,
|
|
@@ -393,7 +442,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
393
442
|
iReckon(sir, data.revocationInfo.reason).willEqual("Key compromised");
|
|
394
443
|
iReckon(sir, data.revocationInfo.proof.claim.verb).willEqual(KEYSTONE_VERB_REVOKE);
|
|
395
444
|
});
|
|
396
|
-
await
|
|
445
|
+
await ifWeMight(sir, 'validates the revocation frame', async () => {
|
|
397
446
|
const errors = await service.validate({
|
|
398
447
|
prevIbGib: genesisKeystone,
|
|
399
448
|
currentIbGib: revokedKeystone,
|
|
@@ -402,7 +451,7 @@ await respecfully(sir, 'Suite D: Revocation', async () => {
|
|
|
402
451
|
});
|
|
403
452
|
iReckon(sir, errors.length).asTo('no validation errors').willEqual(0);
|
|
404
453
|
});
|
|
405
|
-
await
|
|
454
|
+
await ifWeMight(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
|
|
406
455
|
const data = revokedKeystone.data;
|
|
407
456
|
const revokePool = data.challengePools.find(p => p.id === POOL_ID_REVOKE);
|
|
408
457
|
// The pool should exist...
|
|
@@ -432,7 +481,6 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
432
481
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
433
482
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret: bobSecret });
|
|
434
483
|
const challenges = {};
|
|
435
|
-
const bindingMap = {};
|
|
436
484
|
for (let i = 0; i < 10; i++) {
|
|
437
485
|
// Manually replicate ID gen for test
|
|
438
486
|
const raw = await hash({ s: `${config.salt}${Date.now()}${i}` });
|
|
@@ -442,13 +490,11 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
442
490
|
});
|
|
443
491
|
const challenge = await strategy.generateChallenge({ solution });
|
|
444
492
|
challenges[challengeId] = challenge;
|
|
445
|
-
addToBindingMap(bindingMap, challengeId);
|
|
446
493
|
}
|
|
447
494
|
return {
|
|
448
495
|
id,
|
|
449
496
|
config,
|
|
450
497
|
challenges,
|
|
451
|
-
bindingMap,
|
|
452
498
|
isForeign: true,
|
|
453
499
|
metadata: { owner: 'Bob' }
|
|
454
500
|
};
|
|
@@ -467,7 +513,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
467
513
|
});
|
|
468
514
|
});
|
|
469
515
|
await respecfully(sir, 'Happy Path', async () => {
|
|
470
|
-
await
|
|
516
|
+
await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
|
|
471
517
|
const bobPool = await createForeignPool("pool_bob", ["post"]);
|
|
472
518
|
const updatedKeystone = await service.addPools({
|
|
473
519
|
latestKeystone: aliceKeystone,
|
|
@@ -499,7 +545,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
499
545
|
});
|
|
500
546
|
});
|
|
501
547
|
await respecfully(sir, 'Permissions & Logic', async () => {
|
|
502
|
-
await
|
|
548
|
+
await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
|
|
503
549
|
// 1. Create a restricted keystone
|
|
504
550
|
let id = "read_only";
|
|
505
551
|
const restrictedConfig = createStandardPoolConfig({ id, salt: id });
|
|
@@ -528,7 +574,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
528
574
|
}
|
|
529
575
|
iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
|
|
530
576
|
});
|
|
531
|
-
await
|
|
577
|
+
await ifWeMight(sir, 'fails on ID collision', async () => {
|
|
532
578
|
// Try to add "pool_bob" again (it was added in Happy Path)
|
|
533
579
|
const duplicatePool = await createForeignPool("pool_bob");
|
|
534
580
|
let errorCaught = false;
|
|
@@ -568,7 +614,6 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
568
614
|
const strategy = KeystoneStrategyFactory.create({ config });
|
|
569
615
|
const poolSecret = await strategy.derivePoolSecret({ masterSecret: bobSecret });
|
|
570
616
|
const challenges = {};
|
|
571
|
-
const bindingMap = {};
|
|
572
617
|
// Generate a small set of challenges
|
|
573
618
|
for (let i = 0; i < 10; i++) {
|
|
574
619
|
const raw = await hash({ s: `${config.salt}${Date.now()}${i}` });
|
|
@@ -578,13 +623,11 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
578
623
|
});
|
|
579
624
|
const challenge = await strategy.generateChallenge({ solution });
|
|
580
625
|
challenges[challengeId] = challenge;
|
|
581
|
-
addToBindingMap(bindingMap, challengeId);
|
|
582
626
|
}
|
|
583
627
|
return {
|
|
584
628
|
id,
|
|
585
629
|
config,
|
|
586
630
|
challenges,
|
|
587
|
-
bindingMap,
|
|
588
631
|
isForeign: true,
|
|
589
632
|
metadata: { owner: 'Bob', role: 'Delegate' }
|
|
590
633
|
};
|
|
@@ -606,7 +649,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
606
649
|
});
|
|
607
650
|
});
|
|
608
651
|
await respecfully(sir, 'Happy Path', async () => {
|
|
609
|
-
await
|
|
652
|
+
await ifWeMight(sir, 'authorizes and adds a foreign pool', async () => {
|
|
610
653
|
const bobPool = await createForeignPool("pool_bob", ["post"]);
|
|
611
654
|
const updatedKeystone = await service.addPools({
|
|
612
655
|
latestKeystone: aliceKeystone,
|
|
@@ -638,7 +681,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
638
681
|
});
|
|
639
682
|
});
|
|
640
683
|
await respecfully(sir, 'Permissions & Logic', async () => {
|
|
641
|
-
await
|
|
684
|
+
await ifWeMight(sir, 'fails if no pool allows "manage" verb', async () => {
|
|
642
685
|
// 1. Create a restricted keystone (read-only)
|
|
643
686
|
let id = "read_only";
|
|
644
687
|
const restrictedConfig = createStandardPoolConfig({ id, salt: id });
|
|
@@ -667,7 +710,7 @@ await respecfully(sir, 'Suite E: Structural Evolution (addPools)', async () => {
|
|
|
667
710
|
}
|
|
668
711
|
iReckon(sir, errorCaught).asTo('permission denied').isGonnaBeTrue();
|
|
669
712
|
});
|
|
670
|
-
await
|
|
713
|
+
await ifWeMight(sir, 'fails on ID collision', async () => {
|
|
671
714
|
// Try to add "pool_bob" again (it was added in Happy Path)
|
|
672
715
|
const duplicatePool = await createForeignPool("pool_bob");
|
|
673
716
|
let errorCaught = false;
|
|
@@ -719,7 +762,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
719
762
|
});
|
|
720
763
|
});
|
|
721
764
|
await respecfully(sir, 'Proof Granularity & Math', async () => {
|
|
722
|
-
await
|
|
765
|
+
await ifWeMight(sir, 'generates exactly the expected number of solutions', async () => {
|
|
723
766
|
signedKeystone = await service.sign({
|
|
724
767
|
latestKeystone: genesisKeystone,
|
|
725
768
|
masterSecret: aliceSecret,
|
|
@@ -733,7 +776,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
733
776
|
// 2 Sequential + 2 Random = 4
|
|
734
777
|
iReckon(sir, solutions.length).asTo('solution count').willEqual(4);
|
|
735
778
|
});
|
|
736
|
-
await
|
|
779
|
+
await ifWeMight(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
|
|
737
780
|
const proof = signedKeystone.data.proofs[0];
|
|
738
781
|
const poolSnapshot = genesisKeystone.data.challengePools.find(p => p.id === poolId);
|
|
739
782
|
// We iterate every solution in the proof and MANUALLY verify the hash relationship
|
|
@@ -756,7 +799,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
756
799
|
iReckon(sir, calculatedHash).asTo(`Manual hash verification for ${solution.challengeId}`).willEqual(challenge.hash);
|
|
757
800
|
}
|
|
758
801
|
});
|
|
759
|
-
await
|
|
802
|
+
await ifWeMight(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
|
|
760
803
|
const proof = signedKeystone.data.proofs[0];
|
|
761
804
|
const poolSnapshot = genesisKeystone.data.challengePools.find(p => p.id === poolId);
|
|
762
805
|
// The first N keys in the pool should be the FIFO targets.
|
|
@@ -770,9 +813,65 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
770
813
|
iReckon(sir, hasFirst).asTo(`Solution includes 1st FIFO ID (${expectedFifoIds[0]})`).isGonnaBeTrue();
|
|
771
814
|
iReckon(sir, hasSecond).asTo(`Solution includes 2nd FIFO ID (${expectedFifoIds[1]})`).isGonnaBeTrue();
|
|
772
815
|
});
|
|
816
|
+
await ifWeMight(sir, 'verifies Next-Gen Hash-Based Target Binding Selection', async () => {
|
|
817
|
+
const bindingConfig = createStandardPoolConfig({
|
|
818
|
+
id: "binding_pool",
|
|
819
|
+
salt: "binding_salt",
|
|
820
|
+
sequential: 0,
|
|
821
|
+
random: 0,
|
|
822
|
+
targetBinding: 3,
|
|
823
|
+
size: 20
|
|
824
|
+
});
|
|
825
|
+
const tempGenesis = await service.genesis({
|
|
826
|
+
masterSecret: aliceSecret,
|
|
827
|
+
configs: [bindingConfig],
|
|
828
|
+
metaspace: mockMetaspace,
|
|
829
|
+
space: mockSpace,
|
|
830
|
+
});
|
|
831
|
+
// Evolve using the binding pool
|
|
832
|
+
const evolved = await service.sign({
|
|
833
|
+
latestKeystone: tempGenesis,
|
|
834
|
+
masterSecret: aliceSecret,
|
|
835
|
+
poolId: "binding_pool",
|
|
836
|
+
claim: {
|
|
837
|
+
verb: "sign",
|
|
838
|
+
target: "some_target_address^gib"
|
|
839
|
+
},
|
|
840
|
+
metaspace: mockMetaspace,
|
|
841
|
+
space: mockSpace,
|
|
842
|
+
});
|
|
843
|
+
const proof = evolved.data.proofs[0];
|
|
844
|
+
iReckon(sir, proof.solutions.length).asTo('proof solutions count').willEqual(3);
|
|
845
|
+
// Now, let's verify that the selection is perfectly deterministic.
|
|
846
|
+
// If we run selectChallengeIds manually with the same pool and target, it must return the same IDs.
|
|
847
|
+
const poolSnapshot = tempGenesis.data.challengePools.find(p => p.id === "binding_pool");
|
|
848
|
+
const selectedIds = await selectChallengeIds({
|
|
849
|
+
pool: poolSnapshot,
|
|
850
|
+
targetAddr: "some_target_address^gib"
|
|
851
|
+
});
|
|
852
|
+
const proofIds = proof.solutions.map(s => s.challengeId);
|
|
853
|
+
iReckon(sir, selectedIds.length).asTo('selected IDs count').willEqual(3);
|
|
854
|
+
for (const id of selectedIds) {
|
|
855
|
+
iReckon(sir, proofIds.includes(id)).asTo(`proof includes selected ID ${id}`).isGonnaBeTrue();
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
await ifWeMight(sir, 'fails validation if total requested challenges >= size', async () => {
|
|
859
|
+
const invalidConfig = createStandardPoolConfig({
|
|
860
|
+
id: "invalid_pool",
|
|
861
|
+
salt: "invalid_salt",
|
|
862
|
+
sequential: 5,
|
|
863
|
+
random: 5,
|
|
864
|
+
targetBinding: 6,
|
|
865
|
+
size: 15 // total requested is 5+5+6 = 16 >= 15
|
|
866
|
+
});
|
|
867
|
+
const errors = await validateChallengePool({ pool: { id: "invalid_pool", config: invalidConfig, challenges: {} } });
|
|
868
|
+
iReckon(sir, errors.length > 0).asTo('errors length is positive').isGonnaBeTrue();
|
|
869
|
+
const hasCorrectError = errors.some(e => e.includes("Total requested challenges"));
|
|
870
|
+
iReckon(sir, hasCorrectError).asTo('validation error content').isGonnaBeTrue();
|
|
871
|
+
});
|
|
773
872
|
});
|
|
774
873
|
await respecfully(sir, 'DTO & Serialization', async () => {
|
|
775
|
-
await
|
|
874
|
+
await ifWeMight(sir, 'survives a clone/JSON-cycle without corruption', async () => {
|
|
776
875
|
// 1. Create a DTO (simulate network transmission/storage)
|
|
777
876
|
// 'clone' does a JSON stringify/parse under the hood (usually) or structured clone.
|
|
778
877
|
const dto = clone(signedKeystone);
|
|
@@ -788,7 +887,7 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
788
887
|
});
|
|
789
888
|
iReckon(sir, errors.length).asTo('DTO validation errors').willEqual(0);
|
|
790
889
|
});
|
|
791
|
-
await
|
|
890
|
+
await ifWeMight(sir, 'ensures data contains no functions or circular refs', async () => {
|
|
792
891
|
// A crude but effective test: ensure JSON.stringify doesn't throw
|
|
793
892
|
// and the result is equal to the object (if we parsed it back).
|
|
794
893
|
const jsonStr = JSON.stringify(signedKeystone);
|
|
@@ -806,5 +905,392 @@ await respecfully(sir, 'Suite F: Deep Inspection', async () => {
|
|
|
806
905
|
iReckon(sir, parsedKeys.length).asTo('key count matches').willEqual(origKeys.length);
|
|
807
906
|
});
|
|
808
907
|
});
|
|
908
|
+
await respecfully(sir, 'KeystoneProfileBuilder and Modular Schemas', async () => {
|
|
909
|
+
await ifWeMight(sir, 'compiles configs successfully with medium profile', async () => {
|
|
910
|
+
const builder = KeystoneProfileBuilder.buildKeystone('medium')
|
|
911
|
+
.withUsername('alice')
|
|
912
|
+
.withEmail('alice@example.com')
|
|
913
|
+
.withDescription('Alice test keystone')
|
|
914
|
+
.withDetails({ extraInfo: 'abc' });
|
|
915
|
+
const configs = await builder.compileConfigs();
|
|
916
|
+
iReckon(sir, configs.length).asTo('number of pools').willEqual(5);
|
|
917
|
+
// Verify unique salt derivation per pool
|
|
918
|
+
const salts = configs.map(c => c.salt);
|
|
919
|
+
const uniqueSalts = new Set(salts);
|
|
920
|
+
iReckon(sir, uniqueSalts.size).asTo('unique salts count').willEqual(5);
|
|
921
|
+
// Verify frameDetails unification
|
|
922
|
+
const details = builder.getFrameDetails();
|
|
923
|
+
iReckon(sir, details.username).asTo('username').willEqual('alice');
|
|
924
|
+
iReckon(sir, details.email).asTo('email').willEqual('alice@example.com');
|
|
925
|
+
iReckon(sir, details.description).asTo('description').willEqual('Alice test keystone');
|
|
926
|
+
iReckon(sir, details.extraInfo).asTo('extraInfo').willEqual('abc');
|
|
927
|
+
});
|
|
928
|
+
await ifWeMight(sir, 'supports customized overrides', async () => {
|
|
929
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
930
|
+
.customizePool('sync', {
|
|
931
|
+
behavior: {
|
|
932
|
+
size: 15,
|
|
933
|
+
targetBindingCount: 4
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
const configs = await builder.compileConfigs();
|
|
937
|
+
const syncPool = configs.find(c => c.id === 'sync');
|
|
938
|
+
iReckon(sir, syncPool.behavior.size).asTo('customized size').willEqual(15);
|
|
939
|
+
iReckon(sir, syncPool.behavior.targetBindingCount).asTo('customized target binding').willEqual(4);
|
|
940
|
+
});
|
|
941
|
+
});
|
|
942
|
+
await respecfully(sir, 'Keystone Metadata Validation', async () => {
|
|
943
|
+
await ifWeMight(sir, 'validates correct username and description', async () => {
|
|
944
|
+
const service = new KeystoneService_V1();
|
|
945
|
+
const space = new MockIbGibSpace();
|
|
946
|
+
const metaspace = new MockMetaspaceService(space);
|
|
947
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
948
|
+
.withUsername('alice_123.test')
|
|
949
|
+
.withDescription('Valid description: yes, it is!')
|
|
950
|
+
.withDetails({ client: 'respec' });
|
|
951
|
+
const configs = await builder.compileConfigs();
|
|
952
|
+
const frameDetails = builder.getFrameDetails();
|
|
953
|
+
// Should succeed without error
|
|
954
|
+
const keystoneIbGib = await service.genesis({
|
|
955
|
+
masterSecret: 'mysecret',
|
|
956
|
+
configs,
|
|
957
|
+
metaspace: metaspace,
|
|
958
|
+
space: space,
|
|
959
|
+
frameDetails
|
|
960
|
+
});
|
|
961
|
+
const errors = await service.validateGenesisKeystone({ keystoneIbGib });
|
|
962
|
+
iReckon(sir, errors.length).asTo('errors count').willEqual(0);
|
|
963
|
+
});
|
|
964
|
+
await ifWeMight(sir, 'fails validation for invalid username length or character', async () => {
|
|
965
|
+
const service = new KeystoneService_V1();
|
|
966
|
+
const space = new MockIbGibSpace();
|
|
967
|
+
const metaspace = new MockMetaspaceService(space);
|
|
968
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
969
|
+
.withUsername('invalid_user_name_that_is_way_too_long_for_the_sixty_three_char_limit_and_will_fail_regex_check')
|
|
970
|
+
.withDetails({ client: 'respec' });
|
|
971
|
+
const configs = await builder.compileConfigs();
|
|
972
|
+
const frameDetails = builder.getFrameDetails();
|
|
973
|
+
const keystoneIbGib = await service.genesis({
|
|
974
|
+
masterSecret: 'mysecret',
|
|
975
|
+
configs,
|
|
976
|
+
metaspace: metaspace,
|
|
977
|
+
space: space,
|
|
978
|
+
frameDetails
|
|
979
|
+
});
|
|
980
|
+
const errors = await service.validateGenesisKeystone({ keystoneIbGib });
|
|
981
|
+
iReckon(sir, errors.length).asTo('errors count').willEqual(1);
|
|
982
|
+
iReckon(sir, errors[0]).includes('invalid username');
|
|
983
|
+
});
|
|
984
|
+
await ifWeMight(sir, 'fails validation for invalid description characters', async () => {
|
|
985
|
+
const service = new KeystoneService_V1();
|
|
986
|
+
const space = new MockIbGibSpace();
|
|
987
|
+
const metaspace = new MockMetaspaceService(space);
|
|
988
|
+
const builder = KeystoneProfileBuilder.buildKeystone('test')
|
|
989
|
+
.withUsername('alice')
|
|
990
|
+
.withDescription('Invalid desc with weird emoji 🚀')
|
|
991
|
+
.withDetails({ client: 'respec' });
|
|
992
|
+
const configs = await builder.compileConfigs();
|
|
993
|
+
const frameDetails = builder.getFrameDetails();
|
|
994
|
+
const keystoneIbGib = await service.genesis({
|
|
995
|
+
masterSecret: 'mysecret',
|
|
996
|
+
configs,
|
|
997
|
+
metaspace: metaspace,
|
|
998
|
+
space: space,
|
|
999
|
+
frameDetails
|
|
1000
|
+
});
|
|
1001
|
+
const errors = await service.validateGenesisKeystone({ keystoneIbGib });
|
|
1002
|
+
iReckon(sir, errors.length).asTo('errors count').willEqual(1);
|
|
1003
|
+
iReckon(sir, errors[0]).includes('invalid description');
|
|
1004
|
+
});
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
// ===========================================================================
|
|
1008
|
+
// SUITE E: DELEGATION (Register/Unregister)
|
|
1009
|
+
// ===========================================================================
|
|
1010
|
+
await respecfully(sir, 'Suite E: Delegation (Register/Unregister)', async () => {
|
|
1011
|
+
const service = new KeystoneService_V1();
|
|
1012
|
+
const parentSecret = "ParentMasterSecret_123";
|
|
1013
|
+
const delegateSecret = "DelegateMasterSecret_456";
|
|
1014
|
+
let mockSpace;
|
|
1015
|
+
let mockMetaspace;
|
|
1016
|
+
let parentKeystone;
|
|
1017
|
+
let delegateKeystone;
|
|
1018
|
+
firstOfEach(sir, async () => {
|
|
1019
|
+
mockSpace = new MockIbGibSpace();
|
|
1020
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1021
|
+
const salt = (await getUUID()).substring(0, 16);
|
|
1022
|
+
const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
|
|
1023
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt + "_manage" });
|
|
1024
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
|
|
1025
|
+
// 1. Create Parent Keystone (with manage pool)
|
|
1026
|
+
parentKeystone = await service.genesis({
|
|
1027
|
+
masterSecret: parentSecret,
|
|
1028
|
+
configs: [defaultConfig, manageConfig],
|
|
1029
|
+
metaspace: mockMetaspace,
|
|
1030
|
+
space: mockSpace,
|
|
1031
|
+
});
|
|
1032
|
+
// 2. Create Delegate Keystone
|
|
1033
|
+
const delegateConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt + "_delegate" });
|
|
1034
|
+
delegateKeystone = await service.genesis({
|
|
1035
|
+
masterSecret: delegateSecret,
|
|
1036
|
+
configs: [delegateConfig],
|
|
1037
|
+
metaspace: mockMetaspace,
|
|
1038
|
+
space: mockSpace,
|
|
1039
|
+
});
|
|
1040
|
+
});
|
|
1041
|
+
await ifWeMight(sir, 'registers a delegate keystone and evolves parent using manage pool', async () => {
|
|
1042
|
+
// Register the delegate
|
|
1043
|
+
const evolvedParent = await service.registerDelegate({
|
|
1044
|
+
parentKeystone,
|
|
1045
|
+
delegateKeystone,
|
|
1046
|
+
masterSecret: parentSecret,
|
|
1047
|
+
metaspace: mockMetaspace,
|
|
1048
|
+
space: mockSpace,
|
|
1049
|
+
});
|
|
1050
|
+
iReckon(sir, evolvedParent).asTo('evolved parent exists').isGonnaBeTruthy();
|
|
1051
|
+
iReckon(sir, evolvedParent.data?.delegates).asTo('delegates map exists').isGonnaBeTruthy();
|
|
1052
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1053
|
+
if (!delegateTjpAddr) {
|
|
1054
|
+
throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 312e986ce4b2ed6c084c03e8f45fa826)`);
|
|
1055
|
+
}
|
|
1056
|
+
const delegateInfo = evolvedParent.data?.delegates?.[delegateTjpAddr];
|
|
1057
|
+
iReckon(sir, delegateInfo).asTo('delegate info registered').isGonnaBeTruthy();
|
|
1058
|
+
iReckon(sir, delegateInfo?.delegateTjpAddr).asTo('delegate tjp matches').willEqual(delegateTjpAddr);
|
|
1059
|
+
});
|
|
1060
|
+
await ifWeMight(sir, 'gets correct delegate status via parent TJP address', async () => {
|
|
1061
|
+
const parentTjpAddr = getTjpAddr({ ibGib: parentKeystone });
|
|
1062
|
+
if (!parentTjpAddr) {
|
|
1063
|
+
throw new Error(`(UNEXPECTED) parentTjpAddr falsy? (E: b7ce2835ccd951d8c99257328be3b826)`);
|
|
1064
|
+
}
|
|
1065
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1066
|
+
if (!delegateTjpAddr) {
|
|
1067
|
+
throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 3e4878ea19dd8034e827466a8bf09526)`);
|
|
1068
|
+
}
|
|
1069
|
+
// Before registration
|
|
1070
|
+
const statusBefore = await service.getDelegateStatus({
|
|
1071
|
+
parentTjpAddr: parentTjpAddr,
|
|
1072
|
+
delegateTjpAddr: delegateTjpAddr,
|
|
1073
|
+
metaspace: mockMetaspace,
|
|
1074
|
+
space: mockSpace,
|
|
1075
|
+
});
|
|
1076
|
+
iReckon(sir, statusBefore.registered).asTo('initially unregistered').isGonnaBeFalse();
|
|
1077
|
+
// Register
|
|
1078
|
+
const evolvedParent = await service.registerDelegate({
|
|
1079
|
+
parentKeystone,
|
|
1080
|
+
delegateKeystone,
|
|
1081
|
+
masterSecret: parentSecret,
|
|
1082
|
+
metaspace: mockMetaspace,
|
|
1083
|
+
space: mockSpace,
|
|
1084
|
+
});
|
|
1085
|
+
// After registration
|
|
1086
|
+
const statusAfter = await service.getDelegateStatus({
|
|
1087
|
+
parentTjpAddr: parentTjpAddr,
|
|
1088
|
+
delegateTjpAddr: delegateTjpAddr,
|
|
1089
|
+
metaspace: mockMetaspace,
|
|
1090
|
+
space: mockSpace,
|
|
1091
|
+
});
|
|
1092
|
+
iReckon(sir, statusAfter.registered).asTo('registered status').isGonnaBeTrue();
|
|
1093
|
+
iReckon(sir, statusAfter.delegateInfo?.delegateTjpAddr).asTo('status tjp matches').willEqual(delegateTjpAddr);
|
|
1094
|
+
});
|
|
1095
|
+
await ifWeMight(sir, 'unregisters a registered delegate keystone', async () => {
|
|
1096
|
+
// Register first
|
|
1097
|
+
const evolvedParent = await service.registerDelegate({
|
|
1098
|
+
parentKeystone,
|
|
1099
|
+
delegateKeystone,
|
|
1100
|
+
masterSecret: parentSecret,
|
|
1101
|
+
metaspace: mockMetaspace,
|
|
1102
|
+
space: mockSpace,
|
|
1103
|
+
});
|
|
1104
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1105
|
+
if (!delegateTjpAddr) {
|
|
1106
|
+
throw new Error(`(UNEXPECTED) delegateTjpAddr falsy? (E: 486b486a8c984a52b8d2b22418c20d26)`);
|
|
1107
|
+
}
|
|
1108
|
+
// Unregister
|
|
1109
|
+
const finalParent = await service.unregisterDelegate({
|
|
1110
|
+
parentKeystone: evolvedParent,
|
|
1111
|
+
delegateTjpAddr,
|
|
1112
|
+
masterSecret: parentSecret,
|
|
1113
|
+
metaspace: mockMetaspace,
|
|
1114
|
+
space: mockSpace,
|
|
1115
|
+
});
|
|
1116
|
+
iReckon(sir, finalParent).asTo('final evolved parent exists').isGonnaBeTruthy();
|
|
1117
|
+
const info = finalParent.data?.delegates?.[delegateTjpAddr];
|
|
1118
|
+
iReckon(sir, info).asTo('delegate info removed').isGonnaBeUndefined();
|
|
1119
|
+
});
|
|
1120
|
+
await ifWeMight(sir, 'throws if parent does not have a manage pool', async () => {
|
|
1121
|
+
// Create a parent WITHOUT a manage pool
|
|
1122
|
+
const salt = (await getUUID()).substring(0, 16);
|
|
1123
|
+
const defaultConfig = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt });
|
|
1124
|
+
const restrictedParent = await service.genesis({
|
|
1125
|
+
masterSecret: parentSecret,
|
|
1126
|
+
configs: [defaultConfig],
|
|
1127
|
+
metaspace: mockMetaspace,
|
|
1128
|
+
space: mockSpace,
|
|
1129
|
+
});
|
|
1130
|
+
let errorCaught = false;
|
|
1131
|
+
try {
|
|
1132
|
+
await service.registerDelegate({
|
|
1133
|
+
parentKeystone: restrictedParent,
|
|
1134
|
+
delegateKeystone,
|
|
1135
|
+
masterSecret: parentSecret,
|
|
1136
|
+
metaspace: mockMetaspace,
|
|
1137
|
+
space: mockSpace,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
catch (e) {
|
|
1141
|
+
errorCaught = true;
|
|
1142
|
+
iReckon(sir, e.message).includes('Manage pool is required');
|
|
1143
|
+
}
|
|
1144
|
+
iReckon(sir, errorCaught).asTo('delegation rejected').isGonnaBeTrue();
|
|
1145
|
+
});
|
|
1146
|
+
});
|
|
1147
|
+
await respecfully(sir, 'Suite G: Delegate & IB Validation', async () => {
|
|
1148
|
+
const service = new KeystoneService_V1();
|
|
1149
|
+
const parentSecret = "ParentMasterSecret_SuiteG";
|
|
1150
|
+
const delegateSecret = "DelegateMasterSecret_SuiteG";
|
|
1151
|
+
let mockSpace;
|
|
1152
|
+
let mockMetaspace;
|
|
1153
|
+
let parentKeystone;
|
|
1154
|
+
let delegateKeystone;
|
|
1155
|
+
firstOfEach(sir, async () => {
|
|
1156
|
+
mockSpace = new MockIbGibSpace();
|
|
1157
|
+
mockMetaspace = new MockMetaspaceService(mockSpace);
|
|
1158
|
+
const salt1 = (await getUUID()).substring(0, 16);
|
|
1159
|
+
const salt2 = (await getUUID()).substring(0, 16);
|
|
1160
|
+
const manageConfig = createStandardPoolConfig({ id: POOL_ID_MANAGE, salt: salt2 });
|
|
1161
|
+
manageConfig.allowedVerbs = [KEYSTONE_VERB_MANAGE];
|
|
1162
|
+
const configs = [
|
|
1163
|
+
createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 }),
|
|
1164
|
+
manageConfig
|
|
1165
|
+
];
|
|
1166
|
+
parentKeystone = await service.genesis({
|
|
1167
|
+
masterSecret: parentSecret,
|
|
1168
|
+
configs,
|
|
1169
|
+
metaspace: mockMetaspace,
|
|
1170
|
+
space: mockSpace,
|
|
1171
|
+
});
|
|
1172
|
+
delegateKeystone = await service.genesis({
|
|
1173
|
+
masterSecret: delegateSecret,
|
|
1174
|
+
configs: [createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: salt1 })],
|
|
1175
|
+
metaspace: mockMetaspace,
|
|
1176
|
+
space: mockSpace,
|
|
1177
|
+
});
|
|
1178
|
+
});
|
|
1179
|
+
await ifWeMight(sir, 'validateKeystoneIb returns true for valid keystone ib and false for others', async () => {
|
|
1180
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'keystone' })).isGonnaBeTrue();
|
|
1181
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'keystone user info' })).isGonnaBeTrue();
|
|
1182
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'invalidAtom' })).isGonnaBeFalse();
|
|
1183
|
+
iReckon(sir, await validateKeystoneIb({ ib: 'not_keystone sub_atom' })).isGonnaBeFalse();
|
|
1184
|
+
});
|
|
1185
|
+
await ifWeMight(sir, 'validateGenesisKeystone fails validation if ib is invalid', async () => {
|
|
1186
|
+
const invalidGenesis = {
|
|
1187
|
+
...parentKeystone,
|
|
1188
|
+
ib: 'not_a_keystone^' + parentKeystone.gib,
|
|
1189
|
+
};
|
|
1190
|
+
const errors = await validateGenesisKeystone({ keystoneIbGib: invalidGenesis });
|
|
1191
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1192
|
+
iReckon(sir, errors[0]).includes('invalid keystone ib');
|
|
1193
|
+
});
|
|
1194
|
+
await ifWeMight(sir, 'validateKeystoneTransition passes for valid delegate registration', async () => {
|
|
1195
|
+
const evolvedParent = await service.registerDelegate({
|
|
1196
|
+
parentKeystone,
|
|
1197
|
+
delegateKeystone,
|
|
1198
|
+
masterSecret: parentSecret,
|
|
1199
|
+
metaspace: mockMetaspace,
|
|
1200
|
+
space: mockSpace,
|
|
1201
|
+
});
|
|
1202
|
+
const errors = await validateKeystoneTransition({
|
|
1203
|
+
currentIbGib: evolvedParent,
|
|
1204
|
+
prevIbGib: parentKeystone,
|
|
1205
|
+
});
|
|
1206
|
+
iReckon(sir, errors.length).willEqual(0);
|
|
1207
|
+
});
|
|
1208
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegates map is mutated without manage proof', async () => {
|
|
1209
|
+
const evolvedParent = await service.registerDelegate({
|
|
1210
|
+
parentKeystone,
|
|
1211
|
+
delegateKeystone,
|
|
1212
|
+
masterSecret: parentSecret,
|
|
1213
|
+
metaspace: mockMetaspace,
|
|
1214
|
+
space: mockSpace,
|
|
1215
|
+
});
|
|
1216
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1217
|
+
// Remove the manage verb proof by converting it to sign
|
|
1218
|
+
for (const proof of tamperedParent.data.proofs) {
|
|
1219
|
+
if (proof.claim) {
|
|
1220
|
+
proof.claim.verb = 'sign';
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
const errors = await validateKeystoneTransition({
|
|
1224
|
+
currentIbGib: tamperedParent,
|
|
1225
|
+
prevIbGib: parentKeystone,
|
|
1226
|
+
});
|
|
1227
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1228
|
+
iReckon(sir, errors.some(e => e.includes('Policy Violation: Delegates map was mutated'))).isGonnaBeTrue();
|
|
1229
|
+
});
|
|
1230
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegate key does not match delegateTjpAddr', async () => {
|
|
1231
|
+
const evolvedParent = await service.registerDelegate({
|
|
1232
|
+
parentKeystone,
|
|
1233
|
+
delegateKeystone,
|
|
1234
|
+
masterSecret: parentSecret,
|
|
1235
|
+
metaspace: mockMetaspace,
|
|
1236
|
+
space: mockSpace,
|
|
1237
|
+
});
|
|
1238
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1239
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1240
|
+
if (!delegateTjpAddr) {
|
|
1241
|
+
throw new Error(`delegateTjpAddr falsy?`);
|
|
1242
|
+
}
|
|
1243
|
+
const info = tamperedParent.data.delegates[delegateTjpAddr];
|
|
1244
|
+
delete tamperedParent.data.delegates[delegateTjpAddr];
|
|
1245
|
+
tamperedParent.data.delegates['mismatched_key'] = info;
|
|
1246
|
+
const errors = await validateKeystoneTransition({
|
|
1247
|
+
currentIbGib: tamperedParent,
|
|
1248
|
+
prevIbGib: parentKeystone,
|
|
1249
|
+
});
|
|
1250
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1251
|
+
iReckon(sir, errors.some(e => e.includes('does not match delegateTjpAddr'))).isGonnaBeTrue();
|
|
1252
|
+
});
|
|
1253
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if delegate addresses have invalid keystone ib', async () => {
|
|
1254
|
+
const evolvedParent = await service.registerDelegate({
|
|
1255
|
+
parentKeystone,
|
|
1256
|
+
delegateKeystone,
|
|
1257
|
+
masterSecret: parentSecret,
|
|
1258
|
+
metaspace: mockMetaspace,
|
|
1259
|
+
space: mockSpace,
|
|
1260
|
+
});
|
|
1261
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1262
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1263
|
+
if (!delegateTjpAddr) {
|
|
1264
|
+
throw new Error(`delegateTjpAddr falsy?`);
|
|
1265
|
+
}
|
|
1266
|
+
tamperedParent.data.delegates[delegateTjpAddr].delegateAddr = 'invalid_ib^12345';
|
|
1267
|
+
const errors = await validateKeystoneTransition({
|
|
1268
|
+
currentIbGib: tamperedParent,
|
|
1269
|
+
prevIbGib: parentKeystone,
|
|
1270
|
+
});
|
|
1271
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1272
|
+
iReckon(sir, errors.some(e => e.includes('not a valid keystone address'))).isGonnaBeTrue();
|
|
1273
|
+
});
|
|
1274
|
+
await ifWeMight(sir, 'validateKeystoneTransition fails if thisAddr is not in the parent timeline history', async () => {
|
|
1275
|
+
const evolvedParent = await service.registerDelegate({
|
|
1276
|
+
parentKeystone,
|
|
1277
|
+
delegateKeystone,
|
|
1278
|
+
masterSecret: parentSecret,
|
|
1279
|
+
metaspace: mockMetaspace,
|
|
1280
|
+
space: mockSpace,
|
|
1281
|
+
});
|
|
1282
|
+
const tamperedParent = JSON.parse(JSON.stringify(evolvedParent));
|
|
1283
|
+
const delegateTjpAddr = getTjpAddr({ ibGib: delegateKeystone });
|
|
1284
|
+
if (!delegateTjpAddr) {
|
|
1285
|
+
throw new Error(`delegateTjpAddr falsy?`);
|
|
1286
|
+
}
|
|
1287
|
+
tamperedParent.data.delegates[delegateTjpAddr].thisAddr = 'keystone^randomgib';
|
|
1288
|
+
const errors = await validateKeystoneTransition({
|
|
1289
|
+
currentIbGib: tamperedParent,
|
|
1290
|
+
prevIbGib: parentKeystone,
|
|
1291
|
+
});
|
|
1292
|
+
iReckon(sir, errors.length > 0).isGonnaBeTrue();
|
|
1293
|
+
iReckon(sir, errors.some(e => e.includes('not a valid historical address in the parent keystone timeline'))).isGonnaBeTrue();
|
|
1294
|
+
});
|
|
809
1295
|
});
|
|
810
1296
|
//# sourceMappingURL=keystone-service-v1.respec.mjs.map
|