keri-ts 2.2.4 → 2.2.5
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/registering.js +19 -0
- package/esm/keri/npm/src/acdc/salting.js +4 -0
- 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 +177 -43
- 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 +109 -23
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +36 -22
- 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 +131 -35
- 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 +16 -3
- 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 +41 -72
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
- 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 +8 -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/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 +28 -19
- 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/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/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 +69 -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 +48 -6
- package/types/app/ipex/group-issuance.d.ts.map +1 -1
- package/types/app/ipex/ipex-discloser.d.ts +14 -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 +35 -11
- 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 +4 -1
- 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/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 +6 -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/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.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
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
12
|
var _RootReplicator_instances, _RootReplicator_deps, _RootReplicator_pending, _RootReplicator_inflight, _RootReplicator_seen, _RootReplicator_dirty, _RootReplicator_pruned, _RootReplicator_attempt, _RootReplicator_refreshDirty, _RootReplicator_derive;
|
|
13
|
-
import { isLocalGroupHab, localGroupMember } from "../multisig/group-members.js";
|
|
13
|
+
import { groupSigningMembers, isLocalGroupHab, localGroupMember } from "../multisig/group-members.js";
|
|
14
14
|
import { replicateRootCopy, RootResealBudgetMs } from "./root-replicating.js";
|
|
15
15
|
/** Most copies one `processOnce` attempts, so a first pass over a large store cannot stall the runtime. */
|
|
16
16
|
export const RootSendsPerPass = 4;
|
|
@@ -18,13 +18,22 @@ export const RootSendsPerPass = 4;
|
|
|
18
18
|
export const RootRetryBaseMs = 1_000;
|
|
19
19
|
export const RootRetryMaxMs = 300_000;
|
|
20
20
|
const idOf = (group, registry, recipient) => [group, registry, recipient].join("/");
|
|
21
|
+
/**
|
|
22
|
+
* What a group's owed copies were last derived from: its latest establishment SAID and its stored signing members.
|
|
23
|
+
* The members change without a new establishment event when a member's KEL arrives after the event was accepted.
|
|
24
|
+
*/
|
|
25
|
+
function derivedFrom(hby, hab) {
|
|
26
|
+
const established = hab.pre && isLocalGroupHab(hby, hab) ? hab.kever?.lastEst.d : null;
|
|
27
|
+
return established ? [established, ...groupSigningMembers(hby, hab.pre)].join(" ") : null;
|
|
28
|
+
}
|
|
21
29
|
/**
|
|
22
30
|
* Derives, sends and records registry root copies for a wallet's local groups.
|
|
23
31
|
*
|
|
24
32
|
* Holds no durable state of its own beyond `Baser.rrsn`; what is pending and the
|
|
25
33
|
* backoff are process-local and rebuilt from state. A host calls `prompt` (or
|
|
26
34
|
* `replicate`) for each Dhke cue and `processOnce` on its loop; both are safe to repeat,
|
|
27
|
-
* and overlapping `processOnce` calls never send one copy twice.
|
|
35
|
+
* and overlapping `processOnce` calls never send one copy twice. Groups on the `changed`
|
|
36
|
+
* deck are prompted by the next pass.
|
|
28
37
|
*/
|
|
29
38
|
export class RootReplicator {
|
|
30
39
|
constructor(deps) {
|
|
@@ -58,8 +67,8 @@ export class RootReplicator {
|
|
|
58
67
|
return yield* this.processDue({ timeoutMs });
|
|
59
68
|
}
|
|
60
69
|
/**
|
|
61
|
-
* The fixed-tock pass: re-derive only the groups that were prompted, then send up
|
|
62
|
-
* due copies. A rotation nobody prompted for waits for {@link RootReplicator.sweep}.
|
|
70
|
+
* The fixed-tock pass: re-derive only the groups that were prompted or are on the `changed` deck, then send up
|
|
71
|
+
* to {@link RootSendsPerPass} due copies. A rotation nobody prompted for waits for {@link RootReplicator.sweep}.
|
|
63
72
|
*/
|
|
64
73
|
*processDue({ timeoutMs = RootResealBudgetMs } = {}) {
|
|
65
74
|
const { clock } = __classPrivateFieldGet(this, _RootReplicator_deps, "f"), deadline = clock.now() + Math.max(timeoutMs, 0);
|
|
@@ -68,7 +77,8 @@ export class RootReplicator {
|
|
|
68
77
|
for (const entry of [...__classPrivateFieldGet(this, _RootReplicator_pending, "f").values()]) {
|
|
69
78
|
if (results.length >= RootSendsPerPass)
|
|
70
79
|
break;
|
|
71
|
-
|
|
80
|
+
// The group's local member signs the copy; one that cannot sign yet keeps it without spending an attempt.
|
|
81
|
+
if (entry.retryAt > clock.now() || !__classPrivateFieldGet(this, _RootReplicator_deps, "f").root.hby.habs.get(entry.group)?.canSign())
|
|
72
82
|
continue;
|
|
73
83
|
const result = yield* __classPrivateFieldGet(this, _RootReplicator_instances, "m", _RootReplicator_attempt).call(this, entry.group, entry.registry, entry.recipient, deadline);
|
|
74
84
|
if (!result)
|
|
@@ -106,7 +116,7 @@ export class RootReplicator {
|
|
|
106
116
|
}
|
|
107
117
|
/** Whether a copy is owed or a group is waiting to be re-derived; in memory, reads no store. */
|
|
108
118
|
pending() {
|
|
109
|
-
return __classPrivateFieldGet(this, _RootReplicator_pending, "f").size > 0 || __classPrivateFieldGet(this, _RootReplicator_dirty, "f").size > 0;
|
|
119
|
+
return __classPrivateFieldGet(this, _RootReplicator_pending, "f").size > 0 || __classPrivateFieldGet(this, _RootReplicator_dirty, "f").size > 0 || !(__classPrivateFieldGet(this, _RootReplicator_deps, "f").changed?.empty ?? true);
|
|
110
120
|
}
|
|
111
121
|
/** Pure read: what is owed and why it has not gone yet. */
|
|
112
122
|
status() {
|
|
@@ -116,8 +126,8 @@ export class RootReplicator {
|
|
|
116
126
|
}
|
|
117
127
|
/**
|
|
118
128
|
* The full check: drop markers of groups that are gone (once) and re-derive each local group that is new or
|
|
119
|
-
* whose establishment event changed. One keyed read per local identifier, so a host runs it
|
|
120
|
-
* many tocks, not every tock.
|
|
129
|
+
* whose establishment event or stored members changed. One keyed read per local identifier, so a host runs it
|
|
130
|
+
* every many tocks, not every tock.
|
|
121
131
|
*/
|
|
122
132
|
sweep() {
|
|
123
133
|
const { root } = __classPrivateFieldGet(this, _RootReplicator_deps, "f"), hby = root.hby;
|
|
@@ -129,10 +139,10 @@ export class RootReplicator {
|
|
|
129
139
|
}
|
|
130
140
|
}
|
|
131
141
|
for (const hab of hby.habs.values()) {
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
142
|
+
const derived = derivedFrom(hby, hab);
|
|
143
|
+
if (!derived || (__classPrivateFieldGet(this, _RootReplicator_seen, "f").get(hab.pre) === derived && !__classPrivateFieldGet(this, _RootReplicator_dirty, "f").has(hab.pre)))
|
|
134
144
|
continue;
|
|
135
|
-
__classPrivateFieldGet(this, _RootReplicator_seen, "f").set(hab.pre,
|
|
145
|
+
__classPrivateFieldGet(this, _RootReplicator_seen, "f").set(hab.pre, derived);
|
|
136
146
|
__classPrivateFieldGet(this, _RootReplicator_dirty, "f").delete(hab.pre);
|
|
137
147
|
__classPrivateFieldGet(this, _RootReplicator_instances, "m", _RootReplicator_derive).call(this, hab.pre);
|
|
138
148
|
}
|
|
@@ -158,14 +168,16 @@ _RootReplicator_deps = new WeakMap(), _RootReplicator_pending = new WeakMap(), _
|
|
|
158
168
|
__classPrivateFieldGet(this, _RootReplicator_inflight, "f").delete(id);
|
|
159
169
|
}
|
|
160
170
|
}, _RootReplicator_refreshDirty = function _RootReplicator_refreshDirty() {
|
|
161
|
-
const
|
|
171
|
+
const { root, changed } = __classPrivateFieldGet(this, _RootReplicator_deps, "f"), hby = root.hby;
|
|
172
|
+
for (let group = changed?.pull(); group !== undefined; group = changed?.pull())
|
|
173
|
+
this.prompt(group);
|
|
162
174
|
for (const group of [...__classPrivateFieldGet(this, _RootReplicator_dirty, "f")]) {
|
|
163
175
|
const hab = hby.habs.get(group);
|
|
164
|
-
const
|
|
176
|
+
const derived = hab ? derivedFrom(hby, hab) : null;
|
|
165
177
|
__classPrivateFieldGet(this, _RootReplicator_dirty, "f").delete(group);
|
|
166
|
-
if (!
|
|
178
|
+
if (!derived)
|
|
167
179
|
continue;
|
|
168
|
-
__classPrivateFieldGet(this, _RootReplicator_seen, "f").set(group,
|
|
180
|
+
__classPrivateFieldGet(this, _RootReplicator_seen, "f").set(group, derived);
|
|
169
181
|
__classPrivateFieldGet(this, _RootReplicator_instances, "m", _RootReplicator_derive).call(this, group);
|
|
170
182
|
}
|
|
171
183
|
}, _RootReplicator_derive = function _RootReplicator_derive(group) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readUrl } from "../../core/wire-fields.js";
|
|
1
2
|
/** Parse one schema reference into the supported resolver contract. */
|
|
2
3
|
export function parseSchemaReference(reference) {
|
|
3
4
|
if (reference.startsWith("sad:")) {
|
|
@@ -7,7 +8,10 @@ export function parseSchemaReference(reference) {
|
|
|
7
8
|
return { kind: "did", url: reference };
|
|
8
9
|
}
|
|
9
10
|
if (reference.startsWith("http://") || reference.startsWith("https://")) {
|
|
10
|
-
|
|
11
|
+
// The reference is a remote credential's `s`; one that does not parse names no cached schema.
|
|
12
|
+
const parsed = readUrl(reference);
|
|
13
|
+
if (!parsed)
|
|
14
|
+
return { kind: "oobi", url: reference };
|
|
11
15
|
const parts = parsed.pathname.split("/").filter((part) => part.length > 0);
|
|
12
16
|
const oobiIndex = parts.lastIndexOf("oobi");
|
|
13
17
|
const said = oobiIndex >= 0 && oobiIndex + 2 === parts.length ? parts[oobiIndex + 1] : undefined;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { ValidationError } from "../../core/errors.js";
|
|
2
|
-
import {
|
|
2
|
+
import { deliverWitnessMessage } from "../witness/receipt-client.js";
|
|
3
3
|
/**
|
|
4
4
|
* Advance one captured set of protocol escrows. Receipt collection includes
|
|
5
5
|
* fanout; a crash before its commit repeats that ordinary finite operation.
|
|
6
6
|
* Dissemination replays accepted TEL evidence to the anchor's historical witness
|
|
7
|
-
* cohort and commits ctel only after every local transmission finished.
|
|
8
|
-
*
|
|
7
|
+
* cohort and commits ctel only after every local transmission finished. A witness
|
|
8
|
+
* that is unreachable, refuses or does not receipt ends only that target: its
|
|
9
|
+
* escrow is retained and the next pass retries it, without an outbox. Local faults
|
|
10
|
+
* (missing issuer Hab, storage) still throw.
|
|
9
11
|
*/
|
|
10
12
|
export function* publishPendingTel(hby, publication, receiptor, http, clock) {
|
|
11
13
|
publication.advance();
|
|
12
14
|
for (const target of publication.pendingReceipts()) {
|
|
13
|
-
yield* receiptor.
|
|
14
|
-
|
|
15
|
+
const outcome = yield* receiptor.attempt(target.issuer, { sn: target.anchorSn, allowTcp: true });
|
|
16
|
+
if (outcome.kind === "receipted")
|
|
17
|
+
publication.receipted(target);
|
|
15
18
|
}
|
|
16
19
|
for (const target of publication.pendingPublications()) {
|
|
17
20
|
const hab = hby.habs.get(target.issuer);
|
|
@@ -19,9 +22,15 @@ export function* publishPendingTel(hby, publication, receiptor, http, clock) {
|
|
|
19
22
|
throw new ValidationError(`Missing local TEL issuer ${target.issuer}.`);
|
|
20
23
|
const diger = publication.reger.tels.getOn(target.pre, target.sn);
|
|
21
24
|
const message = publication.reger.cloneTvt(target.pre, diger);
|
|
25
|
+
let delivered = true;
|
|
22
26
|
for (const witness of target.witnesses) {
|
|
23
|
-
yield*
|
|
27
|
+
const delivery = yield* deliverWitnessMessage(hab, witness, message, http, clock, null);
|
|
28
|
+
if (delivery.kind === "unreachable") {
|
|
29
|
+
delivered = false;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
|
-
|
|
33
|
+
if (delivered)
|
|
34
|
+
publication.published(target);
|
|
26
35
|
}
|
|
27
36
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Queued signed-message publication. Poster owns transport attempts and their concrete outcomes. */
|
|
2
|
-
import { attachmentPayload, collectPayload, concatBytes, Diger, framePayload, MtrDex, parseBytes, Vrsn_2_0, } from "cesr-ts";
|
|
2
|
+
import { attachmentPayload, collectPayload, concatBytes, Diger, framePayload, MtrDex, parseBytes, ParserError, Vrsn_2_0, } from "cesr-ts";
|
|
3
3
|
import { call, resource } from "effection";
|
|
4
4
|
import { Deck } from "../../core/deck.js";
|
|
5
5
|
import { ValidationError } from "../../core/errors.js";
|
|
6
6
|
import { exchange } from "../../core/exchange/protocol-exchanging.js";
|
|
7
7
|
import { Roles } from "../../core/roles.js";
|
|
8
|
-
import { mailboxTopicKey
|
|
8
|
+
import { mailboxTopicKey } from "../mailbox/mailboxing.js";
|
|
9
9
|
import { transportMailboxTopic } from "../mailbox/topic-routing.js";
|
|
10
10
|
import { localGroupMember } from "../multisig/group-members.js";
|
|
11
11
|
import { abortable } from "../runtime/abortable.js";
|
|
@@ -13,10 +13,11 @@ import { PROTOCOL_TOCK_MS } from "../runtime/doing.js";
|
|
|
13
13
|
import { runtimeTurn } from "../runtime/runtime-turn.js";
|
|
14
14
|
import { waitOnClock } from "../runtime/workflow-contract.js";
|
|
15
15
|
import { CesrHttpRefusal } from "../transport/cesr-egress.js";
|
|
16
|
-
import { messengerFrom, streamMessengerFrom } from "../transport/messengering.js";
|
|
16
|
+
import { messengerFrom, messengerUrl, streamMessengerFrom } from "../transport/messengering.js";
|
|
17
17
|
import { requireTcpDialer } from "../transport/tcp-dialer.js";
|
|
18
18
|
import { TransportError } from "../transport/transport-error.js";
|
|
19
19
|
import { introduce } from "./introducing.js";
|
|
20
|
+
import { relayEndpoint, relayRefusal } from "./relaying.js";
|
|
20
21
|
/**
|
|
21
22
|
* A v2 message refused for a peer this node must treat as v1.
|
|
22
23
|
*
|
|
@@ -44,9 +45,47 @@ function knownV1Peer(hby, aid) {
|
|
|
44
45
|
const kever = hby.db.getKever(aid);
|
|
45
46
|
return kever !== null && kever !== undefined && kever.serder.pvrsn.major < 2;
|
|
46
47
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* A queued message whose own bytes cannot be prepared for sending: they are not complete CESR, or a forwarded
|
|
50
|
+
* delivery's source is not exactly one complete message. It describes the event, never this node, so it fails that
|
|
51
|
+
* delivery and the Poster continues with the next.
|
|
52
|
+
*/
|
|
53
|
+
export class UnsendableMessageError extends ValidationError {
|
|
54
|
+
constructor() {
|
|
55
|
+
super(...arguments);
|
|
56
|
+
Object.defineProperty(this, "name", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: "UnsendableMessageError"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Decode a queued message once, before any target is prepared.
|
|
66
|
+
*
|
|
67
|
+
* `ParserError` is the decoder's refusal of the bytes. This is the Poster's only decode of a producer's bytes; the
|
|
68
|
+
* wrappers it later builds and re-parses are its own output, so a failure there stays a fault.
|
|
69
|
+
*
|
|
70
|
+
* @param evt Admitted event whose copied bytes are decoded.
|
|
71
|
+
* @returns The message's complete frames in wire order.
|
|
72
|
+
* @throws UnsendableMessageError when the bytes are not complete CESR.
|
|
73
|
+
*/
|
|
74
|
+
function messageFrames(evt) {
|
|
75
|
+
try {
|
|
76
|
+
return parseBytes(evt.msg);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (!(error instanceof ParserError))
|
|
80
|
+
throw error;
|
|
81
|
+
throw new UnsendableMessageError(`Queued message for ${evt.dest} is not complete CESR: ${error.message}`, {
|
|
82
|
+
cause: error,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Whether any of these frames needs a v2 reader. */
|
|
87
|
+
function framesNeedV2(frames) {
|
|
88
|
+
return frames.some(frame => frame.body.pvrsn.major >= 2 || (frame.gvrsn?.major ?? 1) >= 2);
|
|
50
89
|
}
|
|
51
90
|
/**
|
|
52
91
|
* Preserve resolved endpoint schemes and signed bytes for concrete transport construction.
|
|
@@ -66,6 +105,13 @@ function remoteCesrTarget(endpoint, message) {
|
|
|
66
105
|
* storage). Only recipients without those roles use one witness. Transport
|
|
67
106
|
* completion and opt-in outcomes do not prove remote protocol acceptance. Finite
|
|
68
107
|
* stream admissions use the first present role through the same execution owner.
|
|
108
|
+
*
|
|
109
|
+
* With a relay (an edge runtime) there is no role traversal: every recipient other
|
|
110
|
+
* than the relay gets one `/fwd` delivery to the relay, which does the forwarding,
|
|
111
|
+
* and the relay itself gets the message directly. `sent` then means the relay
|
|
112
|
+
* accepted the wrapper, not that the final recipient received it. A forwarded
|
|
113
|
+
* delivery fails with `RelayUnauthorizedError`, sending nothing, until its wrapper
|
|
114
|
+
* signer (a group's local member) has authorized the relay as its mailbox.
|
|
69
115
|
*/
|
|
70
116
|
export class Poster {
|
|
71
117
|
/**
|
|
@@ -76,9 +122,10 @@ export class Poster {
|
|
|
76
122
|
* @param protocol Borrowed same-database processor for TCP replies; null permits send-only work.
|
|
77
123
|
* @param http Borrowed HTTP implementation selected by composition.
|
|
78
124
|
* @param clock Borrowed clock for transport deadlines; times are milliseconds.
|
|
125
|
+
* @param relay The agent every message goes through; null selects ordinary direct role traversal.
|
|
79
126
|
* @throws RangeError if protocol state belongs to a different database.
|
|
80
127
|
*/
|
|
81
|
-
constructor(hby, mbx, protocol, http, clock) {
|
|
128
|
+
constructor(hby, mbx, protocol, http, clock, relay) {
|
|
82
129
|
/** Borrowed habitat collection and backing accepted protocol state. */
|
|
83
130
|
Object.defineProperty(this, "hby", {
|
|
84
131
|
enumerable: true,
|
|
@@ -170,6 +217,13 @@ export class Poster {
|
|
|
170
217
|
writable: true,
|
|
171
218
|
value: void 0
|
|
172
219
|
});
|
|
220
|
+
/** The agent every message goes through (an edge); null delivers directly (a forwarder). */
|
|
221
|
+
Object.defineProperty(this, "relay", {
|
|
222
|
+
enumerable: true,
|
|
223
|
+
configurable: true,
|
|
224
|
+
writable: true,
|
|
225
|
+
value: void 0
|
|
226
|
+
});
|
|
173
227
|
if (protocol && protocol.kvy.db !== hby.db)
|
|
174
228
|
throw new RangeError("Poster protocol must use the same database");
|
|
175
229
|
this.hby = hby;
|
|
@@ -177,6 +231,7 @@ export class Poster {
|
|
|
177
231
|
this.http = http;
|
|
178
232
|
this.clock = clock;
|
|
179
233
|
this.protocol = protocol;
|
|
234
|
+
this.relay = relay;
|
|
180
235
|
}
|
|
181
236
|
/**
|
|
182
237
|
* Admit signed source bytes; callers must observe an outcome before reporting transmission.
|
|
@@ -383,7 +438,11 @@ export class Poster {
|
|
|
383
438
|
/**
|
|
384
439
|
* Resolve and execute one event using sequential current endpoints.
|
|
385
440
|
* Observed HTTP refusal and TransportError permit remaining targets to run.
|
|
386
|
-
*
|
|
441
|
+
* The event's bytes are decoded once; bytes that are not complete CESR fail the
|
|
442
|
+
* event. `PeerVersionError` and `UnsendableMessageError` from preparation fail
|
|
443
|
+
* only that target: both are decided from the event's own bytes and the peer's
|
|
444
|
+
* accepted version before any store write or I/O for the target. Other
|
|
445
|
+
* construction/storage/cleanup errors are faults and propagate so process can
|
|
387
446
|
* publish or report the failed outcome before exposing the error.
|
|
388
447
|
*
|
|
389
448
|
* @param evt Concrete source-message record; completion/failure lookup uses object identity, not message SAID.
|
|
@@ -395,6 +454,11 @@ export class Poster {
|
|
|
395
454
|
const hab = this.hby.habs.get(evt.src);
|
|
396
455
|
if (!hab)
|
|
397
456
|
throw new Error(`Unknown local sender ${evt.src}`);
|
|
457
|
+
if (this.relay) {
|
|
458
|
+
const refusal = relayRefusal(this.hby, this.relay, this.relaySigner(hab, this.relay, evt.dest));
|
|
459
|
+
if (refusal)
|
|
460
|
+
return { kin: "failed", evt, deliveries, error: refusal };
|
|
461
|
+
}
|
|
398
462
|
const routes = this.findBytesRoutes(hab, evt.dest, evt.topic, evt.kin);
|
|
399
463
|
if (!routes.length) {
|
|
400
464
|
return {
|
|
@@ -404,6 +468,15 @@ export class Poster {
|
|
|
404
468
|
error: new ValidationError(`No end roles for ${evt.dest} to send raw CESR payload.`),
|
|
405
469
|
};
|
|
406
470
|
}
|
|
471
|
+
let frames;
|
|
472
|
+
try {
|
|
473
|
+
frames = messageFrames(evt);
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
if (!(error instanceof UnsendableMessageError))
|
|
477
|
+
throw error;
|
|
478
|
+
return { kin: "failed", evt, deliveries, error };
|
|
479
|
+
}
|
|
407
480
|
// A refused target does not prevent later targets from receiving this event.
|
|
408
481
|
const failed = [];
|
|
409
482
|
try {
|
|
@@ -419,14 +492,19 @@ export class Poster {
|
|
|
419
492
|
eid: endpoint.eid,
|
|
420
493
|
url: endpoint.url,
|
|
421
494
|
urls: endpoint.urls,
|
|
422
|
-
}, evt);
|
|
495
|
+
}, evt, frames);
|
|
423
496
|
}
|
|
424
497
|
catch (error) {
|
|
425
|
-
|
|
498
|
+
// Facts about this event and its peer, never this node: that target fails and the rest still run.
|
|
499
|
+
if (!(error instanceof PeerVersionError || error instanceof UnsendableMessageError))
|
|
426
500
|
throw error;
|
|
427
501
|
failed.push(error);
|
|
428
502
|
continue;
|
|
429
503
|
}
|
|
504
|
+
if (target.kind === "unsignable") {
|
|
505
|
+
failed.push(new ValidationError(`Local signer ${target.signer} cannot sign now; its signer is not held.`));
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
430
508
|
try {
|
|
431
509
|
if (target.kind !== "stored") {
|
|
432
510
|
const refusal = yield* this.sendPreparedTarget(target, evt, signal);
|
|
@@ -486,7 +564,10 @@ export class Poster {
|
|
|
486
564
|
*
|
|
487
565
|
* Controller and agent roles each retain all endpoints. An indirect role picks
|
|
488
566
|
* one random provider, unless an explicitly composed store owns an eligible local
|
|
489
|
-
* AID.
|
|
567
|
+
* AID. An agent role whose endpoint is a local AID with a composed store is that
|
|
568
|
+
* same local store target (given a topic), as the hosted agent keeps the recipient's
|
|
569
|
+
* mail for pickup. Empty location maps retain role selection and become execution failures.
|
|
570
|
+
* With a relay, the relay alone is the route (see {@link relayRoutes}).
|
|
490
571
|
*
|
|
491
572
|
* @param hab Local signing habitat borrowing accepted role/location state.
|
|
492
573
|
* @param recipient Resolved final recipient AID.
|
|
@@ -495,6 +576,8 @@ export class Poster {
|
|
|
495
576
|
* @returns Ordered role groups; no eligible role returns an empty array.
|
|
496
577
|
*/
|
|
497
578
|
findBytesRoutes(hab, recipient, topic, kin = "msg") {
|
|
579
|
+
if (this.relay)
|
|
580
|
+
return this.relayRoutes(hab, this.relay, recipient, topic);
|
|
498
581
|
const ends = hab.endsFor(recipient);
|
|
499
582
|
const roles = [Roles.controller, Roles.agent, Roles.mailbox].filter((role) => Object.keys(ends[role] ?? {}).length);
|
|
500
583
|
if (!roles.length && Object.keys(ends[Roles.witness] ?? {}).length)
|
|
@@ -503,10 +586,16 @@ export class Poster {
|
|
|
503
586
|
roles.splice(1);
|
|
504
587
|
return roles.map((role) => {
|
|
505
588
|
const candidates = Object.entries(ends[role]);
|
|
589
|
+
// An agent hosted by this Habery keeps the recipient's mail itself: store it, never post to our own ingress.
|
|
590
|
+
const localAgent = role === Roles.agent && topic && this.mbx
|
|
591
|
+
&& candidates.find(([eid]) => this.hby.db.prefixes.has(eid));
|
|
592
|
+
if (localAgent) {
|
|
593
|
+
return { kind: "forward", targets: [{ eid: localAgent[0], url: `local-mailbox:${localAgent[0]}`, urls: {} }] };
|
|
594
|
+
}
|
|
506
595
|
if (role === Roles.controller || role === Roles.agent) {
|
|
507
596
|
return {
|
|
508
597
|
kind: "direct",
|
|
509
|
-
targets: candidates.map(([eid, urls]) => ({ eid, url:
|
|
598
|
+
targets: candidates.map(([eid, urls]) => ({ eid, url: messengerUrl(urls), urls })),
|
|
510
599
|
};
|
|
511
600
|
}
|
|
512
601
|
if (!topic)
|
|
@@ -518,14 +607,60 @@ export class Poster {
|
|
|
518
607
|
const [eid, urls] = candidates[Math.floor(Math.random() * candidates.length)];
|
|
519
608
|
return {
|
|
520
609
|
kind: "forward",
|
|
521
|
-
targets: [{ eid, url:
|
|
610
|
+
targets: [{ eid, url: messengerUrl(urls), urls }],
|
|
522
611
|
};
|
|
523
612
|
});
|
|
524
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Route through the relay: one `forward` route to the relay for every other recipient, never a direct fallback,
|
|
616
|
+
* and one `direct` route when the recipient is the relay. Recipient end roles are not read.
|
|
617
|
+
*
|
|
618
|
+
* @param hab Local sending habitat; its wrapper signer must have authorized the relay as mailbox.
|
|
619
|
+
* @param relay The configured relay agent.
|
|
620
|
+
* @param recipient Resolved final recipient AID.
|
|
621
|
+
* @param topic Recipient mailbox topic; the forwarded route requires a nonempty topic.
|
|
622
|
+
* @returns One route to the relay, or none while {@link relayRefusal} refuses the relay for this sender.
|
|
623
|
+
*/
|
|
624
|
+
relayRoutes(hab, relay, recipient, topic) {
|
|
625
|
+
if (relayRefusal(this.hby, relay, this.relaySigner(hab, relay, recipient)))
|
|
626
|
+
return [];
|
|
627
|
+
const endpoint = relayEndpoint(this.hby, relay);
|
|
628
|
+
if (recipient === relay.aid)
|
|
629
|
+
return [{ kind: "direct", targets: [endpoint] }];
|
|
630
|
+
if (!topic)
|
|
631
|
+
throw new ValidationError(`Forwarded delivery requires an explicit topic for ${recipient}.`);
|
|
632
|
+
return [{ kind: "forward", targets: [endpoint] }];
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* The identifier whose mailbox authorization of the relay a delivery needs: the `/fwd` wrapper's signer (see
|
|
636
|
+
* {@link Poster.wrapperSigner}), the same `i` the agent reads to decide it serves the sender. For a group source
|
|
637
|
+
* that is the local member, not the group. A message addressed to the relay is not wrapped and needs none (null).
|
|
638
|
+
*
|
|
639
|
+
* @param hab Local sending habitat.
|
|
640
|
+
* @param relay The configured relay agent.
|
|
641
|
+
* @param recipient Resolved final recipient AID.
|
|
642
|
+
* @returns The wrapper signer's AID, or null when the recipient is the relay.
|
|
643
|
+
*/
|
|
644
|
+
relaySigner(hab, relay, recipient) {
|
|
645
|
+
return recipient === relay.aid ? null : this.wrapperSigner(hab).pre;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* The habitat that signs a `/fwd` wrapper for `hab`. A group cannot sign the wrapper alone: one member's signature
|
|
649
|
+
* does not meet a threshold above one, and the host would escrow the wrapper. The local member wraps; the payload
|
|
650
|
+
* keeps whatever signatures it carries.
|
|
651
|
+
*
|
|
652
|
+
* @param hab Local sending habitat, possibly a group.
|
|
653
|
+
* @returns `hab` itself, or the group's local member.
|
|
654
|
+
* @throws ValidationError when a group has no local member metadata.
|
|
655
|
+
*/
|
|
656
|
+
wrapperSigner(hab) {
|
|
657
|
+
return this.hby.db.groups.has(hab.pre) ? localGroupMember(this.hby, hab.pre) : hab;
|
|
658
|
+
}
|
|
525
659
|
/**
|
|
526
660
|
* Whether `src` has any eligible role (controller, agent, mailbox or witness) from which to reach `dest`, as
|
|
527
661
|
* delivery would find it now. A pure read of accepted endpoint state: it says nothing about whether the
|
|
528
|
-
* endpoint answers. A proposer uses it to refuse before it creates anything it could not publish.
|
|
662
|
+
* endpoint answers. A proposer uses it to refuse before it creates anything it could not publish. With a relay,
|
|
663
|
+
* it is whether {@link relayRefusal} accepts the relay for this sender.
|
|
529
664
|
*/
|
|
530
665
|
canReach(src, dest, topic = "multisig") {
|
|
531
666
|
const hab = this.hby.habs.get(src);
|
|
@@ -538,17 +673,19 @@ export class Poster {
|
|
|
538
673
|
* @param route Selected direct or forwarded endpoint group.
|
|
539
674
|
* @param endpoint One selected endpoint AID and URL from the route.
|
|
540
675
|
* @param evt Original signed source and concrete ordinary/stream operation.
|
|
676
|
+
* @param frames The event's bytes, decoded once by {@link messageFrames}.
|
|
541
677
|
* @returns A prepared remote target, or stored after authorized local mailbox publication.
|
|
542
678
|
* @throws PeerVersionError when the wire needs a v2 reader that this peer is known not to be.
|
|
679
|
+
* @throws UnsendableMessageError when a forwarded ordinary message is not exactly one message.
|
|
543
680
|
*/
|
|
544
|
-
*prepareBytesTarget(hab, route, endpoint, evt) {
|
|
545
|
-
if (
|
|
681
|
+
*prepareBytesTarget(hab, route, endpoint, evt, frames) {
|
|
682
|
+
if (framesNeedV2(frames) && knownV1Peer(this.hby, evt.dest)) {
|
|
546
683
|
throw new PeerVersionError(`Refusing a v2 message for ${evt.dest}, whose latest accepted event is v1.`);
|
|
547
684
|
}
|
|
548
685
|
if (route.kind !== "direct") {
|
|
549
|
-
return yield* this.prepareForwardedTarget(hab, evt.dest, evt.topic, evt, endpoint);
|
|
686
|
+
return yield* this.prepareForwardedTarget(hab, evt.dest, evt.topic, evt, frames, endpoint);
|
|
550
687
|
}
|
|
551
|
-
if (
|
|
688
|
+
if (framesNeedV2(frames) && knownV1Peer(this.hby, endpoint.eid)) {
|
|
552
689
|
throw new PeerVersionError(`Refusing a v2 message for endpoint ${endpoint.eid}, whose latest accepted event is v1.`);
|
|
553
690
|
}
|
|
554
691
|
return remoteCesrTarget(endpoint, evt.msg);
|
|
@@ -560,46 +697,46 @@ export class Poster {
|
|
|
560
697
|
* @param recipient Final recipient AID, distinct from the intermediary endpoint.
|
|
561
698
|
* @param topic Recipient mailbox topic used by the forwarding envelope.
|
|
562
699
|
* @param evt Original signed source and operation; outer envelopes may be rebuilt.
|
|
700
|
+
* @param frames The event's decoded messages: exactly one for an ordinary event, each forwarded alone in a stream.
|
|
563
701
|
* @param endpoint Mailbox/witness endpoint selected for this attempt.
|
|
564
|
-
* @returns Stored for a local endpoint,
|
|
702
|
+
* @returns Stored for a local endpoint, unsignable when the wrapper signer cannot sign now, or signed forwarding
|
|
703
|
+
* wire with its introduction.
|
|
704
|
+
* @throws UnsendableMessageError when an ordinary event is not exactly one message.
|
|
565
705
|
*/
|
|
566
|
-
*prepareForwardedTarget(hab, recipient, topic, evt, endpoint) {
|
|
706
|
+
*prepareForwardedTarget(hab, recipient, topic, evt, frames, endpoint) {
|
|
567
707
|
if (this.mbx && this.hby.db.prefixes.has(endpoint.eid)) {
|
|
568
708
|
const stored = this.mbx.storeMsg(mailboxTopicKey(recipient, topic), mailboxReplyPayload(hab, recipient, topic, evt.msg));
|
|
569
709
|
if (!stored)
|
|
570
710
|
throw new Error("Mailbox publication did not complete");
|
|
571
711
|
return { kind: "stored" };
|
|
572
712
|
}
|
|
573
|
-
|
|
574
|
-
//
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
msgs.push(yield* abortable((signal) => collectPayload(framePayload(frame), signal)));
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
else {
|
|
584
|
-
msgs.push(evt.msg);
|
|
713
|
+
const signer = this.wrapperSigner(hab);
|
|
714
|
+
// Poster does not retry: an event whose wrapper cannot be signed now fails like any other undeliverable one.
|
|
715
|
+
if (!signer.canSign())
|
|
716
|
+
return { kind: "unsignable", signer: signer.pre };
|
|
717
|
+
// A stream forwards each of its messages alone; an ordinary event is forwarded whole, so it must be one message.
|
|
718
|
+
if (evt.kin !== "stream" && frames.length !== 1) {
|
|
719
|
+
throw new UnsendableMessageError(`Forwarding to ${recipient} requires one complete CESR message.`);
|
|
585
720
|
}
|
|
586
721
|
// The provider reads the envelope, so it must read v2 whenever the envelope or child is v2.
|
|
587
|
-
if ((signer.kelVersion.major >= 2 ||
|
|
722
|
+
if ((signer.kelVersion.major >= 2 || framesNeedV2(frames)) && knownV1Peer(this.hby, endpoint.eid)) {
|
|
588
723
|
throw new PeerVersionError(`Refusing a v2 envelope for provider ${endpoint.eid}, whose latest accepted event is v1.`);
|
|
589
724
|
}
|
|
725
|
+
// Prepare every frame before opening the one selected relay attempt.
|
|
590
726
|
const forwarded = [];
|
|
591
|
-
for (const
|
|
592
|
-
forwarded.push(yield* buildForwardedDelivery(signer, { recipient, topic,
|
|
727
|
+
for (const child of frames)
|
|
728
|
+
forwarded.push(yield* buildForwardedDelivery(signer, { recipient, topic, child }));
|
|
593
729
|
const intro = introduce(signer, endpoint.eid);
|
|
594
730
|
let message = concatBytes(intro, ...forwarded);
|
|
595
731
|
if (evt.kin === "stream") {
|
|
596
|
-
|
|
597
|
-
|
|
732
|
+
// This node's own wrappers around already-decoded children: a parse failure here is a fault.
|
|
733
|
+
const prepared = parseBytes(message);
|
|
734
|
+
if (prepared.some(frame => (frame.gvrsn?.major ?? 1) >= 2)) {
|
|
598
735
|
// A v2 peer's default cannot interpret unqualified v1 introduction counters.
|
|
599
736
|
// Declare each prepared frame's actual genus without changing signed bodies.
|
|
600
737
|
// Entirely legacy streams retain their v1-peer-compatible unqualified wire.
|
|
601
738
|
const qualified = [];
|
|
602
|
-
for (const frame of
|
|
739
|
+
for (const frame of prepared) {
|
|
603
740
|
qualified.push(yield* abortable(signal => collectPayload(framePayload(frame), signal)));
|
|
604
741
|
}
|
|
605
742
|
message = concatBytes(...qualified);
|
|
@@ -616,14 +753,11 @@ export class Poster {
|
|
|
616
753
|
* material that should travel with the forwarded request.
|
|
617
754
|
*
|
|
618
755
|
* @param hab Local signing habitat; its state/store lifetime remains with the caller.
|
|
619
|
-
* @param args Final recipient, mailbox topic and
|
|
756
|
+
* @param args Final recipient, mailbox topic and one decoded signed source message.
|
|
620
757
|
* @returns Signed /fwd wire; v2 binds one nested child body, legacy uses pathed embeds.
|
|
621
758
|
*/
|
|
622
759
|
function* buildForwardedDelivery(hab, args) {
|
|
623
|
-
const
|
|
624
|
-
if (frames.length !== 1)
|
|
625
|
-
throw new ValidationError("Forwarding requires one complete CESR message.");
|
|
626
|
-
const child = frames[0];
|
|
760
|
+
const child = args.child;
|
|
627
761
|
// A v2 member's stream is v2 after its introduction, so even a v1 child (such as a
|
|
628
762
|
// KERIpy-form `e`-embed group proposal) travels nested with its own genus selector,
|
|
629
763
|
// like KERIpy `StreamPoster.createForward`. Only v1 members keep the legacy embed.
|