@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.
Files changed (181) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/common/other/graph-helper.d.mts.map +1 -1
  3. package/dist/common/other/graph-helper.mjs +7 -6
  4. package/dist/common/other/graph-helper.mjs.map +1 -1
  5. package/dist/keystone/keystone-config-builder.d.mts +1 -2
  6. package/dist/keystone/keystone-config-builder.d.mts.map +1 -1
  7. package/dist/keystone/keystone-config-builder.mjs +4 -19
  8. package/dist/keystone/keystone-config-builder.mjs.map +1 -1
  9. package/dist/keystone/keystone-constants.d.mts +2 -1
  10. package/dist/keystone/keystone-constants.d.mts.map +1 -1
  11. package/dist/keystone/keystone-constants.mjs +2 -1
  12. package/dist/keystone/keystone-constants.mjs.map +1 -1
  13. package/dist/keystone/keystone-helpers.d.mts +28 -16
  14. package/dist/keystone/keystone-helpers.d.mts.map +1 -1
  15. package/dist/keystone/keystone-helpers.mjs +155 -76
  16. package/dist/keystone/keystone-helpers.mjs.map +1 -1
  17. package/dist/keystone/keystone-policy-types.d.mts +6 -4
  18. package/dist/keystone/keystone-policy-types.d.mts.map +1 -1
  19. package/dist/keystone/keystone-service-v1.d.mts +38 -2
  20. package/dist/keystone/keystone-service-v1.d.mts.map +1 -1
  21. package/dist/keystone/keystone-service-v1.mjs +177 -11
  22. package/dist/keystone/keystone-service-v1.mjs.map +1 -1
  23. package/dist/keystone/keystone-service-v1.respec.mjs +527 -41
  24. package/dist/keystone/keystone-service-v1.respec.mjs.map +1 -1
  25. package/dist/keystone/keystone-types.d.mts +53 -11
  26. package/dist/keystone/keystone-types.d.mts.map +1 -1
  27. package/dist/keystone/policy/keystone-profile-builder.d.mts +25 -0
  28. package/dist/keystone/policy/keystone-profile-builder.d.mts.map +1 -0
  29. package/dist/keystone/policy/keystone-profile-builder.mjs +117 -0
  30. package/dist/keystone/policy/keystone-profile-builder.mjs.map +1 -0
  31. package/dist/keystone/policy/profiles/profile-high.json +84 -0
  32. package/dist/keystone/policy/profiles/profile-low.json +84 -0
  33. package/dist/keystone/policy/profiles/profile-medium.json +84 -0
  34. package/dist/keystone/policy/profiles/profile-session.json +84 -0
  35. package/dist/keystone/policy/profiles/profile-test.json +84 -0
  36. package/dist/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
  37. package/dist/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
  38. package/dist/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
  39. package/dist/keystone/policy/schemas/keystone.high.schema.json +26 -0
  40. package/dist/keystone/policy/schemas/keystone.low.schema.json +26 -0
  41. package/dist/keystone/policy/schemas/keystone.medium.schema.json +26 -0
  42. package/dist/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
  43. package/dist/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
  44. package/dist/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
  45. package/dist/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
  46. package/dist/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
  47. package/dist/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
  48. package/dist/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
  49. package/dist/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
  50. package/dist/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
  51. package/dist/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
  52. package/dist/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
  53. package/dist/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
  54. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts +8 -0
  55. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.d.mts.map +1 -0
  56. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs +582 -0
  57. package/dist/sync/sync-conflict-adv-multitimelines.withid.respec.mjs.map +1 -0
  58. package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts +7 -0
  59. package/dist/sync/sync-conflict-basic-divergence.withid.respec.d.mts.map +1 -0
  60. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs +274 -0
  61. package/dist/sync/sync-conflict-basic-divergence.withid.respec.mjs.map +1 -0
  62. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts +8 -0
  63. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.d.mts.map +1 -0
  64. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs +322 -0
  65. package/dist/sync/sync-conflict-basic-multitimelines.withid.respec.mjs.map +1 -0
  66. package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts +7 -0
  67. package/dist/sync/sync-conflict-text-merge.withid.respec.d.mts.map +1 -0
  68. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs +307 -0
  69. package/dist/sync/sync-conflict-text-merge.withid.respec.mjs.map +1 -0
  70. package/dist/sync/sync-innerspace-constants.withid.respec.d.mts +8 -0
  71. package/dist/sync/sync-innerspace-constants.withid.respec.d.mts.map +1 -0
  72. package/dist/sync/sync-innerspace-constants.withid.respec.mjs +233 -0
  73. package/dist/sync/sync-innerspace-constants.withid.respec.mjs.map +1 -0
  74. package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts +7 -0
  75. package/dist/sync/sync-innerspace-deep-updates.withid.respec.d.mts.map +1 -0
  76. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs +233 -0
  77. package/dist/sync/sync-innerspace-deep-updates.withid.respec.mjs.map +1 -0
  78. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts +7 -0
  79. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.d.mts.map +1 -0
  80. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs +225 -0
  81. package/dist/sync/sync-innerspace-dest-ahead.withid.respec.mjs.map +1 -0
  82. package/dist/sync/sync-innerspace-multiple-timelines.respec.mjs.map +1 -1
  83. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts +7 -0
  84. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.d.mts.map +1 -0
  85. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs +235 -0
  86. package/dist/sync/sync-innerspace-multiple-timelines.withid.respec.mjs.map +1 -0
  87. package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts +7 -0
  88. package/dist/sync/sync-innerspace-partial-update.withid.respec.d.mts.map +1 -0
  89. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs +211 -0
  90. package/dist/sync/sync-innerspace-partial-update.withid.respec.mjs.map +1 -0
  91. package/dist/sync/sync-innerspace.withid.respec.d.mts +8 -0
  92. package/dist/sync/sync-innerspace.withid.respec.d.mts.map +1 -0
  93. package/dist/sync/sync-innerspace.withid.respec.mjs +260 -0
  94. package/dist/sync/sync-innerspace.withid.respec.mjs.map +1 -0
  95. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts +1 -0
  96. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.d.mts.map +1 -1
  97. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs +13 -4
  98. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mjs.map +1 -1
  99. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts +1 -1
  100. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.d.mts.map +1 -1
  101. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs +5 -3
  102. package/dist/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mjs.map +1 -1
  103. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  104. package/dist/sync/sync-saga-coordinator.mjs +165 -145
  105. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  106. package/dist/sync/sync-withid.pingpong.respec.mjs +8 -8
  107. package/dist/sync/sync-withid.pingpong.respec.mjs.map +1 -1
  108. package/dist/timeline/timeline-api.d.mts.map +1 -1
  109. package/dist/timeline/timeline-api.mjs +1 -14
  110. package/dist/timeline/timeline-api.mjs.map +1 -1
  111. package/dist/witness/space/reconciliation-space/reconciliation-space-base.d.mts.map +1 -1
  112. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs +0 -40
  113. package/dist/witness/space/reconciliation-space/reconciliation-space-base.mjs.map +1 -1
  114. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.d.mts.map +1 -1
  115. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +6 -6
  116. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -1
  117. package/ibgib-foundations.md +2 -1
  118. package/package.json +1 -1
  119. package/src/common/other/graph-helper.mts +7 -6
  120. package/src/keystone/README.md +34 -17
  121. package/src/keystone/docs/CRYPTANALYSIS.md +185 -0
  122. package/src/keystone/docs/architecture.md +45 -3
  123. package/src/keystone/docs/delegation.md +57 -0
  124. package/src/keystone/docs/profiles.md +124 -0
  125. package/src/keystone/keystone-config-builder.mts +3 -21
  126. package/src/keystone/keystone-constants.mts +2 -1
  127. package/src/keystone/keystone-helpers.mts +204 -81
  128. package/src/keystone/keystone-policy-types.mts +6 -4
  129. package/src/keystone/keystone-service-v1.mts +223 -11
  130. package/src/keystone/keystone-service-v1.respec.mts +596 -39
  131. package/src/keystone/keystone-types.mts +55 -9
  132. package/src/keystone/policy/IMPLEMENTATION.md +60 -0
  133. package/src/keystone/policy/README.md +94 -0
  134. package/src/keystone/policy/keystone-profile-builder.mts +131 -0
  135. package/src/keystone/policy/profiles/profile-high.json +84 -0
  136. package/src/keystone/policy/profiles/profile-low.json +84 -0
  137. package/src/keystone/policy/profiles/profile-medium.json +84 -0
  138. package/src/keystone/policy/profiles/profile-session.json +84 -0
  139. package/src/keystone/policy/profiles/profile-test.json +84 -0
  140. package/src/keystone/policy/schemas/connect-pool.high.schema.json +23 -0
  141. package/src/keystone/policy/schemas/connect-pool.low.schema.json +23 -0
  142. package/src/keystone/policy/schemas/connect-pool.medium.schema.json +23 -0
  143. package/src/keystone/policy/schemas/keystone.high.schema.json +26 -0
  144. package/src/keystone/policy/schemas/keystone.low.schema.json +26 -0
  145. package/src/keystone/policy/schemas/keystone.medium.schema.json +26 -0
  146. package/src/keystone/policy/schemas/manage-pool.high.schema.json +23 -0
  147. package/src/keystone/policy/schemas/manage-pool.low.schema.json +23 -0
  148. package/src/keystone/policy/schemas/manage-pool.medium.schema.json +23 -0
  149. package/src/keystone/policy/schemas/revoke-pool.high.schema.json +23 -0
  150. package/src/keystone/policy/schemas/revoke-pool.low.schema.json +23 -0
  151. package/src/keystone/policy/schemas/revoke-pool.medium.schema.json +23 -0
  152. package/src/keystone/policy/schemas/sign-pool.high.schema.json +23 -0
  153. package/src/keystone/policy/schemas/sign-pool.low.schema.json +23 -0
  154. package/src/keystone/policy/schemas/sign-pool.medium.schema.json +23 -0
  155. package/src/keystone/policy/schemas/sync-pool.high.schema.json +23 -0
  156. package/src/keystone/policy/schemas/sync-pool.low.schema.json +23 -0
  157. package/src/keystone/policy/schemas/sync-pool.medium.schema.json +23 -0
  158. package/src/sync/docs/security.md +54 -21
  159. package/src/sync/sync-conflict-adv-multitimelines.withid.respec.mts +706 -0
  160. package/src/sync/sync-conflict-basic-divergence.withid.respec.mts +321 -0
  161. package/src/sync/sync-conflict-basic-multitimelines.withid.respec.mts +374 -0
  162. package/src/sync/sync-conflict-text-merge.withid.respec.mts +347 -0
  163. package/src/sync/sync-innerspace-constants.withid.respec.mts +264 -0
  164. package/src/sync/sync-innerspace-deep-updates.withid.respec.mts +272 -0
  165. package/src/sync/sync-innerspace-dest-ahead.withid.respec.mts +266 -0
  166. package/src/sync/sync-innerspace-multiple-timelines.respec.mts +0 -1
  167. package/src/sync/sync-innerspace-multiple-timelines.withid.respec.mts +273 -0
  168. package/src/sync/sync-innerspace-partial-update.withid.respec.mts +249 -0
  169. package/src/sync/sync-innerspace.withid.respec.mts +265 -0
  170. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-peer-websocket-receiver-v1.mts +12 -4
  171. package/src/sync/sync-peer/sync-peer-websocket/sync-peer-websocket-receiver/sync-websocket-peer-helpers.mts +5 -3
  172. package/src/sync/sync-saga-coordinator.mts +159 -143
  173. package/src/sync/sync-withid.pingpong.respec.mts +8 -8
  174. package/src/timeline/timeline-api.mts +1 -15
  175. package/src/timeline/timeline-api.respec.mts +3 -3
  176. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts +0 -46
  177. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +6 -5
  178. package/tsconfig.json +6 -1
  179. package/src/keystone/keystone-policy.schema.json +0 -51
  180. package/src/witness/space/reconciliation-space/reconciliation-space-base.mts.OLD.md +0 -884
  181. 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 { addToBindingMap } from './keystone-helpers.mjs';
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
- const ibGibs: IbGib_V1[] = [];
44
- for (const addr of addrs) {
45
- const x = await this.get({ addr });
46
- if (x) { ibGibs.push(x); }
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 ifWe(sir, 'derivePoolSecret with same inputs returns same output', async () => {
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 ifWe(sir, 'derivePoolSecret with different master secret returns different output', async () => {
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 ifWe(sir, 'derivePoolSecret with different salt returns different output', async () => {
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 ifWe(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
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 ifWe(sir, 'validateSolution fails for mismatched values', async () => {
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 ifWe(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
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 ifWe(sir, 'creates a valid genesis frame and persists it', async () => {
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 ifWe(sir, 'evolves the keystone with a valid proof', async () => {
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 ifWe(sir, 'validates the genesis->signed transition', async () => {
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 ifWe(sir, 'prevents creation of forged frames', async () => {
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 ifWe(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
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 ifWe(sir, 'successfully creates a revocation frame', async () => {
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 ifWe(sir, 'validates the revocation frame', async () => {
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 ifWe(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
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 ifWe(sir, 'authorizes and adds a foreign pool', async () => {
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 ifWe(sir, 'fails if no pool allows "manage" verb', async () => {
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 ifWe(sir, 'fails on ID collision', async () => {
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 ifWe(sir, 'authorizes and adds a foreign pool', async () => {
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 ifWe(sir, 'fails if no pool allows "manage" verb', async () => {
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 ifWe(sir, 'fails on ID collision', async () => {
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 ifWe(sir, 'generates exactly the expected number of solutions', async () => {
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 ifWe(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
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 ifWe(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
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 ifWe(sir, 'survives a clone/JSON-cycle without corruption', async () => {
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 ifWe(sir, 'ensures data contains no functions or circular refs', async () => {
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
  });