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,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-owned operations for a local group's v2 IPEX messages (first user: a group holder's Admit).
|
|
3
|
+
*
|
|
4
|
+
* A group-authored IPEX message completes only when enough members have contributed a signature, and each member's
|
|
5
|
+
* signature is that member's consent. The runtime owns the rest. This module follows the workflow contract
|
|
6
|
+
* (`app/runtime/workflow-contract.ts`):
|
|
7
|
+
*
|
|
8
|
+
* - {@link proposeGroupAdmit} / {@link proposeGroupIpex} (the proposer) and {@link approveGroupIpex} (every other
|
|
9
|
+
* member) are the explicit consents. Each signs or reuses this member's contribution, accepts it locally, records
|
|
10
|
+
* a live operation and returns without waiting on anyone. {@link groupIpexProposals} lists the stored proposals a
|
|
11
|
+
* member may approve.
|
|
12
|
+
* - `processOnce`, driven by the runtime's owner loop, publishes this member's wrapper to the other members (retrying
|
|
13
|
+
* a member that is briefly offline), collects the other members' stored contributions, and, once the message is
|
|
14
|
+
* accepted, lets the elected lead send the completed message to its recipient. It signs nothing beyond the
|
|
15
|
+
* contribution that the consent recorded.
|
|
16
|
+
* - {@link groupIpexStatus} is a pure read and {@link waitForGroupIpex} only observes.
|
|
17
|
+
*
|
|
18
|
+
* Status is derived from the protocol stores: an accepted message reads `done`; a stored proposal without a live
|
|
19
|
+
* operation reads `awaitingConsent` (a member that reloaded consents again with the same call and reuses its
|
|
20
|
+
* recorded signature). Lost on reload, by design: whether the lead had already delivered the completed message,
|
|
21
|
+
* the `failed` and `abandoned` reasons, and the publication results. The runtime never writes wallet inventory; the
|
|
22
|
+
* embedder reacts to the pushed `{kind: "ipex", id: <message>}` change.
|
|
23
|
+
*/
|
|
24
|
+
import { concatBytes, Counter, Vrsn_2_0 } from "cesr-ts";
|
|
25
|
+
import { ValidationError } from "../../core/errors.js";
|
|
26
|
+
import { registryEventStatus, settleRegistryEvent } from "../credentials/registry-settling.js";
|
|
27
|
+
import { isLocalGroupHab } from "../multisig/group-members.js";
|
|
28
|
+
import { MULTISIG_EXN_ROUTE } from "../multisig/grouping.js";
|
|
29
|
+
import { RetryBackoff } from "../runtime/retry-backoff.js";
|
|
30
|
+
import { observeUntil } from "../runtime/workflow-contract.js";
|
|
31
|
+
import { IpexAdmitter } from "./ipex-admitting.js";
|
|
32
|
+
import { approveMultisigIpexLocally, approveStoredMultisigIpexWrappers, findStoredMultisigIpex, groupIpexApproved, prepareAcdcV2GroupIpex, publishGroupIpexProposal, publishInitialGroupIpex, requireAcdcV2GroupUse, sendCompletedGroupIpex, storedMultisigIpexBody, } from "./ipex-grouping.js";
|
|
33
|
+
/** Milliseconds between collections of the other members' stored contributions. */
|
|
34
|
+
export const GROUP_IPEX_COLLECT_MS = 250;
|
|
35
|
+
/** How long delivery failures may last before an operation ends `failed`; a group IPEX message has no deadline. */
|
|
36
|
+
export const GROUP_IPEX_RETRY_BOUND_MS = 600_000;
|
|
37
|
+
function message(error) {
|
|
38
|
+
return error instanceof Error ? error.message : String(error);
|
|
39
|
+
}
|
|
40
|
+
/** The live table of group IPEX operations and its driver; one per host runtime. */
|
|
41
|
+
export class GroupIpexOperations {
|
|
42
|
+
constructor(deps) {
|
|
43
|
+
Object.defineProperty(this, "deps", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
writable: true,
|
|
47
|
+
value: deps
|
|
48
|
+
});
|
|
49
|
+
Object.defineProperty(this, "live", {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
configurable: true,
|
|
52
|
+
writable: true,
|
|
53
|
+
value: new Map()
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(this, "ended", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
configurable: true,
|
|
58
|
+
writable: true,
|
|
59
|
+
value: new Map()
|
|
60
|
+
});
|
|
61
|
+
/** What accepted operations published and delivered in this process; not durable. */
|
|
62
|
+
Object.defineProperty(this, "outcomes", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: new Map()
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Whether a pass has work. */
|
|
70
|
+
active() {
|
|
71
|
+
return this.live.size > 0;
|
|
72
|
+
}
|
|
73
|
+
/** Advance every live operation by one pass. */
|
|
74
|
+
*processOnce() {
|
|
75
|
+
for (const op of [...this.live.values()]) {
|
|
76
|
+
if (op.busy || !this.live.has(op.said))
|
|
77
|
+
continue;
|
|
78
|
+
op.busy = true;
|
|
79
|
+
try {
|
|
80
|
+
yield* this.step(op);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
this.end(op, "failed", message(error));
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
op.busy = false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** End a live operation and tell the sink. */
|
|
91
|
+
end(op, state, reason) {
|
|
92
|
+
this.live.delete(op.said);
|
|
93
|
+
this.ended.set(op.said, { state, reason });
|
|
94
|
+
this.deps.changes?.push({ kind: "ipex", id: op.said });
|
|
95
|
+
}
|
|
96
|
+
/** Record an operation the user consented to. */
|
|
97
|
+
begin(said, group, genus, outbound, deliver = true, prerequisite = null) {
|
|
98
|
+
this.ended.delete(said);
|
|
99
|
+
this.live.set(said, {
|
|
100
|
+
said,
|
|
101
|
+
group,
|
|
102
|
+
genus,
|
|
103
|
+
phase: prerequisite ? "awaitingRegistryUpdate" : outbound ? "publishing" : "awaitingMembers",
|
|
104
|
+
outbound,
|
|
105
|
+
prerequisite,
|
|
106
|
+
publication: null,
|
|
107
|
+
delivered: !deliver,
|
|
108
|
+
deliveries: [],
|
|
109
|
+
retry: new RetryBackoff(),
|
|
110
|
+
nextCollect: 0,
|
|
111
|
+
busy: false,
|
|
112
|
+
});
|
|
113
|
+
this.deps.changes?.push({ kind: "ipex", id: said });
|
|
114
|
+
return { said };
|
|
115
|
+
}
|
|
116
|
+
setPhase(op, phase) {
|
|
117
|
+
if (op.phase === phase)
|
|
118
|
+
return;
|
|
119
|
+
op.phase = phase;
|
|
120
|
+
this.deps.changes?.push({ kind: "ipex", id: op.said });
|
|
121
|
+
}
|
|
122
|
+
/** Run a delivery; a failure keeps the work for a later pass until failures have lasted the bound. */
|
|
123
|
+
*attempt(op, send) {
|
|
124
|
+
const now = this.deps.clock.now();
|
|
125
|
+
if (!op.retry.due(now))
|
|
126
|
+
return false;
|
|
127
|
+
try {
|
|
128
|
+
yield* send();
|
|
129
|
+
op.retry.succeeded();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
op.retry.failed(now, error);
|
|
134
|
+
if (op.retry.exhausted(now, GROUP_IPEX_RETRY_BOUND_MS)) {
|
|
135
|
+
throw new ValidationError(`Delivery kept failing: ${message(error)}`);
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
*step(op) {
|
|
141
|
+
const { hby, protocol, postman, exc, rgy, clock } = this.deps;
|
|
142
|
+
if (op.prerequisite) {
|
|
143
|
+
const read = { db: hby.db, store: rgy.store, protocol };
|
|
144
|
+
const { anchor, proceed } = op.prerequisite;
|
|
145
|
+
if (registryEventStatus(read, anchor).state !== "accepted") {
|
|
146
|
+
if ((yield* settleRegistryEvent(read, anchor)).state !== "accepted")
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
op.outbound = { kind: "initial", wire: yield* proceed() };
|
|
150
|
+
op.prerequisite = null;
|
|
151
|
+
this.setPhase(op, "publishing");
|
|
152
|
+
}
|
|
153
|
+
if (op.outbound) {
|
|
154
|
+
const { kind, wire } = op.outbound;
|
|
155
|
+
const sent = yield* this.attempt(op, function* () {
|
|
156
|
+
op.publication = kind === "initial"
|
|
157
|
+
? yield* publishInitialGroupIpex(hby, protocol, postman, op.group, wire, op.genus)
|
|
158
|
+
: yield* publishGroupIpexProposal(hby, protocol, postman, op.group, wire, op.genus);
|
|
159
|
+
});
|
|
160
|
+
if (!sent)
|
|
161
|
+
return;
|
|
162
|
+
op.outbound = null;
|
|
163
|
+
this.setPhase(op, "awaitingMembers");
|
|
164
|
+
}
|
|
165
|
+
if (!exc.complete(op.said) && clock.now() >= op.nextCollect) {
|
|
166
|
+
op.nextCollect = clock.now() + GROUP_IPEX_COLLECT_MS;
|
|
167
|
+
// This operation exists only because this member consented, so collecting reuses its recorded contribution.
|
|
168
|
+
yield* approveStoredMultisigIpexWrappers(hby, protocol, op.said, null);
|
|
169
|
+
}
|
|
170
|
+
if (!exc.complete(op.said))
|
|
171
|
+
return;
|
|
172
|
+
const embedded = hby.db.exns.get([op.said]);
|
|
173
|
+
requireAcdcV2GroupUse(hby, protocol, embedded);
|
|
174
|
+
if (!op.delivered) {
|
|
175
|
+
if (exc.lead(op.group, op.said)) {
|
|
176
|
+
this.setPhase(op, "delivering");
|
|
177
|
+
const sent = yield* this.attempt(op, function* () {
|
|
178
|
+
op.deliveries = yield* sendCompletedGroupIpex(hby, postman, null, op.group, embedded, op.genus, rgy.store, exc);
|
|
179
|
+
});
|
|
180
|
+
if (!sent)
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
op.delivered = true;
|
|
184
|
+
}
|
|
185
|
+
this.live.delete(op.said);
|
|
186
|
+
this.outcomes.set(op.said, {
|
|
187
|
+
messages: op.publication?.messages ?? [],
|
|
188
|
+
deliveries: op.publication?.deliveries ?? [],
|
|
189
|
+
sent: op.deliveries,
|
|
190
|
+
});
|
|
191
|
+
this.deps.changes?.push({ kind: "ipex", id: op.said });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The proposer's consent to a group-authored v2 IPEX message: signs this member's contribution to the exact
|
|
196
|
+
* `exn` body, accepts it locally and records the operation. Publishing the wrapper to the other members is
|
|
197
|
+
* `processOnce`.
|
|
198
|
+
*
|
|
199
|
+
* @throws ValidationError for a body the group did not author, a non-group identifier, a message awaiting
|
|
200
|
+
* authenticating dependencies, or one already begun.
|
|
201
|
+
*/
|
|
202
|
+
export function* proposeGroupIpex(ops, { hab, exn, nests = new Uint8Array(), gvrsn = Vrsn_2_0, deliver = true }) {
|
|
203
|
+
const { hby, protocol } = ops.deps;
|
|
204
|
+
if (!isLocalGroupHab(hby, hab))
|
|
205
|
+
throw new ValidationError("Group IPEX requires a local group identifier");
|
|
206
|
+
if (exn.pre !== hab.pre || !exn.said)
|
|
207
|
+
throw new ValidationError("The IPEX message must be authored by the group");
|
|
208
|
+
if (ops.live.has(exn.said))
|
|
209
|
+
throw new ValidationError("A group IPEX message was already begun for this SAID");
|
|
210
|
+
const candidate = concatBytes(Counter.makeGVC(gvrsn), exn.raw, Counter.enclose({ qb64: nests, version: gvrsn }));
|
|
211
|
+
const own = yield* prepareAcdcV2GroupIpex(hby, protocol, hab, candidate);
|
|
212
|
+
if (!own)
|
|
213
|
+
throw new ValidationError("ACDC v2 group disclosure awaits authenticating dependencies.");
|
|
214
|
+
return ops.begin(exn.said, hab, gvrsn, { kind: "initial", wire: own }, deliver);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The proposer's consent to a group-authored message that waits for a registry update, as a Grant carrying a
|
|
218
|
+
* presentation proof does: the caller has proposed the update (this member's contribution is its approval) and
|
|
219
|
+
* passes the anchor to wait for. The owner signs the message's contribution, through `prerequisite.proceed`, once
|
|
220
|
+
* the group has accepted the update, under this same consent. Returns after local commit.
|
|
221
|
+
*/
|
|
222
|
+
export function proposeGroupIpexAfter(ops, { hab, exn, gvrsn = Vrsn_2_0, deliver = true, prerequisite }) {
|
|
223
|
+
if (!isLocalGroupHab(ops.deps.hby, hab))
|
|
224
|
+
throw new ValidationError("Group IPEX requires a local group identifier");
|
|
225
|
+
if (exn.pre !== hab.pre || !exn.said)
|
|
226
|
+
throw new ValidationError("The IPEX message must be authored by the group");
|
|
227
|
+
if (ops.live.has(exn.said))
|
|
228
|
+
throw new ValidationError("A group IPEX message was already begun for this SAID");
|
|
229
|
+
return ops.begin(exn.said, hab, gvrsn, null, deliver, prerequisite);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* A group holder's consent to admit an accepted Grant: freezes the Admit body (explicit consent, exact prior
|
|
233
|
+
* authentication and current root truth), then proposes it as {@link proposeGroupIpex} does. The other members list
|
|
234
|
+
* the proposal and approve it; nothing here records inventory.
|
|
235
|
+
*/
|
|
236
|
+
export function* proposeGroupAdmit(ops, { hab, grant, message = "", gvrsn = Vrsn_2_0, deliver }) {
|
|
237
|
+
const { hby, rgy, exc } = ops.deps;
|
|
238
|
+
if (!isLocalGroupHab(hby, hab))
|
|
239
|
+
throw new ValidationError("Group IPEX requires a local group identifier");
|
|
240
|
+
const admit = yield* new IpexAdmitter(hby, rgy.store, exc).prepareAdmitBody(grant, hab.pre, {
|
|
241
|
+
consent: true,
|
|
242
|
+
message,
|
|
243
|
+
gvrsn,
|
|
244
|
+
});
|
|
245
|
+
return { ...(yield* proposeGroupIpex(ops, { hab, exn: admit, gvrsn, deliver })), grant };
|
|
246
|
+
}
|
|
247
|
+
/** Stored v2 IPEX proposals of `hab` (or of every local group) that were proposed by a member, newest unordered. */
|
|
248
|
+
export function groupIpexProposals(ops, hab) {
|
|
249
|
+
const { hby, exc } = ops.deps, found = [];
|
|
250
|
+
for (const [, wrapper] of hby.db.exns.getTopItemIter()) {
|
|
251
|
+
if (wrapper.route !== MULTISIG_EXN_ROUTE || wrapper.pvrsn.major !== 2)
|
|
252
|
+
continue;
|
|
253
|
+
const embedded = storedMultisigIpexBody(hby.db, wrapper);
|
|
254
|
+
const group = embedded?.pre;
|
|
255
|
+
if (!embedded?.route?.startsWith("/ipex/") || !group || (hab && group !== hab.pre)
|
|
256
|
+
|| !hby.habs.get(group) || !isLocalGroupHab(hby, hby.habs.get(group)))
|
|
257
|
+
continue;
|
|
258
|
+
if (found.some((item) => item.said === embedded.said))
|
|
259
|
+
continue;
|
|
260
|
+
found.push({
|
|
261
|
+
said: embedded.said,
|
|
262
|
+
route: embedded.route,
|
|
263
|
+
group,
|
|
264
|
+
wrapper: wrapper.said,
|
|
265
|
+
proposer: wrapper.pre,
|
|
266
|
+
...(typeof embedded.sad?.p === "string" && embedded.sad.p ? { prior: embedded.sad.p } : {}),
|
|
267
|
+
approved: groupIpexApproved(hby, group, embedded),
|
|
268
|
+
complete: exc.complete(embedded.said),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return found;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* A member's consent to a stored group IPEX proposal: verifies the exact stored wrapper, signs or reuses this
|
|
275
|
+
* member's contribution and accepts it locally, and records the operation. Publishing it, collecting the other
|
|
276
|
+
* members' contributions and, for the lead, delivering the completed message are `processOnce`.
|
|
277
|
+
*
|
|
278
|
+
* An accepted message may be approved again: the recorded contribution is reused, and the elected lead delivers the
|
|
279
|
+
* completed message, which a lead that learned of the acceptance from another member's wrapper has not yet done.
|
|
280
|
+
*
|
|
281
|
+
* @throws ValidationError when no v2 proposal is stored for `said`, an operation is already live for it, or this
|
|
282
|
+
* member still lacks the evidence to approve it.
|
|
283
|
+
*/
|
|
284
|
+
export function* approveGroupIpex(ops, said, { deliver = true } = {}) {
|
|
285
|
+
const { hby, protocol } = ops.deps;
|
|
286
|
+
if (ops.live.has(said))
|
|
287
|
+
throw new ValidationError("A group IPEX message was already begun for this SAID");
|
|
288
|
+
const wrapper = findStoredMultisigIpex(hby.db, said);
|
|
289
|
+
if (!wrapper || wrapper.pvrsn.major !== 2)
|
|
290
|
+
throw new ValidationError("No stored group IPEX proposal for this SAID");
|
|
291
|
+
const { groupHab, genus, approved } = yield* approveMultisigIpexLocally(hby, protocol, null, wrapper);
|
|
292
|
+
if (!approved)
|
|
293
|
+
throw new ValidationError("This member lacks the evidence to approve the proposal yet");
|
|
294
|
+
return ops.begin(said, groupHab, genus, { kind: "proposal", wire: approved }, deliver);
|
|
295
|
+
}
|
|
296
|
+
/** Local abandon: stop publishing and collecting. Does not undo a recorded contribution or message the peers. */
|
|
297
|
+
export function abandonGroupIpex(ops, said, reason = "Abandoned by the user") {
|
|
298
|
+
const live = ops.live.get(said);
|
|
299
|
+
if (live)
|
|
300
|
+
ops.end(live, "abandoned", reason);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Pure read of one group IPEX message: what ended in this process, the live table, and otherwise what the stores
|
|
304
|
+
* prove (see the module doc).
|
|
305
|
+
*
|
|
306
|
+
* @throws ValidationError when no stored proposal or accepted message of a local group has this SAID.
|
|
307
|
+
*/
|
|
308
|
+
export function groupIpexStatus(ops, said) {
|
|
309
|
+
const ended = ops.ended.get(said), live = ops.live.get(said);
|
|
310
|
+
if (ended)
|
|
311
|
+
return { state: ended.state, reason: ended.reason };
|
|
312
|
+
if (live)
|
|
313
|
+
return { state: "active", phase: live.phase };
|
|
314
|
+
const { hby, exc } = ops.deps;
|
|
315
|
+
if (exc.complete(said))
|
|
316
|
+
return { state: "done", phase: "accepted" };
|
|
317
|
+
if (!findStoredMultisigIpex(hby.db, said)) {
|
|
318
|
+
throw new ValidationError("No group IPEX proposal is stored for this SAID");
|
|
319
|
+
}
|
|
320
|
+
return { state: "active", phase: "awaitingConsent", needs: "approve" };
|
|
321
|
+
}
|
|
322
|
+
/** Observe until the message is accepted and delivered, or ended, or the budget expires. Starts no worker. */
|
|
323
|
+
export function* waitForGroupIpex(ops, said, { timeoutMs, clock = ops.deps.clock, until = (status) => status.state !== "active" }) {
|
|
324
|
+
const settled = yield* observeUntil(() => until(groupIpexStatus(ops, said)), { timeoutMs, clock });
|
|
325
|
+
const status = groupIpexStatus(ops, said);
|
|
326
|
+
return settled ? { outcome: "settled", status } : { outcome: "timed-out", status, timeoutMs };
|
|
327
|
+
}
|