@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
@@ -1,13 +1,15 @@
1
- import { respecfully, iReckon, ifWe, firstOfAll } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
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 { addToBindingMap } from './keystone-helpers.mjs';
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
- const ibGibs = [];
35
- for (const addr of addrs) {
36
- const x = await this.get({ addr });
37
- if (x) {
38
- ibGibs.push(x);
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 ifWe(sir, 'derivePoolSecret with same inputs returns same output', async () => {
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 ifWe(sir, 'derivePoolSecret with different master secret returns different output', async () => {
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 ifWe(sir, 'derivePoolSecret with different salt returns different output', async () => {
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 ifWe(sir, 'generateSolution -> generateChallenge -> validateSolution loop works', async () => {
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 ifWe(sir, 'validateSolution fails for mismatched values', async () => {
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 ifWe(sir, 'validateSolution fails for mismatched challenge hashes', async () => {
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 ifWe(sir, 'creates a valid genesis frame and persists it', async () => {
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 ifWe(sir, 'evolves the keystone with a valid proof', async () => {
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 ifWe(sir, 'validates the genesis->signed transition', async () => {
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 ifWe(sir, 'prevents creation of forged frames', async () => {
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 ifWe(sir, 'throws error if signing forbidden verb with restricted pool', async () => {
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 ifWe(sir, 'successfully creates a revocation frame', async () => {
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 ifWe(sir, 'validates the revocation frame', async () => {
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 ifWe(sir, 'consumed the revocation pool (Scorched Earth)', async () => {
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 ifWe(sir, 'authorizes and adds a foreign pool', async () => {
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 ifWe(sir, 'fails if no pool allows "manage" verb', async () => {
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 ifWe(sir, 'fails on ID collision', async () => {
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 ifWe(sir, 'authorizes and adds a foreign pool', async () => {
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 ifWe(sir, 'fails if no pool allows "manage" verb', async () => {
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 ifWe(sir, 'fails on ID collision', async () => {
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 ifWe(sir, 'generates exactly the expected number of solutions', async () => {
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 ifWe(sir, 'verifies the math manually (White-box Crypto Check)', async () => {
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 ifWe(sir, 'verifies FIFO logic (Deterministic Selection)', async () => {
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 ifWe(sir, 'survives a clone/JSON-cycle without corruption', async () => {
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 ifWe(sir, 'ensures data contains no functions or circular refs', async () => {
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