keri-ts 2.2.1 → 2.2.3

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 (115) hide show
  1. package/esm/keri/npm/src/_dnt.polyfills.js +1 -127
  2. package/esm/keri/npm/src/acdc/dhking.js +6 -1
  3. package/esm/keri/npm/src/acdc/salting.js +18 -0
  4. package/esm/keri/npm/src/acdc/scheming.js +31 -18
  5. package/esm/keri/npm/src/acdc/unblinding.js +112 -6
  6. package/esm/keri/npm/src/app/delivery/forwarding.js +9 -0
  7. package/esm/keri/npm/src/app/delivery/respondant.js +21 -0
  8. package/esm/keri/npm/src/app/identity/delegating.js +2 -5
  9. package/esm/keri/npm/src/app/identity/habbing.js +52 -6
  10. package/esm/keri/npm/src/app/identity/keeping.js +38 -1
  11. package/esm/keri/npm/src/app/ipex/group-issuance.js +9 -6
  12. package/esm/keri/npm/src/app/ipex/ipex-building.js +2 -1
  13. package/esm/keri/npm/src/app/ipex/ipex-discloser.js +32 -8
  14. package/esm/keri/npm/src/app/ipex/issuance.js +142 -43
  15. package/esm/keri/npm/src/app/ipex/pending-presentations.js +50 -0
  16. package/esm/keri/npm/src/app/ipex/presenting.js +36 -1
  17. package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +150 -36
  18. package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +37 -9
  19. package/esm/keri/npm/src/app/mailbox/mailboxing.js +9 -3
  20. package/esm/keri/npm/src/app/multisig/counseling.js +76 -3
  21. package/esm/keri/npm/src/app/multisig/event-forwarding.js +27 -0
  22. package/esm/keri/npm/src/app/multisig/group-members.js +22 -0
  23. package/esm/keri/npm/src/app/multisig/grouping.js +120 -1
  24. package/esm/keri/npm/src/app/multisig/proposals.js +1 -1
  25. package/esm/keri/npm/src/app/multisig/replace-member.js +34 -0
  26. package/esm/keri/npm/src/app/runtime/agent-runtime.js +43 -23
  27. package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +59 -25
  28. package/esm/keri/npm/src/app/runtime/operation-owners.js +1 -0
  29. package/esm/keri/npm/src/app/runtime/signer-conditions.js +51 -0
  30. package/esm/keri/npm/src/app/transport/cesr-egress.js +6 -0
  31. package/esm/keri/npm/src/core/errors.js +91 -3
  32. package/esm/keri/npm/src/core/json-schema-meta.js +688 -0
  33. package/esm/keri/npm/src/core/json-schema.js +147 -0
  34. package/esm/keri/npm/src/core/kel/eventing.js +20 -2
  35. package/esm/keri/npm/src/core/scheming.js +27 -26
  36. package/esm/keri/npm/src/db/replay/tel-replay.js +6 -0
  37. package/esm/keri/npm/src/db/store-lock.js +12 -0
  38. package/esm/keri/npm/src/runtime/index.js +9 -7
  39. package/esm/keri/npm/src/runtime/web/output-barrier.js +9 -0
  40. package/esm/keri/npm/src/version.js +2 -2
  41. package/package.json +2 -2
  42. package/types/_dnt.polyfills.d.ts +0 -99
  43. package/types/_dnt.polyfills.d.ts.map +1 -1
  44. package/types/acdc/dhking.d.ts.map +1 -1
  45. package/types/acdc/salting.d.ts +2 -0
  46. package/types/acdc/salting.d.ts.map +1 -1
  47. package/types/acdc/scheming.d.ts +2 -0
  48. package/types/acdc/scheming.d.ts.map +1 -1
  49. package/types/acdc/unblinding.d.ts +16 -5
  50. package/types/acdc/unblinding.d.ts.map +1 -1
  51. package/types/app/delivery/forwarding.d.ts +1 -0
  52. package/types/app/delivery/forwarding.d.ts.map +1 -1
  53. package/types/app/delivery/respondant.d.ts +7 -0
  54. package/types/app/delivery/respondant.d.ts.map +1 -1
  55. package/types/app/identity/delegating.d.ts.map +1 -1
  56. package/types/app/identity/habbing.d.ts +21 -1
  57. package/types/app/identity/habbing.d.ts.map +1 -1
  58. package/types/app/identity/keeping.d.ts +18 -0
  59. package/types/app/identity/keeping.d.ts.map +1 -1
  60. package/types/app/ipex/group-issuance.d.ts.map +1 -1
  61. package/types/app/ipex/ipex-building.d.ts.map +1 -1
  62. package/types/app/ipex/ipex-discloser.d.ts +31 -19
  63. package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
  64. package/types/app/ipex/issuance.d.ts +46 -10
  65. package/types/app/ipex/issuance.d.ts.map +1 -1
  66. package/types/app/ipex/pending-presentations.d.ts +62 -1
  67. package/types/app/ipex/pending-presentations.d.ts.map +1 -1
  68. package/types/app/ipex/presenting.d.ts +39 -4
  69. package/types/app/ipex/presenting.d.ts.map +1 -1
  70. package/types/app/ipex/unblinding-workflow.d.ts +51 -12
  71. package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
  72. package/types/app/mailbox/mailbox-poller.d.ts +11 -2
  73. package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
  74. package/types/app/mailbox/mailboxing.d.ts +4 -1
  75. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  76. package/types/app/multisig/counseling.d.ts +22 -1
  77. package/types/app/multisig/counseling.d.ts.map +1 -1
  78. package/types/app/multisig/event-forwarding.d.ts +6 -0
  79. package/types/app/multisig/event-forwarding.d.ts.map +1 -0
  80. package/types/app/multisig/group-members.d.ts +6 -0
  81. package/types/app/multisig/group-members.d.ts.map +1 -1
  82. package/types/app/multisig/grouping.d.ts +26 -0
  83. package/types/app/multisig/grouping.d.ts.map +1 -1
  84. package/types/app/multisig/proposals.d.ts +3 -2
  85. package/types/app/multisig/proposals.d.ts.map +1 -1
  86. package/types/app/multisig/proposing.d.ts +8 -1
  87. package/types/app/multisig/proposing.d.ts.map +1 -1
  88. package/types/app/multisig/replace-member.d.ts +30 -0
  89. package/types/app/multisig/replace-member.d.ts.map +1 -0
  90. package/types/app/runtime/agent-runtime.d.ts +3 -0
  91. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  92. package/types/app/runtime/ipex-auxiliaries.d.ts +8 -3
  93. package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -1
  94. package/types/app/runtime/operation-owners.d.ts.map +1 -1
  95. package/types/app/runtime/signer-conditions.d.ts +32 -0
  96. package/types/app/runtime/signer-conditions.d.ts.map +1 -0
  97. package/types/app/transport/cesr-egress.d.ts +1 -0
  98. package/types/app/transport/cesr-egress.d.ts.map +1 -1
  99. package/types/core/errors.d.ts +20 -1
  100. package/types/core/errors.d.ts.map +1 -1
  101. package/types/core/json-schema-meta.d.ts +19 -0
  102. package/types/core/json-schema-meta.d.ts.map +1 -0
  103. package/types/core/json-schema.d.ts +29 -0
  104. package/types/core/json-schema.d.ts.map +1 -0
  105. package/types/core/scheming.d.ts +1 -0
  106. package/types/core/scheming.d.ts.map +1 -1
  107. package/types/db/replay/tel-replay.d.ts +1 -0
  108. package/types/db/replay/tel-replay.d.ts.map +1 -1
  109. package/types/db/store-lock.d.ts +2 -0
  110. package/types/db/store-lock.d.ts.map +1 -1
  111. package/types/runtime/index.d.ts +11 -9
  112. package/types/runtime/index.d.ts.map +1 -1
  113. package/types/runtime/web/output-barrier.d.ts +1 -0
  114. package/types/runtime/web/output-barrier.d.ts.map +1 -1
  115. package/types/version.d.ts +2 -2
@@ -1,130 +1,3 @@
1
- /**
2
- * Based on [import-meta-ponyfill](https://github.com/gaubee/import-meta-ponyfill),
3
- * but instead of using npm to install additional dependencies,
4
- * this approach manually consolidates cjs/mjs/d.ts into a single file.
5
- *
6
- * Note that this code might be imported multiple times
7
- * (for example, both dnt.test.polyfills.ts and dnt.polyfills.ts contain this code;
8
- * or Node.js might dynamically clear the cache and then force a require).
9
- * Therefore, it's important to avoid redundant writes to global objects.
10
- * Additionally, consider that commonjs is used alongside esm,
11
- * so the two ponyfill functions are stored independently in two separate global objects.
12
- */
13
- //@ts-ignore
14
- import { createRequire } from "node:module";
15
- //@ts-ignore
16
- import { fileURLToPath, pathToFileURL } from "node:url";
17
- //@ts-ignore
18
- import { dirname } from "node:path";
19
- const defineGlobalPonyfill = (symbolFor, fn) => {
20
- if (!Reflect.has(globalThis, Symbol.for(symbolFor))) {
21
- Object.defineProperty(globalThis, Symbol.for(symbolFor), {
22
- configurable: true,
23
- get() {
24
- return fn;
25
- },
26
- });
27
- }
28
- };
29
- export let import_meta_ponyfill_commonjs = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-commonjs")) ??
30
- (() => {
31
- const moduleImportMetaWM = new WeakMap();
32
- return (require, module) => {
33
- let importMetaCache = moduleImportMetaWM.get(module);
34
- if (importMetaCache == null) {
35
- const importMeta = Object.assign(Object.create(null), {
36
- url: pathToFileURL(module.filename).href,
37
- main: require.main == module,
38
- resolve: (specifier, parentURL = importMeta.url) => {
39
- return pathToFileURL((importMeta.url === parentURL
40
- ? require
41
- : createRequire(parentURL))
42
- .resolve(specifier)).href;
43
- },
44
- filename: module.filename,
45
- dirname: module.path,
46
- });
47
- moduleImportMetaWM.set(module, importMeta);
48
- importMetaCache = importMeta;
49
- }
50
- return importMetaCache;
51
- };
52
- })());
53
- defineGlobalPonyfill("import-meta-ponyfill-commonjs", import_meta_ponyfill_commonjs);
54
- export let import_meta_ponyfill_esmodule = (Reflect.get(globalThis, Symbol.for("import-meta-ponyfill-esmodule")) ??
55
- ((importMeta) => {
56
- const resolveFunStr = String(importMeta.resolve);
57
- const shimWs = new WeakSet();
58
- //@ts-ignore
59
- const mainUrl = ("file:///" + process.argv[1].replace(/\\/g, "/"))
60
- .replace(/\/{3,}/, "///");
61
- const commonShim = (importMeta) => {
62
- if (typeof importMeta.main !== "boolean") {
63
- importMeta.main = importMeta.url === mainUrl;
64
- }
65
- if (typeof importMeta.filename !== "string") {
66
- importMeta.filename = fileURLToPath(importMeta.url);
67
- importMeta.dirname = dirname(importMeta.filename);
68
- }
69
- };
70
- if (
71
- // v16.2.0+, v14.18.0+: Add support for WHATWG URL object to parentURL parameter.
72
- resolveFunStr === "undefined" ||
73
- // v20.0.0+, v18.19.0+"" This API now returns a string synchronously instead of a Promise.
74
- resolveFunStr.startsWith("async")
75
- // enable by --experimental-import-meta-resolve flag
76
- ) {
77
- import_meta_ponyfill_esmodule = (importMeta) => {
78
- if (!shimWs.has(importMeta)) {
79
- shimWs.add(importMeta);
80
- const importMetaUrlRequire = {
81
- url: importMeta.url,
82
- require: createRequire(importMeta.url),
83
- };
84
- importMeta.resolve = function resolve(specifier, parentURL = importMeta.url) {
85
- return pathToFileURL((importMetaUrlRequire.url === parentURL
86
- ? importMetaUrlRequire.require
87
- : createRequire(parentURL)).resolve(specifier)).href;
88
- };
89
- commonShim(importMeta);
90
- }
91
- return importMeta;
92
- };
93
- }
94
- else {
95
- /// native support
96
- import_meta_ponyfill_esmodule = (importMeta) => {
97
- if (!shimWs.has(importMeta)) {
98
- shimWs.add(importMeta);
99
- commonShim(importMeta);
100
- }
101
- return importMeta;
102
- };
103
- }
104
- return import_meta_ponyfill_esmodule(importMeta);
105
- }));
106
- defineGlobalPonyfill("import-meta-ponyfill-esmodule", import_meta_ponyfill_esmodule);
107
- export let import_meta_ponyfill = ((...args) => {
108
- const _MODULE = (() => {
109
- if (typeof require === "function" && typeof module === "object") {
110
- return "commonjs";
111
- }
112
- else {
113
- // eval("typeof import.meta");
114
- return "esmodule";
115
- }
116
- })();
117
- if (_MODULE === "commonjs") {
118
- //@ts-ignore
119
- import_meta_ponyfill = (r, m) => import_meta_ponyfill_commonjs(r, m);
120
- }
121
- else {
122
- //@ts-ignore
123
- import_meta_ponyfill = (im) => import_meta_ponyfill_esmodule(im);
124
- }
125
- //@ts-ignore
126
- return import_meta_ponyfill(...args);
127
- });
128
1
  function findLastIndex(self, callbackfn, that) {
129
2
  const boundFunc = that === undefined ? callbackfn : callbackfn.bind(that);
130
3
  let index = self.length - 1;
@@ -175,3 +48,4 @@ if (!Object.hasOwn) {
175
48
  writable: true,
176
49
  });
177
50
  }
51
+ export {};
@@ -639,6 +639,9 @@ export class Dhke {
639
639
  }
640
640
  if (found.size !== op.registries.length)
641
641
  throw new ValidationError("Approved registry scope is absent");
642
+ // The Grant is already accepted when the other party of this Habery finished first: nothing is left to accept.
643
+ if (this.exc.complete(op.grant))
644
+ return { kind: "accept", said: op.grant };
642
645
  for (const { nest, opening } of openings) {
643
646
  nest.attachments.push(parseAttachmentGroup(publicOpeningAttachment(opening), Vrsn_2_0, "txt").group);
644
647
  }
@@ -786,7 +789,9 @@ export class Dhke {
786
789
  pending(op) {
787
790
  this.hab.mgr.requireRegistryRootAccess();
788
791
  this.custody(op);
789
- const input = this.exc.retainedInput(op.grant, { pending: true });
792
+ // A Habery that holds both parties may have accepted the Grant through the other party's exchange already.
793
+ const input = this.exc.retainedInput(op.grant, { pending: true })
794
+ ?? (this.exc.complete(op.grant) ? this.exc.retainedInput(op.grant) : undefined);
790
795
  if (op.abandoned || !input || !bytesEqual(input.serder.raw, op.raw)) {
791
796
  throw new ValidationError("DH operation is unavailable, terminated or expired");
792
797
  }
@@ -31,9 +31,27 @@ import { makeNowIso8601 } from "../time/mod.js";
31
31
  import { validateRip, validateUpdate, verifyRegistryAnchor, vetBlind } from "./regeventing.js";
32
32
  /** A stored registry-root copy that can never be retained as received. */
33
33
  export class RootRefused extends ValidationError {
34
+ constructor() {
35
+ super(...arguments);
36
+ Object.defineProperty(this, "name", {
37
+ enumerable: true,
38
+ configurable: true,
39
+ writable: true,
40
+ value: "RootRefused"
41
+ });
42
+ }
34
43
  }
35
44
  /** A stored registry-root copy whose root cannot yet be verified locally. */
36
45
  export class RootUnverified extends ValidationError {
46
+ constructor() {
47
+ super(...arguments);
48
+ Object.defineProperty(this, "name", {
49
+ enumerable: true,
50
+ configurable: true,
51
+ writable: true,
52
+ value: "RootUnverified"
53
+ });
54
+ }
37
55
  }
38
56
  /** A registry-root copy whose authorization needs KEL, TEL or group state not yet local. */
39
57
  export class RootDeferred extends RootUnverified {
@@ -1,7 +1,7 @@
1
1
  /** Authenticated local ACDC schema validation and static resource lookups; no network resolution. */
2
2
  import { Diger, dumps, ParserError } from "cesr-ts";
3
- import Ajv2020 from "ajv/dist/2020.js";
4
3
  import { ValidationError } from "../core/errors.js";
4
+ import { checkSchemaDocument, prepareSchemas } from "../core/json-schema.js";
5
5
  import { Schemer } from "../core/scheming.js";
6
6
  /** Local issuance caller must supply this dependency before a disclosure can be derived. */
7
7
  export class MissingSchemaError extends ValidationError {
@@ -13,15 +13,29 @@ export class MissingSchemaError extends ValidationError {
13
13
  writable: true,
14
14
  value: schema
15
15
  });
16
+ Object.defineProperty(this, "name", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: "MissingSchemaError"
21
+ });
16
22
  }
17
23
  }
18
24
  /** Invalid schema or credential content cannot be staged as an issuance commitment. */
19
25
  export class FailedSchemaValidationError extends ValidationError {
26
+ constructor() {
27
+ super(...arguments);
28
+ Object.defineProperty(this, "name", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: "FailedSchemaValidationError"
33
+ });
34
+ }
20
35
  }
21
36
  /** Only explicit input/profile refusals are caught; arbitrary failures are not recategorized. */
22
37
  class InvalidSchema extends Error {
23
38
  }
24
- const ValidatorCtor = Ajv2020;
25
39
  const dialect = "https://json-schema.org/draft/2020-12/schema";
26
40
  const dynamic = ["$dynamicAnchor", "$dynamicRef", "$recursiveAnchor", "$recursiveRef"];
27
41
  const singles = [
@@ -300,7 +314,7 @@ export function schemaCompilationCount() {
300
314
  return compilations;
301
315
  }
302
316
  function isCompiled(value) {
303
- return "validate" in value;
317
+ return "prepared" in value;
304
318
  }
305
319
  /** Compile an admitted closure once; regular-expression and duplicate-id refusals are normal invalid outcomes. */
306
320
  function compileClosure(resources, root) {
@@ -334,31 +348,31 @@ function compileClosure(resources, root) {
334
348
  delete result[key];
335
349
  return result;
336
350
  };
337
- const validator = new ValidatorCtor({ strict: false, validateFormats: false });
338
351
  for (const schema of scopes.keys()) {
339
- if (!validator.validateSchema(schema))
352
+ try {
353
+ checkSchemaDocument(schema, "2020-12");
354
+ }
355
+ catch {
340
356
  throw new InvalidSchema("Invalid Draft 2020-12 schema");
357
+ }
341
358
  }
342
359
  const copies = roots.map(copy);
343
- let validate;
360
+ const head = copies.find((schema) => schema.$id === uri(root.said));
361
+ if (!head)
362
+ throw new Error("Admitted schema was not registered");
363
+ let prepared;
344
364
  try {
345
- for (const schema of copies)
346
- validator.addSchema(schema);
347
- validate = validator.getSchema(uri(root.said));
365
+ prepared = prepareSchemas([head, ...copies.filter((schema) => schema !== head)], "2020-12");
348
366
  }
349
367
  catch (error) {
368
+ // A regular expression the engine refuses is a normal invalid outcome, reported with its own message.
350
369
  if (error instanceof SyntaxError && error.message.includes("regular expression")) {
351
370
  return { kind: "invalid", subject: "schema", reason: error.message };
352
371
  }
353
- if (error instanceof Error && /^reference "urn:said:[^"]+" resolves to more than one schema$/.test(error.message)) {
354
- return { kind: "invalid", subject: "schema", reason: error.message };
355
- }
356
372
  throw error;
357
373
  }
358
- if (!validate)
359
- throw new Error("Admitted schema was not registered");
360
374
  compilations++;
361
- return { validator, validate };
375
+ return { prepared };
362
376
  }
363
377
  /** Local static schema closure plus a synchronous Draft 2020-12 validator; legacy Schemer behavior is unchanged. */
364
378
  export function validateSchema(acdc, db, override) {
@@ -388,9 +402,8 @@ export function validateSchema(acdc, db, override) {
388
402
  store.set(root.said, compiled);
389
403
  }
390
404
  }
391
- return compiled.validate(acdc.sad)
392
- ? { kind: "valid" }
393
- : { kind: "invalid", subject: "credential", reason: compiled.validator.errorsText(compiled.validate.errors) };
405
+ const outcome = compiled.prepared.validate(acdc.sad);
406
+ return outcome.valid ? { kind: "valid" } : { kind: "invalid", subject: "credential", reason: outcome.errors };
394
407
  }
395
408
  catch (error) {
396
409
  if (error instanceof InvalidSchema)
@@ -18,7 +18,7 @@ import { auxiliaryQueryWire, envelopeFromFrame, restoreAuxiliaryQuery, } from ".
18
18
  import { abortable } from "../app/runtime/abortable.js";
19
19
  import { Deck } from "../core/deck.js";
20
20
  import { TransIdxSigGroup } from "../core/dispatch.js";
21
- import { ValidationError } from "../core/errors.js";
21
+ import { PresentationClosedError, ValidationError } from "../core/errors.js";
22
22
  import { verifyExchangeProofs, verifyQueryAttachments } from "../core/exchange/exchange-proofing.js";
23
23
  import { Kever } from "../core/kel/kever.js";
24
24
  import { query, reply } from "../core/protocol-eventing.js";
@@ -55,6 +55,38 @@ function ordinal(event) {
55
55
  * sign exact proposals under that approval. Ordinary thresholds elect delivery
56
56
  * from the completed aggregate; neither contribution nor delivery requires a root.
57
57
  */
58
+ /** A query's source event is not known here yet: the issuer's KEL has not reached the anchoring event. */
59
+ class SourceNotYetKnown extends ValidationError {
60
+ constructor(issuer, sn) {
61
+ super(`Query event has an unverified source: the anchoring event ${sn} of ${issuer} is not known`);
62
+ Object.defineProperty(this, "issuer", {
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true,
66
+ value: issuer
67
+ });
68
+ Object.defineProperty(this, "sn", {
69
+ enumerable: true,
70
+ configurable: true,
71
+ writable: true,
72
+ value: sn
73
+ });
74
+ Object.defineProperty(this, "name", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: "SourceNotYetKnown"
79
+ });
80
+ }
81
+ }
82
+ /** Milliseconds between requests for the issuer's KEL while a query waits for its anchoring event. */
83
+ const KEL_ASK_MS = 5000;
84
+ /**
85
+ * Auxiliary query processor for `/ipex/unblind`: the discloser answers an authenticated query for a live Grant it
86
+ * sent, and the disclosee, once it approved a Grant, sends the query and receives the openings. It keeps its
87
+ * operations in the registry database so a restart resumes them, and queues outgoing queries and answers in
88
+ * `outgoing` for the transport owner. See the module comment for the consent rules.
89
+ */
58
90
  export class Unblind {
59
91
  /** Bind the exact Grant verifier's store; the state vocabulary is chosen per approved operation. */
60
92
  constructor({ hab, exc, registry, rvy = hab.rvy, kram = true }) {
@@ -83,6 +115,17 @@ export class Unblind {
83
115
  writable: true,
84
116
  value: false
85
117
  });
118
+ /**
119
+ * Queries held until the issuer's anchoring event is known here, by transaction. The wire is already in `heldu`; this
120
+ * only says which are still waiting and when the issuer's KEL was last asked for. Memory only: a reopened owner
121
+ * processes `heldu` again and the query waits again.
122
+ */
123
+ Object.defineProperty(this, "waiting", {
124
+ enumerable: true,
125
+ configurable: true,
126
+ writable: true,
127
+ value: new Map()
128
+ });
86
129
  Object.defineProperty(this, "hab", {
87
130
  enumerable: true,
88
131
  configurable: true,
@@ -169,6 +212,10 @@ export class Unblind {
169
212
  return xid;
170
213
  }
171
214
  const context = this.exc.presentationContext(grant, { xid, sender, recipient });
215
+ if (!context && this.hab.db.erpy.get([grant])) {
216
+ throw new PresentationClosedError("Presentation is closed: its terminal response (Admit or Spurn) ended the discloser's authority to answer "
217
+ + "queries about it, so an Unblind must be requested before the response is made");
218
+ }
172
219
  if (!context)
173
220
  throw new ValidationError("Presentation is unavailable, terminated or expired");
174
221
  this.operations.set(xid, {
@@ -364,7 +411,19 @@ export class Unblind {
364
411
  this.groupMessage(op, "query", serder, groups, extra);
365
412
  return { kind: "accept" };
366
413
  }
367
- this.receiveEvents(selected, envelope.nests);
414
+ try {
415
+ this.receiveEvents(selected, envelope.nests);
416
+ }
417
+ catch (error) {
418
+ if (!(error instanceof SourceNotYetKnown))
419
+ throw error;
420
+ // Not invalid, only early: keep the query durably, ask for the issuer's KEL, and take it up when it arrives.
421
+ this.hab.db.heldu.pin([this.hab.pre, op.xid], auxiliaryQueryWire(envelope, this.publicEvents(envelope.nests)));
422
+ this.waiting.set(op.xid, { issuer: error.issuer, sn: error.sn, askedAt: 0 });
423
+ this.askForKel(op.xid);
424
+ return { kind: "accept" };
425
+ }
426
+ this.waiting.delete(op.xid);
368
427
  this.live(op);
369
428
  op.requested = selected;
370
429
  op.requestedByPeer = true;
@@ -386,6 +445,42 @@ export class Unblind {
386
445
  * processed again as when first accepted, and one that no longer holds (the Grant ended, its evidence changed)
387
446
  * is dropped. Done once per owner, before the first look at what is owed.
388
447
  */
448
+ /** Ask, through the ordinary missing-state query cue, for the issuer's events up to the anchoring one; at most every few seconds. */
449
+ askForKel(xid) {
450
+ const held = this.waiting.get(xid), now = Date.now();
451
+ if (!held || now - held.askedAt < KEL_ASK_MS)
452
+ return;
453
+ held.askedAt = now;
454
+ this.rvy.cues.push({ kin: "query", pre: held.issuer, route: "logs", q: { s: held.sn.toString(16) } });
455
+ }
456
+ /** Take up the held queries whose anchoring event has since arrived; the rest ask for it again in due time. */
457
+ retryWaiting() {
458
+ for (const [xid, held] of [...this.waiting]) {
459
+ if (this.hab.db.kels.getLast(held.issuer, held.sn) === null) {
460
+ this.askForKel(xid);
461
+ continue;
462
+ }
463
+ const wire = this.hab.db.heldu.get([this.hab.pre, xid]);
464
+ this.waiting.delete(xid);
465
+ if (!wire)
466
+ continue;
467
+ let decision;
468
+ try {
469
+ decision = this.processQuery(restoreAuxiliaryQuery(wire));
470
+ }
471
+ catch (error) {
472
+ if (!(error instanceof ValidationError))
473
+ throw error;
474
+ }
475
+ if (decision?.kind !== "accept")
476
+ this.hab.db.heldu.rem([this.hab.pre, xid]);
477
+ }
478
+ }
479
+ /** Why the query of `xid` has not been answered, when it is waiting for the issuer's anchoring event. */
480
+ missingSource(xid) {
481
+ const held = this.waiting.get(xid);
482
+ return held ? `the anchoring event ${held.sn} of the issuer ${held.issuer} was never received` : null;
483
+ }
389
484
  restoreHeld() {
390
485
  if (this.restored)
391
486
  return;
@@ -695,10 +790,11 @@ export class Unblind {
695
790
  */
696
791
  awaitingReply() {
697
792
  this.restoreHeld();
793
+ this.retryWaiting();
698
794
  if (this.group)
699
795
  return [];
700
796
  return [...this.operations.values()]
701
- .filter((op) => op.sender === this.hab.pre && op.requestedByPeer && !op.abandoned)
797
+ .filter((op) => op.sender === this.hab.pre && (op.requestedByPeer || this.waiting.has(op.xid)) && !op.abandoned)
702
798
  .map(({ xid, grant }) => ({ xid, grant }));
703
799
  }
704
800
  operation(xid) {
@@ -910,9 +1006,16 @@ export class Unblind {
910
1006
  ...envelope.ssts.map((seal) => ({ issuer: seal.i.qb64, number: seal.s, diger: seal.d })),
911
1007
  ];
912
1008
  for (const source of sources) {
913
- if (source.issuer !== rip.issuer || !verifyRegistryAnchor(checked.event, { db: this.hab.db, ...source })) {
1009
+ if (source.issuer !== rip.issuer)
914
1010
  throw new ValidationError("Query event has an unverified source");
1011
+ if (verifyRegistryAnchor(checked.event, { db: this.hab.db, ...source }))
1012
+ continue;
1013
+ const sn = Number(source.number.num);
1014
+ // An anchoring event the issuer's KEL here has not reached is early; one that is known and does not seal is wrong.
1015
+ if (Number.isSafeInteger(sn) && this.hab.db.kels.getLast(source.issuer, sn) === null) {
1016
+ throw new SourceNotYetKnown(source.issuer, sn);
915
1017
  }
1018
+ throw new ValidationError("Query event has an unverified source");
916
1019
  }
917
1020
  const accepted = this.registry.store.seqEvent(regid, first + i);
918
1021
  if (accepted && !bytesEqual(accepted.raw, event.raw)) {
@@ -995,8 +1098,11 @@ export class Unblind {
995
1098
  }
996
1099
  }
997
1100
  }
998
- if (!opening)
999
- throw new ValidationError("Required exact event opening is unavailable");
1101
+ if (!opening) {
1102
+ throw new ValidationError("Required exact event opening is unavailable: this identifier holds no public or carried opening and cannot "
1103
+ + "derive one from its registry root (an update blinded with a random blinder, as `Registry.blind` does "
1104
+ + "without custody, can be opened only by its author or from an opening its author published)");
1105
+ }
1000
1106
  this.checkOpening(op, event, opening, nests);
1001
1107
  return opening;
1002
1108
  }
@@ -22,6 +22,15 @@ import { introduce } from "./introducing.js";
22
22
  * its stream, so sending would lose more than this one message.
23
23
  */
24
24
  export class PeerVersionError extends ValidationError {
25
+ constructor() {
26
+ super(...arguments);
27
+ Object.defineProperty(this, "name", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: "PeerVersionError"
32
+ });
33
+ }
25
34
  }
26
35
  /**
27
36
  * Whether an AID's own accepted KEL ends in a v1 event, which is the only evidence of a v1 reader.
@@ -1,3 +1,4 @@
1
+ import { isLocalGroupHab } from "../multisig/group-members.js";
1
2
  /**
2
3
  * Shared host-side responder for indirect and witness runtimes.
3
4
  *
@@ -55,6 +56,10 @@ export class Respondant {
55
56
  return false;
56
57
  const dest = recipientForCue(cue), topic = topicForCue(cue);
57
58
  const src = senderHabForCue(this.hby, cue, hint);
59
+ if (this.nothingToSend(cue, dest, topic, src)) {
60
+ cues.pull();
61
+ return true;
62
+ }
58
63
  if (!dest || !topic || !src)
59
64
  return false;
60
65
  const msgs = [...hab.processCuesIter([cue])];
@@ -62,6 +67,20 @@ export class Respondant {
62
67
  cues.pull();
63
68
  return true;
64
69
  }
70
+ /**
71
+ * Whether a receipt cue has nothing to send to. A receipt goes to the controller of the event it receipts, so it
72
+ * has nowhere to go when that controller is a group this runtime is a member of (the group's own event: its
73
+ * members are the Counselor's business) or when the controller has no end role to deliver to. Deciding that here
74
+ * keeps an attempt that could only fail from being made, and logged, for every event of a group.
75
+ */
76
+ nothingToSend(cue, dest, topic, src) {
77
+ if ((cue.kin !== "receipt" && cue.kin !== "witness") || !dest || !topic)
78
+ return false;
79
+ const subject = this.hby.habs.get(dest);
80
+ if (subject && isLocalGroupHab(this.hby, subject))
81
+ return true;
82
+ return !!src && !this.postman.canReach(src.pre, dest, topic);
83
+ }
65
84
  /**
66
85
  * Admit already produced witness messages without signing them again.
67
86
  *
@@ -72,6 +91,8 @@ export class Respondant {
72
91
  enqueueMsgs(hab, cue, msgs) {
73
92
  const dest = recipientForCue(cue), topic = topicForCue(cue);
74
93
  const src = senderHabForCue(this.hby, cue, hab);
94
+ if (this.nothingToSend(cue, dest, topic, src))
95
+ return;
75
96
  if (!dest || !topic || !src)
76
97
  return;
77
98
  this.postman.sendAll(msgs.map((msg) => ({ src: src.pre, dest, topic, msg, replies: true })));
@@ -18,6 +18,7 @@ import { DELEGATE_MAILBOX_TOPIC, REPLAY_MAILBOX_TOPIC } from "../../core/mailbox
18
18
  import { Schemes } from "../../core/schemes.js";
19
19
  import { dgKey } from "../../db/core/keys.js";
20
20
  import { createExchangeMessage } from "../exchanges/exchange-messaging.js";
21
+ import { isLocalGroupHab, localGroupMember } from "../multisig/group-members.js";
21
22
  import { sendWitnessMessage } from "../witness/receipt-client.js";
22
23
  import { eventPayloadMessage, eventReplayMessage } from "./habbing.js";
23
24
  export const DELEGATE_REQUEST_ROUTE = "/delegate/request";
@@ -63,10 +64,6 @@ function localHabByPre(hby, pre, context) {
63
64
  }
64
65
  return hab;
65
66
  }
66
- function localGroupMember(hby, groupPre) {
67
- const record = hby.db.getHab(groupPre);
68
- return record?.mid ? hby.habs.get(record.mid) ?? null : null;
69
- }
70
67
  function workflowSaid(serder) {
71
68
  if (!serder.said) {
72
69
  throw new ValidationError("Delegated workflow event is missing a SAID.");
@@ -720,7 +717,7 @@ export class Anchorer {
720
717
  if (pinned) {
721
718
  return localHabByPre(this.hby, pinned, `Delegation workflow ${workflowId(serder)}`);
722
719
  }
723
- return localGroupMember(this.hby, hab.pre) ?? this.communicationHab(serder, hab);
720
+ return isLocalGroupHab(this.hby, hab) ? localGroupMember(this.hby, hab.pre) : this.communicationHab(serder, hab);
724
721
  }
725
722
  /**
726
723
  * Send the delegation chain before the approving event, awaiting actual Poster or witness transmission outcomes.