keri-ts 2.2.3 → 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 +34 -12
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +365 -32
- 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 +188 -47
- 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 +75 -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 +62 -27
- 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 +190 -40
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +46 -73
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
- package/esm/keri/npm/src/app/runtime/doing.js +25 -0
- 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/lmdber.js +25 -6
- package/esm/keri/npm/src/db/core/memstore.js +4 -0
- package/esm/keri/npm/src/db/core/store-owner.js +4 -0
- 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 +75 -7
- 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 +34 -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 +31 -6
- 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 +57 -6
- 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/doing.d.ts +17 -0
- package/types/app/runtime/doing.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/dber.d.ts +5 -0
- package/types/db/core/dber.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/lmdber.d.ts +6 -0
- package/types/db/core/lmdber.d.ts.map +1 -1
- package/types/db/core/memstore.d.ts +2 -0
- package/types/db/core/memstore.d.ts.map +1 -1
- package/types/db/core/store-owner.d.ts +2 -0
- package/types/db/core/store-owner.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,20 +1,23 @@
|
|
|
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";
|
|
12
|
+
import { PROTOCOL_TOCK_MS } from "../runtime/doing.js";
|
|
12
13
|
import { runtimeTurn } from "../runtime/runtime-turn.js";
|
|
14
|
+
import { waitOnClock } from "../runtime/workflow-contract.js";
|
|
13
15
|
import { CesrHttpRefusal } from "../transport/cesr-egress.js";
|
|
14
|
-
import { messengerFrom, streamMessengerFrom } from "../transport/messengering.js";
|
|
16
|
+
import { messengerFrom, messengerUrl, streamMessengerFrom } from "../transport/messengering.js";
|
|
15
17
|
import { requireTcpDialer } from "../transport/tcp-dialer.js";
|
|
16
18
|
import { TransportError } from "../transport/transport-error.js";
|
|
17
19
|
import { introduce } from "./introducing.js";
|
|
20
|
+
import { relayEndpoint, relayRefusal } from "./relaying.js";
|
|
18
21
|
/**
|
|
19
22
|
* A v2 message refused for a peer this node must treat as v1.
|
|
20
23
|
*
|
|
@@ -42,9 +45,47 @@ function knownV1Peer(hby, aid) {
|
|
|
42
45
|
const kever = hby.db.getKever(aid);
|
|
43
46
|
return kever !== null && kever !== undefined && kever.serder.pvrsn.major < 2;
|
|
44
47
|
}
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
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);
|
|
48
89
|
}
|
|
49
90
|
/**
|
|
50
91
|
* Preserve resolved endpoint schemes and signed bytes for concrete transport construction.
|
|
@@ -64,6 +105,13 @@ function remoteCesrTarget(endpoint, message) {
|
|
|
64
105
|
* storage). Only recipients without those roles use one witness. Transport
|
|
65
106
|
* completion and opt-in outcomes do not prove remote protocol acceptance. Finite
|
|
66
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.
|
|
67
115
|
*/
|
|
68
116
|
export class Poster {
|
|
69
117
|
/**
|
|
@@ -74,9 +122,10 @@ export class Poster {
|
|
|
74
122
|
* @param protocol Borrowed same-database processor for TCP replies; null permits send-only work.
|
|
75
123
|
* @param http Borrowed HTTP implementation selected by composition.
|
|
76
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.
|
|
77
126
|
* @throws RangeError if protocol state belongs to a different database.
|
|
78
127
|
*/
|
|
79
|
-
constructor(hby, mbx, protocol, http, clock) {
|
|
128
|
+
constructor(hby, mbx, protocol, http, clock, relay) {
|
|
80
129
|
/** Borrowed habitat collection and backing accepted protocol state. */
|
|
81
130
|
Object.defineProperty(this, "hby", {
|
|
82
131
|
enumerable: true,
|
|
@@ -168,6 +217,13 @@ export class Poster {
|
|
|
168
217
|
writable: true,
|
|
169
218
|
value: void 0
|
|
170
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
|
+
});
|
|
171
227
|
if (protocol && protocol.kvy.db !== hby.db)
|
|
172
228
|
throw new RangeError("Poster protocol must use the same database");
|
|
173
229
|
this.hby = hby;
|
|
@@ -175,6 +231,7 @@ export class Poster {
|
|
|
175
231
|
this.http = http;
|
|
176
232
|
this.clock = clock;
|
|
177
233
|
this.protocol = protocol;
|
|
234
|
+
this.relay = relay;
|
|
178
235
|
}
|
|
179
236
|
/**
|
|
180
237
|
* Admit signed source bytes; callers must observe an outcome before reporting transmission.
|
|
@@ -283,11 +340,16 @@ export class Poster {
|
|
|
283
340
|
}
|
|
284
341
|
return found;
|
|
285
342
|
}
|
|
286
|
-
/**
|
|
287
|
-
|
|
343
|
+
/**
|
|
344
|
+
* Run queued delivery independently of query response intake and producer lifetimes, as a fixed-tock doer:
|
|
345
|
+
* a pass only when something is queued (an emptiness check, no store read), then one tock. A message queued
|
|
346
|
+
* between tocks waits at most `tockMs` before its delivery starts.
|
|
347
|
+
*/
|
|
348
|
+
*deliverDo(tockMs = PROTOCOL_TOCK_MS) {
|
|
288
349
|
while (!this.closed) {
|
|
289
|
-
|
|
290
|
-
|
|
350
|
+
if (!this.evts.empty)
|
|
351
|
+
yield* this.process();
|
|
352
|
+
yield* waitOnClock(this.clock, tockMs);
|
|
291
353
|
}
|
|
292
354
|
}
|
|
293
355
|
/** Runnable and active work determine convergence; consumable observations do not. */
|
|
@@ -376,7 +438,11 @@ export class Poster {
|
|
|
376
438
|
/**
|
|
377
439
|
* Resolve and execute one event using sequential current endpoints.
|
|
378
440
|
* Observed HTTP refusal and TransportError permit remaining targets to run.
|
|
379
|
-
*
|
|
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
|
|
380
446
|
* publish or report the failed outcome before exposing the error.
|
|
381
447
|
*
|
|
382
448
|
* @param evt Concrete source-message record; completion/failure lookup uses object identity, not message SAID.
|
|
@@ -388,6 +454,11 @@ export class Poster {
|
|
|
388
454
|
const hab = this.hby.habs.get(evt.src);
|
|
389
455
|
if (!hab)
|
|
390
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
|
+
}
|
|
391
462
|
const routes = this.findBytesRoutes(hab, evt.dest, evt.topic, evt.kin);
|
|
392
463
|
if (!routes.length) {
|
|
393
464
|
return {
|
|
@@ -397,6 +468,15 @@ export class Poster {
|
|
|
397
468
|
error: new ValidationError(`No end roles for ${evt.dest} to send raw CESR payload.`),
|
|
398
469
|
};
|
|
399
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
|
+
}
|
|
400
480
|
// A refused target does not prevent later targets from receiving this event.
|
|
401
481
|
const failed = [];
|
|
402
482
|
try {
|
|
@@ -412,14 +492,19 @@ export class Poster {
|
|
|
412
492
|
eid: endpoint.eid,
|
|
413
493
|
url: endpoint.url,
|
|
414
494
|
urls: endpoint.urls,
|
|
415
|
-
}, evt);
|
|
495
|
+
}, evt, frames);
|
|
416
496
|
}
|
|
417
497
|
catch (error) {
|
|
418
|
-
|
|
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))
|
|
419
500
|
throw error;
|
|
420
501
|
failed.push(error);
|
|
421
502
|
continue;
|
|
422
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
|
+
}
|
|
423
508
|
try {
|
|
424
509
|
if (target.kind !== "stored") {
|
|
425
510
|
const refusal = yield* this.sendPreparedTarget(target, evt, signal);
|
|
@@ -479,7 +564,10 @@ export class Poster {
|
|
|
479
564
|
*
|
|
480
565
|
* Controller and agent roles each retain all endpoints. An indirect role picks
|
|
481
566
|
* one random provider, unless an explicitly composed store owns an eligible local
|
|
482
|
-
* 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}).
|
|
483
571
|
*
|
|
484
572
|
* @param hab Local signing habitat borrowing accepted role/location state.
|
|
485
573
|
* @param recipient Resolved final recipient AID.
|
|
@@ -488,6 +576,8 @@ export class Poster {
|
|
|
488
576
|
* @returns Ordered role groups; no eligible role returns an empty array.
|
|
489
577
|
*/
|
|
490
578
|
findBytesRoutes(hab, recipient, topic, kin = "msg") {
|
|
579
|
+
if (this.relay)
|
|
580
|
+
return this.relayRoutes(hab, this.relay, recipient, topic);
|
|
491
581
|
const ends = hab.endsFor(recipient);
|
|
492
582
|
const roles = [Roles.controller, Roles.agent, Roles.mailbox].filter((role) => Object.keys(ends[role] ?? {}).length);
|
|
493
583
|
if (!roles.length && Object.keys(ends[Roles.witness] ?? {}).length)
|
|
@@ -496,10 +586,16 @@ export class Poster {
|
|
|
496
586
|
roles.splice(1);
|
|
497
587
|
return roles.map((role) => {
|
|
498
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
|
+
}
|
|
499
595
|
if (role === Roles.controller || role === Roles.agent) {
|
|
500
596
|
return {
|
|
501
597
|
kind: "direct",
|
|
502
|
-
targets: candidates.map(([eid, urls]) => ({ eid, url:
|
|
598
|
+
targets: candidates.map(([eid, urls]) => ({ eid, url: messengerUrl(urls), urls })),
|
|
503
599
|
};
|
|
504
600
|
}
|
|
505
601
|
if (!topic)
|
|
@@ -511,14 +607,60 @@ export class Poster {
|
|
|
511
607
|
const [eid, urls] = candidates[Math.floor(Math.random() * candidates.length)];
|
|
512
608
|
return {
|
|
513
609
|
kind: "forward",
|
|
514
|
-
targets: [{ eid, url:
|
|
610
|
+
targets: [{ eid, url: messengerUrl(urls), urls }],
|
|
515
611
|
};
|
|
516
612
|
});
|
|
517
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
|
+
}
|
|
518
659
|
/**
|
|
519
660
|
* Whether `src` has any eligible role (controller, agent, mailbox or witness) from which to reach `dest`, as
|
|
520
661
|
* delivery would find it now. A pure read of accepted endpoint state: it says nothing about whether the
|
|
521
|
-
* 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.
|
|
522
664
|
*/
|
|
523
665
|
canReach(src, dest, topic = "multisig") {
|
|
524
666
|
const hab = this.hby.habs.get(src);
|
|
@@ -531,17 +673,19 @@ export class Poster {
|
|
|
531
673
|
* @param route Selected direct or forwarded endpoint group.
|
|
532
674
|
* @param endpoint One selected endpoint AID and URL from the route.
|
|
533
675
|
* @param evt Original signed source and concrete ordinary/stream operation.
|
|
676
|
+
* @param frames The event's bytes, decoded once by {@link messageFrames}.
|
|
534
677
|
* @returns A prepared remote target, or stored after authorized local mailbox publication.
|
|
535
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.
|
|
536
680
|
*/
|
|
537
|
-
*prepareBytesTarget(hab, route, endpoint, evt) {
|
|
538
|
-
if (
|
|
681
|
+
*prepareBytesTarget(hab, route, endpoint, evt, frames) {
|
|
682
|
+
if (framesNeedV2(frames) && knownV1Peer(this.hby, evt.dest)) {
|
|
539
683
|
throw new PeerVersionError(`Refusing a v2 message for ${evt.dest}, whose latest accepted event is v1.`);
|
|
540
684
|
}
|
|
541
685
|
if (route.kind !== "direct") {
|
|
542
|
-
return yield* this.prepareForwardedTarget(hab, evt.dest, evt.topic, evt, endpoint);
|
|
686
|
+
return yield* this.prepareForwardedTarget(hab, evt.dest, evt.topic, evt, frames, endpoint);
|
|
543
687
|
}
|
|
544
|
-
if (
|
|
688
|
+
if (framesNeedV2(frames) && knownV1Peer(this.hby, endpoint.eid)) {
|
|
545
689
|
throw new PeerVersionError(`Refusing a v2 message for endpoint ${endpoint.eid}, whose latest accepted event is v1.`);
|
|
546
690
|
}
|
|
547
691
|
return remoteCesrTarget(endpoint, evt.msg);
|
|
@@ -553,46 +697,46 @@ export class Poster {
|
|
|
553
697
|
* @param recipient Final recipient AID, distinct from the intermediary endpoint.
|
|
554
698
|
* @param topic Recipient mailbox topic used by the forwarding envelope.
|
|
555
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.
|
|
556
701
|
* @param endpoint Mailbox/witness endpoint selected for this attempt.
|
|
557
|
-
* @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.
|
|
558
705
|
*/
|
|
559
|
-
*prepareForwardedTarget(hab, recipient, topic, evt, endpoint) {
|
|
706
|
+
*prepareForwardedTarget(hab, recipient, topic, evt, frames, endpoint) {
|
|
560
707
|
if (this.mbx && this.hby.db.prefixes.has(endpoint.eid)) {
|
|
561
708
|
const stored = this.mbx.storeMsg(mailboxTopicKey(recipient, topic), mailboxReplyPayload(hab, recipient, topic, evt.msg));
|
|
562
709
|
if (!stored)
|
|
563
710
|
throw new Error("Mailbox publication did not complete");
|
|
564
711
|
return { kind: "stored" };
|
|
565
712
|
}
|
|
566
|
-
|
|
567
|
-
//
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
msgs.push(yield* abortable((signal) => collectPayload(framePayload(frame), signal)));
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
else {
|
|
577
|
-
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.`);
|
|
578
720
|
}
|
|
579
721
|
// The provider reads the envelope, so it must read v2 whenever the envelope or child is v2.
|
|
580
|
-
if ((signer.kelVersion.major >= 2 ||
|
|
722
|
+
if ((signer.kelVersion.major >= 2 || framesNeedV2(frames)) && knownV1Peer(this.hby, endpoint.eid)) {
|
|
581
723
|
throw new PeerVersionError(`Refusing a v2 envelope for provider ${endpoint.eid}, whose latest accepted event is v1.`);
|
|
582
724
|
}
|
|
725
|
+
// Prepare every frame before opening the one selected relay attempt.
|
|
583
726
|
const forwarded = [];
|
|
584
|
-
for (const
|
|
585
|
-
forwarded.push(yield* buildForwardedDelivery(signer, { recipient, topic,
|
|
727
|
+
for (const child of frames)
|
|
728
|
+
forwarded.push(yield* buildForwardedDelivery(signer, { recipient, topic, child }));
|
|
586
729
|
const intro = introduce(signer, endpoint.eid);
|
|
587
730
|
let message = concatBytes(intro, ...forwarded);
|
|
588
731
|
if (evt.kin === "stream") {
|
|
589
|
-
|
|
590
|
-
|
|
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)) {
|
|
591
735
|
// A v2 peer's default cannot interpret unqualified v1 introduction counters.
|
|
592
736
|
// Declare each prepared frame's actual genus without changing signed bodies.
|
|
593
737
|
// Entirely legacy streams retain their v1-peer-compatible unqualified wire.
|
|
594
738
|
const qualified = [];
|
|
595
|
-
for (const frame of
|
|
739
|
+
for (const frame of prepared) {
|
|
596
740
|
qualified.push(yield* abortable(signal => collectPayload(framePayload(frame), signal)));
|
|
597
741
|
}
|
|
598
742
|
message = concatBytes(...qualified);
|
|
@@ -609,14 +753,11 @@ export class Poster {
|
|
|
609
753
|
* material that should travel with the forwarded request.
|
|
610
754
|
*
|
|
611
755
|
* @param hab Local signing habitat; its state/store lifetime remains with the caller.
|
|
612
|
-
* @param args Final recipient, mailbox topic and
|
|
756
|
+
* @param args Final recipient, mailbox topic and one decoded signed source message.
|
|
613
757
|
* @returns Signed /fwd wire; v2 binds one nested child body, legacy uses pathed embeds.
|
|
614
758
|
*/
|
|
615
759
|
function* buildForwardedDelivery(hab, args) {
|
|
616
|
-
const
|
|
617
|
-
if (frames.length !== 1)
|
|
618
|
-
throw new ValidationError("Forwarding requires one complete CESR message.");
|
|
619
|
-
const child = frames[0];
|
|
760
|
+
const child = args.child;
|
|
620
761
|
// A v2 member's stream is v2 after its introduction, so even a v1 child (such as a
|
|
621
762
|
// KERIpy-form `e`-embed group proposal) travels nested with its own genus selector,
|
|
622
763
|
// like KERIpy `StreamPoster.createForward`. Only v1 members keep the legacy embed.
|