keri-ts 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/keri/npm/src/_dnt.polyfills.js +1 -127
- package/esm/keri/npm/src/acdc/custody.js +23 -0
- package/esm/keri/npm/src/acdc/dhking.js +6 -1
- package/esm/keri/npm/src/acdc/salting.js +18 -0
- package/esm/keri/npm/src/acdc/scheming.js +31 -18
- package/esm/keri/npm/src/acdc/unblinding.js +112 -6
- package/esm/keri/npm/src/app/delivery/forwarding.js +9 -0
- package/esm/keri/npm/src/app/delivery/respondant.js +21 -0
- package/esm/keri/npm/src/app/identity/habbing.js +4 -1
- package/esm/keri/npm/src/app/identity/keeping.js +38 -1
- package/esm/keri/npm/src/app/ipex/group-ipex.js +14 -0
- package/esm/keri/npm/src/app/ipex/group-issuance.js +15 -9
- package/esm/keri/npm/src/app/ipex/ipex-building.js +2 -1
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +32 -8
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +16 -8
- package/esm/keri/npm/src/app/ipex/ipex-node-authentication.js +42 -18
- package/esm/keri/npm/src/app/ipex/issuance.js +136 -40
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +50 -0
- package/esm/keri/npm/src/app/ipex/presenting.js +44 -2
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +141 -30
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +37 -9
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +9 -3
- package/esm/keri/npm/src/app/multisig/counseling.js +83 -4
- package/esm/keri/npm/src/app/multisig/event-forwarding.js +27 -0
- package/esm/keri/npm/src/app/multisig/grouping.js +120 -1
- package/esm/keri/npm/src/app/multisig/multisig-messaging.js +33 -6
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +5 -9
- package/esm/keri/npm/src/app/multisig/proposals.js +1 -1
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +43 -23
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +59 -25
- package/esm/keri/npm/src/app/runtime/operation-owners.js +1 -0
- package/esm/keri/npm/src/app/runtime/signer-conditions.js +51 -0
- package/esm/keri/npm/src/app/transport/cesr-egress.js +6 -0
- package/esm/keri/npm/src/cli/credentials/ipex.js +7 -1
- package/esm/keri/npm/src/cli/messaging/group-join.js +7 -3
- package/esm/keri/npm/src/cli/messaging/multisig.js +37 -11
- package/esm/keri/npm/src/core/errors.js +91 -3
- package/esm/keri/npm/src/core/json-schema-meta.js +688 -0
- package/esm/keri/npm/src/core/json-schema.js +147 -0
- package/esm/keri/npm/src/core/kel/eventing.js +20 -2
- package/esm/keri/npm/src/core/scheming.js +27 -26
- package/esm/keri/npm/src/db/replay/tel-replay.js +6 -0
- package/esm/keri/npm/src/db/store-lock.js +12 -0
- package/esm/keri/npm/src/runtime/index.js +9 -8
- package/esm/keri/npm/src/runtime/web/output-barrier.js +9 -0
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +2 -2
- package/types/_dnt.polyfills.d.ts +0 -99
- package/types/_dnt.polyfills.d.ts.map +1 -1
- package/types/acdc/custody.d.ts +7 -0
- package/types/acdc/custody.d.ts.map +1 -1
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/salting.d.ts +2 -0
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/scheming.d.ts +2 -0
- package/types/acdc/scheming.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +16 -5
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +1 -0
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/delivery/respondant.d.ts +7 -0
- package/types/app/delivery/respondant.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +10 -1
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +18 -0
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/ipex/group-ipex.d.ts +7 -0
- package/types/app/ipex/group-ipex.d.ts.map +1 -1
- package/types/app/ipex/group-issuance.d.ts +1 -1
- package/types/app/ipex/group-issuance.d.ts.map +1 -1
- package/types/app/ipex/ipex-building.d.ts.map +1 -1
- package/types/app/ipex/ipex-discloser.d.ts +31 -19
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/ipex-node-authentication.d.ts +9 -1
- package/types/app/ipex/ipex-node-authentication.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +41 -8
- package/types/app/ipex/issuance.d.ts.map +1 -1
- package/types/app/ipex/pending-presentations.d.ts +62 -1
- package/types/app/ipex/pending-presentations.d.ts.map +1 -1
- package/types/app/ipex/presenting.d.ts +41 -4
- package/types/app/ipex/presenting.d.ts.map +1 -1
- package/types/app/ipex/unblinding-workflow.d.ts +46 -10
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
- package/types/app/mailbox/mailbox-poller.d.ts +11 -2
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +4 -1
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +22 -1
- package/types/app/multisig/counseling.d.ts.map +1 -1
- package/types/app/multisig/event-forwarding.d.ts +6 -0
- package/types/app/multisig/event-forwarding.d.ts.map +1 -0
- package/types/app/multisig/grouping.d.ts +26 -0
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-messaging.d.ts +23 -6
- package/types/app/multisig/multisig-messaging.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/multisig/proposals.d.ts +3 -2
- package/types/app/multisig/proposals.d.ts.map +1 -1
- package/types/app/multisig/proposing.d.ts +8 -1
- package/types/app/multisig/proposing.d.ts.map +1 -1
- package/types/app/runtime/agent-runtime.d.ts +3 -0
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/ipex-auxiliaries.d.ts +8 -3
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -1
- package/types/app/runtime/operation-owners.d.ts.map +1 -1
- package/types/app/runtime/signer-conditions.d.ts +32 -0
- package/types/app/runtime/signer-conditions.d.ts.map +1 -0
- package/types/app/transport/cesr-egress.d.ts +1 -0
- package/types/app/transport/cesr-egress.d.ts.map +1 -1
- package/types/cli/credentials/ipex.d.ts.map +1 -1
- package/types/cli/messaging/group-join.d.ts +4 -1
- package/types/cli/messaging/group-join.d.ts.map +1 -1
- package/types/cli/messaging/multisig.d.ts +1 -0
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/core/errors.d.ts +20 -1
- package/types/core/errors.d.ts.map +1 -1
- package/types/core/json-schema-meta.d.ts +19 -0
- package/types/core/json-schema-meta.d.ts.map +1 -0
- package/types/core/json-schema.d.ts +29 -0
- package/types/core/json-schema.d.ts.map +1 -0
- package/types/core/scheming.d.ts +1 -0
- package/types/core/scheming.d.ts.map +1 -1
- package/types/db/replay/tel-replay.d.ts +1 -0
- package/types/db/replay/tel-replay.d.ts.map +1 -1
- package/types/db/store-lock.d.ts +2 -0
- package/types/db/store-lock.d.ts.map +1 -1
- package/types/runtime/index.d.ts +12 -11
- package/types/runtime/index.d.ts.map +1 -1
- package/types/runtime/web/output-barrier.d.ts +1 -0
- package/types/runtime/web/output-barrier.d.ts.map +1 -1
- 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 {};
|
|
@@ -135,6 +135,29 @@ export class RegistryCustody {
|
|
|
135
135
|
this.options.store.pin(event.said, value);
|
|
136
136
|
return value;
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* The opening of the accepted update that issued `credential`, for a holder of the registry root that did not
|
|
140
|
+
* author the update and so retained no coordinates (a group member that consented to it). The opening is derived
|
|
141
|
+
* from the credential and the `issued` state and must open the update's own commitment; it is then retained like
|
|
142
|
+
* an authored one. Only an unbound issuance update is derived here.
|
|
143
|
+
*/
|
|
144
|
+
issuedOpening(event, credential) {
|
|
145
|
+
if (this.options.store.get(event.said))
|
|
146
|
+
return this.derivedOpening(event);
|
|
147
|
+
if (event.ilk !== "bup" || !event.verify())
|
|
148
|
+
throw new ValidationError("Exact accepted blinded update is required");
|
|
149
|
+
const ordinal = Number(BigInt(`0x${event.sad.n}`));
|
|
150
|
+
const stored = event.regid ? this.options.registry.seqEvent(event.regid, ordinal) : null;
|
|
151
|
+
if (!Number.isSafeInteger(ordinal) || !stored || !bytesEqual(stored.raw, event.raw)) {
|
|
152
|
+
throw new ValidationError("Exact accepted registry event is required");
|
|
153
|
+
}
|
|
154
|
+
const opening = this.derive(event.regid, ordinal, credential, "issued");
|
|
155
|
+
if (!vetBlind(opening, event.sad.b)) {
|
|
156
|
+
throw new ValidationError("The accepted update is not this credential's issuance");
|
|
157
|
+
}
|
|
158
|
+
this.retainDerivedUpdate(event, opening);
|
|
159
|
+
return opening;
|
|
160
|
+
}
|
|
138
161
|
/** Reproduce only the exact accepted event, never today's head or a later credential's coordinates. */
|
|
139
162
|
derivedOpening(event) {
|
|
140
163
|
if (event.ilk !== "bup" || !event.verify())
|
|
@@ -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
|
-
|
|
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 "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
392
|
-
|
|
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
|
-
|
|
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
|
|
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 })));
|
|
@@ -1878,7 +1878,7 @@ export function branToSeedAeid(bran) {
|
|
|
1878
1878
|
* durable DB state rather than depending on process-local creation history.
|
|
1879
1879
|
*/
|
|
1880
1880
|
export function* openHabery(args) {
|
|
1881
|
-
const { name, base = "", temp = false, headDirPath, compat = false, readonly = false, cf: providedCf, createConfig, skipConfig = false, skipSignator = false, opener, stores, bran, seed, aeid, salt, algo, } = args;
|
|
1881
|
+
const { name, base = "", temp = false, headDirPath, compat = false, readonly = false, cf: providedCf, createConfig, skipConfig = false, skipSignator = false, opener, stores, bran, seed, aeid, salt, algo, heldSigners, } = args;
|
|
1882
1882
|
let db;
|
|
1883
1883
|
let ks;
|
|
1884
1884
|
let cf;
|
|
@@ -1921,6 +1921,9 @@ export function* openHabery(args) {
|
|
|
1921
1921
|
algo,
|
|
1922
1922
|
salt: normalizeSaltQb64(salt),
|
|
1923
1923
|
});
|
|
1924
|
+
// Before the Habery exists, so before any runtime built on it can sign. A bad entry fails the open.
|
|
1925
|
+
if (heldSigners)
|
|
1926
|
+
mgr.hold(heldSigners);
|
|
1924
1927
|
const hby = new Habery(name, base, temp, headDirPath, compat, readonly, db, ks, mgr, cf, skipSignator, opener, !!stores);
|
|
1925
1928
|
delivered = true;
|
|
1926
1929
|
return hby;
|
|
@@ -193,6 +193,25 @@ function resolveSuiteCodes(codes, count, code, { label, allowZero = false, }) {
|
|
|
193
193
|
function emptyLot(dt = "") {
|
|
194
194
|
return new PubLot({ pubs: [], ridx: 0, kidx: 0, dt });
|
|
195
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* No private key for `pub`: neither held (`Manager.hold`) nor stored in the Keeper. A custodial host releases
|
|
198
|
+
* its signers on purpose, so a background loop that needs one treats this as a condition to wait out, not as a
|
|
199
|
+
* failure of the runtime. The message is the one the Keeper has always used.
|
|
200
|
+
*/
|
|
201
|
+
export class SignerNotHeldError extends Error {
|
|
202
|
+
constructor(pub) {
|
|
203
|
+
super(`Missing prikey in db for pubkey=${pub}`);
|
|
204
|
+
/** The qualified public key whose private key is missing. */
|
|
205
|
+
Object.defineProperty(this, "pub", {
|
|
206
|
+
enumerable: true,
|
|
207
|
+
configurable: true,
|
|
208
|
+
writable: true,
|
|
209
|
+
value: void 0
|
|
210
|
+
});
|
|
211
|
+
this.name = "SignerNotHeldError";
|
|
212
|
+
this.pub = pub;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
196
215
|
/**
|
|
197
216
|
* Key-management coordinator backed by `Keeper` state.
|
|
198
217
|
*
|
|
@@ -251,6 +270,13 @@ export class Manager {
|
|
|
251
270
|
writable: true,
|
|
252
271
|
value: new Map()
|
|
253
272
|
});
|
|
273
|
+
/** Told after `hold()` borrows signers; a loop that waited for a missing signer resumes on it. */
|
|
274
|
+
Object.defineProperty(this, "holdListeners", {
|
|
275
|
+
enumerable: true,
|
|
276
|
+
configurable: true,
|
|
277
|
+
writable: true,
|
|
278
|
+
value: new Set()
|
|
279
|
+
});
|
|
254
280
|
const { ks, seed = "", aeid = "", pidx = 0, algo = Algos.salty, salt = randomSaltQb64(), tier = Tiers.low, } = args;
|
|
255
281
|
this._ks = ks;
|
|
256
282
|
this._seed = seed;
|
|
@@ -496,6 +522,17 @@ export class Manager {
|
|
|
496
522
|
}
|
|
497
523
|
for (const [pub, signer] of batch)
|
|
498
524
|
this.held.set(pub, signer);
|
|
525
|
+
for (const listener of [...this.holdListeners])
|
|
526
|
+
listener();
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Be told after each `hold()`, so a background loop whose signer was released can resume at once.
|
|
530
|
+
*
|
|
531
|
+
* @returns A function that stops the notifications.
|
|
532
|
+
*/
|
|
533
|
+
onHold(listener) {
|
|
534
|
+
this.holdListeners.add(listener);
|
|
535
|
+
return () => this.holdListeners.delete(listener);
|
|
499
536
|
}
|
|
500
537
|
/**
|
|
501
538
|
* Drop held signer references: all of them, or only those for `pubs`.
|
|
@@ -519,7 +556,7 @@ export class Manager {
|
|
|
519
556
|
return held;
|
|
520
557
|
const signer = this.ks.pris.get(pub, this.signerDecrypter());
|
|
521
558
|
if (!signer) {
|
|
522
|
-
throw new
|
|
559
|
+
throw new SignerNotHeldError(pub);
|
|
523
560
|
}
|
|
524
561
|
return signer;
|
|
525
562
|
}
|