keri-ts 2.2.4 → 2.2.6
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 +14 -14
- package/esm/keri/npm/src/acdc/dhking.js +29 -2
- package/esm/keri/npm/src/acdc/observing.js +10 -3
- package/esm/keri/npm/src/acdc/query-resend.js +74 -0
- package/esm/keri/npm/src/acdc/regengine.js +39 -9
- package/esm/keri/npm/src/acdc/regeventing.js +5 -3
- package/esm/keri/npm/src/acdc/registering.js +19 -0
- package/esm/keri/npm/src/acdc/salting.js +4 -0
- package/esm/keri/npm/src/acdc/unblinding.js +38 -3
- package/esm/keri/npm/src/app/bootstrap/authenticating.js +32 -10
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +363 -30
- package/esm/keri/npm/src/app/credentials/credential-importing.js +33 -4
- package/esm/keri/npm/src/app/credentials/root-replicator.js +27 -15
- package/esm/keri/npm/src/app/credentials/schema-resolving.js +5 -1
- package/esm/keri/npm/src/app/credentials/tel-publishing.js +16 -7
- package/esm/keri/npm/src/app/delivery/forwarding.js +205 -44
- package/esm/keri/npm/src/app/delivery/relaying.js +232 -0
- package/esm/keri/npm/src/app/delivery/respondant.js +8 -5
- package/esm/keri/npm/src/app/exchanges/exchange-messaging.js +1 -1
- package/esm/keri/npm/src/app/exchanges/exchanging.js +65 -9
- package/esm/keri/npm/src/app/identity/delegating.js +66 -9
- package/esm/keri/npm/src/app/identity/habbing.js +190 -71
- package/esm/keri/npm/src/app/identity/keeping.js +111 -39
- package/esm/keri/npm/src/app/identity/retire-identifier.js +98 -0
- package/esm/keri/npm/src/app/ipex/disclosure-replaying.js +116 -0
- package/esm/keri/npm/src/app/ipex/group-ipex.js +122 -25
- package/esm/keri/npm/src/app/ipex/group-issuance.js +137 -37
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +94 -31
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +53 -3
- package/esm/keri/npm/src/app/ipex/ipex-v2.js +7 -2
- package/esm/keri/npm/src/app/ipex/issuance.js +147 -44
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +4 -2
- package/esm/keri/npm/src/app/ipex/presenting.js +3 -18
- package/esm/keri/npm/src/app/ipex/receive-issuance.js +2 -1
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +39 -11
- package/esm/keri/npm/src/app/mailbox/agent-inbox.js +292 -0
- package/esm/keri/npm/src/app/mailbox/forward-handler.js +298 -52
- package/esm/keri/npm/src/app/mailbox/kel-publisher.js +26 -31
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +70 -62
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +20 -4
- package/esm/keri/npm/src/app/multisig/counseling.js +71 -27
- package/esm/keri/npm/src/app/multisig/group-members.js +41 -5
- package/esm/keri/npm/src/app/multisig/grouping.js +50 -2
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +3 -18
- package/esm/keri/npm/src/app/multisig/proposing.js +1 -16
- package/esm/keri/npm/src/app/multisig/rejoin-group.js +99 -0
- package/esm/keri/npm/src/app/multisig/replace-member.js +29 -8
- package/esm/keri/npm/src/app/protocol/parsering.js +45 -17
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +47 -21
- package/esm/keri/npm/src/app/queries/query-replies.js +0 -22
- package/esm/keri/npm/src/app/queries/relay-refresh.js +81 -0
- package/esm/keri/npm/src/app/queries/witness-inquisitor.js +180 -34
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +42 -73
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +8 -1
- package/esm/keri/npm/src/app/runtime/operation-owners.js +26 -56
- package/esm/keri/npm/src/app/runtime/workflow-contract.js +7 -4
- package/esm/keri/npm/src/app/transport/http-client.js +20 -2
- package/esm/keri/npm/src/app/transport/message-source.js +2 -1
- package/esm/keri/npm/src/app/transport/messengering.js +39 -8
- package/esm/keri/npm/src/app/witness/ingress.js +7 -0
- package/esm/keri/npm/src/app/witness/receipt-client.js +43 -16
- package/esm/keri/npm/src/app/witness/receipting.js +62 -20
- package/esm/keri/npm/src/cli/messaging/multisig.js +4 -3
- package/esm/keri/npm/src/core/errors.js +73 -0
- package/esm/keri/npm/src/core/kel/delegation-ancestry.js +11 -2
- package/esm/keri/npm/src/core/kel/eventing.js +25 -28
- package/esm/keri/npm/src/core/kel/kever.js +15 -9
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +16 -0
- package/esm/keri/npm/src/core/mailbox/kram-pruning.js +25 -6
- package/esm/keri/npm/src/core/mailbox/kram-time.js +34 -6
- package/esm/keri/npm/src/core/mailbox/kraming.js +58 -2
- package/esm/keri/npm/src/core/missing-anchor-query.js +20 -0
- package/esm/keri/npm/src/core/routing.js +18 -25
- package/esm/keri/npm/src/core/scheming.js +12 -5
- package/esm/keri/npm/src/core/wire-fields.js +126 -0
- package/esm/keri/npm/src/db/basing.js +4 -13
- package/esm/keri/npm/src/db/core/indexeddb-persistence.js +3 -2
- package/esm/keri/npm/src/db/core/webdber.js +85 -27
- package/esm/keri/npm/src/db/mailbox/mailboxing.js +13 -0
- package/esm/keri/npm/src/db/subing.js +4 -0
- package/esm/keri/npm/src/runtime/index.js +11 -5
- package/esm/keri/npm/src/time/mod.js +5 -0
- package/esm/keri/npm/src/vdr/credential-tsn-record.js +2 -7
- package/esm/keri/npm/src/vdr/eventing.js +71 -28
- package/esm/keri/npm/src/vdr/registry-tsn-record.js +3 -13
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +1 -1
- package/types/_dnt.polyfills.d.ts +9 -9
- package/types/_dnt.polyfills.d.ts.map +1 -1
- package/types/acdc/dhking.d.ts +8 -0
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/observing.d.ts +5 -1
- package/types/acdc/observing.d.ts.map +1 -1
- package/types/acdc/query-resend.d.ts +24 -0
- package/types/acdc/query-resend.d.ts.map +1 -0
- package/types/acdc/regengine.d.ts +19 -1
- package/types/acdc/regengine.d.ts.map +1 -1
- package/types/acdc/regeventing.d.ts.map +1 -1
- package/types/acdc/registering.d.ts +6 -0
- package/types/acdc/registering.d.ts.map +1 -1
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +13 -1
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/bootstrap/authenticating.d.ts +12 -2
- package/types/app/bootstrap/authenticating.d.ts.map +1 -1
- package/types/app/bootstrap/oobiery.d.ts +144 -7
- package/types/app/bootstrap/oobiery.d.ts.map +1 -1
- package/types/app/credentials/credential-importing.d.ts +8 -0
- package/types/app/credentials/credential-importing.d.ts.map +1 -1
- package/types/app/credentials/root-replicator.d.ts +19 -8
- package/types/app/credentials/root-replicator.d.ts.map +1 -1
- package/types/app/credentials/schema-resolving.d.ts.map +1 -1
- package/types/app/credentials/tel-publishing.d.ts +4 -2
- package/types/app/credentials/tel-publishing.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +90 -5
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/delivery/relaying.d.ts +144 -0
- package/types/app/delivery/relaying.d.ts.map +1 -0
- package/types/app/delivery/respondant.d.ts +9 -2
- package/types/app/delivery/respondant.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts +36 -0
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/delegating.d.ts +6 -0
- package/types/app/identity/delegating.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +106 -19
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +54 -19
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/identity/retire-identifier.d.ts +24 -0
- package/types/app/identity/retire-identifier.d.ts.map +1 -0
- package/types/app/ipex/disclosure-replaying.d.ts +61 -0
- package/types/app/ipex/disclosure-replaying.d.ts.map +1 -0
- package/types/app/ipex/group-ipex.d.ts +60 -14
- package/types/app/ipex/group-ipex.d.ts.map +1 -1
- package/types/app/ipex/group-issuance.d.ts +54 -8
- package/types/app/ipex/group-issuance.d.ts.map +1 -1
- package/types/app/ipex/ipex-discloser.d.ts +39 -2
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
- package/types/app/ipex/ipex-grouping.d.ts +23 -2
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/ipex-v2.d.ts +5 -2
- package/types/app/ipex/ipex-v2.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +46 -16
- package/types/app/ipex/issuance.d.ts.map +1 -1
- package/types/app/ipex/pending-presentations.d.ts +4 -2
- package/types/app/ipex/pending-presentations.d.ts.map +1 -1
- package/types/app/ipex/presenting.d.ts.map +1 -1
- package/types/app/ipex/receive-issuance.d.ts +2 -1
- package/types/app/ipex/receive-issuance.d.ts.map +1 -1
- package/types/app/ipex/unblinding-workflow.d.ts +13 -2
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
- package/types/app/mailbox/agent-inbox.d.ts +126 -0
- package/types/app/mailbox/agent-inbox.d.ts.map +1 -0
- package/types/app/mailbox/forward-handler.d.ts +114 -14
- package/types/app/mailbox/forward-handler.d.ts.map +1 -1
- package/types/app/mailbox/kel-publisher.d.ts +12 -22
- package/types/app/mailbox/kel-publisher.d.ts.map +1 -1
- package/types/app/mailbox/mailbox-poller.d.ts +40 -24
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +19 -5
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +32 -11
- package/types/app/multisig/counseling.d.ts.map +1 -1
- package/types/app/multisig/group-members.d.ts +17 -1
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts +17 -1
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +0 -8
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/multisig/proposing.d.ts +4 -19
- package/types/app/multisig/proposing.d.ts.map +1 -1
- package/types/app/multisig/rejoin-group.d.ts +119 -0
- package/types/app/multisig/rejoin-group.d.ts.map +1 -0
- package/types/app/multisig/replace-member.d.ts +15 -1
- package/types/app/multisig/replace-member.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +21 -2
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +25 -5
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/queries/query-replies.d.ts +4 -8
- package/types/app/queries/query-replies.d.ts.map +1 -1
- package/types/app/queries/relay-refresh.d.ts +26 -0
- package/types/app/queries/relay-refresh.d.ts.map +1 -0
- package/types/app/queries/witness-inquisitor.d.ts +56 -5
- package/types/app/queries/witness-inquisitor.d.ts.map +1 -1
- package/types/app/runtime/agent-runtime.d.ts +34 -3
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/cue-runtime.d.ts +15 -3
- package/types/app/runtime/cue-runtime.d.ts.map +1 -1
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -1
- package/types/app/runtime/operation-owners.d.ts +16 -26
- package/types/app/runtime/operation-owners.d.ts.map +1 -1
- package/types/app/runtime/workflow-contract.d.ts +21 -6
- package/types/app/runtime/workflow-contract.d.ts.map +1 -1
- package/types/app/transport/http-client.d.ts +9 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/app/transport/message-source.d.ts +6 -3
- package/types/app/transport/message-source.d.ts.map +1 -1
- package/types/app/transport/messengering.d.ts +10 -1
- package/types/app/transport/messengering.d.ts.map +1 -1
- package/types/app/witness/ingress.d.ts +1 -0
- package/types/app/witness/ingress.d.ts.map +1 -1
- package/types/app/witness/receipt-client.d.ts +19 -1
- package/types/app/witness/receipt-client.d.ts.map +1 -1
- package/types/app/witness/receipting.d.ts +24 -1
- package/types/app/witness/receipting.d.ts.map +1 -1
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/core/cues.d.ts +53 -1
- package/types/core/cues.d.ts.map +1 -1
- package/types/core/errors.d.ts +32 -0
- package/types/core/errors.d.ts.map +1 -1
- package/types/core/kel/delegation-ancestry.d.ts.map +1 -1
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/kel/kever-decisions.d.ts +3 -1
- package/types/core/kel/kever-decisions.d.ts.map +1 -1
- package/types/core/kel/kever.d.ts +7 -1
- package/types/core/kel/kever.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts +12 -0
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kram-pruning.d.ts +5 -1
- package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
- package/types/core/mailbox/kram-time.d.ts +26 -2
- package/types/core/mailbox/kram-time.d.ts.map +1 -1
- package/types/core/mailbox/kraming.d.ts +14 -1
- package/types/core/mailbox/kraming.d.ts.map +1 -1
- package/types/core/missing-anchor-query.d.ts +34 -0
- package/types/core/missing-anchor-query.d.ts.map +1 -0
- package/types/core/routing.d.ts +3 -7
- package/types/core/routing.d.ts.map +1 -1
- package/types/core/scheming.d.ts.map +1 -1
- package/types/core/wire-fields.d.ts +56 -0
- package/types/core/wire-fields.d.ts.map +1 -0
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/indexeddb-persistence.d.ts +3 -2
- package/types/db/core/indexeddb-persistence.d.ts.map +1 -1
- package/types/db/core/webdber.d.ts +32 -6
- package/types/db/core/webdber.d.ts.map +1 -1
- package/types/db/exchange/exchange-records.d.ts +4 -0
- package/types/db/exchange/exchange-records.d.ts.map +1 -1
- package/types/db/mailbox/mailboxing.d.ts +10 -1
- package/types/db/mailbox/mailboxing.d.ts.map +1 -1
- package/types/db/subing.d.ts +2 -0
- package/types/db/subing.d.ts.map +1 -1
- package/types/runtime/index.d.ts +20 -14
- package/types/runtime/index.d.ts.map +1 -1
- package/types/time/mod.d.ts +2 -0
- package/types/time/mod.d.ts.map +1 -1
- package/types/vdr/credential-tsn-record.d.ts.map +1 -1
- package/types/vdr/eventing.d.ts +32 -1
- package/types/vdr/eventing.d.ts.map +1 -1
- package/types/vdr/registry-tsn-record.d.ts.map +1 -1
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/app/runtime/signer-conditions.js +0 -51
- package/types/app/runtime/signer-conditions.d.ts +0 -32
- package/types/app/runtime/signer-conditions.d.ts.map +0 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { ValidationError } from "../../core/errors.js";
|
|
2
|
+
import { Roles } from "../../core/roles.js";
|
|
3
|
+
import { fetchEndpointUrls } from "../mailbox/mailboxing.js";
|
|
4
|
+
import { messengerUrl } from "../transport/messengering.js";
|
|
5
|
+
import { TransportError } from "../transport/transport-error.js";
|
|
6
|
+
/** The relay's location is not accepted yet (its OOBI has not resolved); nothing was sent. */
|
|
7
|
+
export class RelayUnresolvedError extends ValidationError {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
Object.defineProperty(this, "name", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: "RelayUnresolvedError"
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The sending identity has not authorized the relay as its `mailbox`; nothing was sent. The agent relays only for a
|
|
20
|
+
* sender that named it its mailbox, so a wrapper sent before that would be dropped there without an answer.
|
|
21
|
+
*/
|
|
22
|
+
export class RelayUnauthorizedError extends ValidationError {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
Object.defineProperty(this, "name", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: "RelayUnauthorizedError"
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Refuses a request whose origin is neither the relay's OOBI origin nor an accepted relay location. */
|
|
34
|
+
export class RelayOnlyError extends TransportError {
|
|
35
|
+
constructor() {
|
|
36
|
+
super(...arguments);
|
|
37
|
+
Object.defineProperty(this, "name", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: "RelayOnlyError"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read the relay's accepted location, as `Hab.endsFor` reads any endpoint's locations.
|
|
47
|
+
*
|
|
48
|
+
* Locations are accepted only from `/loc/scheme` replies verified against the relay's KEL, so a non-null result
|
|
49
|
+
* means the relay's OOBI (or an equivalent introduction) has resolved. A location no messenger can use
|
|
50
|
+
* ({@link messengerUrl}: one the HTTP client refuses, or a TCP URL without a port) reads as unresolved.
|
|
51
|
+
*
|
|
52
|
+
* @param hby Borrowed accepted-state store holding the relay's location replies.
|
|
53
|
+
* @param relay The configured relay agent.
|
|
54
|
+
* @returns The relay's preferred URL and scheme map, or null until a usable location is accepted.
|
|
55
|
+
*/
|
|
56
|
+
export function relayEndpoint(hby, relay) {
|
|
57
|
+
const accepted = fetchEndpointUrls(hby, relay.aid);
|
|
58
|
+
const urls = {};
|
|
59
|
+
if (accepted.https)
|
|
60
|
+
urls.https = accepted.https;
|
|
61
|
+
if (accepted.http)
|
|
62
|
+
urls.http = accepted.http;
|
|
63
|
+
if (accepted.tcp)
|
|
64
|
+
urls.tcp = accepted.tcp;
|
|
65
|
+
const url = messengerUrl(urls);
|
|
66
|
+
return url ? { eid: relay.aid, url, urls } : null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Whether `pre` has authorized the relay as its mailbox: the accepted row `ends.[pre, mailbox, relay.aid]` is
|
|
70
|
+
* allowed. This is the row the agent reads to decide that it serves the sender, and the edge holds it because it
|
|
71
|
+
* signed and accepted the end-role reply itself (`addMailbox`).
|
|
72
|
+
*
|
|
73
|
+
* @param hby Borrowed accepted-state store holding end-role replies.
|
|
74
|
+
* @param pre Local identifier that signs what goes to the relay (for a group, its local member).
|
|
75
|
+
* @param relay The configured relay agent.
|
|
76
|
+
* @returns True when the mailbox role naming the relay is accepted and allowed.
|
|
77
|
+
*/
|
|
78
|
+
export function relayAuthorized(hby, pre, relay) {
|
|
79
|
+
return hby.db.ends.get([pre, Roles.mailbox, relay.aid])?.allowed === true;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Decide whether the relay can carry a message signed by `signer` now, before anything is prepared or sent.
|
|
83
|
+
*
|
|
84
|
+
* The relay must have an accepted location. A message it is to forward also needs `signer` to have authorized it as
|
|
85
|
+
* mailbox; a message addressed to the relay itself (`signer` null) needs no authorization, as the relay consumes it
|
|
86
|
+
* rather than forwarding it. Onboarding needs neither path: the mailbox add is posted to the relay's `/mailboxes`
|
|
87
|
+
* admin resource and carries the KEL that introduces the edge.
|
|
88
|
+
*
|
|
89
|
+
* @param hby Borrowed accepted-state store holding the relay's location and the signer's end roles.
|
|
90
|
+
* @param relay The configured relay agent.
|
|
91
|
+
* @param signer Local identifier that signs the forwarding wrapper or query; null for a message to the relay itself.
|
|
92
|
+
* @returns The refusal to report, or null when the relay can carry the message.
|
|
93
|
+
*/
|
|
94
|
+
export function relayRefusal(hby, relay, signer) {
|
|
95
|
+
if (!relayEndpoint(hby, relay)) {
|
|
96
|
+
return new RelayUnresolvedError(`Relay ${relay.aid} has no accepted location; its OOBI has not resolved.`);
|
|
97
|
+
}
|
|
98
|
+
if (signer !== null && !relayAuthorized(hby, signer, relay)) {
|
|
99
|
+
return new RelayUnauthorizedError(`${signer} has not authorized relay ${relay.aid} as its mailbox.`);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Refuse a relay nothing can be composed over: its AID must be non-empty and its OOBI an absolute HTTP(S) URL,
|
|
105
|
+
* because the egress guard admits the OOBI's origin and the edge resolves that OOBI before anything else. A
|
|
106
|
+
* composition root (the guard, a wallet) calls it before it opens or sends anything.
|
|
107
|
+
*
|
|
108
|
+
* @param relay Candidate relay, possibly from untyped embedder input.
|
|
109
|
+
* @throws ValidationError when the AID is empty or the OOBI is not an absolute HTTP(S) URL.
|
|
110
|
+
*/
|
|
111
|
+
export function assertRelay(relay) {
|
|
112
|
+
const aid = relay.aid, oobi = relay.oobi;
|
|
113
|
+
const origin = typeof oobi === "string" ? originOf(oobi) : null;
|
|
114
|
+
if (typeof aid !== "string" || !aid || origin === null || !/^https?:\/\//.test(origin)) {
|
|
115
|
+
throw new ValidationError(`Relay requires an AID and an absolute HTTP(S) OOBI URL; got ${String(oobi)}.`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The onboarding steps still missing for delivery through the relay, derived from accepted state on every call;
|
|
120
|
+
* nothing records progress. Order is the order to take them: resolve the relay's OOBI, then authorize each
|
|
121
|
+
* single-signature identifier, then each group (whose inception needs its members to reach each other first).
|
|
122
|
+
*
|
|
123
|
+
* Only identifiers with accepted key state are listed: an identifier that has not completed its inception cannot
|
|
124
|
+
* sign an end-role reply yet. The resolution step comes from {@link relayRefusal}; the authorization steps from
|
|
125
|
+
* {@link relayAuthorized}, the row both the edge's senders and the relay read.
|
|
126
|
+
*
|
|
127
|
+
* @param hby Borrowed accepted-state store holding the relay's location and the local end roles.
|
|
128
|
+
* @param relay The configured relay agent.
|
|
129
|
+
* @returns The missing steps; empty when every accepted local identifier can send and receive through the relay.
|
|
130
|
+
*/
|
|
131
|
+
export function relayOnboarding(hby, relay) {
|
|
132
|
+
const steps = [];
|
|
133
|
+
if (relayRefusal(hby, relay, null))
|
|
134
|
+
steps.push({ step: "resolve-oobi", oobi: relay.oobi });
|
|
135
|
+
const groups = [];
|
|
136
|
+
for (const hab of hby.habs.values()) {
|
|
137
|
+
if (!hab.accepted || relayAuthorized(hby, hab.pre, relay))
|
|
138
|
+
continue;
|
|
139
|
+
if (hby.db.getHab(hab.pre)?.mid)
|
|
140
|
+
groups.push({ step: "authorize-group", pre: hab.pre });
|
|
141
|
+
else
|
|
142
|
+
steps.push({ step: "authorize-member", pre: hab.pre });
|
|
143
|
+
}
|
|
144
|
+
return [...steps, ...groups];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Scheme and authority of a URL, comparable across `http(s):` and `tcp:` (whose WHATWG `origin` is `"null"`).
|
|
148
|
+
*
|
|
149
|
+
* @param url Candidate absolute URL.
|
|
150
|
+
* @returns `scheme://host[:port]`, or null when the URL does not parse.
|
|
151
|
+
*/
|
|
152
|
+
function originOf(url) {
|
|
153
|
+
try {
|
|
154
|
+
const parsed = new URL(url);
|
|
155
|
+
return parsed.host ? `${parsed.protocol}//${parsed.host}` : null;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Whether a URL addresses the relay: its origin is the relay's own OOBI origin or the origin of a currently accepted
|
|
163
|
+
* relay location. The accepted locations are read on every call, so a location accepted (or replaced) later is
|
|
164
|
+
* honored at once. The egress guard refuses everything else, and the edge's Oobiery fetches such a URL directly
|
|
165
|
+
* while it resolves any other origin's OOBI through the relay.
|
|
166
|
+
*
|
|
167
|
+
* @param hby Borrowed accepted-state store holding the relay's location replies.
|
|
168
|
+
* @param relay The configured relay agent.
|
|
169
|
+
* @param url Candidate absolute URL.
|
|
170
|
+
* @returns True when the URL's origin is the relay's.
|
|
171
|
+
*/
|
|
172
|
+
export function isRelayOrigin(hby, relay, url) {
|
|
173
|
+
const origin = originOf(url);
|
|
174
|
+
if (origin === null)
|
|
175
|
+
return false;
|
|
176
|
+
if (origin === originOf(relay.oobi))
|
|
177
|
+
return true;
|
|
178
|
+
return Object.values(fetchEndpointUrls(hby, relay.aid)).some((location) => originOf(location) === origin);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Refuse egress to anything but the relay (see {@link isRelayOrigin}).
|
|
182
|
+
*
|
|
183
|
+
* @param hby Borrowed accepted-state store holding the relay's location replies.
|
|
184
|
+
* @param relay The configured relay agent.
|
|
185
|
+
* @param url Requested URL.
|
|
186
|
+
* @throws RelayOnlyError when the URL's origin is not the relay's.
|
|
187
|
+
*/
|
|
188
|
+
function requireRelayOrigin(hby, relay, url) {
|
|
189
|
+
if (isRelayOrigin(hby, relay, url))
|
|
190
|
+
return;
|
|
191
|
+
throw new RelayOnlyError(`Relay-only egress refused ${originOf(url) ?? "an unparseable URL"}; relay is ${relay.aid}.`);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Wrap an HTTP client (and its TCP dialer, when present) so it reaches only the relay.
|
|
195
|
+
*
|
|
196
|
+
* The guard is the edge's fail-closed boundary: egress kinds not yet routed through the relay (delegation, TEL
|
|
197
|
+
* receipting, witness registration, well-known authentication) fail as `TransportError` instead of going direct.
|
|
198
|
+
* It allows the origin of the relay's OOBI and the origins of the relay's currently accepted locations.
|
|
199
|
+
*
|
|
200
|
+
* @param inner Borrowed client that performs permitted requests.
|
|
201
|
+
* @param hby Borrowed accepted-state store holding the relay's location replies.
|
|
202
|
+
* @param relay The configured relay agent.
|
|
203
|
+
* @returns A client with the same contract whose refused requests throw RelayOnlyError before any I/O.
|
|
204
|
+
* @throws ValidationError when the relay's OOBI is not an absolute HTTP(S) URL.
|
|
205
|
+
*/
|
|
206
|
+
export function relayOnlyHttpClient(inner, hby, relay) {
|
|
207
|
+
assertRelay(relay);
|
|
208
|
+
const dialer = inner.tcp;
|
|
209
|
+
const tcp = dialer
|
|
210
|
+
? {
|
|
211
|
+
messenger(url, clock, protocol) {
|
|
212
|
+
requireRelayOrigin(hby, relay, url);
|
|
213
|
+
return dialer.messenger(url, clock, protocol);
|
|
214
|
+
},
|
|
215
|
+
streamMessenger(url, clock, payload) {
|
|
216
|
+
requireRelayOrigin(hby, relay, url);
|
|
217
|
+
return dialer.streamMessenger(url, clock, payload);
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
: undefined;
|
|
221
|
+
return {
|
|
222
|
+
...(tcp ? { tcp } : {}),
|
|
223
|
+
*request(url, init) {
|
|
224
|
+
requireRelayOrigin(hby, relay, url);
|
|
225
|
+
return yield* inner.request(url, init);
|
|
226
|
+
},
|
|
227
|
+
*open(url, init) {
|
|
228
|
+
requireRelayOrigin(hby, relay, url);
|
|
229
|
+
return yield* inner.open(url, init);
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
@@ -46,26 +46,29 @@ export class Respondant {
|
|
|
46
46
|
* @param hab Local habitat used to construct/sign this output; stores remain borrowed.
|
|
47
47
|
* @param cues Source Deck; only its current head is interpreted, and removed after full publication.
|
|
48
48
|
* @param hint Optional local sender hint used when cue provenance does not resolve a habitat.
|
|
49
|
-
* @returns
|
|
49
|
+
* @returns What became of the head; see {@link CueOutcome}. Only `taken` removes it.
|
|
50
50
|
* @throws Construction/admission failures leave the source cue present.
|
|
51
51
|
*/
|
|
52
52
|
enqueueCue(hab, cues, hint) {
|
|
53
53
|
this.postman.ensureOpen();
|
|
54
54
|
const cue = cues.peekHead();
|
|
55
55
|
if (!cue)
|
|
56
|
-
return
|
|
56
|
+
return "unrouted";
|
|
57
57
|
const dest = recipientForCue(cue), topic = topicForCue(cue);
|
|
58
58
|
const src = senderHabForCue(this.hby, cue, hint);
|
|
59
59
|
if (this.nothingToSend(cue, dest, topic, src)) {
|
|
60
60
|
cues.pull();
|
|
61
|
-
return
|
|
61
|
+
return "taken";
|
|
62
62
|
}
|
|
63
63
|
if (!dest || !topic || !src)
|
|
64
|
-
return
|
|
64
|
+
return "unrouted";
|
|
65
|
+
// A replay carries already-signed messages; receipts, witness receipts and replies are signed here.
|
|
66
|
+
if (cue.kin !== "replay" && !hab.canSign())
|
|
67
|
+
return "unsignable";
|
|
65
68
|
const msgs = [...hab.processCuesIter([cue])];
|
|
66
69
|
this.postman.sendAll(msgs.map((msg) => ({ src: src.pre, dest, topic, msg, replies: true })));
|
|
67
70
|
cues.pull();
|
|
68
|
-
return
|
|
71
|
+
return "taken";
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* Whether a receipt cue has nothing to send to. A receipt goes to the controller of the event it receipts, so it
|
|
@@ -42,7 +42,7 @@ export function createExchangeMessage(hab, args) {
|
|
|
42
42
|
*/
|
|
43
43
|
export function sendExchangeMessage(hby, hab, http, clock, args) {
|
|
44
44
|
return call(function* () {
|
|
45
|
-
const postman = new Poster(hby, null, null, http, clock);
|
|
45
|
+
const postman = new Poster(hby, null, null, http, clock, null);
|
|
46
46
|
yield* resource(function* (provide) {
|
|
47
47
|
try {
|
|
48
48
|
yield* provide();
|
|
@@ -8,7 +8,9 @@ import { verifyExchangeProofs, } from "../../core/exchange/exchange-proofing.js"
|
|
|
8
8
|
import { nestedIpexRoute, supportsNestedExchange } from "../../core/exchange/protocol-exchanging.js";
|
|
9
9
|
import { Kever } from "../../core/kel/kever.js";
|
|
10
10
|
import { consoleLogger } from "../../core/logger.js";
|
|
11
|
+
import { exchangeWindow } from "../../core/mailbox/kram-policy.js";
|
|
11
12
|
import { messageAttachmentGroups, serializeMessage } from "../../core/protocol-serialization.js";
|
|
13
|
+
import { readPrefixer } from "../../core/wire-fields.js";
|
|
12
14
|
import { hasNests, loadNests, prepareNests, publishNests } from "../../db/exchange/exchange-nests.js";
|
|
13
15
|
import { encodeDateTimeToDater, makeNowIso8601, timestampMicros } from "../../time/mod.js";
|
|
14
16
|
import { groupMemberHasKey } from "../multisig/group-members.js";
|
|
@@ -115,6 +117,17 @@ export class Exchanger {
|
|
|
115
117
|
writable: true,
|
|
116
118
|
value: void 0
|
|
117
119
|
});
|
|
120
|
+
/**
|
|
121
|
+
* Lifetimes of the pending exchanges this process's escrow sweep ejected, by SAID. Ejection removes the only
|
|
122
|
+
* stored body, so this is all that still says when such an exchange expired ({@link exchangeExpiry}). Lost on
|
|
123
|
+
* restart, by design: no store keeps ejected exchanges.
|
|
124
|
+
*/
|
|
125
|
+
Object.defineProperty(this, "ejected", {
|
|
126
|
+
enumerable: true,
|
|
127
|
+
configurable: true,
|
|
128
|
+
writable: true,
|
|
129
|
+
value: new Map()
|
|
130
|
+
});
|
|
118
131
|
this.now = now;
|
|
119
132
|
this.hby = hby;
|
|
120
133
|
this.cues = cues ?? new Deck();
|
|
@@ -203,6 +216,9 @@ export class Exchanger {
|
|
|
203
216
|
}
|
|
204
217
|
if (!args.serder.pre || !args.serder.said)
|
|
205
218
|
return reject("Exchange message is missing sender or SAID.");
|
|
219
|
+
// Proof resolution and every later sender read decode this prefix; a remote value is decided here.
|
|
220
|
+
if (!readPrefixer(args.serder.pre))
|
|
221
|
+
return reject(`Exchange sender ${args.serder.pre} is not a valid prefix.`);
|
|
206
222
|
if (args.receiverTime !== undefined && timestampMicros(args.receiverTime) === null) {
|
|
207
223
|
throw new Error("Invalid exchange receiver time");
|
|
208
224
|
}
|
|
@@ -328,6 +344,8 @@ export class Exchanger {
|
|
|
328
344
|
const previous = this.hby.db.exchangeMeta.get([said]);
|
|
329
345
|
const receiverTime = storedReceiverTime(previous) ?? args.receiverTime;
|
|
330
346
|
const mailboxAid = args.mailboxAid ?? previous?.mailboxAid ?? null;
|
|
347
|
+
// Once imported as retained history an exchange stays so; a retry rebuilds its input from the stores.
|
|
348
|
+
const retained = previous?.retained === true || args.retained === true;
|
|
331
349
|
const nests = args.nests?.length ? args.nests : loadNests(this.hby.db, said);
|
|
332
350
|
if (nests.length
|
|
333
351
|
&& (!handler?.acceptsNests || !supportsNestedExchange(args.serder.route ?? "", args.serder.pvrsn.major))) {
|
|
@@ -413,10 +431,11 @@ export class Exchanger {
|
|
|
413
431
|
mailboxAid,
|
|
414
432
|
...(receiverTime !== undefined ? { receiverTime } : {}),
|
|
415
433
|
gvrsn: previous?.gvrsn ?? args.gvrsn ?? args.serder.gvrsn ?? args.serder.pvrsn,
|
|
434
|
+
...(retained ? { retained } : {}),
|
|
416
435
|
};
|
|
417
436
|
// A retry that learns nothing new leaves the stored record as it is rather than committing it again.
|
|
418
437
|
if (!previous || previous.mailboxAid !== meta.mailboxAid || previous.receiverTime !== meta.receiverTime
|
|
419
|
-
|| JSON.stringify(previous.gvrsn) !== JSON.stringify(meta.gvrsn)) {
|
|
438
|
+
|| JSON.stringify(previous.gvrsn) !== JSON.stringify(meta.gvrsn) || (previous.retained === true) !== retained) {
|
|
420
439
|
this.hby.db.exchangeMeta.pin([said], meta);
|
|
421
440
|
}
|
|
422
441
|
if (pending) {
|
|
@@ -430,7 +449,7 @@ export class Exchanger {
|
|
|
430
449
|
}
|
|
431
450
|
return { decision: pending, cues };
|
|
432
451
|
}
|
|
433
|
-
return { decision: { kind: "accept", said }, cues, handling };
|
|
452
|
+
return { decision: { kind: "accept", said }, cues, handling: retained ? { ...handling, retained } : handling };
|
|
434
453
|
}
|
|
435
454
|
/** Retry escrows using ordinary LMDB evidence and retained mailbox provenance. */
|
|
436
455
|
processEscrows() {
|
|
@@ -481,13 +500,18 @@ export class Exchanger {
|
|
|
481
500
|
}
|
|
482
501
|
if (schema && new Diger({ qb64: keys[1] }).qb64 !== keys[1])
|
|
483
502
|
throw new Error("Corrupt schema escrow dependency.");
|
|
503
|
+
// Read before ejection removes the metadata that says whether the exchange is retained history.
|
|
504
|
+
const deadline = this.lifetime(serder);
|
|
484
505
|
const expired = yield* call(() => this.expirePending(serder));
|
|
485
506
|
if (expired) {
|
|
507
|
+
if (deadline)
|
|
508
|
+
this.ejected.set(said, deadline);
|
|
486
509
|
consoleLogger.warn("Exchanger ejected exchange escrow", {
|
|
487
510
|
said,
|
|
488
511
|
pre: serder.pre,
|
|
489
512
|
route: serder.route,
|
|
490
513
|
...expired,
|
|
514
|
+
...(deadline && { deadline }),
|
|
491
515
|
});
|
|
492
516
|
continue;
|
|
493
517
|
}
|
|
@@ -570,11 +594,8 @@ export class Exchanger {
|
|
|
570
594
|
}
|
|
571
595
|
/** Original accepted KRAM lifetime; partial-escrow timestamps never authorize live auxiliary work. */
|
|
572
596
|
exchangeDeadline(serder) {
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
if (!said || !sender || typeof xid !== "string" || !xid)
|
|
576
|
-
return null;
|
|
577
|
-
const cache = this.hby.db.tmsc.get([sender, xid, said]);
|
|
597
|
+
const key = transactionCacheKey(serder);
|
|
598
|
+
const cache = key && this.hby.db.tmsc.get(key);
|
|
578
599
|
if (!cache?.rdt)
|
|
579
600
|
return null;
|
|
580
601
|
const opener = timestampMicros(cache.xdt);
|
|
@@ -583,6 +604,36 @@ export class Exchanger {
|
|
|
583
604
|
}
|
|
584
605
|
return this.deadlineDater(opener + BigInt(cache.xl) * 1000n).dts;
|
|
585
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* When the exchange `said` expires, or expired: its KRAM lifetime, opener plus exchange window. Unlike
|
|
609
|
+
* {@link exchangeDeadline} this keeps answering after KRAM prunes the cache row (at opener + `pxl`, which the
|
|
610
|
+
* default windows make the deadline itself) and after the escrow sweep ejects an expired pending exchange, so a
|
|
611
|
+
* caller can say why the exchange is gone. A pure read. Null when KRAM is off (no exchange window), while KRAM
|
|
612
|
+
* holds the message without accepting it (an incomplete multi-signature message), for a pruned message that
|
|
613
|
+
* continues another message's thread, for an ejected exchange after a restart, and for retained history a local
|
|
614
|
+
* import admitted without KRAM ({@link ExchangeInput.retained}), which never had a window here.
|
|
615
|
+
*/
|
|
616
|
+
exchangeExpiry(said) {
|
|
617
|
+
const db = this.hby.db, serder = db.exns.get([said]) ?? db.epse.get([said]);
|
|
618
|
+
return serder?.said === said ? this.lifetime(serder) : this.ejected.get(said) ?? null;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* The accepted lifetime while KRAM keeps the cache row; once the row is pruned, an exchange that opens its own
|
|
622
|
+
* thread (an `xip`, or an `exn` with no prior, the rule KRAM opened it with) is its own opener, so its stamp
|
|
623
|
+
* plus the exchange window configured now gives the deadline again. Reads no escrow row, so it also works on
|
|
624
|
+
* the body of an exchange that is being ejected. Retained history never had a cache row here, so its stamp gives
|
|
625
|
+
* no deadline: it would only report an old exchange expired for its age.
|
|
626
|
+
*/
|
|
627
|
+
lifetime(serder) {
|
|
628
|
+
const accepted = this.exchangeDeadline(serder), key = transactionCacheKey(serder);
|
|
629
|
+
if (accepted || !key || this.hby.db.tmsc.get(key))
|
|
630
|
+
return accepted;
|
|
631
|
+
if (this.hby.db.exchangeMeta.get([serder.said])?.retained === true)
|
|
632
|
+
return null;
|
|
633
|
+
const opener = serder.ilk === "xip" || !serder.ked?.p ? timestampMicros(serder.stamp) : null;
|
|
634
|
+
const window = exchangeWindow(this.hby.db, serder.ilk, serder.route);
|
|
635
|
+
return opener === null || window === null ? null : this.deadlineDater(opener + BigInt(window) * 1000n).dts;
|
|
636
|
+
}
|
|
586
637
|
/**
|
|
587
638
|
* Rehydrate authenticated retained factors without route handling or writes.
|
|
588
639
|
* Pending-only access also enforces the original deadline with this owner's
|
|
@@ -1104,8 +1155,7 @@ export class Exchanger {
|
|
|
1104
1155
|
if (prior && timestampMicros(prior.dts) === null)
|
|
1105
1156
|
throw new Error("Corrupt missing-schema deadline.");
|
|
1106
1157
|
if (!prior) {
|
|
1107
|
-
const
|
|
1108
|
-
const cache = typeof xid === "string" && xid ? this.hby.db.tmsc.get([serder.pre, xid, said]) : null;
|
|
1158
|
+
const key = transactionCacheKey(serder), cache = key ? this.hby.db.tmsc.get(key) : null;
|
|
1109
1159
|
const opener = cache && timestampMicros(cache.xdt);
|
|
1110
1160
|
if (cache && (opener === null || opener === undefined || !Number.isSafeInteger(cache.pxl) || cache.pxl < 0)) {
|
|
1111
1161
|
throw new Error("Corrupt missing-schema transaction lifetime.");
|
|
@@ -1324,6 +1374,12 @@ export class Exchanger {
|
|
|
1324
1374
|
this.hby.db.emsd?.rem([said]);
|
|
1325
1375
|
}
|
|
1326
1376
|
}
|
|
1377
|
+
/** The KRAM transaction cache key `[sender, xid, said]` of an exchange; null for a message outside a transaction. */
|
|
1378
|
+
function transactionCacheKey(serder) {
|
|
1379
|
+
const said = serder.said, sender = serder.pre;
|
|
1380
|
+
const xid = serder.ilk === "xip" ? said : serder.sad?.x;
|
|
1381
|
+
return said && sender && typeof xid === "string" && xid ? [sender, xid, said] : null;
|
|
1382
|
+
}
|
|
1327
1383
|
function normalizeAttachments(ptds) {
|
|
1328
1384
|
return ptds.map((group) => ({
|
|
1329
1385
|
raw: group.raw.slice(),
|
|
@@ -19,11 +19,21 @@ 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
21
|
import { isLocalGroupHab, localGroupMember } from "../multisig/group-members.js";
|
|
22
|
-
import {
|
|
22
|
+
import { deliverWitnessMessage } from "../witness/receipt-client.js";
|
|
23
23
|
import { eventPayloadMessage, eventReplayMessage } from "./habbing.js";
|
|
24
24
|
export const DELEGATE_REQUEST_ROUTE = "/delegate/request";
|
|
25
25
|
/** Runtime passes to wait between repeated delegator-witness anchor queries. */
|
|
26
26
|
const DELEGATION_ANCHOR_QUERY_RETRY_PASSES = 8;
|
|
27
|
+
/** Keep the delegation request row after the delegator could not take it; the next pass sends it again. */
|
|
28
|
+
function retainedForDelegator(pre, said, error) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "keep",
|
|
31
|
+
pre,
|
|
32
|
+
said,
|
|
33
|
+
phase: "waitingWitnessReceipts",
|
|
34
|
+
reason: `Delegation request is not delivered yet: ${error instanceof Error ? error.message : String(error)}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
27
37
|
function eventKey(serder) {
|
|
28
38
|
const pre = serder.pre;
|
|
29
39
|
const snh = serder.snh;
|
|
@@ -410,6 +420,20 @@ export class Anchorer {
|
|
|
410
420
|
this.http = http;
|
|
411
421
|
this.clock = clock;
|
|
412
422
|
}
|
|
423
|
+
/** Workflow ids (`pre:snh`) of `pre`'s delegated events still in the `dpwe.`, `dune.` or `dpub.` phase. */
|
|
424
|
+
pendingOf(pre) {
|
|
425
|
+
const ids = new Set();
|
|
426
|
+
const { dpwe, dune, dpub } = this.hby.db;
|
|
427
|
+
for (const phase of [dpwe, dune, dpub]) {
|
|
428
|
+
for (const [, serder] of phase.getTopItemIter([pre], { topive: true }))
|
|
429
|
+
ids.add(workflowId(serder));
|
|
430
|
+
}
|
|
431
|
+
return [...ids];
|
|
432
|
+
}
|
|
433
|
+
/** Workflow ids whose pinned communication habitat (the proxy that sends the request) is `pre`. */
|
|
434
|
+
proxiedBy(pre) {
|
|
435
|
+
return [...this.communicationHabPins].filter(([, pinned]) => pinned === pre).map(([id]) => id);
|
|
436
|
+
}
|
|
413
437
|
/**
|
|
414
438
|
* Start one delegated event in witness-receipt escrow and replace any previous local proxy pin.
|
|
415
439
|
*
|
|
@@ -565,6 +589,16 @@ export class Anchorer {
|
|
|
565
589
|
reason: "Delegate witness receipts are not converged yet.",
|
|
566
590
|
};
|
|
567
591
|
}
|
|
592
|
+
// The request is signed by the communication habitat; one that cannot sign yet keeps the row for a later pass.
|
|
593
|
+
if (!communicationHab.canSign()) {
|
|
594
|
+
return {
|
|
595
|
+
kind: "keep",
|
|
596
|
+
pre,
|
|
597
|
+
said,
|
|
598
|
+
phase: "waitingWitnessReceipts",
|
|
599
|
+
reason: `Communication habitat ${communicationHab.pre} cannot sign yet; its signer is not held.`,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
568
602
|
// Do not ask the delegator to approve until the delegate's own witnesses
|
|
569
603
|
// have receipted the event. That preserves the KERIpy approval ordering
|
|
570
604
|
// and avoids anchoring an event the delegate cannot yet complete.
|
|
@@ -588,7 +622,7 @@ export class Anchorer {
|
|
|
588
622
|
// Admission alone cannot release the next send or the delegation phase transition.
|
|
589
623
|
const requested = yield* postman.wait(request);
|
|
590
624
|
if (requested.kin === "failed")
|
|
591
|
-
|
|
625
|
+
return retainedForDelegator(pre, said, requested.error);
|
|
592
626
|
const replay = postman.send({
|
|
593
627
|
src: communicationHab.pre,
|
|
594
628
|
dest: delpre,
|
|
@@ -597,7 +631,7 @@ export class Anchorer {
|
|
|
597
631
|
}, { notify: true });
|
|
598
632
|
const replayed = yield* postman.wait(replay);
|
|
599
633
|
if (replayed.kin === "failed")
|
|
600
|
-
|
|
634
|
+
return retainedForDelegator(pre, said, replayed.error);
|
|
601
635
|
// KERIpy issues the first delegator-witness anchor query in the same
|
|
602
636
|
// publish phase that forwards the delegation request and raw event bytes.
|
|
603
637
|
// Keep that ordering here so delegate-side approval discovery does not
|
|
@@ -657,8 +691,8 @@ export class Anchorer {
|
|
|
657
691
|
// event is learned locally; pinning `aess.` is what lets delegated unescrow
|
|
658
692
|
// and later reopen paths prove the authorization.
|
|
659
693
|
pinAuthorizingSeal(this.hby, serder, delegating);
|
|
660
|
-
this.hby.db.dune.rem(keys);
|
|
661
694
|
if ((hab.kever?.wits.length ?? 0) === 0) {
|
|
695
|
+
this.hby.db.dune.rem(keys);
|
|
662
696
|
this.hby.db.cdel.appendOn(pre, completionDiger(serder));
|
|
663
697
|
this.clearWorkflowPins(serder);
|
|
664
698
|
return {
|
|
@@ -669,7 +703,18 @@ export class Anchorer {
|
|
|
669
703
|
reason: "Delegator approval is anchored and no witness republication is required.",
|
|
670
704
|
};
|
|
671
705
|
}
|
|
672
|
-
|
|
706
|
+
// A delegator or witness that cannot take the publication keeps this row for the next pass.
|
|
707
|
+
const published = yield* this.publishDelegator(serder);
|
|
708
|
+
if (published.kind === "unreachable") {
|
|
709
|
+
return {
|
|
710
|
+
kind: "keep",
|
|
711
|
+
pre,
|
|
712
|
+
said,
|
|
713
|
+
phase: "waitingDelegatorAnchor",
|
|
714
|
+
reason: `Delegation publication is not delivered yet: ${published.error.message}`,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
this.hby.db.dune.rem(keys);
|
|
673
718
|
this.hby.db.dpub.pin(keys, serder);
|
|
674
719
|
return {
|
|
675
720
|
kind: "advance",
|
|
@@ -723,6 +768,8 @@ export class Anchorer {
|
|
|
723
768
|
* Send the delegation chain before the approving event, awaiting actual Poster or witness transmission outcomes.
|
|
724
769
|
*
|
|
725
770
|
* @param serder Accepted delegator event to publish after its required delegation chain.
|
|
771
|
+
* @returns `unreachable` with the first remote failure (a witness or the delegator that is down, refuses or
|
|
772
|
+
* has no usable endpoint); nothing past it is attempted. Local faults still throw.
|
|
726
773
|
*/
|
|
727
774
|
*publishDelegator(serder) {
|
|
728
775
|
const pre = workflowPre(serder);
|
|
@@ -736,7 +783,9 @@ export class Anchorer {
|
|
|
736
783
|
// matches the witness-visible material KERIpy sends for delegated approval.
|
|
737
784
|
for (const msg of this.hby.db.cloneDelegation(kever)) {
|
|
738
785
|
for (const witness of kever.wits) {
|
|
739
|
-
yield*
|
|
786
|
+
const delivery = yield* deliverWitnessMessage(transportHab, witness, msg, this.http, this.clock, this.protocol);
|
|
787
|
+
if (delivery.kind === "unreachable")
|
|
788
|
+
return delivery;
|
|
740
789
|
}
|
|
741
790
|
}
|
|
742
791
|
// Publish the approving event only after the complete delegation chain has been sent.
|
|
@@ -750,11 +799,19 @@ export class Anchorer {
|
|
|
750
799
|
msg: approvedEvent,
|
|
751
800
|
}, { notify: true });
|
|
752
801
|
const outcome = yield* postman.wait(evt);
|
|
753
|
-
if (outcome.kin === "failed")
|
|
754
|
-
|
|
802
|
+
if (outcome.kin === "failed") {
|
|
803
|
+
return {
|
|
804
|
+
kind: "unreachable",
|
|
805
|
+
witness: kever.delpre,
|
|
806
|
+
error: outcome.error instanceof Error ? outcome.error : new Error(String(outcome.error)),
|
|
807
|
+
};
|
|
808
|
+
}
|
|
755
809
|
}
|
|
756
810
|
for (const witness of kever.wits) {
|
|
757
|
-
yield*
|
|
811
|
+
const delivery = yield* deliverWitnessMessage(transportHab, witness, approvedEvent, this.http, this.clock, this.protocol);
|
|
812
|
+
if (delivery.kind === "unreachable")
|
|
813
|
+
return delivery;
|
|
758
814
|
}
|
|
815
|
+
return { kind: "delivered" };
|
|
759
816
|
}
|
|
760
817
|
}
|