keri-ts 2.1.0 → 2.2.0
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/README.md +30 -2
- package/esm/keri/npm/src/acdc/authoring.js +195 -0
- package/esm/keri/npm/src/acdc/custody.js +164 -0
- package/esm/keri/npm/src/acdc/dhking.js +244 -32
- package/esm/keri/npm/src/acdc/regbasing.js +10 -0
- package/esm/keri/npm/src/acdc/salting.js +16 -1
- package/esm/keri/npm/src/acdc/scheming.js +93 -54
- package/esm/keri/npm/src/acdc/unblinding.js +118 -95
- package/esm/keri/npm/src/app/bootstrap/authenticating.js +4 -6
- package/esm/keri/npm/src/app/bootstrap/oobi-resolving.js +41 -0
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +4 -6
- package/esm/keri/npm/src/app/credentials/credential-revoking.js +1 -0
- package/esm/keri/npm/src/app/credentials/credential-workflows.js +2 -0
- package/esm/keri/npm/src/app/credentials/registry-settling.js +44 -0
- package/esm/keri/npm/src/app/credentials/root-replicating.js +68 -60
- package/esm/keri/npm/src/app/credentials/root-replicator.js +198 -0
- package/esm/keri/npm/src/app/credentials/tel-completion.js +36 -0
- package/esm/keri/npm/src/app/delivery/forwarding.js +16 -3
- package/esm/keri/npm/src/app/exchanges/exchanging.js +14 -4
- package/esm/keri/npm/src/app/identity/challenging.js +50 -0
- package/esm/keri/npm/src/app/identity/delegating.js +77 -77
- package/esm/keri/npm/src/app/identity/delegation-approval.js +333 -0
- package/esm/keri/npm/src/app/identity/event-completion.js +154 -0
- package/esm/keri/npm/src/app/identity/habbing.js +129 -6
- package/esm/keri/npm/src/app/identity/keeping.js +77 -4
- package/esm/keri/npm/src/app/ipex/group-ipex.js +327 -0
- package/esm/keri/npm/src/app/ipex/group-issuance.js +902 -0
- package/esm/keri/npm/src/app/ipex/ipex-admitting.js +120 -0
- package/esm/keri/npm/src/app/ipex/ipex-conversation.js +197 -0
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +591 -0
- package/esm/keri/npm/src/app/ipex/ipex-granting.js +93 -0
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +34 -11
- package/esm/keri/npm/src/app/ipex/issuance.js +888 -0
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +46 -0
- package/esm/keri/npm/src/app/ipex/presentation-files.js +200 -0
- package/esm/keri/npm/src/app/ipex/presenting.js +401 -0
- package/esm/keri/npm/src/app/ipex/receive-issuance.js +86 -0
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +660 -0
- package/esm/keri/npm/src/app/mailbox/ipex-arrival.js +103 -0
- package/esm/keri/npm/src/app/mailbox/kel-publisher.js +322 -0
- package/esm/keri/npm/src/app/mailbox/kel-publishing.js +65 -0
- package/esm/keri/npm/src/app/mailbox/kel-submission.js +24 -0
- package/esm/keri/npm/src/app/mailbox/mailbox-admin.js +208 -0
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +212 -47
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +10 -5
- package/esm/keri/npm/src/app/multisig/counseling.js +482 -0
- package/esm/keri/npm/src/app/multisig/group-members.js +18 -0
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +94 -139
- package/esm/keri/npm/src/app/multisig/proposals.js +281 -0
- package/esm/keri/npm/src/app/multisig/proposing.js +122 -0
- package/esm/keri/npm/src/app/notifications/signaling.js +5 -5
- package/esm/keri/npm/src/app/protocol/parsering.js +26 -1
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +9 -3
- package/esm/keri/npm/src/app/queries/query-workflows.js +98 -0
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +106 -55
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +3 -6
- package/esm/keri/npm/src/app/runtime/delivery-outbox.js +87 -0
- package/esm/keri/npm/src/app/runtime/doing.js +17 -0
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +207 -0
- package/esm/keri/npm/src/app/runtime/operation-owners.js +151 -0
- package/esm/keri/npm/src/app/runtime/retry-backoff.js +93 -0
- package/esm/keri/npm/src/app/runtime/workflow-contract.js +80 -0
- package/esm/keri/npm/src/app/transport/http-client.js +25 -6
- package/esm/keri/npm/src/app/witness/http-endpoints.js +4 -4
- package/esm/keri/npm/src/cli/common/event-completion.js +49 -0
- package/esm/keri/npm/src/cli/common/runtime-workers.js +23 -0
- package/esm/keri/npm/src/cli/credentials/ipex.js +157 -190
- package/esm/keri/npm/src/cli/credentials/vc.js +53 -72
- package/esm/keri/npm/src/cli/endpoints/did.js +12 -3
- package/esm/keri/npm/src/cli/endpoints/oobi.js +10 -13
- package/esm/keri/npm/src/cli/identity/challenge.js +8 -34
- package/esm/keri/npm/src/cli/identity/delegate.js +81 -258
- package/esm/keri/npm/src/cli/identity/incept.js +15 -32
- package/esm/keri/npm/src/cli/identity/interact.js +5 -17
- package/esm/keri/npm/src/cli/identity/rotate.js +8 -36
- package/esm/keri/npm/src/cli/index.js +3 -1
- package/esm/keri/npm/src/cli/messaging/group-join.js +79 -0
- package/esm/keri/npm/src/cli/messaging/group-wait.js +88 -0
- package/esm/keri/npm/src/cli/messaging/multisig.js +163 -284
- package/esm/keri/npm/src/cli/messaging/root-copy-lines.js +31 -0
- package/esm/keri/npm/src/core/kel/eventing.js +66 -10
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +39 -6
- package/esm/keri/npm/src/core/mailbox/kraming.js +2 -0
- package/esm/keri/npm/src/core/records.js +23 -0
- package/esm/keri/npm/src/core/routing.js +13 -0
- package/esm/keri/npm/src/db/basing.js +34 -2
- package/esm/keri/npm/src/db/core/indexeddb-persistence.js +221 -0
- package/esm/keri/npm/src/db/indexeddb.js +98 -0
- package/esm/keri/npm/src/db/keeping.js +11 -2
- package/esm/keri/npm/src/db/store-lock.js +150 -0
- package/esm/keri/npm/src/npm/db-indexeddb.browser.js +2 -0
- package/esm/keri/npm/src/npm/db-indexeddb.js +3 -0
- package/esm/keri/npm/src/npm/db.browser.js +2 -0
- package/esm/keri/npm/src/npm/index.browser.js +9 -0
- package/esm/keri/npm/src/npm/runtime-web.browser.js +2 -0
- package/esm/keri/npm/src/npm/runtime-web.js +3 -0
- package/esm/keri/npm/src/npm/runtime.browser.js +2 -0
- package/esm/keri/npm/src/runtime/index.js +51 -4
- package/esm/keri/npm/src/runtime/web/output-barrier.js +47 -0
- package/esm/keri/npm/src/runtime/web/persistent-storage.js +37 -0
- package/esm/keri/npm/src/runtime/web.js +10 -0
- package/esm/keri/npm/src/vdr/tel-publication.js +16 -0
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +15 -2
- package/types/acdc/authoring.d.ts +54 -0
- package/types/acdc/authoring.d.ts.map +1 -0
- package/types/acdc/custody.d.ts +77 -0
- package/types/acdc/custody.d.ts.map +1 -0
- package/types/acdc/dhking.d.ts +94 -3
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/regbasing.d.ts +4 -0
- package/types/acdc/regbasing.d.ts.map +1 -1
- package/types/acdc/salting.d.ts +6 -1
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/scheming.d.ts +2 -0
- package/types/acdc/scheming.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +58 -19
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/bootstrap/authenticating.d.ts +3 -1
- package/types/app/bootstrap/authenticating.d.ts.map +1 -1
- package/types/app/bootstrap/oobi-resolving.d.ts +92 -0
- package/types/app/bootstrap/oobi-resolving.d.ts.map +1 -0
- package/types/app/bootstrap/oobiery.d.ts +3 -1
- package/types/app/bootstrap/oobiery.d.ts.map +1 -1
- package/types/app/credentials/credential-revoking.d.ts +3 -0
- package/types/app/credentials/credential-revoking.d.ts.map +1 -1
- package/types/app/credentials/credential-workflows.d.ts +5 -0
- package/types/app/credentials/credential-workflows.d.ts.map +1 -1
- package/types/app/credentials/registry-settling.d.ts +85 -0
- package/types/app/credentials/registry-settling.d.ts.map +1 -0
- package/types/app/credentials/root-replicating.d.ts +56 -44
- package/types/app/credentials/root-replicating.d.ts.map +1 -1
- package/types/app/credentials/root-replicator.d.ts +105 -0
- package/types/app/credentials/root-replicator.d.ts.map +1 -0
- package/types/app/credentials/tel-completion.d.ts +55 -0
- package/types/app/credentials/tel-completion.d.ts.map +1 -0
- package/types/app/delivery/forwarding.d.ts +6 -0
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/challenging.d.ts +54 -0
- package/types/app/identity/challenging.d.ts.map +1 -1
- package/types/app/identity/delegating.d.ts +21 -4
- package/types/app/identity/delegating.d.ts.map +1 -1
- package/types/app/identity/delegation-approval.d.ts +228 -0
- package/types/app/identity/delegation-approval.d.ts.map +1 -0
- package/types/app/identity/event-completion.d.ts +151 -0
- package/types/app/identity/event-completion.d.ts.map +1 -0
- package/types/app/identity/habbing.d.ts +67 -2
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +49 -3
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/ipex/group-ipex.d.ts +247 -0
- package/types/app/ipex/group-ipex.d.ts.map +1 -0
- package/types/app/ipex/group-issuance.d.ts +281 -0
- package/types/app/ipex/group-issuance.d.ts.map +1 -0
- package/types/app/ipex/ipex-admitting.d.ts +47 -0
- package/types/app/ipex/ipex-admitting.d.ts.map +1 -0
- package/types/app/ipex/ipex-conversation.d.ts +195 -0
- package/types/app/ipex/ipex-conversation.d.ts.map +1 -0
- package/types/app/ipex/ipex-discloser.d.ts +156 -0
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -0
- package/types/app/ipex/ipex-granting.d.ts +145 -0
- package/types/app/ipex/ipex-granting.d.ts.map +1 -0
- package/types/app/ipex/ipex-grouping.d.ts +18 -0
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +443 -0
- package/types/app/ipex/issuance.d.ts.map +1 -0
- package/types/app/ipex/pending-presentations.d.ts +25 -0
- package/types/app/ipex/pending-presentations.d.ts.map +1 -0
- package/types/app/ipex/presentation-files.d.ts +47 -0
- package/types/app/ipex/presentation-files.d.ts.map +1 -0
- package/types/app/ipex/presenting.d.ts +298 -0
- package/types/app/ipex/presenting.d.ts.map +1 -0
- package/types/app/ipex/receive-issuance.d.ts +99 -0
- package/types/app/ipex/receive-issuance.d.ts.map +1 -0
- package/types/app/ipex/unblinding-workflow.d.ts +255 -0
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -0
- package/types/app/mailbox/ipex-arrival.d.ts +104 -0
- package/types/app/mailbox/ipex-arrival.d.ts.map +1 -0
- package/types/app/mailbox/kel-publisher.d.ts +141 -0
- package/types/app/mailbox/kel-publisher.d.ts.map +1 -0
- package/types/app/mailbox/kel-publishing.d.ts +70 -0
- package/types/app/mailbox/kel-publishing.d.ts.map +1 -0
- package/types/app/mailbox/kel-submission.d.ts +18 -0
- package/types/app/mailbox/kel-submission.d.ts.map +1 -0
- package/types/app/mailbox/mailbox-admin.d.ts +113 -0
- package/types/app/mailbox/mailbox-admin.d.ts.map +1 -0
- package/types/app/mailbox/mailbox-poller.d.ts +92 -3
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +3 -0
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +196 -0
- package/types/app/multisig/counseling.d.ts.map +1 -0
- package/types/app/multisig/group-members.d.ts +11 -0
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts +39 -20
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +91 -22
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/multisig/proposals.d.ts +129 -0
- package/types/app/multisig/proposals.d.ts.map +1 -0
- package/types/app/multisig/proposing.d.ts +125 -0
- package/types/app/multisig/proposing.d.ts.map +1 -0
- package/types/app/notifications/signaling.d.ts +3 -1
- package/types/app/notifications/signaling.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +13 -0
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +7 -1
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/queries/query-workflows.d.ts +98 -0
- package/types/app/queries/query-workflows.d.ts.map +1 -0
- package/types/app/runtime/agent-runtime.d.ts +72 -25
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/cue-runtime.d.ts +2 -1
- package/types/app/runtime/cue-runtime.d.ts.map +1 -1
- package/types/app/runtime/delivery-outbox.d.ts +68 -0
- package/types/app/runtime/delivery-outbox.d.ts.map +1 -0
- package/types/app/runtime/doing.d.ts +27 -0
- package/types/app/runtime/doing.d.ts.map +1 -0
- package/types/app/runtime/ipex-auxiliaries.d.ts +74 -0
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -0
- package/types/app/runtime/operation-owners.d.ts +137 -0
- package/types/app/runtime/operation-owners.d.ts.map +1 -0
- package/types/app/runtime/retry-backoff.d.ts +49 -0
- package/types/app/runtime/retry-backoff.d.ts.map +1 -0
- package/types/app/runtime/workflow-contract.d.ts +79 -0
- package/types/app/runtime/workflow-contract.d.ts.map +1 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/cli/common/event-completion.d.ts +26 -0
- package/types/cli/common/event-completion.d.ts.map +1 -0
- package/types/cli/common/runtime-workers.d.ts +23 -0
- package/types/cli/common/runtime-workers.d.ts.map +1 -0
- package/types/cli/credentials/ipex.d.ts.map +1 -1
- package/types/cli/credentials/vc.d.ts +1 -1
- package/types/cli/credentials/vc.d.ts.map +1 -1
- package/types/cli/endpoints/did.d.ts.map +1 -1
- package/types/cli/endpoints/oobi.d.ts +3 -1
- package/types/cli/endpoints/oobi.d.ts.map +1 -1
- package/types/cli/identity/challenge.d.ts +1 -1
- package/types/cli/identity/challenge.d.ts.map +1 -1
- package/types/cli/identity/delegate.d.ts +14 -1
- package/types/cli/identity/delegate.d.ts.map +1 -1
- package/types/cli/identity/incept.d.ts.map +1 -1
- package/types/cli/identity/interact.d.ts.map +1 -1
- package/types/cli/identity/rotate.d.ts.map +1 -1
- package/types/cli/index.d.ts +3 -1
- package/types/cli/index.d.ts.map +1 -1
- package/types/cli/messaging/group-join.d.ts +53 -0
- package/types/cli/messaging/group-join.d.ts.map +1 -0
- package/types/cli/messaging/group-wait.d.ts +34 -0
- package/types/cli/messaging/group-wait.d.ts.map +1 -0
- package/types/cli/messaging/multisig.d.ts +28 -5
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/cli/messaging/root-copy-lines.d.ts +39 -0
- package/types/cli/messaging/root-copy-lines.d.ts.map +1 -0
- package/types/core/cues.d.ts +11 -0
- package/types/core/cues.d.ts.map +1 -1
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts +14 -0
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kraming.d.ts +1 -1
- package/types/core/mailbox/kraming.d.ts.map +1 -1
- package/types/core/records.d.ts +61 -0
- package/types/core/records.d.ts.map +1 -1
- package/types/core/routing.d.ts +6 -0
- package/types/core/routing.d.ts.map +1 -1
- package/types/db/basing.d.ts +8 -2
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/indexeddb-persistence.d.ts +59 -0
- package/types/db/core/indexeddb-persistence.d.ts.map +1 -0
- package/types/db/indexeddb.d.ts +37 -0
- package/types/db/indexeddb.d.ts.map +1 -0
- package/types/db/keeping.d.ts +5 -2
- package/types/db/keeping.d.ts.map +1 -1
- package/types/db/store-lock.d.ts +72 -0
- package/types/db/store-lock.d.ts.map +1 -0
- package/types/npm/db-indexeddb.d.ts +4 -0
- package/types/npm/db-indexeddb.d.ts.map +1 -0
- package/types/npm/runtime-web.d.ts +4 -0
- package/types/npm/runtime-web.d.ts.map +1 -0
- package/types/runtime/index.d.ts +41 -6
- package/types/runtime/index.d.ts.map +1 -1
- package/types/runtime/web/output-barrier.d.ts +30 -0
- package/types/runtime/web/output-barrier.d.ts.map +1 -0
- package/types/runtime/web/persistent-storage.d.ts +36 -0
- package/types/runtime/web/persistent-storage.d.ts.map +1 -0
- package/types/runtime/web.d.ts +11 -0
- package/types/runtime/web.d.ts.map +1 -0
- package/types/vdr/tel-publication.d.ts +6 -0
- package/types/vdr/tel-publication.d.ts.map +1 -1
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/cli/common/tel-completion.js +0 -24
- package/types/cli/common/tel-completion.d.ts +0 -8
- package/types/cli/common/tel-completion.d.ts.map +0 -1
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Group-event Counselor: drives an accepted-or-pending group KEL event through
|
|
3
|
+
* member signatures and witness receipts, as KERIpy `app.grouping.Counselor`.
|
|
4
|
+
*
|
|
5
|
+
* Durable state is KERIpy's, row for row: `gpse` and `gpwe` hold `(pre) ->
|
|
6
|
+
* IoSet[(Number, Diger)]`, `gdee` holds delegated events awaiting the delegator
|
|
7
|
+
* and `cgms` holds `(pre, number.qb64) -> Diger` for completed events. The
|
|
8
|
+
* Counselor never signs; it only observes accepted state and, for witness
|
|
9
|
+
* receipts, asks the injected receipting collaborator to submit or fetch.
|
|
10
|
+
*
|
|
11
|
+
* Divergences from KERIpy, each deliberate:
|
|
12
|
+
* - An escrow row is removed by exact `(Number, Diger)` tuple. KERIpy removes
|
|
13
|
+
* every row of the prefix and never compares digests; here a different SAID
|
|
14
|
+
* accepted at the same sn drops only the stale tuple and reads `superseded`.
|
|
15
|
+
* - Receipts are judged against the event's own governing establishment, not
|
|
16
|
+
* the prefix's current key state.
|
|
17
|
+
* - "Must submit" derives from stored receipts and the election, not from an
|
|
18
|
+
* in-memory cue, so a restarted process resumes. Non-elected members fetch
|
|
19
|
+
* receipts and, after `fallbackAfterMs`, submit as the elected one would;
|
|
20
|
+
* KERIpy stalls when the elected member is offline.
|
|
21
|
+
* - A newer completion at the same `(pre, sn)` replaces the `cgms` digest.
|
|
22
|
+
* - A delegated `dip`/`drt` is accepted by its own delegate before the delegator's
|
|
23
|
+
* anchor and before witnesses receipt it (KERIpy `validateDelegation`: the
|
|
24
|
+
* delegatee accepts its own event to trigger receipting and approval). It
|
|
25
|
+
* waits in `gdee`. The publishing member (elected, or any member once the
|
|
26
|
+
* fallback grace passes) hands it to the Anchorer, which requests approval,
|
|
27
|
+
* awaits the anchor and publishes; the Counselor gates and observes, and
|
|
28
|
+
* writes `cgms` when the Anchorer completes. Any other member queries the
|
|
29
|
+
* delegator's witness for the anchor, pins `aess`, and continues in `gpwe`.
|
|
30
|
+
* Without an Anchorer such events stay in `gdee`, reported `notSupported`.
|
|
31
|
+
*
|
|
32
|
+
* Witness authorization codes, the delegation proxy and retry timers are process
|
|
33
|
+
* memory only, as in KERIpy and the Anchorer. Without a proxy the local group
|
|
34
|
+
* member sends delegation traffic, as KERIpy does for every group event, so a
|
|
35
|
+
* restart never leaves a delegated event waiting for one. Collaborators are
|
|
36
|
+
* narrow (ADR-0013); nothing here reaches a runtime.
|
|
37
|
+
*/
|
|
38
|
+
import { Diger, Ilks, NumberPrimitive, NumDex, Verfer } from "cesr-ts";
|
|
39
|
+
import { ValidationError } from "../../core/errors.js";
|
|
40
|
+
import { Kever } from "../../core/kel/kever.js";
|
|
41
|
+
import { dgKey } from "../../db/core/keys.js";
|
|
42
|
+
import { pinAuthorizingSeal, retryDelegatorWitnessQuery, } from "../identity/delegating.js";
|
|
43
|
+
import { observeUntil } from "../runtime/workflow-contract.js";
|
|
44
|
+
import { groupWitnesser, isLocalGroupHab, localGroupMember } from "./group-members.js";
|
|
45
|
+
/** Grace before a non-elected member submits receipts itself. */
|
|
46
|
+
export const DEFAULT_FALLBACK_AFTER_MS = 60_000;
|
|
47
|
+
/** First and longest wait between receipt attempts for one event. */
|
|
48
|
+
export const RECEIPT_BACKOFF_MS = { first: 1_000, max: 30_000 };
|
|
49
|
+
/** The production receipt retry delays as a policy; only `baseMs` and `maxMs` apply, the Counselor never gives up. */
|
|
50
|
+
export const DEFAULT_RECEIPT_RETRY = Object.freeze({
|
|
51
|
+
baseMs: RECEIPT_BACKOFF_MS.first,
|
|
52
|
+
maxMs: RECEIPT_BACKOFF_MS.max,
|
|
53
|
+
boundMs: 0,
|
|
54
|
+
});
|
|
55
|
+
/** Escrow tables in stage order. */
|
|
56
|
+
const STAGES = [["signatures", "gpse"], ["delegation", "gdee"], ["witnesses", "gpwe"]];
|
|
57
|
+
/** Receipt progress of one event against its own governing establishment. */
|
|
58
|
+
function receiptProgress(db, serder) {
|
|
59
|
+
let est = serder;
|
|
60
|
+
while (est && !est.estive)
|
|
61
|
+
est = est.prior ? db.getEvtSerder(est.pre, est.prior) : null;
|
|
62
|
+
if (!est)
|
|
63
|
+
return { have: 0, need: 0, missing: [] };
|
|
64
|
+
const witnesses = db.wits.get(dgKey(est.pre, est.said)).map(wit => wit.qb64);
|
|
65
|
+
const { indices } = Kever.verifyIndexedSignatures(serder.raw, db.wigs.get(dgKey(serder.pre, serder.said)), witnesses.map(qb64 => new Verfer({ qb64 })));
|
|
66
|
+
return {
|
|
67
|
+
have: indices.length,
|
|
68
|
+
need: Number(est.bner.num),
|
|
69
|
+
missing: witnesses.filter((_, index) => !indices.includes(index)),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Counsels group events through signature and witness stages. */
|
|
73
|
+
export class Counselor {
|
|
74
|
+
/**
|
|
75
|
+
* @param hby Borrowed identities and stores; the Counselor opens nothing.
|
|
76
|
+
* @param receipting Submits or fetches witness receipts for accepted events.
|
|
77
|
+
* @param clock Time source for fallback and retry; only `now` is used.
|
|
78
|
+
* @param changes Optional sink told `{kind:"group", id:"<pre>:<sn>"}` after commits.
|
|
79
|
+
* @param delegation Anchorer and query port for delegated events; omit for non-delegated hosts.
|
|
80
|
+
* @param mailboxes Told when one of the group's rotations completes; the member runtime's publisher then tells
|
|
81
|
+
* the group's mailboxes the new key state, and several members doing so is harmless.
|
|
82
|
+
*/
|
|
83
|
+
constructor(hby, receipting, clock, changes, delegation, mailboxes, receiptRetry = DEFAULT_RECEIPT_RETRY) {
|
|
84
|
+
Object.defineProperty(this, "hby", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
configurable: true,
|
|
87
|
+
writable: true,
|
|
88
|
+
value: hby
|
|
89
|
+
});
|
|
90
|
+
Object.defineProperty(this, "receipting", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
configurable: true,
|
|
93
|
+
writable: true,
|
|
94
|
+
value: receipting
|
|
95
|
+
});
|
|
96
|
+
Object.defineProperty(this, "clock", {
|
|
97
|
+
enumerable: true,
|
|
98
|
+
configurable: true,
|
|
99
|
+
writable: true,
|
|
100
|
+
value: clock
|
|
101
|
+
});
|
|
102
|
+
Object.defineProperty(this, "changes", {
|
|
103
|
+
enumerable: true,
|
|
104
|
+
configurable: true,
|
|
105
|
+
writable: true,
|
|
106
|
+
value: changes
|
|
107
|
+
});
|
|
108
|
+
Object.defineProperty(this, "delegation", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
configurable: true,
|
|
111
|
+
writable: true,
|
|
112
|
+
value: delegation
|
|
113
|
+
});
|
|
114
|
+
Object.defineProperty(this, "mailboxes", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: true,
|
|
117
|
+
writable: true,
|
|
118
|
+
value: mailboxes
|
|
119
|
+
});
|
|
120
|
+
Object.defineProperty(this, "receiptRetry", {
|
|
121
|
+
enumerable: true,
|
|
122
|
+
configurable: true,
|
|
123
|
+
writable: true,
|
|
124
|
+
value: receiptRetry
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(this, "runs", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
configurable: true,
|
|
129
|
+
writable: true,
|
|
130
|
+
value: new Map()
|
|
131
|
+
});
|
|
132
|
+
Object.defineProperty(this, "anchorQuery", {
|
|
133
|
+
enumerable: true,
|
|
134
|
+
configurable: true,
|
|
135
|
+
writable: true,
|
|
136
|
+
value: void 0
|
|
137
|
+
});
|
|
138
|
+
this.anchorQuery = delegation && { hby, querying: delegation.querying, retryPasses: new Map() };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Begin counseling an event. Idempotent and safe before local acceptance.
|
|
142
|
+
*
|
|
143
|
+
* @returns True when a new `gpse` row was added; false when the event is
|
|
144
|
+
* already counseled or complete, or another SAID holds that sn.
|
|
145
|
+
* @throws ValidationError when `pre` is not a local group or the ref is malformed.
|
|
146
|
+
*/
|
|
147
|
+
start(event, inputs = {}) {
|
|
148
|
+
const { pre, sn, said } = event;
|
|
149
|
+
if (!Number.isSafeInteger(sn) || sn < 0 || !said)
|
|
150
|
+
throw new ValidationError("Invalid group event reference.");
|
|
151
|
+
const hab = this.hby.habs.get(pre);
|
|
152
|
+
if (!hab || !isLocalGroupHab(this.hby, hab))
|
|
153
|
+
throw new ValidationError(`${pre} is not a local group.`);
|
|
154
|
+
const db = this.hby.db;
|
|
155
|
+
if (this.settled(event))
|
|
156
|
+
return false; // nothing to counsel, so nothing to retain
|
|
157
|
+
this.retain(event, inputs);
|
|
158
|
+
if (this.stageOf(event))
|
|
159
|
+
return false;
|
|
160
|
+
return db.gpse.add([pre], [NumberPrimitive.fromNum(BigInt(sn)), new Diger({ qb64: said })]);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Replace in-memory inputs of a counseled event, for example witness auths or
|
|
164
|
+
* a proxy after a restart, and re-arm any attempt waiting out its backoff.
|
|
165
|
+
* Ignored for an event that is already complete or superseded.
|
|
166
|
+
*/
|
|
167
|
+
supply(event, inputs) {
|
|
168
|
+
if (!this.settled(event))
|
|
169
|
+
this.retain(event, inputs);
|
|
170
|
+
}
|
|
171
|
+
/** Pure read of one event's state. */
|
|
172
|
+
status(event) {
|
|
173
|
+
const { pre, sn, said } = event;
|
|
174
|
+
const done = this.completed(pre, sn);
|
|
175
|
+
if (done !== null)
|
|
176
|
+
return done === said ? { state: "complete" } : { state: "superseded", accepted: done };
|
|
177
|
+
const accepted = this.hby.db.kels.getLast(pre, sn);
|
|
178
|
+
if (accepted && accepted !== said)
|
|
179
|
+
return { state: "superseded", accepted };
|
|
180
|
+
const stage = this.stageOf(event);
|
|
181
|
+
if (!stage)
|
|
182
|
+
return { state: "unknown" };
|
|
183
|
+
// A counseled event may not be stored locally yet; it is still pending, not unknown.
|
|
184
|
+
const serder = this.hby.db.getEvtSerder(pre, said);
|
|
185
|
+
if (!serder)
|
|
186
|
+
return { state: "pending", stage, elected: null, receipts: { have: 0, need: 0 } };
|
|
187
|
+
const { have, need, missing } = receiptProgress(this.hby.db, serder);
|
|
188
|
+
const elected = accepted ? groupWitnesser(this.hby, pre, { sn, said }) : null;
|
|
189
|
+
const status = { state: "pending", stage, elected, receipts: { have, need } };
|
|
190
|
+
const run = this.runs.get(this.runKey(event));
|
|
191
|
+
const failure = run?.failure ? this.receiptBlock(run, missing) : undefined;
|
|
192
|
+
if (stage === "delegation")
|
|
193
|
+
status.blocked = this.delegationBlock(event) ?? failure;
|
|
194
|
+
else if (stage === "witnesses")
|
|
195
|
+
status.blocked = failure;
|
|
196
|
+
if (!status.blocked)
|
|
197
|
+
delete status.blocked;
|
|
198
|
+
return status;
|
|
199
|
+
}
|
|
200
|
+
/** Why a receipt attempt failed: missing auths for unreceipted witnesses, else the witness's own reason. */
|
|
201
|
+
receiptBlock(run, missing) {
|
|
202
|
+
const unauthorized = missing.filter(witness => !(witness in run.auths));
|
|
203
|
+
return unauthorized.length
|
|
204
|
+
? { kind: "witnessAuth", witnesses: unauthorized }
|
|
205
|
+
: { kind: "witness", reason: run.failure };
|
|
206
|
+
}
|
|
207
|
+
/** What keeps a delegated event in `gdee`, if anything the caller can fix. */
|
|
208
|
+
delegationBlock(event) {
|
|
209
|
+
if (!this.delegation)
|
|
210
|
+
return { kind: "notSupported", feature: "delegation" };
|
|
211
|
+
const delpre = this.hby.db.getKever(event.pre)?.delpre;
|
|
212
|
+
if (delpre && !this.hby.db.getKever(delpre))
|
|
213
|
+
return { kind: "delegatorUnknown", delpre };
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
fallbackElapsed(run) {
|
|
217
|
+
return !!run && run.since !== null && this.clock.now() - run.since >= run.fallbackAfterMs;
|
|
218
|
+
}
|
|
219
|
+
/** True when the event is complete, or another SAID holds its sn. */
|
|
220
|
+
settled({ pre, sn, said }) {
|
|
221
|
+
const accepted = this.hby.db.kels.getLast(pre, sn);
|
|
222
|
+
return (!!accepted && accepted !== said) || this.completed(pre, sn) !== null;
|
|
223
|
+
}
|
|
224
|
+
/** Events still counseled for `pre`, or for every prefix, in stage order. */
|
|
225
|
+
pending(pre) {
|
|
226
|
+
const found = [];
|
|
227
|
+
for (const [stage, table] of STAGES) {
|
|
228
|
+
for (const [[key], [number, diger]] of this.hby.db[table].getTopItemIter(pre ?? "", { topive: !!pre })) {
|
|
229
|
+
found.push({ pre: key, sn: Number(number.num), said: diger.qb64, stage });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return found;
|
|
233
|
+
}
|
|
234
|
+
/** True once the exact event completed; drops its in-memory inputs. */
|
|
235
|
+
complete(event) {
|
|
236
|
+
const done = this.completed(event.pre, event.sn) === event.said;
|
|
237
|
+
if (done)
|
|
238
|
+
this.runs.delete(this.runKey(event));
|
|
239
|
+
return done;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Stop counseling `pre`: remove its escrow rows, completion markers and
|
|
243
|
+
* in-memory inputs. Accepted KEL state is untouched.
|
|
244
|
+
*/
|
|
245
|
+
abandon(pre) {
|
|
246
|
+
const db = this.hby.db;
|
|
247
|
+
const removed = new Set(this.pending(pre).map(event => event.sn));
|
|
248
|
+
for (const [[, number]] of db.cgms.getTopItemIter([pre], { topive: true })) {
|
|
249
|
+
removed.add(Number(new NumberPrimitive({ qb64: number }).num));
|
|
250
|
+
}
|
|
251
|
+
db.transaction(() => {
|
|
252
|
+
for (const [, table] of STAGES)
|
|
253
|
+
db[table].rem([pre]);
|
|
254
|
+
for (const [keys] of [...db.cgms.getTopItemIter([pre], { topive: true })])
|
|
255
|
+
db.cgms.rem(keys);
|
|
256
|
+
});
|
|
257
|
+
for (const key of [...this.runs.keys()])
|
|
258
|
+
if (key.startsWith(`${pre}:`))
|
|
259
|
+
this.runs.delete(key);
|
|
260
|
+
for (const sn of removed)
|
|
261
|
+
this.changes?.push({ kind: "group", id: `${pre}:${sn}` });
|
|
262
|
+
}
|
|
263
|
+
/** One pass over all stages, as KERIpy `processEscrows`. */
|
|
264
|
+
*processOnce() {
|
|
265
|
+
// A runtime calls this every turn; with nothing counseled, three row counts are the whole cost.
|
|
266
|
+
const db = this.hby.db;
|
|
267
|
+
if (db.gpse.cnt() === 0 && db.gdee.cnt() === 0 && db.gpwe.cnt() === 0)
|
|
268
|
+
return;
|
|
269
|
+
this.processSignatures();
|
|
270
|
+
yield* this.processDelegation();
|
|
271
|
+
yield* this.processWitnesses();
|
|
272
|
+
}
|
|
273
|
+
/** Move events accepted with their escrowed SAID on to delegation or witnesses. */
|
|
274
|
+
processSignatures() {
|
|
275
|
+
for (const [[pre], [number, diger]] of [...this.hby.db.gpse.getTopItemIter()]) {
|
|
276
|
+
const event = this.refOf(pre, number, diger);
|
|
277
|
+
if (this.dropIfSuperseded(this.hby.db.gpse, event, number, diger))
|
|
278
|
+
continue;
|
|
279
|
+
if (this.hby.db.kels.getLast(pre, event.sn) !== event.said)
|
|
280
|
+
continue;
|
|
281
|
+
const ilk = this.hby.db.getEvtSerder(pre, event.said)?.ilk;
|
|
282
|
+
const next = ilk === Ilks.dip || ilk === Ilks.drt ? this.hby.db.gdee : this.hby.db.gpwe;
|
|
283
|
+
this.hby.db.transaction(() => {
|
|
284
|
+
this.hby.db.gpse.rem([pre], [number, diger]);
|
|
285
|
+
next.add([pre], [number, diger]);
|
|
286
|
+
});
|
|
287
|
+
this.changes?.push({ kind: "group", id: `${pre}:${event.sn}` });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Delegated events: the publishing member drives the Anchorer, any other member
|
|
292
|
+
* watches for the delegator's anchor. Both wait for the delegator's KEL.
|
|
293
|
+
*/
|
|
294
|
+
*processDelegation() {
|
|
295
|
+
const db = this.hby.db;
|
|
296
|
+
for (const [[pre], [number, diger]] of [...db.gdee.getTopItemIter()]) {
|
|
297
|
+
const event = this.refOf(pre, number, diger);
|
|
298
|
+
if (this.dropIfSuperseded(db.gdee, event, number, diger))
|
|
299
|
+
continue;
|
|
300
|
+
const serder = db.getEvtSerder(pre, event.said);
|
|
301
|
+
const delpre = db.getKever(pre)?.delpre;
|
|
302
|
+
if (!this.delegation || !serder || !delpre || !db.getKever(delpre))
|
|
303
|
+
continue;
|
|
304
|
+
const run = this.retain(event, {});
|
|
305
|
+
run.since ??= this.clock.now();
|
|
306
|
+
const member = localGroupMember(this.hby, pre);
|
|
307
|
+
if (groupWitnesser(this.hby, pre, event) || this.fallbackElapsed(run)) {
|
|
308
|
+
yield* this.publishDelegation(event, serder, run, member, [number, diger]);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.watchAnchor(event, serder, run, member, delpre, [number, diger]);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/** Publishing duty: begin the Anchorer, keep receipts coming, and record completion. */
|
|
316
|
+
*publishDelegation(event, serder, run, member, row) {
|
|
317
|
+
const { anchorer } = this.delegation;
|
|
318
|
+
if (anchorer.complete(event.pre, event.sn)) {
|
|
319
|
+
this.finish(this.hby.db.gdee, event, row);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// The Anchorer asks the delegator only once the event's witnesses have receipted it.
|
|
323
|
+
if (!this.hby.db.fullyWitnessed(serder))
|
|
324
|
+
yield* this.pursueReceipts(event, serder, true);
|
|
325
|
+
if (!run.begun) {
|
|
326
|
+
anchorer.begin(serder, { communicationHab: run.proxy ?? member });
|
|
327
|
+
run.begun = true;
|
|
328
|
+
this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/** Non-publishing duty: find the delegator's sealing event, pin it, and move on to receipts. */
|
|
332
|
+
watchAnchor(event, serder, run, member, delpre, row) {
|
|
333
|
+
const db = this.hby.db;
|
|
334
|
+
retryDelegatorWitnessQuery(this.anchorQuery, serder, run.proxy ?? member);
|
|
335
|
+
const delegating = db.fetchLastSealingEventByEventSeal(delpre, { i: event.pre, s: serder.snh, d: event.said });
|
|
336
|
+
if (!delegating)
|
|
337
|
+
return;
|
|
338
|
+
pinAuthorizingSeal(this.hby, serder, delegating);
|
|
339
|
+
db.transaction(() => {
|
|
340
|
+
db.gdee.rem([event.pre], row);
|
|
341
|
+
db.gpwe.add([event.pre], row);
|
|
342
|
+
});
|
|
343
|
+
this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
|
|
344
|
+
}
|
|
345
|
+
/** Record completion of a counseled event and drop its row from `table`. */
|
|
346
|
+
finish(table, event, row) {
|
|
347
|
+
const db = this.hby.db;
|
|
348
|
+
db.transaction(() => {
|
|
349
|
+
table.rem([event.pre], row);
|
|
350
|
+
db.cgms.pin([event.pre, row[0].qb64], row[1]);
|
|
351
|
+
});
|
|
352
|
+
this.runs.delete(this.runKey(event));
|
|
353
|
+
this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
|
|
354
|
+
const ilk = db.getEvtSerder(event.pre, event.said)?.ilk;
|
|
355
|
+
if (ilk === Ilks.rot || ilk === Ilks.drt) {
|
|
356
|
+
this.mailboxes?.rotated({ kin: "rotated", pre: event.pre, sn: event.sn, said: event.said });
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/** Complete events whose own witness threshold is met; otherwise submit or fetch receipts. */
|
|
360
|
+
*processWitnesses() {
|
|
361
|
+
const db = this.hby.db;
|
|
362
|
+
for (const [[pre], [number, diger]] of [...db.gpwe.getTopItemIter()]) {
|
|
363
|
+
const event = this.refOf(pre, number, diger);
|
|
364
|
+
if (this.dropIfSuperseded(db.gpwe, event, number, diger))
|
|
365
|
+
continue;
|
|
366
|
+
const serder = db.getEvtSerder(pre, event.said);
|
|
367
|
+
if (!serder)
|
|
368
|
+
continue;
|
|
369
|
+
if (!db.fullyWitnessed(serder)) {
|
|
370
|
+
yield* this.pursueReceipts(event, serder);
|
|
371
|
+
if (!db.fullyWitnessed(serder))
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
this.finish(db.gpwe, event, [number, diger]);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/** One backed-off receipt attempt: submit when forced, elected or past the grace, else fetch. */
|
|
378
|
+
*pursueReceipts(event, serder, forceSubmit = false) {
|
|
379
|
+
const run = this.retain(event, {});
|
|
380
|
+
const now = this.clock.now();
|
|
381
|
+
run.since ??= now;
|
|
382
|
+
if (now < run.nextAt)
|
|
383
|
+
return;
|
|
384
|
+
const submit = forceSubmit || groupWitnesser(this.hby, event.pre, event) || now - run.since >= run.fallbackAfterMs;
|
|
385
|
+
try {
|
|
386
|
+
if (submit)
|
|
387
|
+
yield* this.receipting.receipt(event.pre, { sn: event.sn, auths: run.auths });
|
|
388
|
+
else
|
|
389
|
+
yield* this.receipting.get(event.pre, { sn: event.sn, said: event.said });
|
|
390
|
+
run.failure = null;
|
|
391
|
+
run.delay = this.receiptRetry.baseMs;
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
// A witness refusing or being unreachable is a remote outcome: report it in status and retry later.
|
|
395
|
+
run.failure = error instanceof Error ? error.message : String(error);
|
|
396
|
+
run.delay = Math.min(run.delay * 2, this.receiptRetry.maxMs);
|
|
397
|
+
}
|
|
398
|
+
run.nextAt = now + run.delay;
|
|
399
|
+
if (!this.hby.db.fullyWitnessed(serder))
|
|
400
|
+
this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
|
|
401
|
+
}
|
|
402
|
+
/** Remove a stale tuple when another SAID holds its sn; true when removed. */
|
|
403
|
+
dropIfSuperseded(table, event, number, diger) {
|
|
404
|
+
const accepted = this.hby.db.kels.getLast(event.pre, event.sn);
|
|
405
|
+
if (!accepted || accepted === event.said)
|
|
406
|
+
return false;
|
|
407
|
+
table.rem([event.pre], [number, diger]);
|
|
408
|
+
this.runs.delete(this.runKey(event));
|
|
409
|
+
this.changes?.push({ kind: "group", id: `${event.pre}:${event.sn}` });
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
/** Digest of the completed event at `(pre, sn)`, under either number width KERIpy writes. */
|
|
413
|
+
completed(pre, sn) {
|
|
414
|
+
const cgms = this.hby.db.cgms;
|
|
415
|
+
const auto = NumberPrimitive.fromNum(BigInt(sn));
|
|
416
|
+
const huge = new NumberPrimitive({ code: NumDex.Huge, raw: hugeRaw(sn) });
|
|
417
|
+
return (cgms.get([pre, auto.qb64]) ?? cgms.get([pre, huge.qb64]))?.qb64 ?? null;
|
|
418
|
+
}
|
|
419
|
+
/** Stage whose escrow holds exactly this event, if any. */
|
|
420
|
+
stageOf({ pre, sn, said }) {
|
|
421
|
+
for (const [stage, table] of STAGES) {
|
|
422
|
+
for (const [number, diger] of this.hby.db[table].get([pre])) {
|
|
423
|
+
if (Number(number.num) === sn && diger.qb64 === said)
|
|
424
|
+
return stage;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
refOf(pre, number, diger) {
|
|
430
|
+
return { pre, sn: Number(number.num), said: diger.qb64 };
|
|
431
|
+
}
|
|
432
|
+
runKey({ pre, sn, said }) {
|
|
433
|
+
return `${pre}:${sn}:${said}`;
|
|
434
|
+
}
|
|
435
|
+
/** Get or create the in-memory run for an event, applying supplied inputs. */
|
|
436
|
+
retain(event, { auths, fallbackAfterMs, proxy }) {
|
|
437
|
+
if (fallbackAfterMs !== undefined && !(fallbackAfterMs >= 0 && Number.isFinite(fallbackAfterMs))) {
|
|
438
|
+
throw new ValidationError("fallbackAfterMs must be a finite nonnegative duration.");
|
|
439
|
+
}
|
|
440
|
+
const key = this.runKey(event);
|
|
441
|
+
const run = this.runs.get(key)
|
|
442
|
+
?? {
|
|
443
|
+
auths: {},
|
|
444
|
+
fallbackAfterMs: DEFAULT_FALLBACK_AFTER_MS,
|
|
445
|
+
since: null,
|
|
446
|
+
nextAt: 0,
|
|
447
|
+
delay: this.receiptRetry.baseMs,
|
|
448
|
+
failure: null,
|
|
449
|
+
proxy: null,
|
|
450
|
+
begun: false,
|
|
451
|
+
};
|
|
452
|
+
if (proxy) {
|
|
453
|
+
run.proxy = proxy;
|
|
454
|
+
run.begun = false; // the Anchorer pins its sender per begin, so re-pin
|
|
455
|
+
}
|
|
456
|
+
if (auths) {
|
|
457
|
+
run.auths = { ...auths };
|
|
458
|
+
run.nextAt = 0; // New credentials re-arm an attempt that was waiting out its backoff.
|
|
459
|
+
}
|
|
460
|
+
if (fallbackAfterMs !== undefined)
|
|
461
|
+
run.fallbackAfterMs = fallbackAfterMs;
|
|
462
|
+
this.runs.set(key, run);
|
|
463
|
+
return run;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/** Big-endian 16-byte raw for the `0A` (Huge) number code. */
|
|
467
|
+
function hugeRaw(sn) {
|
|
468
|
+
const raw = new Uint8Array(16);
|
|
469
|
+
for (let at = 15, value = BigInt(sn); at >= 0; at--, value >>= 8n)
|
|
470
|
+
raw[at] = Number(value & 0xffn);
|
|
471
|
+
return raw;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Observe `event` until it is no longer pending or the deadline passes. Starts
|
|
475
|
+
* no worker and makes no receipt call; progress comes from `processOnce`
|
|
476
|
+
* running elsewhere.
|
|
477
|
+
*/
|
|
478
|
+
export function* waitForGroupEvent(counselor, event, { timeoutMs, clock }) {
|
|
479
|
+
const done = yield* observeUntil(() => counselor.status(event).state !== "pending", { timeoutMs, clock });
|
|
480
|
+
const status = counselor.status(event);
|
|
481
|
+
return done ? { outcome: "settled", status } : { outcome: "timed-out", status, timeoutMs };
|
|
482
|
+
}
|
|
@@ -78,6 +78,24 @@ export function groupElectedMember(hby, group, coordinator) {
|
|
|
78
78
|
return null;
|
|
79
79
|
return groupSigningMembers(hby, group).find(member => latestGroupContribution(hby.db, member, selection.establishment.verfers)?.index === selection.index) ?? null;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* True when the local member is the witnesser for the exact accepted group event
|
|
83
|
+
* `coordinator`, as KERIpy `_groupWitnesser`: the member's latest contribution to
|
|
84
|
+
* the governing establishment's keys holds the lowest verified signing index.
|
|
85
|
+
*
|
|
86
|
+
* Unlike `groupElectedMember` this does not require that member to be a current
|
|
87
|
+
* signing member, so a historical event keeps its witnesser after a later
|
|
88
|
+
* rotation. Like it, the answer depends on locally stored signatures and suits
|
|
89
|
+
* idempotent duties only.
|
|
90
|
+
*/
|
|
91
|
+
export function groupWitnesser(hby, group, coordinator) {
|
|
92
|
+
const selection = groupWitnesserSelection(hby.db, group, coordinator);
|
|
93
|
+
if (!selection)
|
|
94
|
+
return false;
|
|
95
|
+
const member = localGroupMember(hby, group).pre;
|
|
96
|
+
const contribution = member ? latestGroupContribution(hby.db, member, selection.establishment.verfers) : null;
|
|
97
|
+
return contribution?.index === selection.index;
|
|
98
|
+
}
|
|
81
99
|
/** Return current group signing member AIDs in signing-index order. */
|
|
82
100
|
export function groupSigningMembers(hby, groupPre) {
|
|
83
101
|
const stored = hby.ks.getSmids(groupPre).map((tuple) => tuple[0].qb64);
|